成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[390834]]

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

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

發展歷程

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

發展階段

原生 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 = '關注我的公眾號' 
  5. $content.innerText = '打開微信搜索:『自然醒的筆記本』' 
  6. $box.append($head) 
  7. $box.append($content) 

 

jQuery 操作 DOM

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

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

 

雖然提供了便捷的操作,由于其內部有很多兼容性代碼,在性能上就大打折扣了。而且它的鏈式調用,讓開發者寫出的面條式代碼也經常讓人詬病(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>關注我的公眾號</h1>' + 
  7.     '<div>打開微信搜索:『自然醒的筆記本』</div>' 
  8.   } 
  9. }) 

 

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

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

JSX

[[390835]]

GitHub - OpenJSX/logo: Logo of JSX-IR

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

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

  1. var title = '關注我的公眾號' 
  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 的出現,給前端的開發模式帶來更大的想象空間,更是引入了函數式編程的思想。

  1. UI = fn(state) 

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

lit-html 介紹

lit-html 的出現就盡可能的規避了之前模板引擎的問題,通過現代瀏覽器原生的能力來構建模板。

  • 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('關注我的公眾號''打開微信搜索:『自然醒的筆記本』'), 
  13.   document.body 
  14. ); 

 

模板語法

由于使用了原生的模板字符,可以無需轉義,直接進行使用,而且和 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 內部也提供了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。

構造模板

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

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

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

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

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

log

lit-html 會將這兩個參數傳入 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 方法,該方法會創建一個 template 標簽,然后會將 getHTML 的值傳入 template 標簽的 innerHTML 中。而 getHTML 方法的作用,就是在之前傳入的靜態字符串中間插入 HTML 注釋。前面的案例中,如果調用 getHTML 得到的結果如下。

渲染到頁面

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

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

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

如果 part 緩存不存在,會先構造一個,然后調用 appendInto 方法,該方法會在 DOM 節點的前后插入兩個注釋節點,用于后續插入模板。

  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 方法創建真實的節點,并插入到兩個注釋節點中。下面我們看看 commit方法的具體操作:

  1. export class NodePart { 
  2.   setValue(result) { 
  3.     // 將 templateResult 放入 __pendingValue 屬性中 
  4.     this.__pendingValue = result; 
  5.   } 
  6.   commit() { 
  7.     const value = this.__pendingValue; 
  8.     // 依據 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.     // 調用 templateFactory 構造模板節點 
  19.     const template = templateFactory(value); 
  20.     // 如果之前已經構建過一次模板,則進行更新 
  21.     if (this.value.template === template) { 
  22.       // console.log('更新DOM', value) 
  23.       this.value.update(value.values); 
  24.     } else { 
  25.       // 通過模板節點構造模板實例 
  26.       const instance = new TemplateInstance(template); 
  27.       // 將 templateResult 中的 values 更新到模板實例中 
  28.    const fragment = instance._clone(); 
  29.       instance.update(value.values); 
  30.       // 拷貝模板中的 DOM 節點,插入到頁面 
  31.       this.__commitNode(fragment); 
  32.       // 模板實例放入 value 屬性進行緩存,用于后續判斷是否是更新操作 
  33.       this.value = instance; 
  34.     } 
  35.   } 

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

最后調用 __commitNode 將拷貝模板得到的節點插入真實的 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 構造成虛擬 DOM 的流程,只提供了一個輕量的 html 標簽方法,將模板字符轉化為 TemplateResult,然后用注釋節點去填充動態的位置。TemplateResult 最終也是通過創建 標簽,然后通過瀏覽器內置的 innerHTML 進行模板解析的,這個過程也是十分輕量,相當于能交給瀏覽器的部分全部交給瀏覽器來完成,包括模板創建完后的節點拷貝操作。

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

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

 

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

2013-07-27 21:28:44

2015-10-15 10:30:32

2013-06-27 11:21:17

2018-09-27 18:47:45

AIOpsDevOps

2020-09-27 17:27:58

邊緣計算云計算技術

2021-04-21 07:53:14

云原生PulsarGo

2020-09-16 10:28:54

邊緣計算云計算數據中心

2025-01-03 09:24:10

模型架構論文

2020-06-02 08:05:28

智能電表蜂窩物聯網NB-IoT

2018-09-11 08:00:00

DevOpsAIOps機器學習

2024-02-26 14:46:53

移動計算人工智能5G

2014-07-18 17:14:34

2009-06-26 09:06:01

2013-09-09 16:28:36

2016-01-26 11:58:12

2013-07-09 09:35:03

搜索引擎功能開發搜索

2011-01-27 09:52:43

StuxnetZeus軟件攻擊

2015-01-22 16:16:01

思科IT模式

2010-09-01 17:05:04

無線網絡

2015-09-28 16:24:34

YARNHadoop計算
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产剧情一区二区三区 | 亚洲精品视频在线观看免费 | 国产一区二区在线视频 | 亚洲欧美在线免费观看 | 久热电影 | 亚洲精品片| 久久国产香蕉 | 国产片侵犯亲女视频播放 | 成人激情视频网 | 亚洲第1页 | 麻豆国产精品777777在线 | 国产成人精品午夜 | 欧美456| 中文字幕一区二区三区四区 | 99热都是精品 | 成人在线中文字幕 | 欧美精品久久久久 | 四色永久 | 日日夜夜免费精品 | 久久久高清 | 国产成人精品久久二区二区91 | 国内自拍偷拍一区 | 天堂久| 欧美日韩综合一区 | 盗摄精品av一区二区三区 | 91精品久久久久久久久 | 日日碰狠狠躁久久躁婷婷 | 在线看无码的免费网站 | 精品欧美一区二区三区久久久 | 夜夜骑天天干 | 国产精品视频久久久 | 国产精品一区二区久久久久 | 久久毛片| 久久精品一区二区三区四区 | 国产成人精品一区 | 97色伦网| 午夜免费观看网站 | 成人免费观看男女羞羞视频 | 欧美成人一区二区三区 | av在线免费观看不卡 | 欧美日韩专区 |