日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

vue - 响应式原理梳理(一)

發布時間:2025/3/21 vue 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vue - 响应式原理梳理(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

描述

?我們通過一個簡單的 Vue應用 來演示 Vue的響應式屬性

html:<div id="app">{{message}}</div>js:let vm = new Vue({el: '#app',data: {message: '123'}})

?在應用中,message 屬性即為 響應式屬性

?我們通過 vm.message, vm.$data.message, 可訪問 響應式屬性 message

?當我們通過修改 vm.message(vm.message = '456'), 修改后的數據會 更新到UI界面中

問題

  • 為什么修改 vm.message, 即可觸發 UI更新
  • vm.messagedata.message 的關聯關系;

官方介紹

? vue的官網文檔,對響應式屬性的原理有一個介紹。

把一個普通的 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉為 getter/setter。

每個組件實例都有相應的 watcher 實例對象,它會在組件渲染的過程中把屬性記錄為依賴,之后當依賴項的 setter 被調用時,會通知 watcher 重新計算,從而致使它關聯的組件得以更新。

?官方文檔

? 以上介紹,只是對響應式原理進行了簡單描述,并沒有深入細節。因此本文在源碼層面,對響應式原理進行梳理,對關鍵步驟進行解析。

? 響應式原理涉及到的關鍵步驟如下:

  • 構建vue實例
  • vue實例data屬性初始化,構建響應式屬性
  • 將vue實例對應的template編譯為render函數
  • 構建vue實例的watcher對象
  • 執行render函數,構建VNode節點樹,同時建立響應式屬性和watcher對象的依賴關系
  • 將VNode節點渲染為dom節點樹
  • 修改響應式屬性,觸發watcher的更新,重新執行render函數,生成新的VNode節點樹
  • 對比新舊Vnode,重新渲染dom節點樹

構造函數 - Vue

?Vue.js 給我們提供了一個 全局構造函數 Vue

?通過 new Vue(options) 生成一個 vue實例,從而可以構建一個 Vue應用

?其中,options 為構造vue實例的配置項,即為 { data, methods, computed, filter ... }

/*options:{data: {...},methods: {...},computed: {...},watch: {...}...}*/function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}// 根據options, 初始化vue實例this._init(options)}export default Vue;

? vue實例 構造完畢之后,執行實例私有方法 _init(), 開始初始化。

? 在一個 vue應用 中,存在兩種類型的 vue實例根vue實例組件vue實例

? 根vue實例,由構造函數 Vue 生成。

? 組件vue實例,由組件構造函數 VueComponent 生成,組件構造函數 繼承 自構造函數 Vue

// 全局方法extend, 會返回一個組件構造函數。Vue.extend = function(options) {...// 組件構造函數,用于創建組件var Sub = function VueComponent(options) {this._init(options);};// 子類的prototype繼承自Vue的prototype// 相當于Sub實例可以使用Vue實例的方法Sub.prototype = Object.create(Vue.prototype);...return Sub;}

? 通過一個 根vue實例 和多個 組件vue實例,構成了整個 Vue應用

Vue.prototype._init

? 在_init方法中,vue實例會執行一系列初始化操作。

? 在初始化過程中, 我們通過全局方法 initState 來初始化vue實例的 datapropsmethodscomputedwatch 屬性。

