iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

Web Component 網頁元件之路系列 第 25

[Day25] - Vue 的 Html 字串處理 ( compiler 介紹 )

day-13 介紹 , 當資料改變時 , 我們可以利用 _render 來更新 dom

每次更新 isLogin 的資料時 , 就會用 innerHTML 更新全部的 dom

當我們用 devtool 中觀察時會發現好多區壞都被 reRender 連與 isLogin 資料沒相關的 h2 也被 reRender 了!

也就是說 , isLogin 資料改變時 , 會發生下圖的內容

可是不對啊! 目前 isLogin 資料只有跟 <購買 BTN> 是有相關的 , 我們資料變更時 , 只要 reRender <購買 BTN> 就 OK 吧!

那有沒有什麼方式 , 可以讓我們只 reRender 資料變動的部分呢 ?

這時往 react 看過去 , 就會發現有個 Virtual DOM 的概念 , 追蹤變化的部分 , 只重新 render 變化的部分

是的 , 這個東西很讚 , 但是我們有沒有辦法不引入 前端框架_御三家 來使用 Virtual DOM 呢 ?

御三家圖片

A : 可以 , 那就是我們自行時做一個簡易的 Virtual DOM 的架構 , 在我們的 webcomponent 中 , 這樣就不需要引入前端框架了

接下來來說明一下 , 要如何將 Virtual DOM 的架構放到我們的 webcomponent 中?

我們複習一下上面的圖片會發現

htmlString -> (compiler) -> Virtual DOM -> (renderer) -> DOM

如果更細的拆解 , 我們可以將 compiler 拆分出 tokenizer . parser & transform 三個步驟

htmlString -> (tokenizer) -> tagList -> (parser) -> template_AST -> (transform) -> JS_AST < vnode > -> (render) -> DOM

說明一下 , 轉出來的中間產物大概長怎樣 , 我們更清楚要做出的 function 有的功能是什麼 ? 並將之分析出來

let htmlString = `
  <ul>
    <li>Item_1</li>
    <li>Item_2</li>
  </ul>
`

let tagList = [
  { type:'tagStart' , tagName:'ul' },
  { type:'tagStart' , tagName:'li' },
  { type:'text' , content:'Item_1' },
  { type:'tagEnd' , tagName:'li' },
  { type:'tagStart' , tagName:'li' },
  { type:'text' , content:'Item_2' },
  { type:'tagEnd' , tagName:'li' },
  { type:'tagEnd' , tagName:'ul' }
]

let template_AST = [
  {
    type: "tag",
    tag: "ul",
    children: [
      {
        type: "tag",
        tag: "li",
        children: [
          {
            type: "text",
            text: "Item_1"
          }
        ]
      },
      {
        type: "tag",
        tag: "li",
        children: [
          {
            type: "text",
            text: "Item_2"
          }
        ]
      }
    ]
  }
]

// vnode = JS_AST  
let JS_AST = [
    h("ul", [
      h("li", "Item_1"),
      h("li", "Item_2")
    ])
]

也就是說 , 在 htmlString -> innerHTML 中間有一個東東 , 讓我們可以比對 BEFORE 跟 AFTER 的資料差異 , 再根據差異的做變更

這個東東就是 模板_AST ( DOM Tree )

const oldAst = {
  type: "tag",
  tag: "ul",
  attrs: {
    id: "list"
  },
  children: [
    {
      type: "tag",
      tag: "li",
      children: [
        {
          type: "text",
          text: "Item_1"
        }
      ]
    },
    {
      type: "tag",
      tag: "li",
      children: [
        {
          type: "text",
          text: "Item_2"
        }
      ]
    }
  ]
}

不過要如何將 htmlString 轉成 AST 呢 ?
這時我們就需要一個 compiler 出場了 ٩(๑•̀ω•́๑)۶

/**
 * FINALLY! We'll create our `compiler` function. Here we will link together
 * every part of the pipeline.
 *
 *   1. input  => tokenizer   => tokens
 *   2. tokens => parser      => ast
 *   3. ast    => transformer => newAst
 *   4. newAst => generator   => output
 */

function compiler(input) {
  let tokens = tokenizer(input);
  let ast    = parser(tokens);
  let newAst = transformer(ast);
  let output = codeGenerator(newAst);

  // and simply return the output!
  return output;
}

根據 the-super-tiny-compiler 的程式碼 , 我們可能需要有以下的幾個步驟來組成 compiler

1. htmlString  => tokenizer   => tokens
2. tokens      => parser      => 模板_AST
3. 模板_AST     => transformer => JS_AST
4. JS_AST      => generator   => output

下面我們來一步一步組出 compiler 吧!(๑¯∀¯๑)

參考資料


上一篇
[Day24] - 介紹 Svelte.js 如何使用
下一篇
[day26] - Vue 的 Html 字串處理 ( 簡易版 tokenizer 實作 )
系列文
Web Component 網頁元件之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言