手把手教你從零寫一個簡單的 VUE--模板篇

教程目錄
1.手把手教你從零寫一個簡單的 VUE
2.手把手教你從零寫一個簡單的 VUE--模板篇css

Hello,我又回來了,上一次的文章教會了你們如何書寫一個簡單 VUE,裏面實現了VUE 的數據驅動視圖渲染模板,更新到頁面的過程,簡單的帶你們瞭解了相似 VUE 這樣子的數據驅動視圖框架的工做流程,今天我來給你們講一講做爲一個前端框架最爲核心的部分---模板,代碼仍是放在文章的最後,請隨意下載html

模板的分類

在介紹咱們實現的模板語言以前,咱們先來了解下,如今市面上比較流行的模板語言:前端

PHP/ASP/JSP風格

<%if(list.length ){%>   <ol>
      <%for(n = 0; n < list.length; ++n ){%>
        <li>
          <%=list[n]%>
        </li>
      <%}%>   
     </ol> 
   <%}%>

這種是最接近於 js 變成語言的語法,比較直觀,可是因爲存在< >的分隔符,對 IDE不太友好,不太好進行格式化處理vue

mustcache風格

{{#if list.length}}  
    <ol>
    {{#each list item}}
      <li>
        {{item}}
      </li>
    {{/each}}   
    </ol> 
    {{/if}}

這種是artTemplate默認的語法,高級語法有限,一般難自定義拓展git

DSL風格語法

<ol dsl-if="list.length">   
    <li dsl-for="item in list">
    
    </li> 
  </ol>

首先介紹下什麼是DSLDSL全稱是Domain Specific Language/DSL領域專用語言,其基本思想是求專不求全,用於解決一個類型,一個領域的問題。好比Vue裏面的v-xxxVue稱之爲指令,其實就是一個DSL,用於解決模板語法等問題,這種模板因爲在html語法裏面至關於標籤的屬性,因此對IDE友好,不會影響格式化操做。github

Vue的模板語法至關於結合了 DSL語法和 mustcache風格, 邏輯控制部分使用DSL語法,輸出展現部分使用 mustcache風格正則表達式

模板引擎設計思路

下面是這個模板引擎的思路:算法

字符串模板語法定義

首先咱們要定義一種模板語法,按照上一節的說明,咱們使用DSL風格語法,下面是咱們測試用的模板
clipboard.png
咱們採用最簡單的將模板寫在script標籤的配置方式,能夠看到咱們定義了幾個DSL,分別是dsl-if,dsl-for,dsl-html,分別用於判斷,循環和直接輸出 html,還有使用mustcache做爲字符串輸出語法。固然這個只是一個簡單的模板DSL語言,主要爲了講解思路,真正的模板須要更加多的模板語法,具體能夠參照 VUE文檔json

模板解析成爲 AST

首先解釋下什麼是AST,AST 全稱爲abstract syntax tree(抽象語法樹),是源代碼的抽象語法結構的樹狀表現形式,每種源碼均可以被抽象成爲AST,好比咱們經常使用的 js,css,json 等,均可以解析成爲 AST
把模板解析成爲AST,就是將模板的 html 結構進行解析,變成一顆附帶結構、關係、屬性的抽象樹,這樣作方便與後面咱們屢次對模板進行處理,減小了屢次解析字符串帶來的損耗,同時變成一顆樹的數據結構以後更加方便於咱們的遍歷,關於AST的優勢缺點你們能夠執行搜索,這裏就不展開說明了
上面的字符串模板解析完成以後,會變成如下的一個AST
clipboard.png
能夠看到字符串模板變成了一個object數組,每一個 obj 表明一個節點,裏面包含了這個 obj 的屬性,類型,父子關係,用到的DSL等等。這個能夠當作是咱們的模板的一箇中間態,爲咱們進行進一步處理打下了基礎。segmentfault

AST 轉換成爲 模板函數

聯繫上一篇文章,其實模板函數的構造都大同小異,基本是都是經過拼接函數字符串,而後經過Function對象轉換成一個函數,變成一個函數以後,只要傳入對應的數據,函數就會返回一個模板數據渲染好的 html 字符串。下面是例子中經過AST
clipboard.png
這是個函數體,而後使用new Function,就變成一個真正的函數了,至於這個函數體的解釋,我將放在下面具體實現進行講解

數據與模板函數結合生成 html

因爲本文主要是講模板的實現,所以數據部分仍是使用延續上一篇文章的綁定,在初始化或者數據發生改變的時候,響應的函數會對數據所關聯的模板函數進行從新調用,生成新的html,從新進行渲染。

模板的開發思路咱們就在上面都說明了,主要總結下就是將字符串模板變成 ast,ast 變成模板函數,而後就能夠結合數據進行 html 生成及渲染了

具體實現方法

首先說明下本教程的方法是對思路的實現,並不是徹底使用 vue 的實現方法,vue 是一個完整的框架,裏面涉及的東西比較多,咱們的實現是爲了讓你們更好的瞭解 vue 的原理,而非徹底實現

字符串模板變成AST部分

1.模板預處理:
因爲字符串模板是人爲處理的,所以書寫的時候可能會出現標籤不配對,標籤未關閉等問題,所以咱們要先作些預處理,來去除這些干擾,作法有不少種,好比經過一些語法分析的工具進行解析,這裏咱們使用一種比較簡單的方式進行處理,代碼以下(/src/core/render.js):
clipboard.png
能夠看到我建立了一個div標籤,而後將字符串模板放進去裏面,這樣子瀏覽器會對模板進行解析處理,而後咱們再經過innerHTML去除先後空格以後拿出來,這樣就對字符串模板進行了處理。
備註:咱們按照 vue 的規則,一個模板只有一個根節點,因此咱們取了childNodes[0]

2.生成 ast:
上面咱們對字符串模板進行了預處理,接下去咱們要將字符串模板轉換成ast,代碼比較長,你們有興趣能夠看下/src/compiler/ast/parse.js,下面說下解析思路
解析經過正則表達式配合 String.replace(regExt,fn),正則表達式爲/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g,解析出來標籤和標籤上面的屬性,而後按照需求進行存儲,就生成 ast

ast 生成模板函數

生成模板函數的思路就是遞歸遍歷ast 樹,對不一樣的類型節點,不一樣的NSL,調用不一樣的生成函數,最後組合成爲模板函數字符串,代碼以下(/src/compiler/compiler-helper.js):
clipboard.png
能夠看到,處理的函數對 DSL還有不一樣的標籤類型進行處理,而後都返回了一個輔助函數的調用,好比_i,_f,_c等等,這裏的輔助函數是在模板函數被調用的時候才真正的被調用的,下面咱們舉例說明一個輔助函數_c
clipboard.png
這個輔助函數的功能是用於生成節點,能夠看到調用了這個函數以後,對應的 ast 裏面的節點被真正生成,變成dom節點,而且會把孩子節點進行插入,經過不少輔助函數的遞歸嵌套調用,最終模板函數一調用,就能夠結合數據渲染出來真實的dom節點

下面說一個比較細的知識點,就是輔助函數的調用,咱們知道上面的輔助函數調用在生成的時候,其實都是字符串,而後經過new Function讓他變成真正的函數,那麼問題就來了,咱們知道new Function是的做用域和運行時的代碼是隔離的,是調用不到外面的_c,_f等輔助函數的,那是如何實現調用的呢,這裏用了一個咱們不多使用的關鍵字with,這個關鍵字在不少書籍裏面都不推薦使用,由於他的做用是修改with包含代碼塊的做用域,若是濫用會致使代碼的邏輯不可控,可是在模板函數裏面這個關鍵字有奇效,他能夠方便的規定把當前的代碼做用域傳到模板函數裏面,從而使得模板函數裏面能夠調用到運行時做用域的函數。你們能夠看下上一小節生成的模板函數字符串,會發現就是用整個with(that){}包裹起來的,在模板函數運行時,將當前做用域直接傳入便可,代碼以下:
clipboard.png

結合數據運行

至此,咱們已經生成了模板函數,經過傳入數據運行模板函數,就能夠生成 dom,代碼以下:
clipboard.png
能夠咱們直接把compiler_helper附帶上 data 做爲做用域,直接調用了模板函數,就能夠生成dom,再結合咱們第一篇文章寫的數據監聽,就能夠實現簡單的數據驅動視圖

後話

至此,咱們的VUE模板的基本實現已經介紹完成了,這裏主要是介紹如何去實現一個模板引擎的思路,因此功能上上面的實現不是完整的,只是實現了一些簡單的語法,你們能夠下下代碼繼續補充。

思考

細心的人可能會發現,咱們上面的模板有個問題,就是若是改了數據中的其中一個數值,那麼整個模板都得從新編譯,從新渲染,這實際上是很是損耗性能的,這其實就是我下一篇文章要講的,模板渲染的效率問題,先提出幾個關鍵詞 虛擬dom,diff 算法,最小化渲染,吊吊你們的胃口,哈哈,下一篇文章我會進行全面的介紹,相信學習完下一篇文章,你們會對現有市面上的數據驅動框架的模板部分有個全面的瞭解~下一篇文章更加精彩哦~~求關注
最後附上源碼點我點我,各位客官給個 star 唄~~

相關文章
相關標籤/搜索