vue2与vue3对比
阅读数:157 评论数:0
跳转到新版页面分类
html/css/js
正文
一、生命周期
beforeCreate #实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用
created #实例创建完成后被立即同步调用
beforeMount #挂载开始之前被调用
mounted #实例被挂载后调用
beforeDestroy #
destroyed #
beforeUpdate #数据发生改变后,DOM 被更新之前被调用
updated #虚拟 DOM 重新渲染和更新完毕之后被调用
activated #keep-alive 缓存的组件激活时调用
deactivated # keep-alive 缓存的组件失活时调用
vue3中新引入了setup生命周期,setup在beforeCreate生命周期之前执行,这个时候data与methods还没有初始化,dom没有挂载,意味着在setup方法里不能获取data、methods、computed与dom。
destroyed生命周期选项被重命名为unmounted
beforeDestroy生命周期选项被重命名为beforeUnmount
(1)vue2到vue3映射关系
beforeCreate -> use setup() 创建实例前
created -> use setup()
beforeMount -> onBeforeMount 挂载dom前
mounted -> onMounted 挂载dom后
beforeUpdate -> onBeforeUpdate 更新组件前
updated -> onUpdated 更新件后
beforeDestroy -> onBeforeUnmount 卸载销毁前
destroyed -> onUnmounted 卸载销毁后
errorCaptured -> onErrorCaptured
(2)引用时只需导入钩子并将它闪包在setup方法中即可。
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from 'vue'
export default {
setup() {
onBeforeMount(() => {
// ...
})
onMounted(() => {
// ...
})
onBeforeUpdate(() => {
// ...
})
onUpdated(() => {
// ...
})
onBeforeUnmount(() => {
// ...
})
onUnmounted(() => {
// ...
})
onActivated(() => {
// ...
})
onDeactivated(() => {
// ...
})
onErrorCaptured(() => {
// ...
})
}
}
二、绑定数据
1、vue2中使用Object.defineProperty劫持数据,即其中的get和set方法,使用发布订阅模式实现数据绑定。
var obj= { name:"banana" };
var name = "apple"
Object.defineProperty(obj, "name", {
get : function() { return name; },
set : function(newValue) { name = newValue; },
value: "banana",
writable: true, // 为 true 时, value才能被赋值运算符改变 默认是false
enumerable : true, // 能否枚举 默认是false
configurable : true // 描述符能够被改变 默认是false
});
Object.defineProperty的缺陷在于需要尝试遍历并对每个属性进行劫持,而对于没有属性数组而言,数组的索引也可以视为被支持的属性,但是和对象相同,对于新增的元素而言,不会触发监听事伯,vue对此的解决方案是支持数组原型链上的函数,即便如此也仍旧无法监听数组长度的修改。
vue2使用es5的Object.defineProperty,vue3使用es6的Proxy。Proxy可以直接监听对象而非属性,也可以直接监听数组的变化,重点还是很好的性能。
Proxy可以监听对象的基本操作,而defineProperty是对象基本操作的一个。
通过下例来理解:
<script>
var target = {
name: "xiaoming",
age: 18
}
// handler 是一个对象
const handler = {
set(target, prop, value) {
let result = Reflect.set(target, prop, value)
console.log("设置的操作" + result)
return true;
},
get(target, value) {
let result = Reflect.get(target, value)
console.log("获取的的操作" + result)
}
}
let proxy = new Proxy(target, handler);
proxy.coure = "java"
console.log(proxy)
</script>
handler可以有以下几种属性
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
三、定义数据和方法
1、vue2定义数据使用data,定义方法使用methods
export default {
name: 'App',
data(){
return {
title: 'vue3',
num: 3
}
},
methods:{
hello () {
alert('hello vue3');
}
}
}
(1)定义数据和方法在setup()方法里面,return一个对象
由于执行setup函数时,组件实例还未创建,因此this是undefined。
export default {
name: 'App',
setup(){
return {
title: 'vue3',
num: 3,
hello () {
alert('hello vue3');
}
}
}
}
<template>
<div class="container">
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<button @click="changeName">修改名字</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// 普通数据
// const obj = {
// name: 'HS',
// age: 18,
// }
// 响应式数据
const obj = reactive({
name: 'HS',
age: 18,
})
// 修改名字
const changeName = () => {
obj.name = "ZJP";
}
return { obj, changeName }
}
}
</script>
<template>
<div>
{{ name }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
name: 'App',
setup() {
// 1.响应式数据对象
const obj = reactive({
name: 'HS',
age: 18,
})
// 2.模板中只需要使用name数据
// 注意:从响应式数据对象中结构出来的属性数据,不再是响应式数据
// const { name } = obj; 不能直接解构,出来的是一个普通数据
const name = toRef(obj, 'name');
const updateName = () => {
// toRef转换响应式数据包装成对象,value是存放值的位置
name.value = "ZJP";
}
return {
name,
updateName,
}
}
}
</script>
<template>
<div>
{{ name }}
{{ age }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
name: 'App',
setup() {
// 1.响应式数据对象
const obj = reactive({
name: 'HS',
age: 18,
})
// 2.解构或者展开响应式数据对象
// const { name, age } = obj;
// console.log(name, age);
// const obj2 = { ...obj };
// console.log(obj2);
// 以上方式导致数据就不是响应式数据了
const obj3 = toRefs(obj);
console.log(obj3);
const updateName = () => {
obj3.name.value = "ZJP";
}
return {
...obj3,
updateName,
}
}
}
</script>
常用于简单类型定义为响应式数据。
<template>
<div>
{{ name }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
const name = ref("HS");
const updateName = () => {
name.value = "ZJP";
}
return {
name,
updateName,
}
}
}
</script>
<template>
<div class="container">
<div>今年:{{age}}岁</div>
<div>后年:{{newAge}}岁</div>
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed(()=>{
// 该函数的返回值就是计算属性的值
return age.value + 2
})
return {age, newAge}
}
}
</script>
另一种写法
<template>
<div class="container">
<div>今年:{{ age }}岁</div>
<div>后年:{{ newAge }}岁</div>
<input type="number" v-model="newAge" />
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup() {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed({
// get函数,获取计算属性的值
get() {
return age.value + 2
},
// set函数,当你给计算属性设置值的时候触发
set(value) {
age.value = value - 2;
}
})
return { age, newAge }
}
}
</script>
<!-- name -->
<template>
<div>
<input type="text" v-model="name1" />
<input type="text" v-model="name2" />
<input type="text" v-model="name3.aaa.bbb.ccc" />
{{ name3 }}
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from "vue";
/**
*
*/
interface bbb {
ccc: String;
}
interface aaa {
bbb: bbb;
}
interface text {
aaa: aaa;
}
const name1 = ref<string>("");
const name2 = ref<string>("");
const name3 = ref<text>({
aaa: {
bbb: {
ccc: "123",
},
},
});
// 情况一:监听单个
// watch(name1, (newValue, oldValue) => {
// console.log(newValue, oldValue);
// });
// 情况二:监听多个 --- 以数组的形式书写,同时newValue, oldValue返回值也是数组
// watch([name1, name2], (newValue, oldValue) => {
// console.log(newValue, oldValue);
// });
// 情况三:监听对象 --- 如果为ref则需要开启第三个属性 deep: true,
// --- 如果为reactive则不需要开启第三个属性
// --- 注意:监听到的对象都是新的返回值
// watch(
// name3,
// (newValue, oldValue) => {
// console.log(newValue, oldValue);
// },
// {
// deep: true,
// immediate: true, // 立即执行一次
// flush: "post", // pre 默认值 组件更新前调用 sync 同步调用 post 组件更新后调用
// }
// );
// 情况四:监听对象的某个属性,需要声明reactive,同时写成一个类似get的形式把要监听的属性返回
watch(
() => name3.aaa.bbb.ccc,
(newValue, oldValue) => {
console.log(newValue, oldValue);
}
);
</script>
<style scoped lang="scss"></style>
四、指令
(1)vue2
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
(2)vue3
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
###### 变化
prop:value => modelValue
事件:input => update:modelValue
在vue2中,v-for的优先级高。
在vue3中,v-if的优先级高。
五、修饰符
在vue2中,v-on.native修饰符把原生的事件绑定到子组件根元素上。
<my-component
v-on:close="handleComponentEvent"
v-on:click.native="handleNativeClickEvent"
/>
在vue3中,.native修饰符已被移除,同时新增emits选项
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
子组件
<script>
export default {
emits: ['close']
}
</script>
六、mixin
vue3中更推荐使用hook(组合api)
1、全局混入
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
// vue2.0 Vue.mixin({ 全局混入的选项对象 })
// vue3.0 app.mixin({ 全局混入的选项对象 })
app.mixin({
// 在任何组件 dom准备好的时候 打印一句话
methods: {
say() {
console.log(this.$el,'dom准备好了');
}
},
mounted() {
this.say();
}
})
app.mount('#app');
2、局部混入
// mixins.js
// 配置对象
export const followMixin = {
data () {
return {
loading: false
}
},
methods: {
followFn () {
this.loading = true
// 模拟请求
setTimeout(()=>{
// 省略请求代码
this.loading = false
},2000)
}
}
}
<template>
<div class="container1">
<h1>
作者:周杰伦
<a href="javascript:;" @click="followFn">{{ loading ? "请求中..." : "关注" }}</a>
</h1>
<hr />
<Son />
</div>
</template>
<script>
import Son from "./Son.vue";
import { followMixin } from "./mixins.js";
export default {
name: "App",
components: {
Son,
},
mixins: [followMixin],
};
</script>