Vue3.0 新特性学习(一)

setup 内部也可以调用生命周期钩子,但是 Vue3 并没有提供 beforeCreatecreated 对应的钩子,由上篇文章可知,setup 是早于这两个钩子执行的,因此 setup 本身就可以胜任这两个钩子的工作,并且官方也是这么说的:

因为 setup 是围绕 beforeCreatecreated 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

其他的钩子名称有所改变,beforeMount 变成了 onBeforeMount,其他钩子的命名规则都与这个相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 无法知道是哪个值被更新,因此获取不到变化的新值与旧值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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')

运行结果:

watchwatchEffect 都返回了一个 unwatch 方法用于取消侦听

1
2
3
4
5
6
7
8
9
10
11
12
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可以编写组合式函数,可以清楚地知道该变量来自哪个组合式函数。

下面是两个简单的例子:

1
2
3
4
5
6
7
8
9
10
<div id="app" v-cloak>
<div>
{{listLoading}}
<button @click="toggleLoading">改变状态</button>
</div>
<div>
{{count}}
<button @click="addCount">+1</button>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 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 文章中就不多介绍。