日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

讓我們通過構(gòu)建一個現(xiàn)代 JavaScript 框架來學(xué)習(xí)它是如何工作的

來源: 責(zé)編: 時間:2023-12-18 09:47:13 245觀看
導(dǎo)讀在我的日常工作中,我致力于一個 JavaScript 框架(LWC)。盡管我已經(jīng)在這個項目上工作了將近三年,但我仍然覺得自己是一個業(yè)余愛好者。當(dāng)我閱讀有關(guān)更大的框架世界的信息時,常常因為不了解的事情太多而感到不知所措。然而,學(xué)

在我的日常工作中,我致力于一個 JavaScript 框架(LWC)。盡管我已經(jīng)在這個項目上工作了將近三年,但我仍然覺得自己是一個業(yè)余愛好者。當(dāng)我閱讀有關(guān)更大的框架世界的信息時,常常因為不了解的事情太多而感到不知所措。3J728資訊網(wǎng)——每日最新資訊28at.com

然而,學(xué)習(xí)事物的最佳方法之一是親自動手構(gòu)建。而且,我們要繼續(xù)保持那些 “距上一個 JavaScript 框架的天數(shù)” 模因的持續(xù)。因此,讓我們來編寫我們自己的現(xiàn)代 JavaScript 框架吧!3J728資訊網(wǎng)——每日最新資訊28at.com

什么是“現(xiàn)代 JavaScript 框架”?

React 是一個出色的框架,我不是來貶低它的。但在這篇文章中,“現(xiàn)代 JavaScript 框架”指的是“React 時代后的框架” - 即 Lit、Solid、Svelte、Vue 等。3J728資訊網(wǎng)——每日最新資訊28at.com

由于 React 在前端領(lǐng)域占據(jù)主導(dǎo)地位已久,每個更新的框架都在其陰影下成長。這些框架都受到了 React 的很大啟發(fā),但它們在演進(jìn)中以出奇的相似方式遠(yuǎn)離了 React。盡管 React 本身一直在創(chuàng)新,但我發(fā)現(xiàn)后來的框架在現(xiàn)今更類似于彼此,而不是 React。3J728資訊網(wǎng)——每日最新資訊28at.com

為了保持簡單,我還將避免討論像 Astro、Marko 和 Qwik 這樣以服務(wù)器為先的框架。這些框架在其自身的領(lǐng)域表現(xiàn)出色,但與以客戶端為重點的框架相比,它們來自稍微不同的思維傳統(tǒng)。因此,在本文中,我們只討論客戶端渲染。3J728資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)代框架有哪些特點?

從我的角度來看,React 時代后的框架都集中在相同的基本理念上:3J728資訊網(wǎng)——每日最新資訊28at.com

  1. 使用響應(yīng)性(例如 signals)進(jìn)行 DOM 更新。
  2. 使用克隆的模板進(jìn)行 DOM 渲染。
  3. 使用像 <template> 和 Proxy 這樣的現(xiàn)代 Web API,使上述所有工作更容易。

需要明確的是,這些框架在微觀層面上差異很大,它們在處理諸如 Web 組件、編譯和用戶界面 API 等方面的方式也不同。并非所有的框架都使用 Proxy。但總體來說,大多數(shù)框架作者似乎在上述理念上達(dá)成了共識,或者正在朝著這個方向發(fā)展。3J728資訊網(wǎng)——每日最新資訊28at.com

因此,對于我們自己的框架,讓我們試著實現(xiàn)這些理念的最基本部分,首先從響應(yīng)性開始。3J728資訊網(wǎng)——每日最新資訊28at.com

響應(yīng)性

人們常說 “?React 不是響應(yīng)式的”。這意味著 React 具有更多基于拉取而不是推送的模型。簡單來說,在最糟糕的情況下,React 假設(shè)整個虛擬 DOM 樹都需要從頭開始重建,防止這些更新的唯一方法是實現(xiàn) React.memo(或在舊時代,shouldComponentUpdate)。3J728資訊網(wǎng)——每日最新資訊28at.com

虛擬 DOM 緩解了“摧毀一切,從頭開始”的策略的一些成本,但并未完全解決它。要求開發(fā)人員編寫正確的 memo 代碼是一場失敗的戰(zhàn)斗(請查看 ?React Forget,這是一個持續(xù)嘗試解決此問題的項目)。3J728資訊網(wǎng)——每日最新資訊28at.com

