1. 程式人生 > >Vue.js 原始碼實現

Vue.js 原始碼實現

目錄

  • Vue.js 程式碼實現
    • 1. 步驟一
    • 2. 步驟二
    • 3.步驟三
  • Vue.js 工作機制
    • 初始化
    • 編譯
    • 響應式
    • 虛擬dom
    • 更新檢視
    • 編譯

Vue.js 程式碼實現

檢驗學習效果的最好方法就是自己造輪子。最近在學習Vue原始碼,寫了一個迷你版vue,實現資料響應式。從step1到step3.2,是開發步驟和實現思路,每一步都可以獨立執行。

程式碼地址:https://github.com/dora-zc/miniature-vue

目錄結構

.

├── README.md

├── step0

│ └── defineProperty_test.html

├── step1

│ ├── XVue.js

│ └── index.html

├── step2

│ ├── XVue.js

│ └── index.html

├── step3.1

│ ├── XVue.js

│ ├── compile.js

│ └── index.html

└── step3.2

​ ├── XVue.js

​ ├── compile.js

​ └── index.html

以上每個step資料夾對應下面的每一步驟,代表了程式碼實現的順序,每個資料夾下的程式碼都可以獨立執行。

1. 步驟一

建立XVue.js。

建立Vue類,通過Observer劫持監聽所有屬性。

observe函式的作用:遞迴遍歷data選項,它當中的defineReactive函式為data中每一個key定義getter和setter,達到資料劫持的目的。

步驟一對應程式碼目錄:step1

2. 步驟二

處理頁面上的<div>{{msg}}</div>,也就是收集依賴,當msg的值發生變化時,檢視需要做出相應的變化。因此需要建立依賴管理器,把所有依賴儲存起來,當資料發生變化的時候再去更新對應的依賴。

2.1 建立Dep類

Dep負責將檢視中的所有依賴收集管理,包括依賴新增和派發通知

1- 在Dep類中建立陣列deps=[],用來存放Watcher的例項

2-建立addDep方法,新增Watcher

3-建立notify方法,通知所有的Wather執行更新。遍歷deps陣列,呼叫每個Wather的更新方法

2.2 建立監聽器Watcher類

Watcher是具體的更新執行者。

1-將當前Watcher例項新增到Dep.target上。

Dep.target = this

之後在get時,就能通過Dep.target拿到當前Watcher的例項。

2-建立update方法

3-set方法中,呼叫dep.notify,讓依賴管理器通知更新,則所有的Watcher會執行update方法

那麼問題來了:Watcher在什麼時候收集最合適?

在defineReactive函式的get方法中,get方法觸發時,把Watcher放進Dep.target中。

那麼問題又來了:為什麼是在get方法中呢?

因為在掃描檢視中的依賴時,如果掃描到<div>{{msg}}</div>,此時一定會去訪問msg的值,就會觸發get。一旦get被觸發,就能將Watcher放進dep中,實現依賴收集的目的。所以get是一個合適的時間點。

程式碼測試:在get中輸出dep.deps,如果Watcher已經放進去了,並且控制檯打印出Watcher中的update方法中的log,說明這一步操作成功了。

至此,已經完成的工作如下:

步驟二對應程式碼目錄:step2

現在,Watcher發生變化時,檢視還沒有更新,下面我們將要完成檢視更新的操作。

首先,需要Compile對介面模板解析指令,進行編譯,編譯的階段實際是建立Watcher的階段。Watcher是由編譯器建立的。編譯器在做依賴收集的時候,順便把Watcher建立了。Watcher在建立的時候,立刻就能知道它將來要更新的是誰,它應該被誰管理,它發生變化以後值應該是什麼。於是Watcher就知道調誰(Updater去做更新了)。

3.步驟三

建立compile.js,用於掃描模板中所有依賴(指令、插值、繫結、事件…),建立更新函式和Watcher

3.1 掃描模板

1-建立編譯器Compile類,接收兩個引數,el(宿主元素或選擇器)和vm(當前vue例項)

2-建立node2Fragment函式,將dom節點( $el )截成程式碼塊( 轉換為Fragment )來處理,而不是直接做dom操作,提高執行效率

3-建立compile函式,執行編譯( 將模板中的動態值替換為真實的值 ),傳入程式碼塊

4-將生成的結果追加至宿主元素

3.1.1 node2Fragment函式

