
自Vue3.0发布后,借着空闲摸鱼的时间还有下班后的时间,零零散散的看过几篇大佬们总结的Vue3.0的相关内容知识,但是因为公司项目中Vue3.0暂时无法使用,脑子中没有一个很完整地概念,总感觉知识体系是这一块那一块拼凑起来的,所以借着写这篇文章总结一下。之前没仔细看过官网文档,所以这次主要是看文档加自己理解。有哪里不对,还望各位,不吝指教。(PS:这次也算是自己逼着自己分享的,之前想写一点东西,但是总不知要写点什么,算是第一次正儿八经的写🤦♂️🤦♂️🤦♂️。文笔不好,基础不牢,大佬们轻喷🍗🍗🍗)
安装
-
通过脚手架 vue/cli
命令行输入
vue create <project name>
,然后会出现下图所示的选项,可以选择Vue2.x、Vue3.x版本来创建项目,这里需要注意vue/cli 的版本需要大于4.5 ,否则输入以上命令后,默认是以Vue2.x创建项目的。 -
通过Vite
命令行输入
npm init vite <project-name> -- --template vue

值得注意的新特性
组合式API - Composition Api
1. 为什么?
在Vue2.x中,我们通常使用data、methods、computed等组件选项来组织业务逻辑,如果功能项不多,开发起来还相对轻松;但是一旦页面功能增加,开发时就要在各个选项内来回跳转,开发体验极其不好。同时这种碎片化使得理解和维护组件变的困难,功能模块复用也比较麻烦。
2. 组合式API与传统方式(Option Api)的对比
这里推荐 @大帅老猿 老师的做了一夜动画,就为让大家更好的理解Vue3的Composition Api,简单生动易懂。
3. 组合式API的入口setup
参数(props, context)
-
props
setup
函数中的props
是响应式的,当传入新的prop时,它将被更新。javascriptexport default { props: { title: String }, setup(props) { console.log(props.title) } }
Tips :但是因为props是响应式的,不能使用ES6解构,它会消除prop的的响应式。如果需要解构prop,可以在setup函数中使用toRefs函数完成此操作。对于这一块的个人理解:直接使用解构取得元素,直接消除了Proxy对数据的响应式包装,直接把封装好的数据的.value值取出(好像😂),返回普通的元素对象;而使用了toRefs函数后,则每个数据都是ObjectRefImpl对象,保持响应式。这里贴一下ES6解构的原理理解
javascriptconst book = reactive({ author: 'Vue Team', year: '2020', title: 'Vue 3 Guide', description: 'You are reading this book right now ;)', price: { after: 11, before: 22 } }) let formatBook = toRefs(book) let { price: p } = toRefs(book) let { price } = book console.log(formatBook) console.log(p) console.log(p.value) console.log(price) // 分别输出 // {author: ObjectRefImpl, year: ObjectRefImpl, title: ObjectRefImpl, description: ObjectRefImpl, price: ObjectRefImpl} // ObjectRefImpl {_object: Proxy, _key: "price", __v_isRef: true} // Proxy {after: 11, before: 22} // Proxy {after: 11, before: 22}
-
Context
context
是一个普通的javascript对象,它暴露组件的三个属性:attrs
、slots
、emit
,这些属性在Vue2.x中需要通过this
才能访问到,它不是响应式的,意味着你可以安全的对context
使用解构。(名词解释摘自官网文档)attrs
-->(V2)this.$attrs:包含了父作用域中不作为prop被识别(且获取)的attribute绑定。
javascript// 父组件 <template> <Test v-model:name="userName" cn="china"></Test> </template> // 子组件 Test.vue <template> <div>Test {{ name }}</div> </template> <script> export default { props: { name: String }, setup(props, context) { let { attrs } = context return { cn: attrs.cn // china } } } </script>
slots
-->(V2)this.$slots:用来分发被插槽分发的内容;每个具名插槽有其相应的property;default property包括了所有没有在具名插槽中的节点或v-slot:default的内容。
javascript// 父组件 <Init> <div>D E F A U L T</div> <template v-slot:footer> <div>T H I S I S F O O T E R</div> </template> </Init> // 子组件 <template> <slot></slot> <slot name="footer"></slot> </template> <script> export default { setup(props, context) { let { slots } = context console.log(slots) // Proxy {_: 1, __vInternal: 1, footer: ƒ, default: ƒ} } } </script>
emit
-->(V2)this.$emit
访问组件的property
先看下setup
的运行时间:
javascript
export default {
setup(props, context) {
console.log('setup')
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
// 输出
// setup
// beforeCreate
// created
}
执行setup
时,组件实例尚未被创建。因此只能访问一下property:props
,attrs
,slots
,emit
,换句话说,你将无法访问一下组件选项:data
,computed
,methods
。
使用this
在setup()内部,this不是该活跃实例的引用,因为setup()是在解析其他组件选项之前被调用的,所以setup()内部的this的行为与其他选项中的this完全不同。
生命周期钩子
选项式 API | Hook inside setup |
---|---|
beforeCreate |
Not needed* |
created |
Not needed* |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
errorCaptured |
onErrorCaptured |
renderTracked |
onRenderTracked |
renderTriggered |
onRenderTriggered |
activated |
onActivated |
deactivated |
onDeactivated |
setup这个函数是在beforeCreate和created之前运行的,所以可以用它来代替这两个钩子函数。项目开发过程中尽量避免两种生命周期的混用。
4. 响应性API
reactive
返回对象的响应式副本;响应式转换是"深层"的 --- 它影响所有嵌套property。
这里推荐 深入源码理解Vue3 reactive
readonly
接受一个对象(响应式或纯对象)或ref并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套property也是只读的。
javascript
const original = reactive({ count: 0 })
const copy = readonly(original)
copy.count++ // 变更副本将失败并导致警告
ref
接受一个内部值(通常是基本数据类型)并返回一个响应式且可变的ref的对象。ref对象具有指向内部值的单个property.value
。
这里推荐 深入源码剖析Vue3 ref
javascript
const count = ref(0)
toRef
可以用来为源响应式对象上的某个property新创建一个ref。然后,ref可以被传递,它会保持对其源property的响应式连接。
javascript
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
state.foo++
console.log(fooRef.value) // 2
toRefs
将响应式对象转换为普通对象,其中结果对象的每个property都是指向原始对象相应property的ref。
javascript
const state = reactive({
foo: 1,
bar: 2
})
const format = toRefs(state)
console.log(state)
console.log(format)
// state: Proxy {foo: 1, bar: 2}
// format:
/**
{foo: ObjectRefImpl, bar: ObjectRefImpl}
bar: ObjectRefImpl {_object: Proxy, _key: "bar", __v_isRef: true}
foo: ObjectRefImpl {_object: Proxy, _key: "foo", __v_isRef: true}
**/