偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

[[390834]]

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

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

發(fā)展歷程

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

發(fā)展階段

原生 DOM API

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

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

 

jQuery 操作 DOM

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

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

 

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

模板操作

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

mustache

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

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

 

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

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

JSX

[[390835]]

GitHub - OpenJSX/logo: Logo of JSX-IR

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

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

  1. var title = '關(guān)注我的公眾號(hào)' 
  2. var content = '打開(kāi)微信搜索:『自然醒的筆記本』' 
  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),給前端的開(kāi)發(fā)模式帶來(lái)更大的想象空間,更是引入了函數(shù)式編程的思想。

  1. UI = fn(state) 

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

lit-html 介紹

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

  • ES6 提供的模板字面量;
  • Web Components 提供的<template> 標(biāo)簽;
  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)注我的公眾號(hào)''打開(kāi)微信搜索:『自然醒的筆記本』'), 
  13.   document.body 
  14. ); 

 

模板語(yǔ)法

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

  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. ); 

 

除了這種寫(xiě)法上的便利,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. ); 

 

樣式的綁定

除了使用原生模板字符串編寫(xiě)模板外,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. ); 

 

渲染流程

做為一個(gè)模板引擎,lit-html 的主要作用就是將模板渲染到頁(yè)面上,相比起 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); 

 通過(guò)前面的案例也能看出,lit-html 對(duì)外常用的兩個(gè) api 是 html 和 render。

構(gòu)造模板

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

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

 html 標(biāo)簽函數(shù)會(huì)接受多個(gè)參數(shù),第一個(gè)參數(shù)為靜態(tài)字符串組成的數(shù)組,后面的參數(shù)為動(dòng)態(tài)傳入的表達(dá)式。我們可以寫(xiě)一個(gè)案例,看看傳入的 html 標(biāo)簽函數(shù)的參數(shù)到底長(zhǎng)什么樣:

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

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

log

lit-html 會(huì)將這兩個(gè)參數(shù)傳入 TemplateResult 中,進(jìn)行實(shí)例化操作。

  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.  } 

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

渲染到頁(yè)面

render 方法會(huì)接受兩個(gè)參數(shù),第一個(gè)參數(shù)為 html 標(biāo)簽函數(shù)返回的 TemplateResult,第二個(gè)參數(shù)為一個(gè)真實(shí)的 DOM 節(jié)點(diǎn)。

  1. export const parts = new WeakMap(); 
  2. export const render = (result, container) => { 
  3.   // 先獲取DOM節(jié)點(diǎn)之前對(duì)應(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 進(jìn)行節(jié)點(diǎn)的創(chuàng)建或更新 
  14.   part.commit(); 
  15. }; 

render 階段會(huì)先到 parts 里面查找之前構(gòu)造過(guò)的 part 緩存。可以將 part 理解為一個(gè)節(jié)點(diǎn)的構(gòu)造器,用來(lái)將 template 的內(nèi)容渲染到真實(shí)的 DOM 節(jié)點(diǎn)中。

如果 part 緩存不存在,會(huì)先構(gòu)造一個(gè),然后調(diào)用 appendInto 方法,該方法會(huì)在 DOM 節(jié)點(diǎn)的前后插入兩個(gè)注釋節(jié)點(diǎn),用于后續(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.   } 

 

然后通過(guò) commit 方法創(chuàng)建真實(shí)的節(jié)點(diǎn),并插入到兩個(gè)注釋節(jié)點(diǎn)中。下面我們看看 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 的不同類型進(jìn)行不同的操作 
  9.     if (value instanceof TemplateResult) { 
  10.       // 通過(guò) html 標(biāo)簽方法得到的 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é)點(diǎn) 
  19.     const template = templateFactory(value); 
  20.     // 如果之前已經(jīng)構(gòu)建過(guò)一次模板,則進(jìn)行更新 
  21.     if (this.value.template === template) { 
  22.       // console.log('更新DOM', value) 
  23.       this.value.update(value.values); 
  24.     } else { 
  25.       // 通過(guò)模板節(jié)點(diǎn)構(gòu)造模板實(shí)例 
  26.       const instance = new TemplateInstance(template); 
  27.       // 將 templateResult 中的 values 更新到模板實(shí)例中 
  28.    const fragment = instance._clone(); 
  29.       instance.update(value.values); 
  30.       // 拷貝模板中的 DOM 節(jié)點(diǎn),插入到頁(yè)面 
  31.       this.__commitNode(fragment); 
  32.       // 模板實(shí)例放入 value 屬性進(jìn)行緩存,用于后續(xù)判斷是否是更新操作 
  33.       this.value = instance; 
  34.     } 
  35.   } 

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

最后調(diào)用 __commitNode 將拷貝模板得到的節(jié)點(diǎn)插入真實(shí)的 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 并沒(méi)有類似 Vue、React 那種將模板或 JSX 構(gòu)造成虛擬 DOM 的流程,只提供了一個(gè)輕量的 html 標(biāo)簽方法,將模板字符轉(zhuǎn)化為 TemplateResult,然后用注釋節(jié)點(diǎn)去填充動(dòng)態(tài)的位置。TemplateResult 最終也是通過(guò)創(chuàng)建 標(biāo)簽,然后通過(guò)瀏覽器內(nèi)置的 innerHTML 進(jìn)行模板解析的,這個(gè)過(guò)程也是十分輕量,相當(dāng)于能交給瀏覽器的部分全部交給瀏覽器來(lái)完成,包括模板創(chuàng)建完后的節(jié)點(diǎn)拷貝操作。

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

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

 

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

2013-07-27 21:28:44

2015-10-15 10:30:32

2013-06-27 11:21:17

2020-09-27 17:27:58

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

2018-09-27 18:47:45

AIOpsDevOps

2024-02-26 14:46:53

移動(dòng)計(jì)算人工智能5G

2018-09-11 08:00:00

DevOpsAIOps機(jī)器學(xué)習(xí)

2020-06-02 08:05:28

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

2025-01-03 09:24:10

模型架構(gòu)論文

2020-09-16 10:28:54

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

2021-04-21 07:53:14

云原生PulsarGo

2013-09-09 16:28:36

2014-07-18 17:14:34

2009-06-26 09:06:01

2016-01-26 11:58:12

2013-07-09 09:35:03

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

2011-01-27 09:52:43

StuxnetZeus軟件攻擊

2015-01-22 16:16:01

思科IT模式

2010-09-01 17:05:04

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

2018-09-25 07:00:50

點(diǎn)贊
收藏

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