相反,現(xiàn)代框架使用了一種推送式的響應(yīng)模型。在這種模型中,組件樹的各個部分訂閱狀態(tài)更新,并僅在相關(guān)狀態(tài)更改時更新 DOM。這更注重“默認(rèn)情況下高性能”的設(shè)計,以換取一些前期記賬(bookkeeping)成本(特別是在內(nèi)存方面),以跟蹤哪些狀態(tài)部分與 UI 的哪些部分相關(guān)。3J728資訊網(wǎng)——每日最新資訊28at.com

需要注意的是,這種技術(shù)不一定與虛擬 DOM 方法不兼容:Preact Signals 和 Million 這樣的工具表明你可以有一個混合系統(tǒng)。如果你的目標(biāo)是保留現(xiàn)有的虛擬 DOM 框架(例如 React),但對于性能敏感的情況有選擇地應(yīng)用推送模型,則這是有用的。3J728資訊網(wǎng)——每日最新資訊28at.com

在這篇文章中,我不打算詳細(xì)討論信號本身的細(xì)節(jié),或者像細(xì)粒度響應(yīng)等更微妙的主題,但我會假設(shè)我們將使用一種響應(yīng)系統(tǒng)。3J728資訊網(wǎng)——每日最新資訊28at.com

注意:在討論什么算是 “響應(yīng)式” 時有很多細(xì)微差別。我的目標(biāo)是在這里對比 React 和 React 時代后的框架,尤其是 Solid、Svelte v5 的“runes”模式和 Vue Vapor。3J728資訊網(wǎng)——每日最新資訊28at.com

克隆 DOM 樹

很長一段時間以來,在 JavaScript 框架的共同智慧中,渲染 DOM 的最快方法被認(rèn)為是逐個創(chuàng)建和掛載每個 DOM 節(jié)點。換句話說,您使用諸如 createElement、setAttribute 和 textContent 這樣的 API 逐步構(gòu)建 DOM:3J728資訊網(wǎng)——每日最新資訊28at.com

const div = document.createElement('div')div.setAttribute('class', 'blue')div.textContent = 'Blue!'

另一種選擇是將一個龐大的 HTML 字符串直接插入 innerHTML,讓瀏覽器為您解析它:3J728資訊網(wǎng)——每日最新資訊28at.com

const container = document.createElement('div')container.innerHTML = `  <div class="blue">Blue!</div>`

這種天真的方法有一個很大的缺點:如果您的 HTML 中有任何動態(tài)內(nèi)容(例如,紅色而不是藍(lán)色),那么您將需要一遍又一遍地解析 HTML 字符串。此外,您會在每次更新時清除 DOM,這將重置諸如 <input> 的值之類的狀態(tài)。3J728資訊網(wǎng)——每日最新資訊28at.com

注意:使用 innerHTML 也涉及到 ?安全性問題。但在本文的目的中,讓我們假設(shè) HTML 內(nèi)容是可信任的。3J728資訊網(wǎng)——每日最新資訊28at.com

然而,有一天人們發(fā)現(xiàn),解析一次 HTML,然后對整個內(nèi)容調(diào)用 cloneNode(true) 是相當(dāng)快的:3J728資訊網(wǎng)——每日最新資訊28at.com

const template = document.createElement('template')template.innerHTML = `  <div class="blue">Blue!</div>`template.content.cloneNode(true) // this is fast!

在這里,我使用了 <template> 標(biāo)簽,它的優(yōu)勢在于創(chuàng)建 “不活動”(inert)的 DOM。換句話說,諸如 <img> 或 <video autoplay> 這樣的元素不會自動開始下載任何內(nèi)容。3J728資訊網(wǎng)——每日最新資訊28at.com

與手動使用 DOM API 相比,這種克隆技術(shù)有多快呢?為了演示,這里有一個?小型基準(zhǔn)測試。Tachometer 報告稱,在 Chrome 中,克隆技術(shù)大約快 50%,在 Firefox 中快 15%,在 Safari 中快 10%(這將根據(jù) DOM 大小和迭代次數(shù)而變化,但您能夠理解主要趨勢)。3J728資訊網(wǎng)——每日最新資訊28at.com

有趣的是,<template> 是一個較新的瀏覽器 API,在 IE11 中不可用,最初設(shè)計用于 Web 組件。有些諷刺的是,這種技術(shù)現(xiàn)在被用于各種 JavaScript 框架,無論它們是否使用 Web 組件。3J728資訊網(wǎng)——每日最新資訊28at.com

