We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
beforeCreate
created
beforeMount
mounted
el
vm.$el
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
errorCaptured
参考:cn.vuejs.org/v2/api/#选项-…
以下是整个生命周期图示:
![img](data:image/svg+xml;utf8,)
参考:cn.vuejs.org/v2/guide/in…
被替换
重命名
新增的
新增的以下2个方便调试 debug 的回调钩子:
debug
参考:vue-composition-api-rfc.netlify.com/api.html#li…
特别说明
由于 Vue3.x 是兼容 Vue2.x 的语法的,因此为了保证 Vue2.x 的语法能正常在 Vue3.x 中运行,大部分 Vue2.x 的回调函数还是得到了保留。比如:虽然 beforeCreate 、 created 被 setup() 函数替代了,也就是说在 Vue3.x 中建议使用 setup(),而不是旧的API,但是如果你要用,代码也是正常执行的。
setup()
但是,以下2个生命周期钩子函数被改名后,在 Vue3.x 中将不会再有 beforeDestroy 和 destroyed。
另外,假如 Vue3.x 在 Q2 如期 Release 的话,大家一定要注意,在混合使用 Vue2.x 和 Vue3.x 语法的时候,特别要注意这2套API的回调函数的执行顺序。
如果大家看了我上一篇文章 VUE 3.0 学习探索入门系列 - 纠结要不要升级到Vue3.0?该如何升级?(5),我说过当 Vue3.x 正式 Realease 以后,我可能会先使用 Vue2.x + Composition API,完了再使用 Vue3.x 搭建新的基础框架。
Vue2.x + Composition API
Vue3.x
那我今天主要关心的一个问题,一旦我使用 Vue2.x + Composition API 进入过渡期后,当我再使用 Vue3.x 搭建新的框架,那么之前的基础组件还能使用么?
先测试下生命周期函数的执行顺序。
如下示例,在 Vue2.x 中引入兼容包 Composition API,然后Vue2.x 和 Vue3.x 的生命周期函数混合使用。
<template> <div> <p> {{ id }} </p> <p> {{ name }} </p> </div> </template> <script> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from '@vue/composition-api'; export default { setup() { const id = ref(1) console.log('setup') onBeforeMount(() => { console.log('onBeforeMount') }) onMounted(() => { console.log('onMounted') }) onBeforeUpdate(() => { console.log('onBeforeUpdate') }) onUpdated(() => { console.log('onUpdated') }) onBeforeUnmount(() => { console.log('onBeforeUnmount') }) onUnmounted(() => { console.log('onUnmounted') }) // 测试 update 相关钩子 setTimeout(() => { id.value = 2 }, 2000) return { id } }, data() { console.log('data') return { name: 'lilei' } }, beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') setTimeout(() => { this.id = 3; }, 4000) }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeUnmount() { }, unmounted() { console.log('unmounted') }, beforeDestroy() { console.log('beforeDestroy') }, destroyed() { console.log('destroyed') } } </script>
执行结果:
1. beforeCreate 2. setup 3. data 4. created 5. beforeMount 6. onBeforeMount 7. mounted 8. onMounted 9. beforeUpdate 10. onBeforeUpdate 11. updated 12. onUpdated 13. beforeDestroy 14. onBeforeUnmount 15. destroyed 16. onUnmounted
结论
在 Vue2.x 中通过补丁形式引入 Composition API,进行 Vue2.x 和 Vue3.x 的回调函数混用时:Vue2.x 的回调函数会相对先执行,比如:mounted 优先于 onMounted。
Vue2.x
Composition API
onMounted
以下直接使用 Vue3.x 语法,看看其在兼容 Vue2.x 情况下,生命周期回调函数混合使用的执行顺序。
<template> <div> <p> {{ id }} </p> <p> {{ name }} </p> </div> </template> <script> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onRenderTracked, onRenderTriggered } from 'vue'; export default { setup() { const id = ref(1) console.log('setup') onBeforeMount(() => { console.log('onBeforeMount') }) onMounted(() => { console.log('onMounted') }) onBeforeUpdate(() => { console.log('onBeforeUpdate') }) onUpdated(() => { console.log('onUpdated') }) onBeforeUnmount(() => { console.log('onBeforeUnmount') }) onUnmounted(() => { console.log('onUnmounted') }) onRenderTracked(() => { console.log('onRenderTracked') }) onRenderTriggered(() => { console.log('onRenderTriggered') }) // 测试 update 相关钩子 setTimeout(() => { id.value = 2; }, 2000) return { id } }, data() { console.log('data') return { name: 'lilei' } }, beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') setTimeout(() => { this.id = 3; }, 4000) }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeUnmount() { console.log('beforeUnmount') }, unmounted() { console.log('unmounted') } } </script> <style scoped> </style>
1. beforeCreate 2. data 3. created 4. onRenderTracked 5. onRenderTracked 6. onBeforeMount 7. beforeMount 8. onMounted 9. mounted 10. onRenderTriggered 11. onRenderTracked 12. onRenderTracked 13. onBeforeUpdate 14. beforeUpdate 15. onUpdated 16. updated 17. onBeforeUnmount 18. beforeUnmount 19. onUnmounted 20. unmounted
在 Vue3.x 中,为了兼容 Vue2.x 的语法,所有旧的生命周期函数得到保留(除了 beforeDestroy 和 destroyed),当生命周期混合使用时:Vue3.x 的生命周期相对优先于 Vue2.x 的执行,比如:onMounted 比 mounted 先执行。
综上所述:
通过对比可以得出:当你的主版本是哪个,当生命周期混用时,谁的回调钩子就会相对优先执行。
所以,这里就会有点坑!为了给减小以后不必要的麻烦,如果大家在 Vue2.x 中通过补丁形式引入 Composition API的使用的时候,建议:
以下内容,大部分参考官方: Vue Composition API
setup 是 Composition API 的核心,可以说也是整个 Vue3.x 的核心。
setup
data
render
为什么说 setup 灵活了呢?因为在这个函数中,每个生命周期可以是一个函数,在里面执行,以函数的方式编程。
下面看看其几个核心特点:
1 返回一组数据给template
<template> <div>{{ count }} {{ object.foo }}</div> </template> <script> import { ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) // expose to template return { count, object } } } </script>
2 使用jsx语法
<script> import { h, ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) return () => h('div', [ h('p', { style: 'color: red' }, count.value), h('p', object.foo) ]) } } </script>
3 Typescript Type
interface Data { [key: string]: unknown } interface SetupContext { attrs: Data slots: Slots emit: ((event: string, ...args: unknown[]) => void) } function setup( props: Data, context: SetupContext ): Data
4 参数
需要注意的是,在 setup 函数中,取消了 this!两方面的原因:
this
由于 setup 是一个入口函数,本质是面向函数编程了,而 this 是面向对象的一种体现!在完全基于函数的编程世界中,这个 this 就很难在能达到跟 Vue2.x 那种基于 OOP 思想的 Options 机制的实现的效果。
同样是基于函数式编程,那么如果加上 this,对于新手而言,本来 this 就不好理解,这时候就更加懵逼了。比如:
setup() { function onClick() { this // 如果有 this,那么这里的 this 可能并不是你期待的! } }
取消了 this,取而代之的是 setup 增加了2个参数:
setup(props, context) { // props // context.attrs // context.slots // context.emit }
也许你会有疑问,仅有这2个参数就够了么?够了。你在 Vue2.x 的时候,this 无法就是获取一些 data、props、computed、methods 等么?
其实,这2个参数都是外部引入的,这个没办法只能带入初始化函数中。除此之外,你组件上用到的所有 this 能获取的数据,现在都相当于在 setup 中去定义了,相当于局部变量一样,你还要 this 干嘛呢?
比如:
setup(props, context) { // data const count = ref(1) // 生命周期钩子函数 onMounted(() => { console.log('mounted!') }) // 计算函数 const plusOne = computed(() => count.value + 1) // methods 方法 const testMethod = () => { console.log('methods'); } // return to template return { count, testMethod } }
一切在 setup 中,都相当于变成了 局部变量 了,你还要 this 干嘛?
局部变量
当然,如果你要讲 Vue2.x 和 Vue3.x 混用!那就很别扭了,以后用 this,以后又不能用,你自己也会懵逼,所以在此建议:虽然Vue3.x兼容Vue2.x语法,但是不建议混合使用各自语法!
被 reactive 方法包裹后的 对象 就变成了一个代理对象,相当于 Vue2.x 中的 Vue.observable()。也就可以实现页面和数据之间的双向绑定了。
reactive
对象
Vue.observable()
这个包裹的方法是 deep 的,对所有嵌套的属性都生效。
deep
注意: 一般约定 reactive 的参数是一个对象,而下文提到的 ref 的参数是一个基本元素。但如果反过来也是可以的,reactive 其实可以是任意值,比如:reactive(123) 也是可以变成一个代理元素,可以实现双向绑定。
ref
reactive(123)
<template> <div> <p>{{ obj1.cnt }}</p> <p>{{ obj2.cnt }}</p> </div> </template> <script> import { reactive } from 'vue' setup() { // 普通对象 const obj1 = { cnt: 1 } // 代理对象 const obj2 = reactive({ cnt: 1 }) obj1.cnt++ obj2.cnt++ return { obj1, obj2 } } </script>
页面显示结果:
1 2
可以看到,普通对象属性更新时,页面是不会同步更新的。只有代理对象,才可以实现双向绑定。
被 ref 方法包裹后的 元素 就变成了一个代理对象。一般而言,这里的元素参数指 基本元素 或者称之为 inner value,如:number, string, boolean,null,undefied 等,object 一般不使用 ref,而是使用上文的 reactive。
元素
基本元素
inner value
也就是说 ref 一般适用于某个元素的;而 reactive 适用于一个对象。
ref 也就相当于把单个元素转换成一个 reactive 对象了,对象默认的键值名是:value。
value
setup() { const count = ref(100) }
被 ref 包裹后的元素变成一个代理对象,效果就相当于:
setup() { const count = reactive({ value: 100 }) }
因为变成了一个代理对象,所以取值的时候需要 .value:
.value
setup() { const count = ref(100) console.log(count.value) // output: 100 }
另外 ref 的结果在 template 上使用时,会自动打开 unwrap,不需要再加 .value
template
unwrap
<template> <div>{{ count }}</div> </template> <script> export default { setup() { return { count: ref(0) } } } </script>
以下是一些基本元素 ref 的结果:
setup() { console.log(ref(100).value) // output: 100 console.log(ref('test').value) // output: test console.log(ref(true).value) // output: true console.log(ref(null).value) // output: null console.log(ref(undefined).value) // output: undefined console.log(ref({}).value) // output: {} }
判断一个对象是否 ref 代理对象。
const unwrapped = isRef(foo) ? foo.value : foo
将一个 reactive 代理对象打平,转换为 ref 代理对象,使得对象的属性可以直接在 template 上使用。
看看下面的例子你可能就明白它的作用了。
<template> <p>{{ obj.count }}</p> <p>{{ count }} <p>{{ value }} </template> <script> export default { setup() { const obj = reactive({ count: 0, value: 100 }) return { obj, // 如果这里的 obj 来自另一个文件, // 这里就可以不用包裹一层 key,可以将 obj 的元素直接平铺到这里 // template 中可以直接获取属性 ...toRefs(obj) } } } </script>
与 Vue2.x 中的作用类似,获取一个计算结果。当然功能有所增强,不仅支持取值 get(默认),还支持赋值 set。
get
set
注意: 结果是一个 ref 代理对象,js中取值需要 .value。
正常获取一个计算结果:
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 报错,因为未实现 set 函数,无法赋值操作!
当 computed 参数使用 object 对象书写时,使用 get 和 set 属性。set 属性可以将这个对象编程一个可写的对象。
也就是说 computed 不仅可以获取一个计算结果,它还可以反过来处理 ref 或者 reactive 对象!
computed
const count = ref(1) const plusOne = computed({ get: () => count.value + 100, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0 console.log(plusOne.value) // 100
plusOne.value = 1 相当于给计算对象赋值,会触发 set 函数,于是 count 值被修改了。
plusOne.value = 1
count
使用 readonly 函数,可以把 普通 object 对象、reactive 对象、ref 对象 返回一个只读对象。
readonly
普通 object 对象
reactive 对象
ref 对象
返回的 readonly 对象,一旦修改就会在 console 有 warning 警告。程序还是会照常运行,不会报错。
const original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => { // 只要有数据变化,这个函数都会执行 console.log(copy.count) }) // 这里会触发 watchEffect original.count++ // 这里不会触发上方的 watchEffect,因为是 readonly。 copy.count++ // warning!
还有一些 API 诸如 watch 、watchEffect 等,这里就不说了,以上是一些比较重要的的语法,希望能对大家有所帮助。
watch
watchEffect
(全文完)
The text was updated successfully, but these errors were encountered:
sunmaobin
No branches or pull requests
1 Vue2.x 生命周期回顾
beforeCreate
,在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。created
,在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前尚不可用。beforeMount
,在挂载开始之前被调用:相关的 render 函数首次被调用。mounted
,实例被挂载后调用,这时el
被新创建的vm.$el
替换了。 如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el
也在文档内。beforeUpdate
,数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。updated
,由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。activated
,被 keep-alive 缓存的组件激活时调用。deactivated
,被 keep-alive 缓存的组件停用时调用。beforeDestroy
,实例销毁之前调用。在这一步,实例仍然完全可用。destroyed
,实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。errorCaptured
,当捕获一个来自子孙组件的错误时被调用。参考:cn.vuejs.org/v2/api/#选项-…
以下是整个生命周期图示:
![img](data:image/svg+xml;utf8,)
参考:cn.vuejs.org/v2/guide/in…
2 Vue3.x 生命周期变化
被替换
重命名
新增的
新增的以下2个方便调试
debug
的回调钩子:参考:vue-composition-api-rfc.netlify.com/api.html#li…
特别说明
由于 Vue3.x 是兼容 Vue2.x 的语法的,因此为了保证 Vue2.x 的语法能正常在 Vue3.x 中运行,大部分 Vue2.x 的回调函数还是得到了保留。比如:虽然
beforeCreate
、created
被setup()
函数替代了,也就是说在 Vue3.x 中建议使用setup()
,而不是旧的API,但是如果你要用,代码也是正常执行的。但是,以下2个生命周期钩子函数被改名后,在 Vue3.x 中将不会再有
beforeDestroy
和destroyed
。另外,假如 Vue3.x 在 Q2 如期 Release 的话,大家一定要注意,在混合使用 Vue2.x 和 Vue3.x 语法的时候,特别要注意这2套API的回调函数的执行顺序。
3 Vue2.x + Composition API 对比 Vue3.x 生命周期执行顺序
如果大家看了我上一篇文章 VUE 3.0 学习探索入门系列 - 纠结要不要升级到Vue3.0?该如何升级?(5),我说过当 Vue3.x 正式 Realease 以后,我可能会先使用
Vue2.x + Composition API
,完了再使用Vue3.x
搭建新的基础框架。那我今天主要关心的一个问题,一旦我使用
Vue2.x + Composition API
进入过渡期后,当我再使用Vue3.x
搭建新的框架,那么之前的基础组件还能使用么?先测试下生命周期函数的执行顺序。
3.1 Vue2.x + Composition API 生命周期执行顺序
如下示例,在 Vue2.x 中引入兼容包 Composition API,然后Vue2.x 和 Vue3.x 的生命周期函数混合使用。
执行结果:
结论
在
Vue2.x
中通过补丁形式引入Composition API
,进行Vue2.x
和Vue3.x
的回调函数混用时:Vue2.x 的回调函数会相对先执行,比如:mounted
优先于onMounted
。3.2 Vue3.x 生命周期执行顺序
以下直接使用 Vue3.x 语法,看看其在兼容 Vue2.x 情况下,生命周期回调函数混合使用的执行顺序。
执行结果:
结论
在
Vue3.x
中,为了兼容Vue2.x
的语法,所有旧的生命周期函数得到保留(除了beforeDestroy
和destroyed
),当生命周期混合使用时:Vue3.x 的生命周期相对优先于 Vue2.x 的执行,比如:onMounted
比mounted
先执行。4 Vue2.x + Composition API 过度到 Vue3.x 生命周期总结
综上所述:
Vue2.x
中通过补丁形式引入Composition API
,进行Vue2.x
和Vue3.x
的回调函数混用时:Vue2.x 的回调函数会相对先执行,比如:mounted
优先于onMounted
。Vue3.x
中,为了兼容Vue2.x
的语法,所有旧的生命周期函数得到保留(除了beforeDestroy
和destroyed
)。当生命周期混合使用时:Vue3.x 的生命周期相对优先于 Vue2.x 的执行,比如:onMounted
比mounted
先执行。通过对比可以得出:当你的主版本是哪个,当生命周期混用时,谁的回调钩子就会相对优先执行。
所以,这里就会有点坑!为了给减小以后不必要的麻烦,如果大家在
Vue2.x
中通过补丁形式引入Composition API
的使用的时候,建议:5 Composition API 核心语法
以下内容,大部分参考官方: Vue Composition API
5.1 setup 主执行函数
setup
是 Composition API 的核心,可以说也是整个 Vue3.x 的核心。setup
就是将 Vue2.x 中的beforeCreate
和created
代替了,以一个setup
函数的形式,可以灵活组织代码了。setup
还可以 return 数据或者 template,相当于把data
和render
也一并代替了!为什么说
setup
灵活了呢?因为在这个函数中,每个生命周期可以是一个函数,在里面执行,以函数的方式编程。下面看看其几个核心特点:
1 返回一组数据给template
2 使用jsx语法
3 Typescript Type
4 参数
需要注意的是,在
setup
函数中,取消了this
!两方面的原因:由于
setup
是一个入口函数,本质是面向函数编程了,而this
是面向对象的一种体现!在完全基于函数的编程世界中,这个this
就很难在能达到跟 Vue2.x 那种基于 OOP 思想的 Options 机制的实现的效果。同样是基于函数式编程,那么如果加上 this,对于新手而言,本来 this 就不好理解,这时候就更加懵逼了。比如:
取消了
this
,取而代之的是setup
增加了2个参数:也许你会有疑问,仅有这2个参数就够了么?够了。你在 Vue2.x 的时候,
this
无法就是获取一些 data、props、computed、methods 等么?其实,这2个参数都是外部引入的,这个没办法只能带入初始化函数中。除此之外,你组件上用到的所有 this 能获取的数据,现在都相当于在 setup 中去定义了,相当于局部变量一样,你还要 this 干嘛呢?
比如:
一切在
setup
中,都相当于变成了局部变量
了,你还要this
干嘛?当然,如果你要讲 Vue2.x 和 Vue3.x 混用!那就很别扭了,以后用 this,以后又不能用,你自己也会懵逼,所以在此建议:虽然Vue3.x兼容Vue2.x语法,但是不建议混合使用各自语法!
5.2 reactive 方法
被
reactive
方法包裹后的对象
就变成了一个代理对象,相当于 Vue2.x 中的Vue.observable()
。也就可以实现页面和数据之间的双向绑定了。这个包裹的方法是
deep
的,对所有嵌套的属性都生效。注意: 一般约定
reactive
的参数是一个对象,而下文提到的ref
的参数是一个基本元素。但如果反过来也是可以的,reactive
其实可以是任意值,比如:reactive(123)
也是可以变成一个代理元素,可以实现双向绑定。比如:
页面显示结果:
可以看到,普通对象属性更新时,页面是不会同步更新的。只有代理对象,才可以实现双向绑定。
5.3 ref 方法
被
ref
方法包裹后的元素
就变成了一个代理对象。一般而言,这里的元素参数指基本元素
或者称之为inner value
,如:number, string, boolean,null,undefied 等,object 一般不使用ref
,而是使用上文的reactive
。也就是说
ref
一般适用于某个元素的;而reactive
适用于一个对象。ref
也就相当于把单个元素转换成一个reactive
对象了,对象默认的键值名是:value
。比如:
被
ref
包裹后的元素变成一个代理对象,效果就相当于:因为变成了一个代理对象,所以取值的时候需要
.value
:另外
ref
的结果在template
上使用时,会自动打开unwrap
,不需要再加.value
以下是一些基本元素
ref
的结果:5.3 isRef 方法
判断一个对象是否
ref
代理对象。5.4 toRefs 方法
将一个
reactive
代理对象打平,转换为ref
代理对象,使得对象的属性可以直接在template
上使用。看看下面的例子你可能就明白它的作用了。
5.5 computed 函数
与 Vue2.x 中的作用类似,获取一个计算结果。当然功能有所增强,不仅支持取值
get
(默认),还支持赋值set
。注意: 结果是一个
ref
代理对象,js中取值需要.value
。正常获取一个计算结果:
当 computed 参数使用 object 对象书写时,使用 get 和 set 属性。set 属性可以将这个对象编程一个可写的对象。
也就是说
computed
不仅可以获取一个计算结果,它还可以反过来处理ref
或者reactive
对象!plusOne.value = 1
相当于给计算对象赋值,会触发set
函数,于是count
值被修改了。5.5 readonly 函数
使用
readonly
函数,可以把普通 object 对象
、reactive 对象
、ref 对象
返回一个只读对象。返回的 readonly 对象,一旦修改就会在 console 有 warning 警告。程序还是会照常运行,不会报错。
还有一些 API 诸如
watch
、watchEffect
等,这里就不说了,以上是一些比较重要的的语法,希望能对大家有所帮助。(全文完)
The text was updated successfully, but these errors were encountered: