国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Vue2 源碼分析

alin / 1677人閱讀

摘要:應用啟動一般是通過,所以,先從該構造函數著手。構造函數文件該文件只是構造函數,原型對象的聲明分散在當前目錄的多個文件中構造函數接收參數,然后調用。

源碼版本:v2.1.10

分析目標

通過閱讀源碼,對 Vue2 的基礎運行機制有所了解,主要是:

Vue2 中數據綁定的實現方式

Vue2 中對 Virtual DOM 機制的使用方式

源碼初見

項目構建配置文件為 build/config.js,定位 vue.js 對應的入口文件為 src/entries/web-runtime-with-compiler.js,基于 rollup 進行模塊打包。

代碼中使用 flow 進行接口類型標記和檢查,在打包過程中移除這些標記。為了閱讀代碼方便,在 VS Code 中安裝了插件 Flow Language Support,然后關閉工作區 JS 代碼檢查,這樣界面就清爽很多了。

Vue 應用啟動一般是通過 new Vue({...}),所以,先從該構造函數著手。

注:本文只關注 Vue 在瀏覽器端的應用,不涉及服務器端代碼。

Vue 構造函數

文件:src/core/instance/index.js

該文件只是構造函數,Vue 原型對象的聲明分散在當前目錄的多個文件中:

init.js:._init()

state.js:.$data .$set() .$delete() .$watch()

render.js:._render() ...

events.js:.$on() .$once() .$off() .$emit()

lifecycle.js:._mount() ._update() .$forceUpdate() .$destroy()

構造函數接收參數 options ,然后調用 this._init(options)。

._init() 中進行初始化,其中會依次調用 lifecycle、events、render、state 模塊中的初始化函數。

Vue2 中應該是為了代碼更易管理,Vue 類的定義分散到了上面的多個文件中。

其中,對于 Vue.prototype 對象的定義,通過 mixin 的方式在入口文件 core/index.js 中依次調用。對于實例對象(代碼中通常稱為 vm)則通過 init 函數在 vm._init() 中依次調用。

Vue 公共接口

文件:src/core/index.js

這里調用了 initGlobalAPI() 來初始化 Vue 的公共接口,包括:

Vue.util

Vue.set

Vue.delete

Vue.nextTick

Vue.options

Vue.use

Vue.mixin

Vue.extend

asset相關接口:配置在 src/core/config.js

Vue 啟動過程

調用 new Vue({...}) 后,在內部的 ._init() 的最后,是調用 .$mount() 方法來“啟動”。

web-runtime-with-compiler.jsweb-runtime.js 中,定義了 Vue.prototype.$mount()。不過兩個文件中的 $mount() 最終調用的是 ._mount() 內部方法,定義在文件 src/core/instance/lifecycle.js 中。

Vue.prototype._mount(el, hydrating)

簡化邏輯后的偽代碼:

vm = this
vm._watcher = new Watcher(vm, updateComponent)

接下來看 Watcher。

Watcher

文件:src/core/observer/watcher.js

先看構造函數的簡化邏輯:

// 參數:vm, expOrFn, cb, options
this.vm = vm
vm._watchers.push(this)
// 解析 options,略....
// 屬性初始化,略....
this.getter = expOrFn // if `function`
this.value = this.lazy ? undefined : this.get()

由于缺省的 lazy 屬性值為 false,接著看 .get() 的邏輯:

pushTarget(this) // !
value = this.getter.call(this.vm, this.vm)
popTarget()
this.cleanupDeps()
return value

先看這里對 getter 的調用,返回到 ._mount() 中,可以看到,是調用了 vm._update(vm._render(), hydrating),涉及兩個方法:

vm._render():返回虛擬節點(VNode)

vm._update()

來看 _update() 的邏輯,這里應該是進行 Virtual DOM 的更新:

// 參數:vnode, hydrating
vm = this
prevEl = vm.$el
prevVnode = vm._vnode
prevActiveInstance = activeInstance
activeInstance = vm
vm._vnode = vnode
if (!prevVnode) {
  // 初次加載
  vm.$el = vm.__patch__(vm.$el, vnode, ...)
} else {
  // 更新
  vm.$el = vm.__patch__(prevVnode, vnode)
}
activeInstance = prevActiveInstance
// 后續屬性配置,略....

參考 Virtual DOM 的一般邏輯,這里是差不多的處理過程,不再贅述。

