导语
Vue3已经是大势所趋,但是目前社会上项目的使用率仍然不是很高,所以大多数前端小伙伴们也只停留在“听说VUE3出来了呢”,具体做了哪些改动呢?并不是很清楚,有哪些优势?新增/删除了哪些功能?写法上改变了多少?都不是很清楚,这篇文章呢,就是为了清晰地讲一讲我对Vue3的版本理解,以及对比和Vue2的区别在哪里。这里不会讲很高深的源码,不会像大佬们一上来就是那些Fragment、Suspense、Teleport、Tree-Shaking、Composition API专业名词,咱没接触过的人,看这一脸懵逼的。这篇文章从最基础的Vue3项目创建入口,一步步讲解过去,觉得有收获的同学们点个赞收藏起来,此篇文章将持续性更新,有特殊理解的同学们也可以留言,我会去了解后对文章做一些补充(ps:文章模块将加上留言小伙伴的ID)~
新建Vue3项目
//step1 用cli创建项目
vue create <project_name>
//step2 选择
Default (Vue 3) ([Vue 3] babel, eslint)
// step3 创建vue.config.js文件
module.exports = {
lintOnSave: false,
}
// 添加上这么一句话,是为了关闭eslink的检查,方便咱们开发测试
Vue实例的创建及挂载
//Vue2的写法
import Vue from 'Vue' //引入Vue构造函数
const vm = new Vue({
render:h=> h(App)
});
// 挂载
vm.$mount('#app')
console.log(vm)
//Vue3的写法
import { createApp } from 'vue' //引入一个名为createApp的工厂函数
const app = createApp(App);
// 挂载
app.mount('#app');
console.log(app)
Vue2的写法在Vue3中是行不通的,因为导入不到Vue这个构造函数
我们在两个Vue2项目和Vue3项目中分别打印出vm
和app
实例:从下面两幅图我们能看出来,Vue3实例化的app比Vue2实例化出来的vm小了很多,Vue2上的很多方法其实大家都不常用甚至没用过,在Vue3都不挂载在实例上了,实现了更轻量级
模板语法
打开App.vue文件,唯一和Vue2不同的是:Template不再强调一定要有根标签了。
setup函数
setup
函数可以说是Vue3
的属性和方法入口。在Vue2
中,为什么使用的是data
、methods
、computed
。在Vue3
中我们把属性和方法都放在setup
函数中。setup
函数中有以下几个特点:
1.setup(props,context):接收两个参数
(1) props :接收来自父组件传来的参数
(2) context :上下文,主要包含3个使用参数:attrs,emits,slots,相当于Vue2中this的
attrs,emits,slots
props:['msg'],
emits:['eventOne']
// Vue3中,使用了emits需要像props一样,在页面中声明调用了函数,否则则会警告(虽然还是能用)
2.有返回值,返回值可以是两种:
(1) 返回 对象 ,返回的对象中的属性方法,可在模板中直接使用;
(2) 返回 渲染函数 ,可以自定义渲染的内容;
3.函数内部没有this
;
4.当内部有异步函数,需要使用到await
的时候,可以直接使用,不需要在setup
前面加async
;
//最基础的写法
import { h } from 'vue'
setup() {
let name = '连颜少';
let age = 18;
function sayhello() {
alert(`我叫${name},我每年都${age}.你好`)
}
function changeInfo() {
name = 'new 连颜少';
age = 28;
console.log(name,age);
}
// 1.返回对象
return {
name,age,sayhello,changeInfo
}
// 2.返回渲染函数
// return ()=> h('h1',name)
}
ref
我们触发上述changeInfo
函数,在开发者工具里打印出来的数据已经改变,但页面上并没有响应式生效。想要让Vue
监听到数据的变化,实现响应式,要引入ref
去声明,这时候变量就是双向绑定的了
import { h,ref } from 'vue'
setup() {
let name = ref('连颜少');
let age = ref(18);
function sayhello() {
alert(`我叫${name},我每年都${age}.你好`)
}
function changeInfo() {
name.value = 'new 连颜少';
age.value = 28;
console.log(name,age);
}
// 1.返回对象
return {
name,age,sayhello,changeInfo
}
}
为什么要.value
才能修改呢,我们打印了一下age
,发现经过ref
声明的变量,结构变成了一个对象,里面的value
存储着我们的变量,这边和Vue2
一样是通过defineProperty
的方法,通过监听get
和set
方法去实现的响应式
那,如果声明的是对象呢是数组呢?用ref
也是可以实现的。
let job = ref({
type:'front-end',
salary:'30k'
})
function changeInfo() {
job.value = {
type:'back-end',
salary:'20k'
}
console.log(job)
}
// 可以看出来value是一个Proxy代理实例,这边ref通过检测要声明的是Object,于是调用了reactive函数
// reactive又是什么呢,是一个封装好的Proxy代理
reactive
import {reactive} from 'vue'
let job = reactive({
type:'front-end',
salary:'30k'
})
function changeInfo() {
job = {
type:'back-end',
salary:'20k'
}
console.log(job)
}
// 这里job就不需要.value去取值了
// 为了方便,不用每次都.value去取值,这边建议大家直接用reactive声明一个大对象取名为data,将之return出去
let data = reactive({
game:'',
number1:1,
arr:['lian','yan','shao'],
person: {
name:'连颜少',
hobby:['game','girl']
}
})
return {data}
// 这样模板就可以data.value 去拿到值了,相对来说好看一些
reactive实际上就是封装了ES6的Proxy
,这边对比下Vue2
中如何监听对象和数组。
vue2和vue3的响应式原理区别
Vue2
监听对象的时候,也是用的defineProperty
,并进行递归判断,如果是嵌套对象,则一直递归到所有值去监听
监听数组的时候,重写数组的方法,对数组的方法进行了包裹 存在的问题:
- 1.新增属性,删除属性,界面不会更新,需要调用
set/delete
- 2.数组直接通过下标修改,页面不会更新,需要调用
splice
,或者set
Vue3
setup() {
let person = {
name:'连颜少',
hobby:['girl','front-end']
}
let p = new Proxy(person,{}); // Proxy接收两个参数,一个是原对象,一个是原型方法
function changeInfo() {
console.log('p',p)
console.log('person',person)
p.hobby[1] = '嘿嘿嘿'
}
// 1.返回对象
return {
person,changeInfo,p
}
}
数据确实是修改成功了,并且同步到person
,但是Vue
并没有捕获到数据的变化,没办法去执行页面更新的操作,所以要修改调用的方法。
重写了get、set、deleteProperty方法,并做监听(这边用console代替~) target:原对象,propName:属性名,value:更改的新值 然而,源码也并非这么写的,这样的写法,直接修改了person这个原对象,这时候就要引入Reflect
let person = {
name:'连颜少',
age:18
}
const p = new Proxy(person,{
get(target,propName) {
console.log(`这里是vue监听的动作,获取到${propName}属性`)
return target[propName]
},
set(target,propName,value) {
console.log(`这里是vue更新的动作,修改${propName}属性,去更新页面`)
target[propName] = value
},
deletePrroperty(target,propName) {
console.log(`这里是vue更新的动作,删除了${propName}属性,去更新页面`)
return delete target[propName]
}
})
Reflect
Reflect
引入了基本绝大多数Object
的方法,基本Object
能实现的,Reflect
都能替代。
当我们用defineProperty
重复去定义一个变量的时候,会报错,这对js单线程来说是很严重的,会阻断所有操作,因为在Vue2
中,用了大量的try...catch
去捕获这类错误,而Reflect
则用返回值 true/false
去替代报错
let person = {
name:'连颜少',
age:18
}
const p = new Proxy(person,{
get(target,propName) {
console.log(`这里是vue监听的动作,获取到${propName}属性`)
return Reflect.get(target,paopName)
},
set(target,propName,value) {
console.log(`这里是vue更新的动作,修改${propName}属性,去更新页面`)
Reflect.set(target,paopName,value)
},
deletePrroperty(target,propName) {
console.log(`这里是vue更新的动作,删除了${propName}属性,去更新页面`)
return Reflect.deleteProperty(target,paopName)
}
})
这里备注一个小知识点:在Vue3项目中,能用Vue2的(data、methods、computed)写法吗?
答案是:能。但是
- 1.
Vue2
的写法能取到Vue3
的数据及方法,但是Vue3
的写法取不到Vue2
的数据和方法, - 2.当
Vue2
和Vue3
的声明的属性重复时,Vue3
会覆盖Vue2
结语
下一篇会继续分析:Vue2
和Vue3
中 computed
,watch
,生命周期和hooks
等区别
如果你觉得此文对你有一丁点帮助,点个赞,给我一点儿鼓励哈~
其他有趣文章的传送门: