Vue 3.0 中的響應(yīng)式是如何實(shí)現(xiàn)的?
(????)??嗨,我是你穩(wěn)定更新、持續(xù)輸出的勾勾。

在上篇《當(dāng)面試官問:為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?》中,我們梳理了響應(yīng)式原理的相關(guān)內(nèi)容。本篇,來看看源碼。
Vue2 源碼解讀
首先找到響應(yīng)式代碼的處理位置:

看完Vue2.x 響應(yīng)式的代碼,我們?cè)倩剡^頭來思考最開始的問題,為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?
為什么重寫?如果之前好好的,重寫就沒有意義。
那之前存在什么問題,換句話問就是 defineProperty 有什么問題?
Object.defineProperty 的問題
其實(shí), defineProperty 的問題,在 Vue2.x 的手冊(cè)中已經(jīng)說過了。
“哎,很多人就是不看文檔啊”
https://cn.vuejs.org/v2/guide/reactivity.html#%E5%AF%B9%E4%BA%8E%E6%95%B0%E7%BB%84
下面分別使用 Vue2 和 Vue3 實(shí)現(xiàn)了一個(gè)小功能,代碼一模一樣,功能當(dāng)然也一樣,但是,在 Vue2 中就會(huì)有 Bug,而運(yùn)行在 vue3 中的,則沒有任何問題。
Vue2:
<template><div class="about"><h1>This is an about pageh1><p v-for="(v, k) in users">{{ v.names }}p><button @click="changes">更新button>div>template><script>export default {data() {return {users: [{ id: 1, names: "路飛-v2" },{ id: 2, names: "鳴人-v2" },],};},methods: {changes() {// this.users[0] = {id:'0',names:'liuneng'}// this.users[1].names = 'lnsdsdfg'this.users[1] = { id: "1", names: "劉能-v2" };},},};script><style lang="stylus" scoped>style>
Vue3:
<template><div class="about"><h1>This is an about pageh1><p v-for="(v, k) in users">{{ v.names }}p><button @click="changes">更新button>div>template><script>export default {data() {return {users: [{ id: 1, names: "路飛-v3" },{ id: 2, names: "鳴人-v3" },],};},methods: {changes() {// this.users[0] = {id:'0',names:'liuneng'}// this.users[1].names = 'lnsdsdfg'this.users[1] = { id: "1", names: "劉能-v3" };},},};script>
其核心點(diǎn)在于 defineProperty 不能很好的實(shí)現(xiàn)對(duì)數(shù)組下標(biāo)的監(jiān)控,而在 Vue2 的實(shí)現(xiàn)代碼中,沒有更好的方案對(duì)此進(jìn)行改善,尤大索性直接放棄了實(shí)現(xiàn)。
關(guān)于這個(gè)問題,尤大也在 github 做過回應(yīng),截個(gè)圖給大家看看。

那么,Vue 目前還沒有解決的問題,Vue3 中顯然是已經(jīng)解決了。
問題是,Vue3 是如何解決的呢?
前面在 Vue3 的代碼中我們使用的是傳統(tǒng)的 Options Api 來實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式,而在 Vue3 中全新的 Composition Api 也實(shí)現(xiàn)了響應(yīng)式系統(tǒng)。
我們先來感受一下 Composition Api 的基礎(chǔ)用法.
Composition API 的響應(yīng)式系統(tǒng)
ref 響應(yīng)式
<template><button @click="addNu"> Composition API: {{nu}}button>template><script>// 引入 refimport {ref} from "vue"export default {setup() {// 定義 ref 響應(yīng)式數(shù)據(jù)const nu = ref(1);// 定義函數(shù)function addNu(){nu.value++;}// 將數(shù)據(jù)和方法返回,即可在模板中直接使用return {nu,addNu};},};script>
reactive 響應(yīng)式
<template><button @click="addNu"> Composition API: {{nu}}button><h2>{{revData.name}}h2><h3 @click="ageAdd">年齡:{{revData.age}}h3><p v-for="(v,k) in revData.skill"> {{v}} p>template><script>// 引入 refimport {reactive, ref} from "vue"export default {setup() {// 定義 ref 響應(yīng)式數(shù)據(jù)const nu = ref(1);// reactive 響應(yīng)式數(shù)據(jù)const revData = reactive({name: '路飛',age: 22,skill:['橡膠機(jī)關(guān)槍','吃雞腿'],})function ageAdd(){revData.age++}// 定義函數(shù)function addNu(){nu.value++;}// 將數(shù)據(jù)和方法返回,即可在模板中直接使用return {nu,addNu,revData,ageAdd};},};script>
Vue3 中的響應(yīng)式是如何實(shí)現(xiàn)的呢?
關(guān)鍵點(diǎn)在于 Proxy 函數(shù)。
Proxy 實(shí)現(xiàn)原理
使用 Proxy 實(shí)現(xiàn)的響應(yīng)式代碼,要比使用 defineProperty 的代碼簡(jiǎn)單得多。
因?yàn)?Proxy 天然地能夠?qū)φ麄€(gè)對(duì)象做監(jiān)聽,而不需要對(duì)數(shù)據(jù)行遍歷后做監(jiān)聽,同時(shí)也就解決了數(shù)組下標(biāo)的問題。
我們來一段模擬代碼看一下:
<div id="app">hellodiv><script>// 模擬 Vue 中的 data 選項(xiàng)let data = {msg: 'hello',count: 0}// 模擬 Vue 實(shí)例const vm = new Proxy(data, {// 執(zhí)行代理行為的函數(shù)// 當(dāng)訪問 vm 的成員會(huì)執(zhí)行get (target, key) {console.log('get, key: ', key, target[key])return target[key]},// 當(dāng)設(shè)置 vm 的成員會(huì)執(zhí)行set (target, key, newValue) {console.log('set, key: ', key, newValue)if (target[key] === newValue) {return}target[key] = newValuedocument.querySelector('#app').textContent = target[key]}})// 測(cè)試vm.msg = 'Hello World'console.log(vm.msg)script>
好了,如果面試官再問你“為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)”,你該知道怎么回答了叭(●'?'●)。
推薦閱讀:
當(dāng)面試官問:為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?
喜大普奔!Element UI for Vue 3.0 來了!
點(diǎn)點(diǎn)“贊”和“在看”,保護(hù)頭發(fā),減少bug。
