经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
Vue3.3 的新功能的一些体验
来源:cnblogs  作者:金色海洋(jyk)  时间:2023/5/17 8:52:46  对本文有异议

Vue3 在大版本 3.3 里面推出来了一些新功能(主要是语法糖),网上有各种文章,但是看起来似乎是一样的。
我觉得吧,有新特性了,不能光看,还要动手尝试一下。

DefineOptions 宏定义

先来一个简单的,以前我们有时候想设个name,有时候不想让组件自动继承属性,这时候需要单独设置一个script进行设置,现在简化了操作,直接使用 defineOptions 即可。

  1. <script setup lang="ts">
  2. defineOptions({
  3. name: 'Foo',
  4. inheritAttrs: false,
  5. // ... 更多自定义属性
  6. })
  7. </script>

defineModel

defineModel 这是一个语法糖,目前需要手动开启,否则无法识别。

  1. import { defineConfig } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. // https://vitejs.dev/config/
  4. export default defineConfig({
  5. plugins: [vue({
  6. script: {
  7. defineModel: true,
  8. propsDestructure: true // 解构 props
  9. }
  10. })],
  11. })

有人嫌弃 组件内部 v-model 的实现方式有点繁琐,所以就做了这个语法糖给大家减少代码量,我们也来体验一下。

  1. const modelValue = defineModel()
  2. console.log(modelValue)

我们看看 的结构

  1. {__v_isRef: true}
  2. value: (...)
  3. __v_isRef: true
  4. get value: ? value()
  5. set value: ? value(value)

只是一个普通的对象看不出来有什么名堂,我们来看一下内部的实现方式:

  1. function useModel(props, name, options) {
  2. const i = getCurrentInstance();
  3. if (process.env.NODE_ENV !== "production" && !i) {
  4. warn(`useModel() called without active instance.`);
  5. return ref();
  6. }
  7. if (process.env.NODE_ENV !== "production" && !i.propsOptions[0][name]) {
  8. warn(`useModel() called with prop "${name}" which is not declared.`);
  9. return ref();
  10. }
  11. if (options && options.local) {
  12. const proxy = ref(props[name]);
  13. watch(
  14. () => props[name], // 监听外部组件的值的变化
  15. (v) => proxy.value = v // 赋值给内部属性
  16. );
  17. watch(proxy, (value) => { // 监听内部属性的变化
  18. if (value !== props[name]) {
  19. i.emit(`update:${name}`, value); // 提交给外部组件
  20. }
  21. });
  22. return proxy;
  23. } else {
  24. return {
  25. __v_isRef: true,
  26. get value() {
  27. return props[name]; // 返回外部组件的值
  28. },
  29. set value(value) {
  30. i.emit(`update:${name}`, value); // 内部组件赋值,提交给外部组件
  31. }
  32. };
  33. }
  34. }

前面各种判断,然后option模式下返回一个 ref,setup 模式下返回一个对象。取值的时候,返回 props[name]

Props 的响应式解构

我个人是不喜欢解构的,直接使用不香吗?其实vue表面上不让我们用,其实内部悄悄的在用,比如上面那个useModel 不就是嘛。

这个也是实验性的,想要体验需要手动设置,设置方法在上面。

  1. const { name } = defineProps<{ name: string }>()
  2. watchEffect(() => {
  3. console.log(`name is: ${name}`)
  4. })
  5. const aa = computed(() => { return name + '响应'})

