新特性总结
Fragment 组件,借鉴react Suspense 组件,借鉴react v-model 通过指定具体名可以同时绑定多个
为支持 tree-sharking 引入方式都以模块化引入
| 功能 | 新特性 | 实例 | 实际应用 |
|---|---|---|---|
| 指令 | 动态指令名 | <a :[key]="url"> ... | 实际项目作用还未知 |
| $watch | 可以是函数返回值,相当于将computed后进行监听 | this.$watch(()=>this.a+this.b,()=>{}) | props 两个属性具有相同响应方法时,可以 省略computed的合并,直接使用 |
| watch | 对象值为一个函数名或函数数组 | 函数名 :可以将响应函数写在methods中函数数组:将响应逐一调用函数 | 代码组织上可能更好看些 |
| watchEffect | 监听内部所涉及到的响应对象,并在页面渲染前就执行。 | ||
| emits | 新增实例 | ①验证emit的数据是否达到预期。②使组件代码中的emit事件更加一目了然 | |
| v-mode | ①由原来的:value,@input语法糖,改成:modelValue @update:modelValue即将原来v-bind的sync修饰符与v-model合并②可以指定具体名称③自定义修饰符 | v-model =‘a’=>{props:['modelValue','modelModifiers'],methods:{emit(){this.$emit('update:modelValue',xxx)}}}v-model:hello.big =‘a’=>{props:['hello','helloModifiers'],methods:{emit(){this.'helloModifiers.big=="big"this.$emit('update:hello',xxx)}}} | 对组件中的value变量更加容易区分。自定义修饰符是v-model功能更强大,而不需要通过额外的props来控制 |
| provide/inject | 可以通过 reactive 或ref函数来实现响应式绑定 | provide(){return{ f_data:reactive({ a:this.m })}} | |
| defineAsyncComponent | 新增实例, | defineAsyncComponent(()=>import(xx)) | 与Suspense 配合对异步组件的加载和错误捕获进行控制 |
| Suspense | 新增实例, | ||
| ref | setup中 ref的实现不再用$refs使用 | setup |
setup函数的注意点:
- 1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
- 2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
- 3、setup函数只能是同步的不能是异步的
reactive ref 二者的区别,在js中,赋值时 ref定义的变量 需要赋值给其value对象,而template则可以直接赋值
实践总结
-
reactive 是对对象地址做监听处理,若执行过程中改变变量的所指向的引用地址则监听响应被取消。
-
attrs 所对应的地址不能被监听。可以重新解构成新的地址,并赋值给新的变量, 并在onUpdate从新在赋值一遍即可。
-
defineProps 是在编译时就执行,所以不能放在函数中返回
-
ref 作用在组件上时,若v-bind:ref形式 则返回一个参数为当前dom的回调函数 若使用
<script setup>则不能获取到子组件的对象实例, -
$attrs 现在包含元素上所有的 attributes,包括class,style,vue2.0不包括,迁移时可能会有样式问题
-
attrs中。v-bind="listeners"统一为 v-bind="$attrs"
-
emits 作用:① 对emit事件返回值做校验。②对为未声明的事件绑定到原生dom上,即原有的native修饰符移除了
-
watchEffect 是对方法内所有的响应属性进行监听。复
杂的业务组件中不建议使用,否则会造成溯源困难 -
$nextTick 返回一个promise对象,所以可以配合async await 使用
demo页面
<template>
<h1>Demo</h1>
<Suspense>
<template #default>
<attrs-value ref="ref_dom"
v-model:ref_plain="ref_plain"
:ref_obj="ref_obj"
:reactive_obj="reactive_obj"
:ref_self="ref_self"
:toRefs_plain="toRefs_plain"
:readonly_reactive="readonly_reactive"
@t_emit="onT_Emit">
<template #ref_plain>
<button @click="() => { ref_plain++;} ">up+1</button>
</template>
<template #ref_obj>
<button @click="() => { ref_obj.count++;} ">up+1</button>
</template>
<template #reactive_obj>
<input v-model.number="reactive_obj.count" type="number" />
</template>
<template #ref_self>
<button @click=" () => { ref_self++; }">up+1</button>
</template>
<template #toRefs_plain>
<button @click=" () => { toRefs_plain++; }">up+1</button>
</template>
<template #readonly_reactive>
<button @click=" () => { readonly_reactive.count++; }">up+1</button>
</template>
</attrs-value>
</template>
<template #fallback>
<div>Suspense Loading</div>
</template>
</Suspense>
<Teleport to="#Teleport" >
<div style="text-align:center;">
<h1>this is Teleport</h1>
</div>
<Suspense>
<attrs-value ref="ref_dom" :ref_self="ref_self">
<template #ref_self>
<button @click=" () => { ref_self++; }">up+1</button>
</template>
</attrs-value>
</Suspense>
</Teleport>
</template>
<script setup>
import {
customRef, reactive, ref,
computed,
onMounted,
watch,
toRefs,
defineProps,
useContext,
defineAsyncComponent,
readonly
} from "vue"
defineAsyncComponent(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(import('./attrs-value.vue'))
}, 2000)
})
})
const attrsValue = defineAsyncComponent({
loader: () => new Promise((resolve) => {
setTimeout(() => {
resolve(import('./attrs-value.vue'))
}, 2000)
}),
// 加载异步组件时要使用的组件
loadingComponent() {
//当组件为纯函数时,return 的就是 render函数
return "loading"
},
errorComponent() {
//当组件为纯函数时,return 的就是 render函数
return "error"
},
timeout: 1000,
// 定义组件是否可挂起 | 默认值:true
// suspensible: false,
})
defineProps(["msg", "test"])
const reactive_obj = reactive({ count: 0 })
const ref_plain = ref(0)
let ref_obj = ref({ count: 1 })
let { toRefs_plain } = toRefs(reactive({ toRefs_plain: 1 }))
const ref_dom = ref(null)
watch(
ref_obj,
() => {
console.log('%c当ref为一个对象时,watch 必须用deep:true方能监听到变化,ref_obj' + ref_obj.value, 'color:red')
},
{ deep: true, immediate: true }
)
const onT_Emit = (e) => {
console.log("emit send:%c" + e, "color: red;font-size:20px")
}
const ref_debounc_customRef = (value, daytime = 500) =>
customRef((track, trigger) => {
let time = null
return {
get() {
track()
return value
},
set(newvalue) {
if (time) clearTimeout(time)
time = setTimeout(() => {
value = newvalue
trigger()
}, daytime)
}
}
})
const ref_self = ref_debounc_customRef(10)
const readonly_reactive = readonly(reactive_obj)
</script>
<style lang='less' scoped>
</style>
attrs-value
<template>
<table style="width: 100%;">
<tr>
<td>变量名</td>
<td>当前值</td>
<td>操作</td>
</tr>
<tr
v-for="(value, key) in list"
:key="key"
>
<td>{{ value.label }}</td>
<td>{{ value.value }}</td>
<td>
<slot :name="value.label" />
</td>
</tr>
</table>
</template>
<script setup>
/*
该组件以表格的形式显示父组件传入的属性对象值因操作后的变化。
同时为每个属性对象提供与其变量名一致的slot。
*/
import {
defineEmit,
useContext,
computed,
reactive,
onUpdated,
} from "vue";
const context = useContext();
const { Event, Attrs } = Object.keys(context.attrs).reduce((obj, key) => {
obj[key.indexOf("on") === 0 ? 'Event' : 'Attrs'][key] = context.attrs[key]
return obj
}, { Event: {}, Attrs: {} })
const attrs = reactive(Attrs);
onUpdated(() => {
Object.keys(attrs).forEach((v) => {
attrs[v] = context.attrs[v];
});
});
const emit = defineEmit(["t_emit"]);
emit("t_emit", "hello! this is table-item dom");
const list = computed(() => {
const Obj = { ...attrs };
return Object.keys(Obj).reduce((arr, attr) => {
let obj = {
label: attr,
value: Obj[attr],
};
arr.push(obj);
return arr;
}, []);
});
</script>
<style lang='less' scoped>
</style>