綜上,這里的 watcher 主要作用應該是在數據發生變更時,觸發重新渲染和更新視圖的處理:vm._update(vm._render())。

接下來,我們看下 watcher 是如何發揮作用的,參考 Vue 1.0 的經驗,下面應該是關于依賴收集、數據綁定方面的細節了,而這一部分,和 Vue 1.0 差別不大。

數據綁定

watcher.get() 中調用的 pushTarget()popTarget() 來自文件:src/core/observer/dep.js。

pushTarget()popTarget() 兩個方法,用于處理 Dep.target,顯然 Dep.targetwather.getter 的調用過程中會用到,調用時會涉及到依賴收集,從而建立起數據綁定的關系。

Dep 類的 .dep() 方法中用到了 Dep.target,調用方式為:

Dep.target.addDep(this)

可以想見,在使用數據進行渲染的過程中,會對數據屬性進行“讀”操作,從而觸發 dep.depend(),進而收集到這個依賴關系。下面來找一下這樣的調用的位置。

state.js 中找到一處,makeComputedGetter() 函數中通過 watcher.depend() 間接調用了 dep.depend()。不過 computedGetter 應該不是最主要的地方,根據 Vue 1.0 的經驗,還是要找對數據進行“數據劫持”的地方,應該是defineReactive()。

defineReactive() 定義在文件 src/core/observer/index.js。

// 參數:obj, key, val, customSetter?
dep = new Dep()
childOb = observe(val)
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function () {
    // 略,調用了 dep.depend()
  },
  set: function () {
    // 略,調用 dep.notify()
  }
})

結合 Vue 1.0 經驗,這里應該就是數據劫持的關鍵了。數據原有的屬性被重新定義,屬性的 get() 被調用時,會通過 dep.depend() 收集依賴關系,記錄到 vm 中;而在 set() 被調用時,則會判斷屬性值是否發生變更,如果發生變更,則通過 dep.notify() 來通知 vm,從而觸發 vm 的更新操作,實現 UI 與數據的同步,這也就是數據綁定后的效果了。

回過頭來看 state.js,是在 initProps() 中調用了 defineReactive()。而 initProps()initState() 中調用,后者則是在 Vue.prototype._init() 中被調用。

不過最常用的其實是在 initData() 中,對初始傳入的 data 進行劫持,不過里面的過程稍微繞一些,是將這里的 data 賦值到 vm._data 并且代理到了 vm 上,進一步的處理還涉及 observe()Observer 類。這里不展開了。

綜上,數據綁定的實現過程為:

初始化:new Vue() -> vm._init()

數據劫持:initState(vm) -> initProps(), initData() -> dep.depend()

依賴收集:vm.$mount() -> vm._mount() -> new Watcher() -> vm._render()

渲染

首先來看 initRender(),這里在 vm 上初始化了兩個與創建虛擬元素相關的方法:

vm._c()

vm.$createElement()

其內部實現都是調用 createElement(),來自文件:src/core/vdom/create-element.js

而在 renderMixin() 中初始化了 Vue.prototype._render() 方法,其中創建 vnode 的邏輯為:

render = vm.$options.render
try {
  vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
  // ...
}

這里傳入 render() 是一個會返回 vnode 的函數。

接下來看 vm._update() 的邏輯,這部分在前面有介紹,初次渲染時是通過調用 vm.__patch__() 來實現。那么 vm.__patch__() 是在哪里實現的呢?在 _update() 代碼中有句注釋,提到:

    // Vue.prototype.__patch__ is injected in entry points
    // based on the rendering backend used.

在文件 web-runtime.js 中,找到了:

Vue.prototype.__patch__ = inBrowser ? patch : noop

顯然示在瀏覽器環境下使用 patch(),來自:src/platforms/web/runtime/patch.js,其實現是通過 createPatchFunction(),來自文件 src/core/vdom/patch。

OK,以上線索都指向了 vdom 相關的模塊,也就是說,顯然是 vdom 也就是 Virtual DOM 參與了渲染和更新。

不過還有個問題沒有解決,那就是原始的字符串模塊,是如何轉成用于 Virtual DOM 創建的函數調用的呢?這里會有一個解析的過程。

回到入口文件 web-runtime-with-compiler.js,在 Vue.prototype.$mount() 中,有一個關鍵的調用:compileToFunctions(template, ...),template 變量值為傳入的參數解析得到的模板內容。

模板解析

文件:src/platforms/web/compiler/index.js

