vue2与vue3的区别

最近一直有人在问我vue2和vue3有什么区别,今天在这里简单说哈。

根节点

vue2

<template>
  <div id="app">
  	...
  </div>
</template>

vue3
vue3中可以存在多个节点

<template>
  <div>...</div>
  <a>...</a>
  <p>...</p>
</template>

全局属性

vue2

  • 对于一些第三方插件,vue2中通常使用prototype原型来挂载到vue对象中
import Vue from 'vue'
Vue.prototype.$http=Axiox
Vue.prototype.$echart= Echart

vue3

  • vue3中提供了一个名为globalProperties的全局属性配置,可以代替vue2中的prototype
app.config.globalProperties.$http = Axios
app.config.globalProperties.$echart = Echart
  • 使用$http
import {getCurrentInstance} from 'vue'

setup () {
	const { ctx } = getCurrentInstance();
	onMounted(() => {
		console.log(ctx.$http)
	})
    .......
}

异步组件

vue2

component: () => import('@/views/homePage/index.vue'),

vue3
在vue3中引入了一个新的方法 --->defineAsyncComponent定义异步组件,来包裹vue2引入方式里面的内容

import { defineAsyncComponent } from 'vue'
 component: defineAsyncComponent(() => import('./NextPage.vue'))

建立data

vue2

export default {
  props: {
    title: String
  },
  data () {
    return {
      username: '',
      password: ''
    }
  }
}

vue3
使用reactive()方法来声名我们的数据为反应性数据
使用setup()方法来返回我们的反应性数据,从而我们的template可以获取这些反应性数据

import { reactive } from 'vue'
export default {
  props: {
    title: String
  },
  setup () {
    const state = reactive({
      username: '',
      password: ''
    })

    return { state }
  }
}

data渲染

vue2
直接写入data变量渲染

<div>Values: {{ username + ' ' + password }}</div>
export default {
  props: {
    title: String
  },
  data () {
    return {
      username: 'hu',
      password: '123'
    }
  }
}

vue3
vue3中的反应数据(Reactive Data)是包含在一个反应状态(Reactive State)变量中。所以我们需要访问这个反应状态来获取数据值。

<div>Values: {{ state.username + ' ' + state.password }}</div>
import { reactive } from 'vue'
export default {
  props: {
    title: String
  },
  setup () {
      const state = reactive({
        username: 'hu',
        password: '123'
      })
  
      return { state }
    }
}

还可以这样写,引入toRefs

<div>Values: {{ username + ' ' + password }}</div>
import { reactive ,toRefs} from 'vue'
export default {
  props: {
    title: String
  },
  setup () {
      const state = reactive({
        username: 'hu',
        password: '123'
      })
  
      return { ...toRefs(state) }
    }
}

methods

vue2

export default {
	//....
	  methods: {
		login () {
		  // 登陆方法
		}
	  }
}

vue3
我们需要先声名一个方法然后在setup()方法中返回(return),这样我们的组件内就可以调用这个方法了。

export default {
  //...
  setup () {
    const login = () => {
      // 登陆方法
    }
    return { 
      login,
    }
  }
}

生命周期钩子

vue2

export default {
  //...
  mounted () {
    console.log('组件已挂载')
  },
  methods: {
    login () {
      // login method
    }
  }
}

vue3
在vue3 生周期钩子不是全局可调用的了,需要另外从vue中引入。和刚刚引入reactive一样,生命周期的挂载钩子叫onMounted
引入后我们就可以在setup()方法里面使用onMounted挂载的钩子了。

import { reactive, onMounted } from 'vue'
export default {
  setup () {
    // ..
    onMounted(() => {
      console.log('组件已挂载')
    })
    // ...
  }
}

Computed

vue2

export default {
  // .. 
  computed: {
    num () {
      return 2 + 3
    }
  }
}

vue3
vue3 的设计模式给予开发者们按需引入需要使用的依赖包。这样一来就不需要多余的引用导致性能或者打包后太大的问题。Vue2就是有这个一直存在的问题。
在vue3 使用计算属性,我们先需要在组件内引入computed