建立一個新的fragment,將原生節點移動至fragment

返回fragment,傳給編譯函式進行編譯

3.1.2 compile函式

獲取所有的孩子節點,進行遍歷,判斷節點型別,並作出相應的判斷

處理元素節點

處理文字節點( 只處理{{msg}} 這種情況,其他的全部不處理)

...其他的節點型別暫時不判斷了

遍歷可能存在的子節點,往下遞迴

下面是compile函式中的兩個核心方法

1-compileElement方法:編譯元素節點

<div v-text="test" @click="onClick">{{msg}}</div>

拿到所有屬性名稱,進行遍歷

2-compileText方法:編譯文字節點

程式碼測試:

在XVue constructor中,建立編譯器例項,將宿主元素el和當前vue例項作為引數傳入。

如果compileElement和compileText兩個函式能觸發,控制檯打印出"開始編譯元素節點"和"開始編譯文字節點",則說明功能正常,可以繼續讓下走了。

對應程式碼:step3.1

3.2 編譯元素節點和文字節點,並建立更新函式

3.2.1 編譯元素節點compileElement方法實現

獲取節點所有屬性,進行遍歷。判斷指令和事件,已經相應的處理方法。

指令只試著處理v-text,v-html,v-model三個,其他的暫不處理

v-model:雙向繫結還需要處理檢視對模型的更新

3.2.2 建立更新器函式

更新器函式:接收四個引數,node,vm,exp,dir(指令)

針對指令的更新器主要是在做dom操作

在更新器函式中建立Watcher例項,當Watcher監聽到變化的時候,就能觸發檢視的更新。

至此,全部程式碼已經完成,雙向資料繫結順利實現!

對應程式碼:step3.2

Vue.js 工作機制

初始化

在new Vue()之後,Vue會呼叫初始化函式,會初始化宣告週期、事件、props、methods、data、computed和watcher等。其中最重要的是通過Object.defineProperty設定setter和getter,用來實現響應式和依賴收集。

初始化之後會呼叫$.mount掛載元件。

編譯

編譯模組分為三個階段:

1-parse

使用正則解析模板中的vue的指令、變數等等,形成抽象語法樹AST

2-optimize

標記一些靜態節點,用作後面的效能優化,在diff的時候直接略過

3-generate

把第一步生成的AST轉化為渲染函式 render function

響應式

這一塊是vue最核心的內容。初始化的時候通過defineProperty進行繫結,設定通知的機制,當編譯生成的渲染函式被實際渲染的時候,會觸發getter進行依賴收集,在資料變化的時候,觸發setter進行更新。

虛擬dom

虛擬dom是由react首創,Vue2開始支援,就是用JavaScript物件來描述dom結構,資料修改的時候,我們先修改虛擬dom中的資料,然後陣列做diff演算法,最後再彙總所有的diff,力求做最少的dom操作,畢竟js裡對比很快,而真實的dom操作太慢了。

<div name="小菠蘿" style="color:red" @click="xx">
   <a>click me</a>
</div>
// vdom
{
   tag:'div',
   props:{
      name:'小菠蘿',
      style: {color:red},
      onClick:xx
   },
   children:[
      {
        tag:'a',
        text:'click me'
      }
   ]
}

更新檢視

資料修改觸發setter,然後監聽器會通知進行修改,通過對比兩個dom樹,得到改變的地方,就是patch,然後只需要把這些差異修改即可。

編譯

compile的核心邏輯是獲取dom,遍歷dom,獲取{{}}格式的變數,以及每個dom的屬性,擷取v-和@開頭的部分來設定響應式。

相關推薦

Vue.js 原始碼實現

目錄 Vue.js 程式碼實現 1. 步驟一 2. 步驟二 3.步驟三 Vue.js 工作機制 初始化 編譯 響應式

Vue.js 原始碼全方位深入解析 學精學透 Vue 原理實現

第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-文件1-

vue.js+element實現簡單的後臺管理系統(一)

最近公司趕專案,要求做一個後臺管理系統,靜態半天,介面一天,測試一天。 看了一下需求,10個頁面,16+介面,雖說調取資料的比較多,實際寫起來感覺東西還是蠻多的,也在網上查閱了很多資料,感覺都是一些開箱即用的後臺管理系統,一開始我也是想拿來修改一下就好,結果看了好幾個github上面的,感覺並不是很貼合需求