注:供參考,這里是在 Solid、Vue Vapor 和 Svelte v5 中對 <template> 使用 cloneNode 的方式。3J728資訊網(wǎng)——每日最新資訊28at.com

這種技術(shù)有一個主要挑戰(zhàn),即如何在不重置 DOM 狀態(tài)的情況下高效更新動態(tài)內(nèi)容。在構(gòu)建我們的玩具框架時,我們將在后面詳細(xì)介紹這一點。3J728資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)代 JavaScript API

我們已經(jīng)接觸到了一個在很大程度上很有幫助的新 API,那就是 <template>。另一個穩(wěn)步獲得關(guān)注的是 ?Proxy,它可以使構(gòu)建響應(yīng)系統(tǒng)變得更加簡單。3J728資訊網(wǎng)——每日最新資訊28at.com

在構(gòu)建我們的玩具示例時,我們還將使用帶標(biāo)簽的模板文字(tagged template literals)創(chuàng)建一個像這樣的 API:3J728資訊網(wǎng)——每日最新資訊28at.com

const dom = html`  <div>Hello ${ name }!</div>`

并非所有的框架都使用這個工具,但一些顯著的框架包括 Lit、HyperHTML 和 ArrowJS。帶標(biāo)簽的模板文字可以在不需要編譯器的情況下更輕松地構(gòu)建人體工學(xué)的 HTML 模板 API。3J728資訊網(wǎng)——每日最新資訊28at.com

步驟 1:構(gòu)建響應(yīng)性

響應(yīng)性是我們將構(gòu)建框架的基礎(chǔ)。響應(yīng)性將定義狀態(tài)的管理方式以及在狀態(tài)更改時 DOM 如何更新。3J728資訊網(wǎng)——每日最新資訊28at.com

讓我們從一些“夢幻代碼”開始,以說明我們想要的:3J728資訊網(wǎng)——每日最新資訊28at.com

const state = {} state.a = 1state.b = 2 createEffect(() => {  state.sum = state.a + state.b})

基本上,我們想要一個稱為 state 的“魔術(shù)對象”,有兩個屬性:a 和 b。每當(dāng)這些屬性發(fā)生變化時,我們希望設(shè)置 sum 為這兩個屬性的和。3J728資訊網(wǎng)——每日最新資訊28at.com

假設(shè)我們事先不知道屬性(或者沒有編譯器來確定它們),一個普通的對象將無法滿足這個要求。所以讓我們使用 Proxy,它可以在設(shè)置新值時作出反應(yīng):3J728資訊網(wǎng)——每日最新資訊28at.com

const state = new Proxy({}, {  get(obj, prop) {    onGet(prop)    return obj[prop]  },  set(obj, prop, value) {    obj[prop] = value    onSet(prop, value)    return true  }})

目前,我們的 Proxy 沒有做任何有趣的事情,只是給我們提供了一些 onGet 和 onSet 鉤子。所以讓我們使其在微任務(wù)之后刷新更新:3J728資訊網(wǎng)——每日最新資訊28at.com

let queued = false function onSet(prop, value) {  if (!queued) {    queued = true    queueMicrotask(() => {      queued = false      flush()    })  }}

注意:如果您對 queueMicrotask 不熟悉,它是一個較新的 DOM API,基本上與 Promise.resolve().then(...) 相同,但輸入更少。3J728資訊網(wǎng)——每日最新資訊28at.com

為什么要刷新更新呢?主要是因為我們不希望運行太多的計算。如果我們在 a 和 b 都改變時更新,那么我們將無用地計算兩次和。通過將刷新合并到一個微任務(wù)中,我們可以變得更加高效。3J728資訊網(wǎng)——每日最新資訊28at.com

接下來,讓我們讓刷新更新 sum:3J728資訊網(wǎng)——每日最新資訊28at.com

function flush() {  state.sum = state.a + state.b}

這很好,但它還不是我們的“夢幻代碼”。我們需要實現(xiàn) createEffect,以便僅在 a 和 b 更改時計算 sum(而不是在其他地方更改時)。3J728資訊網(wǎng)——每日最新資訊28at.com

為此,讓我們使用一個對象來跟蹤哪些效果需要運行哪些屬性:3J728資訊網(wǎng)——每日最新資訊28at.com

const propsToEffects = {}

接下來是至關(guān)重要的部分!我們需要確保我們的效果可以訂閱正確的屬性。為此,我們將運行效果,記錄它調(diào)用的任何 get 調(diào)用,并創(chuàng)建屬性與效果之間的映射。3J728資訊網(wǎng)——每日最新資訊28at.com