看打印效果,只是普通的string,那么是如何实现响应的呢?还得看看“编译后”的代码是什么样子的。

  1. setup(__props, { expose: __expose }) {
  2. __expose();
  3. watchEffect(() => {
  4. console.log(`name is: ${__props.name}`);
  5. });
  6. const aa = computed(() => {
  7. return __props.name + "\u54CD\u5E94";
  8. });

编译后会生成一个 setup 函数,props 通过 参数 __props 传入,需要监听的地方,会把 name 变成 __props.name,这样就实现响应性了。也就是说,还是一个语法糖。

从外部文件引入 props 的定义( 单文件组件类型导入)

从外部引入 props 的定义,这个功能非常实用,以前封装UI库,想实现共享属性定义的时候卡了好久,使用OptionAPI,还是使用CompositionAPI,都各有优缺点,最后只好折中一下。

现在支持外部导入那就方便多了。

比如我们先在一个ts文件里面定义一个接口:

  1. export interface IFromItemProps {
  2. /**
  3. * 表单的 model
  4. */
  5. model: {[key: string]: any},
  6. /**
  7. * 对应的字段名称
  8. */
  9. colName: string,
  10. /**
  11. * 控件的备选项,单选、多选、等控件需要
  12. */
  13. optionList?: Array<{
  14. label: string,
  15. value: string | number | boolean,
  16. disabled: boolean
  17. }>,
  18. /**
  19. * 是否显示可清空的按钮,默认显示
  20. */
  21. clearable?: boolean,
  22. /**
  23. * 浮动的提示信息,部分控件支持
  24. */
  25. title?: string,
  26. /**
  27. * 组件尺寸
  28. */
  29. size?: string
  30. }

text

然后我们可以 基于 el-input 做一个自己的 nf-text ,然后引入接口定义,还可以在 nf-list 等里面引入,这比以前使用的方式正规多了,也能更好的支持TS。

  1. <template>
  2. <el-input
  3. v-model="model[colName]"
  4. v-bind="$attrs"
  5. :id="'c' + colName"
  6. :name="'c' + colName"
  7. :size="size"
  8. :clearable="clearable"
  9. >
  10. </el-input>
  11. </template>
  1. <script setup lang="ts">
  2. // 引入 类型定义
  3. import type { IFromItemProps } from './base'
  4. // 定义 props
  5. const props = defineProps<IFromItemProps>()
  6. console.log('props - text', props)
  7. </script>

看看效果

  1. Proxy {model: Proxy, colName: 'name', title: '姓名', size: 'small', clearable: true, …}
  2. [[Handler]]: Object
  3. [[Target]]: Proxy
  4. [[Handler]]: Object
  5. [[Target]]: Object
  6. clearable: true
  7. colName: "name"
  8. model: Proxy {name: 'jyk', city: Array(0), time: ''}
  9. optionList: undefined
  10. size: "small"
  11. title: "姓名"
  12. [[Prototype]]: Object
  13. [[IsRevoked]]: false
  14. [[IsRevoked]]: false

list

你可能会觉得,这封装的有意义吗?只看一个确实没啥意思,不过表单里面不是只有文本框这一种,还需要其他类型,定义接口就是为了统一风格。

我们再封装一个select看看:

  1. <template>
  2. <el-select
  3. v-model="model[colName]"
  4. v-bind="$attrs"
  5. :id="'c' + colName"
  6. :name="'c' + colName"
  7. :size="size"
  8. :clearable="clearable"
  9. :multiple="multiple"
  10. >
  11. <el-option
  12. v-for="item in optionList"
  13. :key="'select' + item.value"
  14. :label="item.label"
  15. :value="item.value"
  16. :disabled="item.disabled"
  17. >
  18. </el-option>
  19. </el-select>
  20. </template>

这里处理了一下 el-option ,使用 v-for 创建 el-option。

  1. <script setup lang="ts">
  2. import type { IFromItemProps } from './base'
  3. const props = defineProps<IFromItemProps & {multiple?: boolean}>()
  4. console.log('props - list', props)
  5. </script>

最后看一下使用情况

  1. import nfText from './form/text.vue'
  2. import nfList from './form/list.vue'
  3. import nfDatetime from './form/datetime.vue'
  4. const model = reactive({
  5. name: 'jyk',
  6. city: '',
  7. time: ''
  8. })
  9. const myText = {
  10. colName: 'name'
  11. }
  12. const myList = {
  13. colName: 'city',
  14. multiple: true,
  15. optionList: [
  16. {
  17. label: '北京',
  18. value: 1
  19. },
  20. {
  21. label: '上海',
  22. value: 2
  23. }
  24. ]
  25. }
  1. <nf-text :model="model" v-bind="myText"></nf-text>
  2. <nf-list :model="model" v-bind="myList"></nf-list>
  3. ...

封装之后,我们不用关心组件是否需要子组件(比如el-select需要设置 el-option),都是<nf-text v-bind="myText"></nf-text>这种简单粗暴的方式,而组件需要的属性,我们可以做成json的形式,这样更方便。

另外大家不要忘记 vue 提供的动态组件(component :is="xxx"),这样我们用 v-for 就可以把一个表单里的所有子组件都给遍历出来,不用一个一个的写了。

小结

目前只对这几个新特性感兴趣体验了一下,其他的还没来得及。还有一个 props 设置默认值的问题,可以使用 withDefaults:

  1. const props = withDefaults(defineProps< IFromItemProps >(), {
  2. clearable: true
  3. })

只是好像 默认值的部分需要直接写进去。这个,等待以后更新吧,估计以后都会支持外部导入的方式吧。

参考文档

  • Announcing Vue 3.3 | The Vue Point
  • Vue 3.3 主要新特性详解 - 三咲智子 Kevin Deng

参考资料

[1] Generic component enhancements - Discussion #436: https://github.com/vuejs/rfcs/discussions/436

[2] unplugin-vue-define-options - npm: https://www.npmjs.com/package/unplugin-vue-define-options

[3] Announcing Vue 3.3 | The Vue Point: https://blog.vuejs.org/posts/vue-3-3

[4] Vue 3.3 主要新特性详解 - 三咲智子 Kevin Deng: https://xlog.sxzz.moe/vue-3-3

Vue3.3 发布:十分钟速递

Vue3.3 正式发布!

官方帮助文档

原文链接:https://www.cnblogs.com/jyk/p/17406348.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号