setup函数
1.setpup的执行时机在beforCreate之前,所以在在setup函数中无法使用data和methods,并且在setup中vue把this修改为了undefined。
2.setup函数只能是同步的
3.在setup函数中添加响应式数据数要配合ref()和reactive()方法。
reactive
setup(){
const state = reactive({
count:0
})
return { state }
}
<div>
{{ state.count }}
</div>
1.单独使用reactive方法返回的是一个对象在使用的时候需要用state.count来读取数据。
2.reactive接收的参数必须是一个对象(json/Arr)
如果传入了其它对象,直接修改对象的值界面不会自动更新。如果想更新看,可以通过重新赋值的方式。
ref
setup(){
const count = ref(0)
// 改变值
count.value++
return { count }
}
<div>
{{ count }}
</div>
1.ref的本质还是reactive,在给ref函数传递一个值之后,ref函数的底层会把ref转变成reactive。
ref(0)>reactive({value:0})
所以单独使用ref方法创建响应式对象,在改变其值的时候需要使用count.value,但是在HTML中可以直接用count取值。
2.vue通过数据的私有属性__v_ref来判断是否为ref对象,对外提供了isRef()方法用于判断是否为ref对象。reactive可以使用isReactiv()方法判断。
toRef和ref的区别
ref->复制原始数据,改变响应式数据不会影响原始数据
toRef->引用原始数据,改变响应式数据会改变原始数据
ref->改变响应式数据,界面自动更新
toRef->改变响应式数据,界面不会自动更新
toRef的应用场景:响应式数据和原始数据关联,且更新响应式数据后不希望更新界面的情况下可以使用toRef方法。
toRefs方法
toRefs方法是toRef的简写,当对象有多个属性需要转化为ref对象的时候可以使用toRefs方法。
customRef方法自定义ref
用customRef方法会返回一个ref对象,并且可以显式控制依赖追踪和触发
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
ref和reactive组合使用
setup(){
const state = reactive({
count:0
})
const { count } = toRefs(state) // 把普通数据转换成ref方法对应的响应式数据
// 改变值
state.count++;
return { count }
}
ref和reactive递归监听和非递归监听
1.ref和reactive默认情况下都是递归监听,但是递归监听存在性能上的弊端(每一个层的对象都转换成proxy对象消耗性能较大),如果数据量大,非常消耗性能。
2.通过shallowRef和shallowReactive可以创建非递归监听(一般在数据量比较大的时候才用)。
注意:如果是通过shallowRef创建数据,vue监听的只是第一层数据value(如state.value)的变。,因为ref是会转换成reactive,本质上value才是第一层。
vue3提供了triggerRef()方法,用于手动更新ref对象,但是没有triggerReactive。
toRow方法
toRow()方法可以获取ref和reactive对象的原始数据。修改用toRow方法获得的对象可以实现对ref和reactive原始数据的修改,且不会触发监听也不会更新UI界面。
**注意:**获取ref对象的原始数据的时候必须写明是获取value(toRow(state.value))
用于提升性能
markRow方法
markRow方法可以让一个对象永远不会被监听,如果markRow(obj),就算之后用obj创建reactive或者ref对象也不会被监听
setup中使用computed
setup() {
const state = reactive({
count: 1,
num: 0
})
setTimeout(() => {
state.count++
}, 1000)
// import 引入后可直接在setup中使用
state.num = computed(() => {
return state.count + 1
})
return {state}
},
watchEffect
watchEffect的作用为传入一个函数,立即执行,并响应式的追踪它的依赖,在依赖改变时重新运行函数。作用类似watch。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 打印出 0
setTimeout(() => {
count.value++
// -> 打印出 1
}, 100)
readonly
1.readonly用于创建一个只读的数据,并且是递归只读
2.shallowReadonly用于创建一个不递归只读的只读数据
3.const和readonly的区别:const创建的对象的属性可以变化,而readonly不行。
const是赋值保护,readonly是属性保护。
Vue3常用语法的变化
v-for 中的 Ref 数组
在 Vue 3 中,v-for中的ref将不再在 $ref 中自动创建数组。要从单个绑定获取多个 ref,需要将 ref 绑定到一个更灵活的函数上
<div v-for="item in list" :ref="setItemRef"></div>
// option api
export default {
data() {
return {
itemRefs: []
}
},
methods: {
setItemRef(el) {
this.itemRefs.push(el)
}
},
beforeUpdate() {
this.itemRefs = []
},
updated() {
console.log(this.itemRefs)
}
}
// composition api
import { ref, onBeforeUpdate, onUpdated } from 'vue'
export default {
setup() {
let itemRefs = []
const setItemRef = el => {
itemRefs.push(el)
}
onBeforeUpdate(() => {
itemRefs = []
})
onUpdated(() => {
console.log(itemRefs)
})
return {
itemRefs,
setItemRef
}
}
}
v-model变化
1.vue3现在可以在同一个组件上使用多个 v-model 进行双向绑定;
2.现在可以自定义 v-model 修饰符
3.v-bind 的 .sync 修饰符和组件的 model选项已移除,可用 v-model 作为代替
在vue2中组件上的v-model是硬性绑定的value prop和input事件,修改属性或者事件名称需要在子组件中添加model选项。
在vue3中自定义组件v-model相当于传递了modelvalue prop并接受抛出的update:modelValue事件
<ChildComponent v-model:title="pageTitle" />
// 相当于
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
这也可以作为 .sync 修饰符的替代,而且可以在自定义组件上使用多个 v-model。
4.vue3 自定义修饰符
添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件
<my-component v-model.capitalize="myText"></my-component>
app.component('my-component', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
template: `
<input type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)">
`,
created() {
console.log(this.modelModifiers) // { capitalize: true }
}
})
对于带参数的 v-model 绑定,生成的 prop 名称将为 arg + "Modifiers"
<my-component v-model:description.capitalize="myText"></my-component>
app.component('my-component', {
props: ['description', 'descriptionModifiers'],
emits: ['update:description'],
template: `
<input type="text"
:value="description"
@input="$emit('update:description', $event.target.value)">
`,
created() {
console.log(this.descriptionModifiers) // { capitalize: true }
}
})
v-bind合并行为
在vue2中如果一个元素同时定义了v-bind=“object”和一个相同的单独的property,那么这个单独的property总是会覆盖object。
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="red"></div>
在vue3中声明的顺序决定了何如合并
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>
v-if和v-for优先级变化
在vue2中v-if和v-for一起使用是大忌,因为v-for会优先起作用,每次循环都要判断一次v-if,但是在vue3中v-if总是优先于v-for生效,现在两个终于可以一起用了。
移除v-on.native修饰符
在vue2中传递给带有v-on的组件的事件监听器只有通过this.$emit才能出发。要将原生的DOM事件监听器加到组组件根元素上,要使用.native修饰器。
vue3中v-on的.native修饰符已经被移除了,新增了emits选项,允许子组件定义真正被触发的事件。对于子组件中没有被定义被组件触发的所有事件监听器,vue会把它们当作原生事件监听器添加到子组件根元素中(除非在子组件的选项中设置了 inheritAttrs: false)。
$children移除
vue3不能使用this.refs。
$attrs包括
class & style
在 Vue 2 的虚拟 DOM 实现中对 class 和 style attribute 有一些特殊处理。因此,它们不包括在 $attrs 中,但是仍然会应用到组件的根元素。
MyComponent:
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
<my-component id="my-id" class="my-class"></my-component>
在vue2中使用时生成的html为:
<label class="my-class">
<input type="text" id="my-id" />
</label>
class自动应用到了label上,而在vue3中生成的html为:
<label>
<input type="text" id="my-id" class="my-class" />
</label>
移除$listeners
attrs的一部分了。
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
如果这个组件接收一个 id attribute 和一个 v-on:close 监听器,那么 $attrs 对象现在将如下所示:
{
id: 'my-input',
onClose: () => console.log('close Event triggered')
}
生成 prop 默认值的工厂函数不再能访问 this
替代方案:
1.把组件接收到的原始 prop 作为参数传递给默认函数;
2.注入 API 可以在默认函数中使用。
import { inject } from 'vue'
export default {
props: {
theme: {
default (props) {
// `props` 是传递给组件的原始值。
// 在任何类型/默认强制转换之前
// 也可以使用 `inject` 来访问注入的 property
return inject('theme', 'default-theme')
}
}
}
}