為了解釋清楚,記住我們的“夢幻代碼”是:3J728資訊網(wǎng)——每日最新資訊28at.com

createEffect(() => {  state.sum = state.a + state.b})

當(dāng)這個函數(shù)運行時,它調(diào)用了兩個 getter:state.a 和 state.b。這些 getter 應(yīng)該觸發(fā)響應(yīng)系統(tǒng)注意到該函數(shù)依賴于這兩個屬性。3J728資訊網(wǎng)——每日最新資訊28at.com

為了實現(xiàn)這一點,讓我們從一個簡單的全局變量開始,用于跟蹤“當(dāng)前”效果:3J728資訊網(wǎng)——每日最新資訊28at.com

let currentEffect

然后,createEffect 函數(shù)將在調(diào)用函數(shù)之前設(shè)置此全局變量:3J728資訊網(wǎng)——每日最新資訊28at.com

function createEffect(effect) {  currentEffect = effect  effect()  currentEffect = undefined}

這里的重要之處在于,效果會立即被調(diào)用,同時全局的 currentEffect 在提前設(shè)置。這是我們跟蹤它可能調(diào)用的任何 getter 的方式。3J728資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在,我們可以在我們的 Proxy 中實現(xiàn) onGet,它將設(shè)置全局 currentEffect 與屬性之間的映射:3J728資訊網(wǎng)——每日最新資訊28at.com

function onGet(prop) {  const effects = propsToEffects[prop] ??      (propsToEffects[prop] = [])  effects.push(currentEffect)}

運行一次后,propsToEffects 應(yīng)該如下所示:3J728資訊網(wǎng)——每日最新資訊28at.com

{  "a": [theEffect],  "b": [theEffect]}

這里的 theEffect 是我們想要運行的“sum”函數(shù)。3J728資訊網(wǎng)——每日最新資訊28at.com

接下來,我們的 onSet 應(yīng)該將需要運行的任何效果添加到一個 dirtyEffects 數(shù)組中:3J728資訊網(wǎng)——每日最新資訊28at.com

const dirtyEffects = [] function onSet(prop, value) {  if (propsToEffects[prop]) {    dirtyEffects.push(...propsToEffects[prop])    // ...  }}

此時,我們已經(jīng)有了所有的要素,使 flush 調(diào)用所有 dirtyEffects:3J728資訊網(wǎng)——每日最新資訊28at.com

function flush() {  while (dirtyEffects.length) {    dirtyEffects.shift()()  }}

把它們結(jié)合在一起,我們現(xiàn)在有了一個完全功能的響應(yīng)性系統(tǒng)!您可以自己嘗試在 DevTools 控制臺中設(shè)置 state.a 和 state.b - 只要其中一個發(fā)生更改,state.sum 就會更新。3J728資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在,有很多高級情況我們在這里沒有涵蓋:3J728資訊網(wǎng)——每日最新資訊28at.com

  • 在效果拋出錯誤時使用 try/catch
  • 避免運行相同的效果兩次
  • 防止無限循環(huán)
  • 在后續(xù)運行中訂閱效果到新的屬性(例如,如果某些 getter 僅在 if 塊中被調(diào)用)

然而,對于我們的玩具示例來說,這已經(jīng)足夠了。讓我們繼續(xù)進(jìn)行 DOM 渲染。3J728資訊網(wǎng)——每日最新資訊28at.com

步驟 2:DOM 渲染

我們現(xiàn)在有了一個功能完備的響應(yīng)性系統(tǒng),但它實質(zhì)上是“無頭”的。它可以跟蹤變化并計算效果,但僅此而已。3J728資訊網(wǎng)——每日最新資訊28at.com

然而,在某個時候,我們的 JavaScript 框架實際上需要將一些 DOM 渲染到屏幕上(這其實是整個目的)。3J728資訊網(wǎng)——每日最新資訊28at.com

在本節(jié)中,讓我們暫時忘記響應(yīng)性,想象一下我們只是嘗試構(gòu)建一個函數(shù),它能夠 1)構(gòu)建一個 DOM 樹,和 2)高效地更新它。3J728資訊網(wǎng)——每日最新資訊28at.com

再次,讓我們從一些“夢幻代碼”開始:3J728資訊網(wǎng)——每日最新資訊28at.com

function render(state) {  return html`    <div class="${state.color}">${state.text}</div>  `}

