自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

下一代的模板引擎:lit-html

開發(fā) 前端
今天來看看基于這個原生技術(shù),Google 二次封存的框架 lit-html。今天會重點介紹 lit-html 的用法以及優(yōu)勢。

[[390834]]

前面的文章介紹了 Web Components 的基本用法,今天來看看基于這個原生技術(shù),Google 二次封存的框架 lit-html。

其實早在 Google 提出 Web Components 的時候,就在此基礎(chǔ)上發(fā)布了 Polymer 框架。只是這個框架一直雷聲大雨點小,內(nèi)部似乎也對這個項目不太滿意,然后他們團隊又開發(fā)了兩個更加現(xiàn)代化的框架(或者說是庫?):lit-html、lit-element,今天的文章會重點介紹 lit-html 的用法以及優(yōu)勢。

發(fā)展歷程

在講到 lit-html 之前,我們先看看前端通過 JavaScript 操作頁面,經(jīng)歷過的幾個階段:

發(fā)展階段

原生 DOM API

最早通過 DOM API 操作頁面元素,操作步驟較為繁瑣,而且 JS 引擎與瀏覽器 DOM 對象的通信相對耗時,頻繁的 DOM 操作對瀏覽器性能影響較大。

  1. var $box = document.getElementById('box'
  2. var $head = document.createElement('h1'
  3. var $content = document.createElement('div'
  4. $head.innerText = '關(guān)注我的公眾號' 
  5. $content.innerText = '打開微信搜索:『自然醒的筆記本』' 
  6. $box.append($head) 
  7. $box.append($content) 

 

jQuery 操作 DOM

jQuery 的出現(xiàn),讓 DOM 操作更加便捷,內(nèi)部還做了很多跨瀏覽器的兼容性處理,極大的提升了開發(fā)體驗,并且還擁有豐富的插件體系和詳細的文檔。 

  1. var $box = $('#box'
  2.  
  3. var $head = $('<h1/>', { text: '關(guān)注我的公眾號' }) 
  4. var $content = $('<div/>', { text: '打開微信搜索:『自然醒的筆記本』' }) 
  5.  
  6. $box.append($head, $content) 

 

雖然提供了便捷的操作,由于其內(nèi)部有很多兼容性代碼,在性能上就大打折扣了。而且它的鏈式調(diào)用,讓開發(fā)者寫出的面條式代碼也經(jīng)常讓人詬病(PS. 個人認為這也不能算缺點,只是有些人看不慣罷了)。

模板操作

『模板引擎』最早是后端 MVC 框架的 View 層,用來拼接生成 HTML 代碼用的。比如,mustache 是一個可以用于多個語言的一套模板引擎。

mustache

后來前端框架也開始搗鼓 MVC 模式,漸漸的前端也開始引入了模板的概念,讓操作頁面元素變得更加順手。下面的案例,是 angluar.js 中通過指令來使用模板:

  1. var app = angular.module("box", []); 
  2.  
  3. app.directive("myMessage"function (){ 
  4.   return { 
  5.     template : '' + 
  6.     '<h1>關(guān)注我的公眾號</h1>' + 
  7.     '<div>打開微信搜索:『自然醒的筆記本』</div>' 
  8.   } 
  9. }) 

 

后來的 Vue 更是將模板與虛擬 DOM 進行了結(jié)合,更進一步的提升了 Vue 中模板的性能,但是模板也有其缺陷存在。

  • 不管是什么模板引擎,在啟動時,解析模板是需要花時間,這是沒有辦法避免的;
  • 連接模板與 JavaScript 的數(shù)據(jù)比較麻煩,而且在數(shù)據(jù)更新時還需進行模板的更新;
  • 各式各樣的模板創(chuàng)造了自己的語法結(jié)構(gòu),使用不同的模板引擎,就需要重新學習一遍其語法糖,這對開發(fā)體驗不是很友好;

JSX

[[390835]]

GitHub - OpenJSX/logo: Logo of JSX-IR

React 在官方文檔中這樣介紹 JSX:

“JSX,是一個 JavaScript 的語法擴展。我們建議在 React 中配合使用 JSX,JSX 可以很好地描述 UI 應(yīng)該呈現(xiàn)出它應(yīng)有交互的本質(zhì)形式。JSX 可能會使人聯(lián)想到模板語言,但它具有 JavaScript 的全部功能。

  1. var title = '關(guān)注我的公眾號' 
  2. var content = '打開微信搜索:『自然醒的筆記本』' 
  3.  
  4. const element = <div> 
  5.   <h1>{title}</h1> 
  6.   <div>{content}</div> 
  7. </div>; 
  8.  
  9. ReactDOM.render( 
  10.   element, 
  11.   document.getElementById('root'

 

JSX 的出現(xiàn),給前端的開發(fā)模式帶來更大的想象空間,更是引入了函數(shù)式編程的思想。

  1. UI = fn(state) 

 但是這也帶來了一個問題,JSX 語法必須經(jīng)過轉(zhuǎn)義,將其處理成 React.createElement 的形式,這也提高了 React 的上手難度,很多新手望而卻步。

lit-html 介紹

lit-html 的出現(xiàn)就盡可能的規(guī)避了之前模板引擎的問題,通過現(xiàn)代瀏覽器原生的能力來構(gòu)建模板。

  • ES6 提供的模板字面量;
  • Web Components 提供的<template> 標簽;
  1. // Import lit-html 
  2. import {html, render} from 'lit-html'
  3.  
  4. // Define a template 
  5. const template = (title, content) => html` 
  6.   <h1>${title}</h1> 
  7.   <div>${content}</div> 
  8. `; 
  9.  
  10. // Render the template to the document 
  11. render( 
  12.   template('關(guān)注我的公眾號''打開微信搜索:『自然醒的筆記本』'), 
  13.   document.body 
  14. ); 

 

模板語法

由于使用了原生的模板字符,可以無需轉(zhuǎn)義,直接進行使用,而且和 JSX 一樣也能使用 JavaScript 語法進行遍歷和邏輯控制。

  1. const skillTpl = (title, skills) => html` 
  2.   <h2>${title || '技能列表' }</h2> 
  3.   <ul> 
  4.     ${skills.map(i => html`<li>${i}</li>`)} 
  5.   </ul> 
  6. `; 
  7.  
  8. render( 
  9.   skillTpl('我的技能', ['Vue''React''Angluar']), 
  10.   document.body 
  11. ); 

 

除了這種寫法上的便利,lit-html 內(nèi)部也提供了Vue 類似的事件綁定方式。

  1. const Input = (defaultValue) => html` 
  2.   name: <input value=${defaultValue} @input=${(evt) => { 
  3.     console.log(evt.target.value) 
  4.   }} /> 
  5. `; 
  6.  
  7. render( 
  8.   Input('input your name'), 
  9.   document.body 
  10. ); 

 

樣式的綁定

除了使用原生模板字符串編寫模板外,lit-html 天生自帶的 CSS-in-JS 的能力。

  1. import {html, render} from 'lit-html'
  2. import {styleMap} from 'lit-html/directives/style-map.js'
  3.  
  4. const skillTpl = (title, skills, highlight) => { 
  5.  const styles = { 
  6.    backgroundColor: highlight ? 'yellow' : ''
  7.  }; 
  8.  return html` 
  9.    <h2>${title || '技能列表' }</h2> 
  10.    <ul style=${styleMap(styles)}> 
  11.      ${skills.map(i => html`<li>${i}</li>`)} 
  12.    </ul> 
  13.  ` 
  14. }; 
  15.  
  16. render( 
  17.  skillTpl('我的技能', ['Vue''React''Angluar'], true), 
  18.  document.body 
  19. ); 

 

渲染流程

做為一個模板引擎,lit-html 的主要作用就是將模板渲染到頁面上,相比起 React、Vue 等框架,它更加專注于渲染,下面我們看看 lit-html 的基本工作流程。

  1. // Import lit-html 
  2. import { html, render } from 'lit-html'
  3.  
  4. // Define a template 
  5. const myTemplate = (name) => html`<p>Hello ${name}</p>`; 
  6.  
  7. // Render the template to the document 
  8. render(myTemplate('World'), document.body); 

 通過前面的案例也能看出,lit-html 對外常用的兩個 api 是 html 和 render。

構(gòu)造模板

html 是一個標簽函數(shù),屬于 ES6 新增語法,如果不記得標簽函數(shù)的用法,可以打開 Mozilla 的文檔(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals)復(fù)習下。

  1. export const html = (strings, ...values) => { 
  2.   …… 
  3. }; 

 html 標簽函數(shù)會接受多個參數(shù),第一個參數(shù)為靜態(tài)字符串組成的數(shù)組,后面的參數(shù)為動態(tài)傳入的表達式。我們可以寫一個案例,看看傳入的 html 標簽函數(shù)的參數(shù)到底長什么樣:

  1. const foo = '吳彥祖'
  2. const bar = '梁朝偉'
  3.  
  4. html`<p>Hello ${foo}, I'm ${bar}</p>`; 

整個字符串會被動態(tài)的表達式進行切割成三部分,這個三個部分會組成一個數(shù)組,做為第一個參數(shù)傳入 html 標簽函數(shù),而動態(tài)的表達式經(jīng)過計算后得到的值會做為后面的參數(shù)一次傳入,我們可以將 strings 和 values 打印出來看看:

log

lit-html 會將這兩個參數(shù)傳入 TemplateResult 中,進行實例化操作。

  1. export const html = (strings, ...values) => { 
  2.   return new TemplateResult(strings, values); 
  3. }; 
  1.  
  2. const marker = `{{lit-${String(Math.random()).slice(2)}}}`; 
  3. const nodeMarker = `<!--${marker}-->`; 
  4.  
  5. export class TemplateResult { 
  6.  constructor(strings, values) { 
  7.   this.strings = strings; 
  8.   this.values = values
  9.  } 
  10.  getHTML() { 
  11.   const l = this.strings.length - 1; 
  12.   let html = ''
  13.   let isCommentBinding = false
  14.   for (let i = 0; i < l; i++) { 
  15.    const s = this.strings[i]; 
  16.    html += s + nodeMarker; 
  17.   } 
  18.   html += this.strings[l]; 
  19.   return html; 
  20.  } 
  21.  getTemplateElement() { 
  22.   const template = document.createElement('template'); 
  23.   let value = this.getHTML(); 
  24.   template.innerHTML = value; 
  25.   return template; 
  26.  } 

實例化的 TemplateResult 會提供一個 getTemplateElement 方法,該方法會創(chuàng)建一個 template 標簽,然后會將 getHTML 的值傳入 template 標簽的 innerHTML 中。而 getHTML 方法的作用,就是在之前傳入的靜態(tài)字符串中間插入 HTML 注釋。前面的案例中,如果調(diào)用 getHTML 得到的結(jié)果如下。

渲染到頁面

render 方法會接受兩個參數(shù),第一個參數(shù)為 html 標簽函數(shù)返回的 TemplateResult,第二個參數(shù)為一個真實的 DOM 節(jié)點。

  1. export const parts = new WeakMap(); 
  2. export const render = (result, container) => { 
  3.   // 先獲取DOM節(jié)點之前對應(yīng)的緩存 
  4.   let part = parts.get(container); 
  5.   // 如果不存在緩存,則重新創(chuàng)建 
  6.   if (part === undefined) { 
  7.     part = new NodePart() 
  8.     parts.set(container, part); 
  9.     part.appendInto(container); 
  10.   } 
  11.   // 將 TemplateResult 設(shè)置到 part 中 
  12.   part.setValue(result); 
  13.   // 調(diào)用 commit 進行節(jié)點的創(chuàng)建或更新 
  14.   part.commit(); 
  15. }; 

render 階段會先到 parts 里面查找之前構(gòu)造過的 part 緩存??梢詫?part 理解為一個節(jié)點的構(gòu)造器,用來將 template 的內(nèi)容渲染到真實的 DOM 節(jié)點中。

如果 part 緩存不存在,會先構(gòu)造一個,然后調(diào)用 appendInto 方法,該方法會在 DOM 節(jié)點的前后插入兩個注釋節(jié)點,用于后續(xù)插入模板。

  1. const createMarker = () => document.createComment(''); 
  2. export class NodePart { 
  3.   appendInto(container) { 
  4.     this.startNode = container.appendChild(createMarker()); 
  5.     this.endNode = container.appendChild(createMarker()); 
  6.   } 

 

然后通過 commit 方法創(chuàng)建真實的節(jié)點,并插入到兩個注釋節(jié)點中。下面我們看看 commit方法的具體操作:

  1. export class NodePart { 
  2.   setValue(result) { 
  3.     // 將 templateResult 放入 __pendingValue 屬性中 
  4.     this.__pendingValue = result; 
  5.   } 
  6.   commit() { 
  7.     const value = this.__pendingValue; 
  8.     // 依據(jù) value 的不同類型進行不同的操作 
  9.     if (value instanceof TemplateResult) { 
  10.       // 通過 html 標簽方法得到的 value 
  11.       // 肯定是 TemplateResult 類型的 
  12.       this.__commitTemplateResult(value); 
  13.     } else { 
  14.       this.__commitText(value); 
  15.     } 
  16.   } 
  17.   __commitTemplateResult(value) { 
  18.     // 調(diào)用 templateFactory 構(gòu)造模板節(jié)點 
  19.     const template = templateFactory(value); 
  20.     // 如果之前已經(jīng)構(gòu)建過一次模板,則進行更新 
  21.     if (this.value.template === template) { 
  22.       // console.log('更新DOM', value) 
  23.       this.value.update(value.values); 
  24.     } else { 
  25.       // 通過模板節(jié)點構(gòu)造模板實例 
  26.       const instance = new TemplateInstance(template); 
  27.       // 將 templateResult 中的 values 更新到模板實例中 
  28.    const fragment = instance._clone(); 
  29.       instance.update(value.values); 
  30.       // 拷貝模板中的 DOM 節(jié)點,插入到頁面 
  31.       this.__commitNode(fragment); 
  32.       // 模板實例放入 value 屬性進行緩存,用于后續(xù)判斷是否是更新操作 
  33.       this.value = instance; 
  34.     } 
  35.   } 

實例化之后的模板,首先會調(diào)用 instance._clone() 進行一次拷貝操作,然后通過 instance.update(value.values) 將計算后的動態(tài)表達式插入其中。

最后調(diào)用 __commitNode 將拷貝模板得到的節(jié)點插入真實的 DOM 中。

  1. export class NodePart { 
  2.   __insert(node) { 
  3.     this.endNode.parentNode.insertBefore(node, this.endNode); 
  4.   } 
  5.   __commitNode(value) { 
  6.     this.__insert(value); 
  7.     this.value = value; 
  8.   } 

 

可以看到 lit-html 并沒有類似 Vue、React 那種將模板或 JSX 構(gòu)造成虛擬 DOM 的流程,只提供了一個輕量的 html 標簽方法,將模板字符轉(zhuǎn)化為 TemplateResult,然后用注釋節(jié)點去填充動態(tài)的位置。TemplateResult 最終也是通過創(chuàng)建 標簽,然后通過瀏覽器內(nèi)置的 innerHTML 進行模板解析的,這個過程也是十分輕量,相當于能交給瀏覽器的部分全部交給瀏覽器來完成,包括模板創(chuàng)建完后的節(jié)點拷貝操作。

  1. export class TemplateInstance { 
  2.   _clone() { 
  3.     const { element } = this.template; 
  4.     const fragment = document.importNode(element.content, true); 
  5.     // 省略部分操作…… 
  6.     return fragment; 
  7.   } 

其他lit-html 只是一個高效的模板引擎,如果要用來編寫業(yè)務(wù)代碼還缺少了類似 Vue、React 提供的生命周期、數(shù)據(jù)綁定等能力。為了完成這部分的能力,Polymer 項目組還提供了另一個框架:lit-element,可以用來創(chuàng)建 WebComponents。

除了官方的 lit-element 框架,Vue 的作者還將 Vue 的響應(yīng)式部分剝離,與 lit-html 進行了結(jié)合,創(chuàng)建了一個 vue-lit(https://github.com/yyx990803/vue-lit) 的框架,一共也就寫了 70 行代碼,感興趣可以看看。

 

責任編輯:姜華 來源: 自然醒的筆記本
相關(guān)推薦

2013-07-27 21:28:44

2015-10-15 10:30:32

2013-06-27 11:21:17

2021-04-21 07:53:14

云原生PulsarGo

2018-09-27 18:47:45

AIOpsDevOps

2020-09-27 17:27:58

邊緣計算云計算技術(shù)

2018-09-11 08:00:00

DevOpsAIOps機器學習

2020-06-02 08:05:28

智能電表蜂窩物聯(lián)網(wǎng)NB-IoT

2024-02-26 14:46:53

移動計算人工智能5G

2025-01-03 09:24:10

模型架構(gòu)論文

2020-09-16 10:28:54

邊緣計算云計算數(shù)據(jù)中心

2014-07-18 17:14:34

2009-06-26 09:06:01

2013-07-09 09:35:03

搜索引擎功能開發(fā)搜索

2013-09-09 16:28:36

2016-01-26 11:58:12

2015-01-22 16:16:01

思科IT模式

2010-09-01 17:05:04

無線網(wǎng)絡(luò)

2011-01-27 09:52:43

StuxnetZeus軟件攻擊

2018-09-25 07:00:50

點贊
收藏

51CTO技術(shù)棧公眾號