函數 compileToFunctions() 的基本邏輯:

// 參數:template, options?, vm?
res = {}
compiled = compile(template, options)
res.render = makeFunction(compiled.render)
// 拷貝數組元素:
// res.staticRenderFns <= compiled.staticRenderFns
return res

這里對模板進行了編譯(compile()),最終返回了根據編譯結果得到的 render()、staticRenderFns。再看 web-runtime-with-compiler.jsVue.prototype.$mount() 的邏輯,則是將這里得到的結果寫入了 vm.$options 中,也就是說,后面 vm._render() 中會使用這里的 render()

再來看 compile() 函數,這里是實現模板解析的核心,來做文件 src/compiler/index.js,基本邏輯為:

// 參數:template, options
ast = parse(template.trim(), options)
optimize(ast, options)
code = generate(ast, options)
return {
  ast,
  render: code.render,
  staticRenderFns: code.staticRenderFns
}

邏輯很清晰,首先從模板進行解析得到抽象語法樹(ast),進行優化,最后生成結果代碼。整個過程中肯定會涉及到 Vue 的語法,包括指令、組件嵌套等等,不僅僅是得到構建 Virtual DOM 的代碼。

需要注意的是,編譯得到 render 其實是代碼文本,通過 new Function(code) 的方式轉為函數。

總結

Vue2 相比 Vue1 一個主要的區別在于引入了 Virtual DOM,但其 MVVM 的特性還在,也就是說仍有一套數據綁定的機制。

此外,Virtual DOM 的存在,使得原有的視圖模板需要轉變為函數調用的模式,從而在每次有更新時可以重新調用得到新的 vnode,從而應用 Virtual DOM 的更新機制。為此,Vue2 實現了編譯器(compiler),這也意味著 Vue2 的模板可以是純文本,而不必是 DOM 元素。

Vue2 基本運行機制總結為:

文本模板,編譯得到生成 vnode 的函數(render),該過程中會識別并記錄 Vue 的指令和其他語法

new Vue() 得到 vm 對象,其中傳入的數據會進行數據劫持處理,從而可以收集依賴,實現數據綁定

渲染過程是將所有數據交由渲染函數(render)進行調用得到 vnode,應該 Virtual DOM 的機制實現初始渲染和更新

寫在最后

對 Vue2 的源碼分析,是基于我之前對 Vue1 的分析和對 Virtual DOM 的了解,見【鏈接】中之前的文章。

水平有限,錯漏難免,歡迎指正。

感謝閱讀!

鏈接

Vue 雙向數據綁定原理分析 - luobotang

一起理解 Virtual DOM - luobotang

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81768.html

相關文章

  • vue2.0源碼分析之理解響應式架構

    摘要:分享前啰嗦我之前介紹過如何實現和。我們采用用最精簡的代碼,還原響應式架構實現以前寫的那篇源碼分析之如何實現和可以作為本次分享的參考。到現在為止,我們再看那張圖是不是就清楚很多了總結我非常喜歡,以上代碼為了好展示,都采用最簡單的方式呈現。 分享前啰嗦 我之前介紹過vue1.0如何實現observer和watcher。本想繼續寫下去,可是vue2.0橫空出世..所以 直接看vue2.0吧...

    chenatu 評論0 收藏0
  • vue2源碼框架和流程分析

    摘要:流程圖盜用一下官網關于生命周期的圖,對照之前的內容梳理一下對照上面的分析基本上可以找到各個鉤子函數的位置,下面那個銷毀的我就沒用做分析了。。。 vue整體框架和主要流程分析 之前對看過比較多關于vue源碼的文章,但是對于整體框架和流程還是有些模糊,最后用chrome debug對vue的源碼進行查看整理出這篇文章。。。。 本文對vue的整體框架和整體流程進行簡要的分析,不對某些具體的細...

    tain335 評論0 收藏0
  • Vue2 transition源碼分析

    摘要:至此算是找到了源碼位置。至此進入過渡的部分完畢。在動畫結束后,調用了由組件生命周期傳入的方法,把這個元素的副本移出了文檔流。這篇并沒有去分析相關的內容,推薦一篇講非常不錯的文章,對構造函數如何來的感興趣的同學可以看這里 Vue transition源碼分析 本來打算自己造一個transition的輪子,所以決定先看看源碼,理清思路。Vue的transition組件提供了一系列鉤子函數,...

    Genng 評論0 收藏0

發表評論

0條評論

alin

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<