Vue3.0 新特性学习(二)
setup
内部也可以调用生命周期钩子,但是 Vue3 并没有提供 beforeCreate
和 created
对应的钩子,由上篇文章可知,setup
是早于这两个钩子执行的,因此 setup
本身就可以胜任这两个钩子的工作,并且官方也是这么说的:
因为
setup
是围绕beforeCreate
和created
生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在setup
函数中编写。
其他的钩子名称有所改变,beforeMount
变成了 onBeforeMount
,其他钩子的命名规则都与这个相同。
Vue.createApp({
setup(props) {
Vue.onBeforeMount(() => {
console.log(1)
})
Vue.onMounted(() => {
console.log(2)
})
},
beforeMount() {
console.log(3)
},
mounted() {
console.log(4)
},
})
app.mount('#app')
运行结果:
同样的钩子,setup
内部的钩子调用早于外部的钩子。
computed
const app = Vue.createApp({
setup(props) {
const a = Vue.ref(1)
const b = Vue.computed(() => a.value * 2)
const c = Vue.computed({
get: () => a.value * 3,
set:val => a.value = val + 3
})
console.log(a.value) // 1
console.log(+ b.value) // 2
console.log(c.value) // 3
c.value = 2
console.log(c.value) // 15
console.log(a.value) // 5
console.log(b.value) // 10
console.log(c.value) // 15
return {
a,
b,
c
}
},
})
app.mount('#app')
computed 用法与之前没什么差别。
watch
const app = Vue.createApp({
setup(props) {
// 侦听 ref
const a = Vue.ref(1)
Vue.watch(a, (val, prevVal) => {
console.log(`a 被改变了:新值为${val},旧值为${prevVal}`)
})
a.value = 2
// 侦听 getter
const b = Vue.reactive({
x: 1,
y: 2
})
Vue.watch(() => b.x, (val, prevVal) => {
console.log(`b.x 被改变了:新值为${val},旧值为${prevVal}`)
})
b.x = 2
// 监听多个源
const { c, d } = Vue.toRefs(Vue.reactive({ c: 3, d: 4 }))
Vue.watch([c, d], (newValArr, preValArr) => {
console.log(newValArr, preValArr)
})
c.value = 7
d.value = 7
return {
a,
b
}
},
})
app.mount('#app')
运行结果:
watchEffect
watchEffect
接受一个回调函数,在这个回调函数中用到的任意一个响应式数据更新时,这个回调函数都会被执行,也就是说它可以监听多个响应式数据。它与 watch
很相似,但也有需要注意的地方:
watchEffect
无需指定要监听的属性,它自动会收集依赖,而watch
必须指定。watchEffect
因为需要收集依赖。所以它在组件初始化的时候就会运行一遍,而watch
默认不会运行,除非手动配置immediate: true
。watchEffect
无法知道是哪个值被更新,因此获取不到变化的新值与旧值。
const app = Vue.createApp({
setup(props, context) {
// 响应式
const a = Vue.ref(0)
// 非响应式
let b = 1
Vue.watchEffect(() => {
console.log(`有值更新了!a:${a.value};b:${b}。`)
})
setTimeout(() => {
// 不触发 watchEffect
b = 5
}, 1000)
setTimeout(() => {
// 触发 watchEffect
a.value ++
}, 2000)
return {
listLoading,
toggleLoading
}
},
})
app.mount('#app')
运行结果:
watch
与 watchEffect
都返回了一个 unwatch 方法用于取消侦听
const app = Vue.createApp({
setup(props, context) {
const unWatch = Vue.watch(() => {})
// 取消侦听
unWatch()
const unWatchEffect = Vue.watchEffect(() => {})
// 取消侦听
unWatchEffect()
return {}
},
})
app.mount('#app')
组合式函数
以前我们需要在组件之间共享代码时,一般使用 mixins
或作用域插槽。但它们都有一些硬伤:
mixins
多了之后变量会十分混乱,在引用文件中根本无法区分哪个变量或方法来自哪个mixins
。- 作用域插槽没有变量混乱的问题,因为它的数据只能在模板中访问,但这恰好也是它的缺点。
在 Vue3 中,我们配合组合式 API可以编写组合式函数,可以清楚地知道该变量来自哪个组合式函数。
下面是两个简单的例子:
<div id="app" v-cloak>
<div>
{{listLoading}}
<button @click="toggleLoading">改变状态</button>
</div>
<div>
{{count}}
<button @click="addCount">+1</button>
</div>
</div>
// listLoading 状态的切换
function useToggleLoading(user) {
const listLoading = Vue.ref(false)
const toggleLoading = () => {
listLoading.value = !listLoading.value
}
return {
listLoading,
toggleLoading
}
}
// count 计数
function useAddCount(user) {
const count = Vue.ref(0)
const addCount = () => {
count.value++
}
return {
count,
addCount
}
}
const app = Vue.createApp({
setup(props, context) {
// 来自 useToggleLoading
const { listLoading, toggleLoading } = useToggleLoading()
//来自 useAddCount
const { count, addCount } = useAddCount()
return {
listLoading,
toggleLoading,
count,
addCount
}
},
})
app.mount('#app')
Vue2 与 Vue3 的主要区别就写到这里,还有其他很多我觉得用得相对较少的 API 文章中就不多介绍。