目录
Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))
Vue3 :ES6的Proxy API对数据代理( track 收集依赖, trigger 触发更新)
Vue2 :Options API选项类型(属性data、methods、computed)
Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)
onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)
diff方法优化:只对比有静态标记(patchflag)的节点
静态提升:不参与更新的元素,只会被创建一次,在渲染时直接复用即可。
computed:监听多值,有缓存,依赖值发生变化触发重算,用于计算
immediate: 【默认false】,是否在组件创建时立即执行一次
vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))
nextTick:DOM更新之后执行回调函数,返回一个Promise
vue2:基于promise>MutationObserver>setImmediate>setTimeout实现
vue3:{ proxy, appContext } = getCurrentInstance()
核心变化
响应式原理
Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))
Vue3 :ES6的Proxy API对数据代理( track 收集依赖, trigger 触发更新)
API 类型
Vue2 :Options API选项类型(属性data
、methods
、computed
)
选项在组件实例化后会被合并到组件实例中,可以在组件内部访问和使用。
Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)
定义数据变量和方法
Vue2:数据放在data选项 中
Vue3 :setup()方法
生命周期
Vue3
使用:先导入
// vue3
<script setup>
import { onMounted } from 'vue'; // 使用前需引入生命周期钩子
onMounted(() => {
// ...
});
// 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不会被覆盖
onMounted(() => {
// ...
});
</script>
// vue2
<script>
export default {
mounted() { // 直接调用生命周期钩子
// ...
},
}
</script>
合并:beforeCreate+created=
setup
新增on:destroy->unmount
onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
},
methods: {
throwError() {
// 人为地抛出一个错误
throw new Error('An error occurred.');
}
},
onErrorCaptured(error, component, details) {
console.error('Captured an error in component:', component);
console.error('Error details:', error);
console.error('Details:', details);
// 可以选择返回 false 来阻止错误继续传播
// return false;
}
};
</script>
Fragment碎片组件:树
本质上 Vue3 每个组件还是一个根节点,因为 DOM 树只能是树状结构的,只是 Vue3 在编译阶段新增了判断,如果当前组件不只一个根元素,就添加一个 fragment
组件把这个多根组件的给包起来,相当于这个组件还是只有一个根节点。而 fragment
跟 keep-alive
一样是一个不会被渲染出来的内置组件
Vue2 不支持碎片
Vue3 支持碎片,可以拥有多个根节点
// vue2中在template里存在多个根节点会报错
<template>
<header></header>
<main></main>
<footer></footer>
</template>
// 只能存在一个根节点,需要用一个<div>来包裹着
<template>
<div>
<header></header>
<main></main>
<footer></footer>
</div>
</template>
非核心变化【但重要,可根据标题引导一波提问】
优化
diff方法优化:只对比有静态标记(patchflag)的节点
静态提升:不参与更新的元素,只会被创建一次,在渲染时直接复用即可。
vue2无论元素是否参与更新,每次都会重新创建然后再渲染
事件侦听器缓存
默认情况下onClick会被视为动态绑定,所以每次都会追踪它的变化,但是因为是同一个函数,所以不用追踪变化,直接缓存起来复用即可
按需引入:treeSharking 体积更小
computed:监听多值,有缓存,依赖值发生变化触发重算,用于计算
计算属性本质上是 computed watcher
缓存通过dirty
控制
初始化时,计算属性并不会立即计算(vue的优化)
都支持函数(仅可读)、对象字面量(可写)写法
vue2 函数
运行 this.fullName = 'John Doe'
时,收到警告
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed:{
// fullName计算属性的 getter
fullName(){
return this.firstName + ' ' + this.lastName
}
}
}
}
vue3对象字面量
运行 this.fullName = 'John Doe'
时,setter 会被调用, this.firstName
和 this.lastName更新
<template>
<div>
<p>Full Name: {{ fullName }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// Note: we are using destructuring assignment syntax here.
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
watch:监听值,无缓存,用于执行操作
watch本质上是 user watcher
vue2 属性(配置项:immediate,deep)
immediate:
【默认false】,是否在组件创建时立即执行一次
deep:
【默认false】,是否实现深度监听
watch: {
// 监听 userId 的变化,触发 getData 方法
userId: 'getData',
// 监听 userName 的变化,触发 getData 方法
userName(newName, oldName) {
this.getData()
},
// 监听 userInfo 的变化,触发 getData 方法
userInfo: {
// 处理函数,在 userInfo 发生变化时触发
handler(newVal, oldVal) {
this.getData()
},
// 在组件创建时立即执行一次处理函数
immediate: true,
// 深度监听 userInfo 内部的属性变化
deep: true
}
}
vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))
<script setup>
import { watch, ref, reactive } from 'vue'
const name = ref('沐华')
const data = reactive({
age: 18,
money: 100000000000000000000,
children: []
})
// 监听 ref 属性
watch(name, (newName, oldName) => { ... })
// 监听其他属性、路由或者状态管理的都这样
watch(
() => data.age,
(newAge, oldAge) => { ... }
)
// 监听多个属性,数组放多个值,返回的新值和老值也是数组形式
watch([data.age, data.money], ([newAge, newMoney], [oldAge, oldMoney]) => { ... })
// 第三个参数是一个对象,为可配置项,有5个可配置属性
watch(data.children, (newList, oldList) => { ... }, {
// 这两个和 Vue2 一样,没啥说的
immediate: true,
deep: true,
// 回调函数的执行时机,默认在组件更新之前调用。更新后调用改成post
flush: 'pre', // 默认值是 pre,可改成 post 或 sync
// 下面两个是调试用的
onTrack (e) { debugger }
onTrigger (e) { debugger }
})
</script>
nextTick:DOM更新之后执行回调函数,返回一个Promise
function nextTick(callback?: () => void): Promise<void>
“tick”(事件循环)
在 Vue 中更改响应式状态时, Promise.then()将DOM更新事件缓存在微任务队列中
vue2:基于promise>MutationObserver>setImmediate>setTimeout实现
执行优先级:微任务(
MutationObserver>promise)>宏任务(
setImmediate>setTimeout)
setImmediate
在当前事件循环的结束后立即执行
setTimeout
则是在指定的延迟时间后才执行。
因此,setImmediate
的执行时机比 setTimeout
更早,更接近于微任务的执行时机,响应更快
async increment() {
this.count++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
vue3:基于Promise实现
vue3
基于 Proxy 响应式,而Proxy基于promise
<script setup>
import { nextTick} from 'vue'
// 方式 一
const handleClick = async () => {
await nextTick()
console.log('')
}
// 方式二
nextTick(() => {
console.log('')
})
// 方式三
nextTick().then(() => {
console.log('')
})
</script>
this
vue2:this=组件实例($router,$ref)
vue3:{ proxy, appContext } = getCurrentInstance()
<script setup>
import { getCurrentInstance } from 'vue'
// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext } = getCurrentInstance()
// 这个 global 就是全局实例
const global = appContext.config.globalProperties
</script>