Vue.prototype._init = function(options) {var vm = this;... // 其他初始化過程, 包括建立子vue實例和父vue實例的對應關系、給vue實例添加自定義事件、執行beforeCreated回調函數等// 初始化props屬性、data屬性、methods屬性、computed屬性、watch屬性initState(vm);... // 其他初始化過程,比如執行created回調函數// vue實例初始化完成以后,掛載vue實例,將模板渲染成htmlif(vm.$options.el) {vm.$mount(vm.$options.el);}};function initState (vm: Component) {vm._watchers = [];// new Vue(options) 中的 optionsconst opts = vm.$options; // 將props配置項中屬性轉化為vue實例的響應式屬性if (opts.props) initProps(vm, opts.props); // 將 methods配置項中的方法添加到 vue實例對象中if (opts.methods) initMethods(vm, opts.methods);// 將data配置項中的屬性轉化為vue實例的響應式屬性if (opts.data) {initData(vm)} else {observe(vm._data = {}, true /* asRootData */)}...}

? 其中,initData 方法會將 data配置項 中的屬性全部轉化為 vue實例響應式屬性

initData

initData 方法的主要過程:

  • 根據data配置項,創建vue實例的私有屬性: _data
  • 通過 observe 方法,將 _data 對象中的屬性轉化為 響應式屬性
  • 通過全局方法proxy, 建立 vue實例_data 的關聯關系。
function initData(vm) {// 獲取data配置項對象var data = vm.$options.data;// 組件實例的data配置項是一個函數data = vm._data = typeof data === 'function'? getData(data, vm): data || {};// 獲取data配置項的屬性值var keys = Object.keys(data);// 獲取props配置項的屬性值var props = vm.$options.props;// 獲取methods配置項的屬性值;var methods = vm.$options.methods;var i = keys.length;while(i--) {var key = keys[i];{// methods配置項和data配置項中的屬性不能同名if(methods && hasOwn(methods, key)) {warn(("method \"" + key + "\" has already been defined as a data property."),vm);}}// props配置項和data配置項中的屬性不能同名if(props && hasOwn(props, key)) {"development" !== 'production' && warn("The data property \"" + key + "\" is already declared as a prop. " +"Use prop default value instead.",vm);} else if(!isReserved(key)) { // 如果屬性不是$,_ 開頭(vue的保留屬性)// 建立 vue實例 和 _data 的關聯關系性proxy(vm, "_data", key);}}// 觀察data對象, 將對象屬性全部轉化為響應式屬性observe(data, true /* asRootData */);}

observe

? 全局方法 observe 的作用是用來觀察一個對象,將_data對象的屬性全部轉化為 響應式屬性

// observe(_data, true)function observe(value, asRootData) {if(!isObject(value)) {return}var ob;...// ob = new Observer(value);...return ob;}var Observer = function Observer(value) {...if(Array.isArray(value)) {// 如果value是數組,對數組每一個元素執行observe方法this.observeArray(value);} else {// 如果value是對象, 遍歷對象的每一個屬性, 將屬性轉化為響應式屬性this.walk(value);}};// 如果要觀察的對象時數組, 遍歷數組,然后調用observe方法將對象的屬性轉化為響應式屬性Observer.prototype.observeArray = function observeArray(items) {for(var i = 0, l = items.length; i < l; i++) {observe(items[i]);}};// 遍歷obj的屬性,將obj對象的屬性轉化為響應式屬性Observer.prototype.walk = function walk(obj) {var keys = Object.keys(obj);for(var i = 0; i < keys.length; i++) {// 給obj的每一個屬性都賦予getter/setter方法。// 這樣一旦屬性被訪問或者更新,這樣我們就可以追蹤到這些變化defineReactive(obj, keys[i], obj[keys[i]]);}};

defineReactive

? 通過 defineProperty 方法, 提供屬性的 getter/setter 方法。

? 讀取 屬性時,觸發 getter,將與響應式屬性相關的vue實例保存起來。

? 修改 屬性時,觸發 setter,更新與響應式屬性相關的vue實例。

function defineReactive(obj, key, val, customSetter, shallow) {// 每一個響應式屬性都會有一個 Dep對象實例, 該對象實例會存儲訂閱它的Watcher對象實例var dep = new Dep();// 獲取對象屬性key的描述對象var property = Object.getOwnPropertyDescriptor(obj, key);// 如果屬性是不可配置的,則直接返回if(property && property.configurable === false) {return}// 屬性原來的getter/settervar getter = property && property.get;var setter = property && property.set;// 如果屬性值是一個對象,遞歸觀察屬性值,var childOb = !shallow && observe(val);// 重新定義對象obj的屬性keyObject.defineProperty(obj, key, {enumerable : true,configurable : true,get : function reactiveGetter() {// 當obj的某個屬性被訪問的時候,就會調用getter方法。var value = getter ? getter.call(obj) : val;// 當Dep.target不為空時,調用dep.depend 和 childOb.dep.depend方法做依賴收集if(Dep.target) {// 通過dep對象, 收集依賴關系dep.depend();if(childOb) {childOb.dep.depend();}// 如果訪問的是一個數組, 則會遍歷這個數組, 收集數組元素的依賴if(Array.isArray(value)) {dependArray(value);}}return value},set : function reactiveSetter(newVal) {// 當改變obj的屬性是,就會調用setter方法。這是就會調用dep.notify方法進行通知var value = getter ? getter.call(obj) : val;/* eslint-disable no-self-compare */if(newVal === value || (newVal !== newVal && value !== value)) {return}/* eslint-enable no-self-compare */if("development" !== 'production' && customSetter) {customSetter();}if(setter) {setter.call(obj, newVal);} else {val = newVal;}childOb = !shallow && observe(newVal);// 當響應式屬性發生修改時,通過dep對象通知依賴的vue實例進行更新dep.notify();}});}

? 響應式屬性, 通過一個 dep 對象, 收集依賴響應式屬性的vue實例,在屬性改變時 通知vue實例更新

? 一個 響應式屬性, 對應一個 dep 對象。

Dep

? 在觀察者設計模式中,有兩種角色:SubjectObserver

? Subject 會維護一個 Observer的依賴列表。當 Subject 發生變化時,會通知 Observer 更新。

? 在vue中,響應式屬性作為Subject, vue實例作為Observer, 響應式屬性的更新會通知vue實例更新。

? 響應式屬性通過 dep 對象來收集 依賴關系 。一個響應式屬性,對應一個dep對象。

var Dep = function Dep() {// dep對象的idthis.id = uid++;// 數組,用來存儲依賴響應式屬性的Observerthis.subs = [];};// 將Observer添加到dep對象的依賴列表中Dep.prototype.addSub = function addSub(sub) {// Dep對象實例添加訂閱它的Watcherthis.subs.push(sub);};// 將Observer從dep對象的依賴列表中刪除Dep.prototype.removeSub = function removeSub(sub) {// Dep對象實例移除訂閱它的Watcherremove(this.subs, sub);};// 收集依賴關系Dep.prototype.depend = function depend() {// 把當前Dep對象實例添加到當前正在計算的Watcher的依賴中if(Dep.target) {Dep.target.addDep(this);}};// 通知Observer更新Dep.prototype.notify = function notify() {// stabilize the subscriber list firstvar subs = this.subs.slice();// 遍歷所有的訂閱Watcher,然后調用他們的update方法for(var i = 0, l = subs.length; i < l; i++) {subs[i].update();}};

proxy

? 通過 defineProperty 方法, 給vue實例對象添加屬性,提供屬性的 getter/setter 方法。

? 讀取vue實例的屬性( data配置項中的同名屬性 ), 觸發 getter,讀取 _data 的同名屬性。

? 修改vue實例的屬性( data配置項中的同名屬性 ), 觸發 setter,修改 _data 的同名屬性。

// proxy(vm, _data, 'message')function proxy(target, sourceKey, key) {sharedPropertyDefinition.get = function proxyGetter() {return this[sourceKey][key]};sharedPropertyDefinition.set = function proxySetter(val) {this[sourceKey][key] = val;};Object.defineProperty(target, key, sharedPropertyDefinition);}

? 通過 proxy 方法,vue實例 可代理私有屬性 _data, 即通過 vue實例 可以訪問/修改 響應式屬性

總結

? 結合源碼理解, 響應式屬性 的原理為:

總結

以上是生活随笔為你收集整理的vue - 响应式原理梳理(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。