Vue.js 原始碼解析

介紹 Vue.js原始碼分析,記錄了個人學習Vue.js原始碼的過程中的一些心得以及收穫。以及對於Vue框架,周邊庫的一些個人見解。 在學習的過程中我為Vue.js(2.3.0)、Vuex(2.4.0)、Vue-router(3.0.1)加上了註釋,分別在資料夾vue-src、vuex-sr

angular.jsvue.js實現函數去抖(debounce)

搜索輸入框 sea class 方案 get clas 電路 dia ive 問題描述 搜索輸入框中,只當用戶停止輸入後,才進行後續的操作,比如發起Http請求等。 學過電子電路的同學應該知道按鍵防抖。原理是一樣的:就是說當調用動作n毫秒後,才會執行該動作,若在這n毫秒內又

Vue.js原始碼 生命週期 LifeCycle 學習

callHook 。  我們來看看 callHook 程式碼: export function callHook (vm: Component, hook: string) {   const handlers = vm.$options[hook] // 獲取Vue選項中的

10分鐘快速精通rollup.js——Vue.js原始碼打包原理深度分析

前言 本教程是rollup.js系列教程的最後一篇,我將基於目前非常流行的Vue.js框架,深度分析Vue.js原始碼打包過程,讓大家深入理解複雜的前端框架是如何利用rollup.js進行打包的。通過這一篇教程的學習,相信大家可以更好地應用rollup.js為自己的專案服務。 說明:本教程基於Vue

vue-router 原始碼實現前端路由的兩種方式

在學習 vue-router 的程式碼之前,先來簡單瞭解一下前端路由。 前端路由主要有兩種實現方法: Hash 路由 History 路由 先來看看這兩種方法的實現原理。 接著我們將用它們來簡單實現一個自己的前端路由。 前端路由 Hash 路由 u

從template到DOM(Vue.js原始碼角度看內部執行機制)

寫在前面 這篇文章算是對最近寫的一系列Vue.js原始碼的文章(https://github.com/answershuto/learnVue)的總結吧,在閱讀原始碼的過程中也確實受益匪淺,希望自己的這些產出也會對同樣想要學習Vue.js原始碼的小夥伴有所幫助。之前這篇文章同樣在我司(大搜車)的

Vue.js原始碼——事件機制

寫在前面 因為對Vue.js很感興趣,而且平時工作的技術棧也是Vue.js,這幾個月花了些時間研究學習了一下Vue.js原始碼,並做了總結與輸出。 文章的原地址:https://github.com/answershuto/learnVue。 在學習過程中,為Vue加上了中文的註釋https:/

Vue.js原始碼看非同步更新DOM策略及nextTick

寫在前面 因為對Vue.js很感興趣,而且平時工作的技術棧也是Vue.js,這幾個月花了些時間研究學習了一下Vue.js原始碼,並做了總結與輸出。 文章的原地址:https://github.com/answershuto/learnVue。 在學習過程中,為Vue加上了中文的註釋https:/

Vue.js原始碼目錄及構建

1.Vue.js 目錄結構 下面是Github上Vue原始碼的目錄結構 其中src為原始碼部分,結構如下: src ├── compiler # 編譯相關 ├── core # 核心程式碼 ├── platforms

最新Vue.js 原始碼全方位深入解析分享

第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-文件1-6 Vue.js

vue.js如何實現購物車加減操作

vue.js如何實現購物車加減操作 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta htt

Vue.js 原始碼全方位深入解析(完整版)

. 第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-文

Vue.js 原始碼全方位深入解析(已完結)

第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-文件

某課實戰Vue.js 原始碼全方位深入解析

第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-文件

某網Vue.js 原始碼全方位深入解析(完整版)

第1章 準備工作介紹了 Flow、Vue.js 的原始碼目錄設計、Vue.js 的原始碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文件1-4 認識 Flow1-5 Vue.js 原始碼目錄設計-

Vue.js 原始碼全方位深入解析

10-1 keep-alive(1) 10-2 keep-alive(2) 10-3 keep-alive(3) 10-4 keep-alive(4) 10-5 keep-alive(5) 10-6 keep-alive(6) 10-7 transition(1) 10-8 tra

vue.js簡單實現checkbox全選,反選,多選

1.html <table> <tr> <th class=""> <div class=""> <input type="checkbox" id="checkAll" @click="cli