import { reactive, onMounted, computed } from 'vue'
export default {
  props: {
    title: String
  },
  setup () {
    const state = reactive({
      a: 2,
      b: 3,
      num: computed(() => state.a + state.b)
    })
    // ...
  }

Props

vue2

export default {
  props: {
    title: String
  },
  mounted () {
      console.log('title: ' + this.title)
  }
}

vue3
在vue3 中,this无法直接拿到props属性,emit events(触发事件)和组件内的其他属性。不过全新的setup()方法可以接收两个参数
props- 不可变的组件参数
content - Vue3 暴露出来的属性(emit,slots,attrs)

setup (props) {
    // ...

    onMounted(() => {
      console.log('title: ' + props.title)
    })
	
    // ...
}

emit

vue2

login () {
      this.$emit('login', {
        username: this.username,
        password: this.password
      })
 }

vue3
在 Vue3中,我们刚刚说过this已经不是和vue2代表着这个组件了,所以我们在setup()中的第二个参数content对象中就有emit,这个是和this.$emit是一样的。那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。

setup (props, { emit }) {
    // ...
    const login = () => {
      emit('login', {
        username: state.username,
        password: state.password
      })
    }

    // ...
}

数据绑定

vue2 数据劫持
核心方法: Object.defineProperty

H5方法,所以不兼容IE8以下

初始化Vue时,对data进行了劫持,每个属性都通过Object.defineProperty变成了getter/setter,一旦数据发生改变,就会触发set,然后去更新view

let obj = {},
value = 1
Object.defineProperty(obj,'a',{
    get() {
        console.log('这里监听到了数据获取')
        return value
    },
    set(newValue, value) {
        if(newValue !== value) {
            value = newValue
            console.log('这里监听到了数据更改')
        }
    }
})
console.log(obj.a) // 这里监听到了数据获取   1
obj.a = 2 // 这里监听到了数据更改

vue3
Object.defineProperty有一些缺陷,不仅要遍历data逐个劫持,还不能监听到数组的改变,所以VUE3使用了ES6的Proxy

let data = {
    msg: {
        a: 10
    },
    arr: [1, 2, 3]
}
let handler = {
    get(target, key) {
        // 懒监听,去获取的时候才监听对象里面的对象,而不是直接递归循环监听
        console.log('获取key: ' + key)
        if (typeof target[key] === 'object' && target[key] !== null) {
            return new Proxy(target[key], handler)
        }
        return Reflect.get(target, key)
    },
    set(target, key, value) {
        let oldValue = target[key]
        console.log('更新key: ' + key)
        if (oldValue !== value) {
            // 通知view更新
        }
        return Reflect.set(target, key, value)
    }
}
let proxy = new Proxy(data, handler)
proxy.arr.push(4)

输出结果
proxy
为什么每次都有length,其实Proxy的监听数组实现是把数组变成了一个类数组对象

let arr = {
    '0': 1,
    '1': 2,
    length: 2
}

Proxy除了get,set还有deleteProperty/apply/getOwnPropertyDescriptor等等12个方法,恰好与Reflect对应,所以在这些方法里面可以实现拦截器

set(target, key, value) {
    if(key[0] === '_') {
        throw new Error('这是私有变量,不能更改')
    }
    return Reflect.set(target, key, value)
}

  转载请注明: 小浩之随笔 vue2与vue3的区别

 上一篇
了解Reflect 了解Reflect
Reflect对象是一个全局的普通的对象。Reflect的原型就是Object. Reflect对象和Object对象的区别 Reflect:是javaScript的内置对象,它不是一个函数对象,不可构造,即不可以和new一起用,所提供的
2021-08-22
下一篇 
vue3使用路由 vue3使用路由
下载 npm install vue-router@4 配置路由 暴露出一个createRouter方法,用来创建路由对象 通过defineAsyncComponent方法来实现路由的懒加载 import { defineAsy
  目录