摘要:所以整個的核心,就是如何實現(xiàn)這三樣?xùn)|西以上摘自囧克斯博客的一篇文章從版本開始這個時候的項目結(jié)構(gòu)如下源碼在里面,為打包編譯的代碼,為打包后代碼放置的位置,為測試代碼目錄。節(jié)點類型摘自資源另一位作者關(guān)于源碼解析
本項目的源碼學(xué)習(xí)筆記是基于 Vue 1.0.9 版本的也就是最早的 tag 版本,之所以選擇這個版本,是因為這個是最原始沒有太多功能拓展的版本,有利于更好的看到 Vue 最開始的骨架和脈絡(luò)以及作者的最初思路。而且能和后續(xù)的 1.x.x 版本做對比,發(fā)現(xiàn)了作者為了修復(fù) bug 而做出的很多有趣的改進甚至回退,如 vue nextTick 的版本迭代經(jīng)歷了更新、回退和再次更新
原文地址
項目地址
Vue.js 是一個典型的 MVVM 框架,整個程序從最上層分為
1 全局設(shè)計:包括全局接口、默認選項
2 vm 實例設(shè)計: 包括接口設(shè)計(vm 原型)、實例初始化過程設(shè)計(vm構(gòu)造函數(shù))
構(gòu)造函數(shù)核心的工作內(nèi)容:
整個實例初始化過程,關(guān)鍵在于將 數(shù)據(jù)(Model) 和 視圖(view)建立起關(guān)聯(lián)關(guān)系:
通過 observer 對 data 進行了監(jiān)聽,并且提供訂閱某個數(shù)據(jù)項的變化的能力
把 template 解析成一段 document fragment,然后解析其中的 directive,得到每一個 directive 所依賴的數(shù)據(jù)項及其更新方法。比如 v-text="message" 被解析之后 (這里僅作示意,實際程序邏輯會更嚴謹而復(fù)雜):
所依賴的數(shù)據(jù)項 this.$data.message,以及
相應(yīng)的視圖更新方法 node.textContent = this.$data.message
通過 watcher 把上述兩部分結(jié)合起來,即把 directive 中的數(shù)據(jù)依賴訂閱在對應(yīng)數(shù)據(jù)的 observer 上,這樣當(dāng)數(shù)據(jù)變化的時候,就會觸發(fā) observer,進而觸發(fā)相關(guān)依賴對應(yīng)的視圖更新方法,最后達到模板原本的關(guān)聯(lián)效果。
所以整個 vm 的核心,就是如何實現(xiàn) observer, directive (parser), watcher 這三樣?xùn)|西
從 v1.0.9 版本開始以上摘自囧克斯博客的一篇文章
這個時候的項目結(jié)構(gòu)如下:
源碼在 src 里面,build 為打包編譯的代碼,dist 為打包后代碼放置的位置, test 為測試代碼目錄。
從 package.json 里可以了解到項目用到的依賴包以及項目的開發(fā)和運行方式,其中編譯代碼是:
"build": "node build/build.js",
于是我們到對應(yīng)的這個文件里:
var fs = require("fs") var zlib = require("zlib") var rollup = require("rollup") var uglify = require("uglify-js") var babel = require("rollup-plugin-babel") var replace = require("rollup-plugin-replace") var version = process.env.VERSION || require("../package.json").version var banner = "/*! " + " * Vue.js v" + version + " " + " * (c) " + new Date().getFullYear() + " Evan You " + " * Released under the MIT License. " + " */" // CommonJS build. // this is used as the "main" field in package.json // and used by bundlers like Webpack and Browserify. rollup.rollup({ entry: "src/index.js", plugins: [ babel({ loose: "all" }) ] }) ...
可以知道這個時候用的是 rollup 來進行打包編譯的, 入口文件是 __src/index.js__,index.js 的代碼很簡潔:
import Vue from "./instance/vue" import directives from "./directives/public/index" import elementDirectives from "./directives/element/index" import filters from "./filters/index" import { inBrowser } from "./util/index" Vue.version = "1.0.8" /** * Vue and every constructor that extends Vue has an * associated options object, which can be accessed during * compilation steps as `this.constructor.options`. * * These can be seen as the default options of every * Vue instance. */ Vue.options = { directives, elementDirectives, filters, transitions: {}, components: {}, partials: {}, replace: true } export default Vue // devtools global hook /* istanbul ignore if */ if (process.env.NODE_ENV !== "production") { if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit("init", Vue) } }
從這里可以知道實例 vue 的實現(xiàn)在 src/instance/vue 中, 還涉及了 directives 應(yīng)該是用于指令解析的方法和 filter 過濾器,這個在 2.0 已經(jīng)不存在但在 1.0 使用比較頻繁的功能, 同時 inBrowser 應(yīng)該是用來判斷是否是瀏覽器環(huán)境,說明 src/util 是一個工具類的目錄,這里一個個驗證
工具類方法 inBrowser首先看 inBrowser__, 發(fā)現(xiàn) __util/index.js 也只是一個工具函數(shù)入口文件:
export * from "./lang" export * from "./env" export * from "./dom" export * from "./options" export * from "./component" export * from "./debug" export { defineReactive } from "../observer/index"
從字面可以知道涉及到的工具類有 語言、環(huán)境?、dom操作、options?、組件化、開發(fā)類、實時定義? 這些類型的工具, 而 inBrowser 應(yīng)該屬于 env 或者 dom,在 util/env 中找到了其實現(xiàn):
... // Browser environment sniffing export const inBrowser = typeof window !== "undefined" && Object.prototype.toString.call(window) !== "[object Object]" ...
這里利用瀏覽器的全局對象 window 做區(qū)分,因為在 nodejs 環(huán)境下是沒有 window 這個全局對象的,所以判斷 typeof window 是否不為 "undefined" 且不是由用戶自己創(chuàng)建的一個普通對象,如果是的話, Object.prototype.toString.call(window) // === [object Object]
而在瀏覽器環(huán)境下,則是這樣的情況:
typeof window // "object" Object.prototype.toString.call(window) // "[object Window]"Vue 實例構(gòu)造函數(shù)實現(xiàn)
再來看 __src/instance/vue__, 應(yīng)該是實現(xiàn)了vue的實例初始化函數(shù),從代碼可以知道是一個實例的構(gòu)造函數(shù),也是頂層實現(xiàn),底層代碼位于子目錄的 api 和 internal,分別實現(xiàn)了公用的方法和私有的方法變量等
import initMixin from "./internal/init" import stateMixin from "./internal/state" import eventsMixin from "./internal/events" import lifecycleMixin from "./internal/lifecycle" import miscMixin from "./internal/misc" import globalAPI from "./api/global" import dataAPI from "./api/data" import domAPI from "./api/dom" import eventsAPI from "./api/events" import lifecycleAPI from "./api/lifecycle" /** * The exposed Vue constructor. * * API conventions: * - public API methods/properties are prefixed with `$` * - internal methods/properties are prefixed with `_` * - non-prefixed properties are assumed to be proxied user * data. * * @constructor * @param {Object} [options] * @public */ function Vue (options) { this._init(options) } // install internals initMixin(Vue) ... // install APIs globalAPI(Vue) ... export default Vue
目錄如下:
從注釋可以知道,尤大用前綴 $ 標(biāo)記公用方法和變量,用 _標(biāo)記私有的方法和變量,沒有前綴的變量可能用來代理用戶數(shù)據(jù)
從引入的文件可以知道私有方法和變量分別有 lifecycleMixin 生命周期、eventsMixin 事件機制、stateMixin 狀態(tài)、miscMixin 過濾器, 以及實例的共有方法API: 全局 globalAPI 、數(shù)據(jù)綁定 dataAPI、DOM操作 domAPI、事件操作 eventsAPI、生命周期 lifecycleAPI
通過 initMixin(Vue) 向 Vue 的 prototype 添加原型方法:
export default function (Vue) { Vue.prototype.方法 = function(options) { ... } }
具體如何實現(xiàn)都在 api 和 internal 這兩個文件夾里面,所以 src/instance 是 vue 實例構(gòu)造函數(shù)的實現(xiàn)
directives、 filter 和 elementDirectives// src/index.js import Vue from "./instance/vue" import directives from "./directives/public/index" import elementDirectives from "./directives/element/index" import filters from "./filters/index" import { inBrowser } from "./util/index" Vue.options = { directives, elementDirectives, filters, transitions: {}, components: {}, partials: {}, replace: true } export default Vue // devtools global hook /* istanbul ignore if */ if (process.env.NODE_ENV !== "production") { if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit("init", Vue) } }
index.js 里剩下這三個都是作為 Vue.options 里的變量存在的,前面知道了 Vue 的構(gòu)造函數(shù)實現(xiàn),知道了利用 工具類 inBrowser 來判斷是否處于瀏覽器,在判斷window是否存在 __VUE_DEVTOOLS_GLOBAL_HOOK__ 這個變量, 如果存在,那么代表瀏覽器安裝了 vue 的調(diào)試插件,那么還會調(diào)用這個變量的方法 init 告訴插件已經(jīng)初始化好了 vue 對象。
從1.0 官網(wǎng)文檔 custom-directive 中可以知道 directive 是讓開發(fā)者開發(fā)自己的指令,具體例子如下
而 element-directive 和 directive 類似,只是形式上是作為一個元素存在,無法傳輸給元素數(shù)據(jù),但是可以操作元素的屬性
這是一個強大的功能,讓開發(fā)者決定數(shù)據(jù)改變時以怎樣的形式渲染到視圖里,強大的功能代碼量也不少,光 directives 里就20幾個文件
從 src/directives/public/index 這個入口文件可以知道 custom directive 含有的方法和屬性:
// text & html import text from "./text" import html from "./html" // logic control import vFor from "./for" import vIf from "./if" import show from "./show" // two-way binding import model from "./model/index" // event handling import on from "./on" // attributes import bind from "./bind" // ref & el import el from "./el" import ref from "./ref" // cloak import cloak from "./cloak" // must export plain object export default { text, html, "for": vFor, "if": vIf, show, model, on, bind, el, ref, cloak }
可以看到 directive 包含了 文本操作、邏輯操作(循環(huán)、條件)、雙向綁定(這個是比較有趣且重要的額部分)、事件綁定、數(shù)據(jù)綁定、dom綁定還有一個cloak用于未渲染完成的樣式情況
two-way binding 即 vue 中的 v-model 屬性,是對表單輸入類型的元素如 textarea、select 以及不同 type 的 input 元素做雙向綁定,其余類型的元素則不支持這種綁定
// src/directives/public/index.js import { warn, resolveAsset } from "../../../util/index" import text from "./text" import radio from "./radio" import select from "./select" import checkbox from "./checkbox" const handlers = { text, radio, select, checkbox } export default { priority: 800, twoWay: true, handlers: handlers, params: ["lazy", "number", "debounce"], /** * Possible elements: *
通過判斷元素的元素名稱來確定采用哪一種綁定和更新,對于 textarea 處理方法和 type 為 text 的 input 一樣,而 type 為 number 也和 type 為 test的一樣
這里的 priority 還不確定是干什么的
再挑其中比較常見的 text handle:
import { _toString } from "../../util/index" export default { bind () { this.attr = this.el.nodeType === 3 ? "data" : "textContent" }, update (value) { this.el[this.attr] = _toString(value) } }
它利用節(jié)點類型 nodeType 來判斷是文本還是元素, nodeType 為 3 的時候為文本節(jié)點,綁定取值方法為 this.attr["data"], 如果為元素節(jié)點,則為 this.attr["textContent"] 取得元素內(nèi)的所有文本,如果對整個 html 取 textcontent,那么就會取到所有的文本內(nèi)容。
節(jié)點類型:
(摘自http://www.w3school.com.cn/js...
資源另一位作者關(guān)于 vue 2.1.7 源碼解析
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/85027.html
摘要:特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進步。 特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...
摘要:主要特性模板渲染響應(yīng)式雙向數(shù)據(jù)綁定組件化開發(fā)路由虛擬好處初始視圖沒有優(yōu)勢,反而中間多了一層虛擬,所以性能沒有提高更新視圖優(yōu)勢明顯減少重復(fù)生成與刪除操作,減少查詢定位元素的操作,能修改操作完成的就絕不使用生成與刪除來操作腳手架是什么有什么作 vuejs主要特性? 模板渲染 響應(yīng)式雙向數(shù)據(jù)綁定 組件化開發(fā) 路由 虛擬DOM好處? 初始視圖沒有優(yōu)勢,反而中間多了一層虛擬DOM,所以性能...
這是講 ahooks 源碼的第一篇文章,簡要就是以下幾點: 加深對 React hooks 的理解。 學(xué)習(xí)如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。 培養(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫是一個對源碼閱讀不錯的選擇。 注:本系列對 ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見詳情。 第一篇主要介紹 a...
摘要:由進行開發(fā)和維護,代發(fā)布于年月,現(xiàn)在主要是。狀態(tài)是只讀的,只能通過來改變,以避免競爭條件這也有助于調(diào)試。文件大小為,而為,為。請記住,性能基準(zhǔn)只能作為考慮的附注,而不是作為判斷標(biāo)準(zhǔn)。使用的人員報告說,他們永遠不必閱讀庫的源代碼。 本文當(dāng)時寫在本地,發(fā)現(xiàn)換電腦很不是方便,在這里記錄下。 angular,react & vue 2018/07/23 2016年,對于JavaScript來說...
閱讀 1342·2021-09-24 10:26
閱讀 3655·2021-09-06 15:02
閱讀 605·2019-08-30 14:18
閱讀 577·2019-08-30 12:44
閱讀 3119·2019-08-30 10:48
閱讀 1936·2019-08-29 13:09
閱讀 1994·2019-08-29 11:30
閱讀 2279·2019-08-26 13:36