正如我提到的,我正在使用帶標(biāo)簽的模板文字,就像 Lit 一樣,因為我發(fā)現(xiàn)它們是一種在不需要編譯器的情況下編寫 HTML 模板的好方法。(我們馬上會看到為什么我們實際上可能希望使用編譯器。)3J728資訊網(wǎng)——每日最新資訊28at.com

我們從之前復(fù)用了我們的 state 對象,這次有一個 color 和 text 屬性。也許 state 是這樣的:3J728資訊網(wǎng)——每日最新資訊28at.com

state.color = 'blue'state.text = 'Blue!'

當(dāng)我們將這個 state 傳遞給 render 時,它應(yīng)該返回應(yīng)用了 state 的 DOM 樹:3J728資訊網(wǎng)——每日最新資訊28at.com

<div class="blue">Blue!</div>

然而,在我們繼續(xù)之前,我們需要簡要了解一下帶標(biāo)簽的模板文字。我們的 html 標(biāo)簽只是一個接收兩個參數(shù)的函數(shù):tokens(靜態(tài) HTML 字符串的數(shù)組)和 expressions(評估的動態(tài)表達(dá)式):3J728資訊網(wǎng)——每日最新資訊28at.com

function html(tokens, ...expressions) {}

在這種情況下,tokens 是(去掉空白):3J728資訊網(wǎng)——每日最新資訊28at.com

[  "<div class=/"",  "/">",  "</div>"]

和 expressions:3J728資訊網(wǎng)——每日最新資訊28at.com

[  "blue",  "Blue!"]

tokens 數(shù)組的長度始終比 expressions 數(shù)組長 1,因此我們可以簡單地將它們一起進(jìn)行壓縮:3J728資訊網(wǎng)——每日最新資訊28at.com

const allTokens = tokens    .map((token, i) => (expressions[i - 1] ?? '') + token)

這將給我們一個字符串?dāng)?shù)組:3J728資訊網(wǎng)——每日最新資訊28at.com

[  "<div class=/"",  "blue/">",  "Blue!</div>"]

我們可以將這些字符串連接在一起以生成我們的 HTML:3J728資訊網(wǎng)——每日最新資訊28at.com

const htmlString = allTokens.join('');

然后,我們可以使用 innerHTML 將其解析為 <template>:3J728資訊網(wǎng)——每日最新資訊28at.com

function parseTemplate(htmlString) {  const template = document.createElement('template');  template.innerHTML = htmlString;  return template;}

這個模板包含了我們的惰性 DOM(在技術(shù)上是 DocumentFragment),我們可以隨時克隆它:3J728資訊網(wǎng)——每日最新資訊28at.com

const cloned = template.content.cloneNode(true);

當(dāng)然,每次調(diào)用 html 函數(shù)時都解析完整的 HTML 對性能來說不是很好。幸運的是,帶標(biāo)簽的模板文字具有一個內(nèi)建特性,將在這里非常有幫助。3J728資訊網(wǎng)——每日最新資訊28at.com

對于帶標(biāo)簽的模板文字的每個獨特用法,每當(dāng)調(diào)用該函數(shù)時,tokens 數(shù)組始終相同 - 實際上,它是相同的對象!3J728資訊網(wǎng)——每日最新資訊28at.com

例如,考慮這種情況:3J728資訊網(wǎng)——每日最新資訊28at.com

function sayHello(name) {  return html`<div>Hello ${name}</div>`;}

每當(dāng)調(diào)用 sayHello 時,tokens 數(shù)組將始終相同:3J728資訊網(wǎng)——每日最新資訊28at.com

[  "<div>Hello ",  "</div>"]

tokens 的唯一不同之處是對帶標(biāo)簽?zāi)0宓耐耆煌恢茫?span style="display:none">3J728資訊網(wǎng)——每日最新資訊28at.com

html`<div></div>`html`<span></span>` // 與上述不同

我們可以利用這一點,通過使用 WeakMap 將 tokens 數(shù)組映射到生成的模板:3J728資訊網(wǎng)——每日最新資訊28at.com

