上一次学习了基础的 API ,这次学习一些高级一点的 API。
Vue 初始化变化(上一篇章遗漏了)
原来的写法是:
1 2 3 4 5 6 7 8 9
| new Vue({ router, store, render: h => h(App) }).$mount('#app')
Vue.prototype.$toast = msg => console.log(msg)
|
新的写法是:
1 2 3 4 5 6
| const app = createApp(App, { user: 'yukaPriL' })
app.use(router).use(store).mount('#app')
app.config.globalProperties.$toast = msg => console.log(msg)
|
main.js
一般都是项目生成出来,很少自己改,所以还好。
高级 API 用法
高级用法主要是更好的实现逻辑,是对基础 API 的封装和补充。
toRef / toRefs
之前文章提到过一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| setup () { const state = reactive({ count: 0, sum: computed(() => state.count * 9.99) })
... return { state } }
|
直接返回时候,是不能进行对象展开的,展开后会导致失去响应。
但我们可以对返回内容进行包裹,再实现一次响应式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| setup () { const state = reactive({ v1: 1, v2: 2 }) const state2 = reactive({ v3: 3 }) setTimeout(() => { state.v1++ state.v2++ state2.v3++ }, 2000)
return { ...toRefs(state), v3: toRef(state2, 'v3') } }
|
通过使用 toRef
toRefs
,使得展开后的对象仍然具有响应性,不管我们后续再模板中使用,还是写 hooks 组件,都可以直接使用其值(而不是需要访问对象的属性):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <h1>ToRefs</h1> <p>v1:{{ v1 }}</p> <p>v2:{{ v2 }}</p> <p>v3:{{ v3 }}</p> </div> </template>
<template> <div> <h1>ToRefs</h1> <p>v1:{{ state.v1 }}</p> <p>v2:{{ state.v2 }}</p> <p>v3:{{ state2.v3 }}</p> </div> </template>
|
1 2 3 4 5 6
| const { v1, v2, v3 } = useDemo()
const { state, state2 } = useDemo()
|
总之,toRef
toRefs
在项目中应该非常常用!
新的内置组件:Teleport (传送门)
比如官网的例子:
1 2 3 4 5 6 7 8
| <body> <div style="position: relative;"> <h3>Tooltips with Vue 3 Teleport</h3> <div> <modal-button></modal-button> </div> </div> </body>
|
这个 modal-button
最后生成 html 后,就在 body > div > div 下,如果想把它生成在 body 最后一个节点后,基本上比较难实现。
之所以认为比较难实现,是因为我只会 vue 2 的基础用法,复杂的根本不会…
React 一直都有一个 createPortal
,可以很方便的解决这个问题。
这次,Vue 3 提供了这样一个组件来实现。
is 系列 isRef、isProxy、isReactive、isReadonly
最早使用 rc2 版本测试过一次,后来发现 rc5 版本发生了一些变化,倒是无法解释了。所以在出正式版之前,不会在测试了。
等正式版出来后再测试看看,如果能解释就放出来,解释不了就等等看其他人有没有高见。
生命周期
估计是为了更容易上手新的 composition-api,Vue 3.0 还提供了一套生命周期概念。这点比 React-hooks 容易多了。不过这些生命周期应该在实际项目中很少被用到,因为没有生命周期的 React-hooks 一样可以做的很好。
1 2 3 4 5 6 7 8
| beforeCreate -> 使用 setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted
|
beforeCreate
:我从来没有接触过,这个不多说,感觉是模拟不了的。
created
:现在直接在 setup()
里写就行了。
beforeMount
mounted
:对于 Vue 确实需要,否则无法判断元素是否挂载完成。虽然可以进行模拟,但比较场景化,不同场合写法会有出入。React 是通过 useEffect(()=>{}, [])
来模拟。对应的 Vue 可以用 watchEffect
模拟。watch
应该不行,因为默认不会执行第一次监听。具体实现可以参考下文例子。要注意的是,功能虽然实现了,但执行次数超过一次,需要特殊处理。
destroyed
:由于 Vue watchEffect
也支持清理副作用 onInvalidate
,所以可能模拟出组件销毁,不过估计也要借助 ref
等其他辅助参数来实现,成本一样非常高。
beforeUpdate
:我也从来没用过,也不多说了,感觉是模拟不了的。
updated
:肯定可以通过 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
| export default { name: 'Lifecycles', setup () { console.log('[Lifecycles] setup')
const state = reactive({ value: 0, list: [] }) const textRef = ref(null)
const onTestClick = () => { state.value++ }
watchEffect(() => { console.log('Lifecycles 模拟mounted -- L1') // 这里会执行2次,第一次相当于 beforeMount,第二次是mounted // 所以这里要判断,如果是 beforeMount,是没有 textRef.value 值的 if (textRef.value) textRef.value.textContent = '12345' }) watchEffect(() => { // 比如发送请求 console.log('Lifecycles 模拟mounted -- L2') // 如果 state.list 后续不会有任何修改,这里可以模拟mounted setTimeout(() => { state.list = [1, 2, 3, 4, 5] }) })
return { state, textRef, onTestClick, } } }
|
–END–