初识Vue3,带你认识Vue2和Vue3的区别(一)

5,967 阅读3分钟

导语

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项目中分别打印出vmapp实例:从下面两幅图我们能看出来,Vue3实例化的app比Vue2实例化出来的vm小了很多,Vue2上的很多方法其实大家都不常用甚至没用过,在Vue3都不挂载在实例上了,实现了更轻量级

image.png

模板语法

打开App.vue文件,唯一和Vue2不同的是:Template不再强调一定要有根标签了。

setup函数

setup函数可以说是Vue3的属性和方法入口。在Vue2中,为什么使用的是datamethodscomputed。在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的方法,通过监听getset 方法去实现的响应式

image.png 那,如果声明的是对象呢是数组呢?用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代理

image.png

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. 1.新增属性,删除属性,界面不会更新,需要调用set/delete
  2. 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. 1.Vue2的写法能取到Vue3的数据及方法,但是Vue3的写法取不到Vue2的数据和方法,
  2. 2.当Vue2Vue3的声明的属性重复时,Vue3会覆盖Vue2

结语

下一篇会继续分析:Vue2Vue3computed,watch,生命周期和hooks等区别

如果你觉得此文对你有一丁点帮助,点个赞,给我一点儿鼓励哈~

其他有趣文章的传送门:

image.png