const tokensToTemplate = new WeakMap();function html(tokens, ...expressions) {  let template = tokensToTemplate.get(tokens);  if (!template) {    // ...    template = parseTemplate(htmlString);    tokensToTemplate.set(tokens, template);  }  return template;}

這有點令人驚嘆的概念,但 tokens 數(shù)組的唯一性實際上意味著我們可以確保每次對 html 進(jìn)行調(diào)用時只解析一次 HTML。3J728資訊網(wǎng)——每日最新資訊28at.com

接下來,我們只需要一種方法來使用 expressions 數(shù)組(與 tokens 不同,它可能在每次調(diào)用時都不同)更新克隆的 DOM 節(jié)點。3J728資訊網(wǎng)——每日最新資訊28at.com

為了簡單起見,讓我們只是用占位符替換 expressions 數(shù)組中的每個索引:3J728資訊網(wǎng)——每日最新資訊28at.com

const stubs = expressions.map((_, i) => `__stub-${i}__`);

如果我們像以前一樣將其壓縮,它將創(chuàng)建這個 HTML:3J728資訊網(wǎng)——每日最新資訊28at.com

<div class="__stub-0__">  __stub-1__</div>

我們可以編寫一個簡單的字符串替換函數(shù)來替換這些占位符:3J728資訊網(wǎng)——每日最新資訊28at.com

function replaceStubs(string) {  return string.replaceAll(/__stub-(/d+)__/g, (_, i) => (    expressions[i]  ));}

現(xiàn)在每當(dāng)調(diào)用 html 函數(shù)時,我們可以克隆模板并更新占位符:3J728資訊網(wǎng)——每日最新資訊28at.com

const element = cloned.firstElementChild;for (const { name, value } of element.attributes) {  element.setAttribute(name, replaceStubs(value));}element.textContent = replaceStubs(element.textContent);

注意:我們使用 firstElementChild 來獲取模板中的第一個頂級元素。對于我們的玩具框架,我們假設(shè)只有一個。3J728資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在,這仍然不是非常高效的 - 特別是,我們正在更新不一定需要更新的 textContent 和屬性。但對于我們的玩具框架來說,這已經(jīng)足夠好了。3J728資訊網(wǎng)——每日最新資訊28at.com

我們可以通過使用不同的 state 進(jìn)行渲染來測試它:3J728資訊網(wǎng)——每日最新資訊28at.com

document.body.appendChild(render({ color: 'blue', text: 'Blue!' }));document.body.appendChild(render({ color: 'red', text: 'Red!' }));

這樣就可以了!3J728資訊網(wǎng)——每日最新資訊28at.com

步驟 3:結(jié)合響應(yīng)性和 DOM 渲染

由于我們已經(jīng)有了上面渲染系統(tǒng)中的 createEffect,現(xiàn)在我們可以將兩者結(jié)合起來根據(jù)狀態(tài)更新 DOM:3J728資訊網(wǎng)——每日最新資訊28at.com

const container = document.getElementById('container'); createEffect(() => {  const dom = render(state);  if (container.firstElementChild) {    container.firstElementChild.replaceWith(dom);  } else {    container.appendChild(dom);  }});

這實際上是有效的!我們可以將這個與響應(yīng)性部分的 “sum” 示例結(jié)合起來,只需創(chuàng)建另一個效果來設(shè)置文本:3J728資訊網(wǎng)——每日最新資訊28at.com

createEffect(() => {  state.text = `Sum is: ${state.sum}`;});

這將呈現(xiàn) “Sum is 3”:3J728資訊網(wǎng)——每日最新資訊28at.com

你可以嘗試操作這個玩具示例。如果你設(shè)置 state.a = 5,那么文本將自動更新為 “Sum is 7”。3J728資訊網(wǎng)——每日最新資訊28at.com

下一步

有許多改進(jìn)我們可以對這個系統(tǒng)進(jìn)行,特別是 DOM 渲染部分。3J728資訊網(wǎng)——每日最新資訊28at.com

最值得注意的是,我們?nèi)鄙僖环N更新深度 DOM 樹內(nèi)元素內(nèi)容的方法,例如:3J728資訊網(wǎng)——每日最新資訊28at.com

<div class="${color}">  <span>${text}</span></div>

為此,我們需要一種方法來唯一標(biāo)識模板內(nèi)的每個元素。有很多方法可以做到這一點:3J728資訊網(wǎng)——每日最新資訊28at.com

  1. Lit 在解析 HTML 時使用一套正則表達(dá)式和字符匹配的系統(tǒng),以確定占位符是否在屬性或文本內(nèi)容中,以及目標(biāo)元素的索引(按深度優(yōu)先 TreeWalker 順序)。
  2. Svelte 和 Solid 等框架在編譯期間有幸解析整個 HTML 模板,這提供了相同的信息。它們還生成調(diào)用 firstChild 和 nextSibling 遍歷 DOM 的代碼,以找到要更新的元素。

注意:使用 firstChild 和 nextSibling 進(jìn)行遍歷類似于 TreeWalker 方法,但比 element.children 更高效。這是因為瀏覽器在內(nèi)部使用鏈表來表示 DOM。3J728資訊網(wǎng)——每日最新資訊28at.com

無論我們決定采用 Lit 風(fēng)格的客戶端解析還是 Svelte/Solid 風(fēng)格的編譯時解析,我們想要的是類似于這樣的映射:3J728資訊網(wǎng)——每日最新資訊28at.com

[  {    elementIndex: 0, // 上面的 <div>    attributeName: 'class',    stubIndex: 0 // 表達(dá)式數(shù)組中的索引  },  {    elementIndex: 1 // 上面的 <span>    textContent: true,    stubIndex: 1 // 表達(dá)式數(shù)組中的索引  }]

這些綁定將告訴我們確切需要更新哪些元素,需要設(shè)置哪個屬性(或 textContent),以及在哪里找到替換占位符的表達(dá)式。3J728資訊網(wǎng)——每日最新資訊28at.com

下一步是避免每次都克隆模板,而是直接基于表達(dá)式更新 DOM。換句話說,我們不僅想要一次解析 - 我們只想一次克隆和設(shè)置綁定。這將將每個后續(xù)更新減少到最少的 setAttribute 和 textContent 調(diào)用。3J728資訊網(wǎng)——每日最新資訊28at.com

注意:你可能會想知道模板克隆的目的是什么,如果我們最終還是需要調(diào)用 setAttribute 和 textContent。答案是,大多數(shù) HTML 模板在很大程度上都是靜態(tài)內(nèi)容,只有一些動態(tài)的“孔”。通過使用模板克隆,我們克隆了絕大多數(shù)的 DOM,只對“孔”做額外的工作。這是使這個系統(tǒng)如此出色的關(guān)鍵洞察。3J728資訊網(wǎng)——每日最新資訊28at.com

另一個有趣的模式是實現(xiàn)迭代(或重復(fù)器),這帶來了一系列的挑戰(zhàn),比如在更新之間協(xié)調(diào)列表以及處理有效替換的“鍵”。3J728資訊網(wǎng)——每日最新資訊28at.com

不過我有點疲倦,這篇博文已經(jīng)夠長了。所以我把剩下的部分留給讀者自己來完成吧!3J728資訊網(wǎng)——每日最新資訊28at.com

結(jié)論

就是這樣。在這篇(冗長的)博文中,我們實現(xiàn)了自己的 JavaScript 框架。請隨意將其用作你全新 JavaScript 框架的基礎(chǔ),發(fā)布到世界上,激怒 Hacker News 的群眾。3J728資訊網(wǎng)——每日最新資訊28at.com

個人而言,我發(fā)現(xiàn)這個項目非常有教育意義,這也是我一開始為什么要做的一部分。我還希望用一個更小、更自定義的解決方案替換我的表情符號選擇器組件的當(dāng)前框架。在這個過程中,我成功地編寫了一個微小的框架,通過所有現(xiàn)有的測試,并比當(dāng)前實現(xiàn)小約 6kB,我對此感到相當(dāng)自豪。3J728資訊網(wǎng)——每日最新資訊28at.com

在將來,我認(rèn)為如果瀏覽器 API 足夠全面,將更容易構(gòu)建自定義框架將會很有趣。例如,DOM Part API 提案將消除我們上面構(gòu)建的 DOM 解析和替換系統(tǒng)的很多繁瑣工作,同時也為潛在的瀏覽器性能優(yōu)化敞開了大門。我還可以想象(帶有一些瘋狂的手勢)Proxy 的擴(kuò)展可能會使構(gòu)建完整的響應(yīng)性系統(tǒng)變得更容易,而不用擔(dān)心刷新、批處理或循環(huán)檢測等細(xì)節(jié)。3J728資訊網(wǎng)——每日最新資訊28at.com

如果所有這些東西都到位,那么你可以想象在實際上擁有一個“在瀏覽器中的 Lit”,或者至少一種快速構(gòu)建你自己“在瀏覽器中的 Lit”的方法。與此同時,我希望這個小練習(xí)有助于說明一些框架作者考慮的事情,以及你最喜歡的 JavaScript 框架底層的一些機制。3J728資訊網(wǎng)——每日最新資訊28at.com

感謝 Pierre-Marie Dartus 在這篇文章初稿中提供的反饋。3J728資訊網(wǎng)——每日最新資訊28at.com

原文:?https://nolanlawson.com/2023/12/02/lets-learn-how-modern-javascript-frameworks-work-by-building-one/3J728資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-48363-0.html讓我們通過構(gòu)建一個現(xiàn)代 JavaScript 框架來學(xué)習(xí)它是如何工作的

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 真實還原面試過程,被問懵了

下一篇: 圖解系統(tǒng)設(shè)計之Instagram

標(biāo)簽:
  • 熱門焦點
  • 線程通訊的三種方法!通俗易懂

    線程通信是指多個線程之間通過某種機制進(jìn)行協(xié)調(diào)和交互,例如,線程等待和通知機制就是線程通訊的主要手段之一。 在 Java 中,線程等待和通知的實現(xiàn)手段有以下幾種方式:Object 類下
  • 三言兩語說透柯里化和反柯里化

    JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是兩種很有用的技術(shù),可以幫助我們寫出更加優(yōu)雅、泛用的函數(shù)。本文將首先介紹柯里化和反柯里化的概念、實現(xiàn)原理和應(yīng)用
  • 2023年,我眼中的字節(jié)跳動

    此時此刻(2023年7月),字節(jié)跳動從未上市,也從未公布過任何官方的上市計劃;但是這并不妨礙它成為中國最受關(guān)注的互聯(lián)網(wǎng)公司之一。從2016-17年的抖音強勢崛起,到2018年的&ldquo;頭騰
  • 2天漲粉255萬,又一賽道在抖音爆火

    來源:運營研究社作者 | 張知白編輯 | 楊佩汶設(shè)計 | 晏談夢潔這個暑期,旅游賽道徹底火了:有的「地方」火了&mdash;&mdash;貴州村超旅游收入 1 個月超過 12 億;有的「博主」火了&m
  • 網(wǎng)紅炒股不為了賺錢,那就是耍流氓!

    來源:首席商業(yè)評論6月26日高調(diào)宣布入市,網(wǎng)絡(luò)名嘴大v胡錫進(jìn)居然進(jìn)軍了股市。在一次財經(jīng)媒體峰會上,幾個財經(jīng)圈媒體大佬就&ldquo;胡錫進(jìn)炒股是否知道認(rèn)真報道&rdquo;展開討論。有
  • 機構(gòu)稱Q2國內(nèi)智能手機銷量同比下滑4% vivo份額重回第1

    7月29日消息,根據(jù)市場調(diào)查機構(gòu)Counterpoint Research公布的最新報告,2023年第2季度中國智能手機銷量同比下降4%,創(chuàng)新自2014年以來第2季度銷量新低。報
  • 首發(fā)天璣9200+ iQOO Neo8系列發(fā)布首銷售價2299元起

    2023年5月23日晚,iQOO Neo8系列正式發(fā)布。其中,Neo系列首款Pro之作——iQOO Neo8 Pro強悍登場,限時售價3099元起;價位段最強性能手機iQOO Neo8同期上市
  • iQOO Neo8 Pro真機諜照曝光:天璣9200+和V1+旗艦雙芯加持

    去年10月,iQOO推出了iQOO Neo7系列機型,不僅搭載了天璣9000+,而且是同價位唯一一款天璣9000+直屏旗艦,一經(jīng)上市便受到了用戶的廣泛關(guān)注。在時隔半年后,
  • 電博會與軟博會實現(xiàn)"線下+云端"的雙線融合

    在本次“電博會”與“軟博會”雙展會利好條件的加持下,既可以發(fā)揮展會拉動人流、信息流、資金流實現(xiàn)快速交互流動的作用,繼而推動區(qū)域經(jīng)濟(jì)良性發(fā)展;又可以聚
Top 主站蜘蛛池模板: 略阳县| 琼海市| 宿松县| 大安市| 扶绥县| 定南县| 泰宁县| 双桥区| 宁化县| 兴安盟| 丰顺县| 城步| 上虞市| 上思县| 疏附县| 邯郸市| 渝中区| 奎屯市| 临猗县| 区。| 库车县| 西畴县| 青铜峡市| 米脂县| 佛冈县| 宝丰县| 天长市| 西盟| 东源县| 诏安县| 佳木斯市| 乌鲁木齐县| 平乐县| 长沙县| 永嘉县| 西乡县| 宁晋县| 河源市| 尖扎县| 苏尼特右旗| 正宁县|