嵌入式項目的微小(512字節)虛擬DOM模板引擎
| 即 /邊緣 | Firefox | 鉻合金 | 野生動物園 | 歌劇 | ios野生動物園 | Chrome for Android |
|---|---|---|---|---|---|---|
| 邊緣14+ | 45+ | 49+ | 10+ | 37+ | 10.2+ | 55+ |
。
為什麼?因為使用此類庫,您可以在緊密的空間環境(例如IoT設備)中創建強大的GUI,即使是額外的字節實際上至關重要!
設計小小:庫絕不能超過512字節的大小。目的不是要有另一個模板引擎,而是在512個字節中具有盡可能多的功能。如果需要新功能,則必須將另一個功能縮減,否則必須降低範圍。
為未來建立:圖書館正在大力利用ES6規格,這意味著它不受較舊的瀏覽器的支持。目前,它得到了市場上90%的瀏覽器的支持,但預計這將在明年內接近100%。
聲明性:以結構化的自然方式描述您的HTML DOM,可幫助您創建功能強大但可讀的用戶界面。
面向組件的:就像react.js一樣, dom促進了功能組件的使用。
“少寫”加速器:該庫API專門為具有簡短的功能名稱和加速器而設計,使您可以用更少的代碼來描述您的視圖。
.dom的項目您在項目中.dom嗎?分叉此存儲庫,然後將其添加到列表中!
對於最小足跡,請在您的項目中包括dotdom.min.js.gz (512b)。
< script src =" dotdom.min.js.gz " />另外,您可以直接在腳本之前包含庫的縮小版本。只需複制縮小的代碼即可。
如果您已經知道react.js,則以下示例可以幫助您了解.dom原語與反應的關係。
渲染非常簡單的DOM結構。
| 反應 | .dom |
|---|---|
ReactDOM . render (
React . createElement ( 'div' , null , 'Hello world' ) ,
document . body
) ; | R (
H ( 'div' , 'Hello world' ) ,
document . body
) |
創建一個可以通過屬性的組件。
| 反應 | .dom |
|---|---|
function Hello ( props ) {
return React . createElement (
'div' , null , `Hello ${ props . toWhat } `
) ;
}
ReactDOM . render (
React . createElement (
Hello , { toWhat : 'World' } , null
) ,
document . body
) ; | function Hello ( props ) {
return H ( 'div' , `Hello ${ props . toWhat } ` ) ;
}
R (
H ( Hello , { toWhat : 'World' } ) ,
document . body
) |
創建可以維持自己狀態的組件。
| 反應 | .dom |
|---|---|
class Clickable extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
clicks : 0
} ;
}
render ( ) {
const { clicks } = this . state ;
return React . createElement (
'button' , {
onClick ( ) {
this . setState ( { clicks : clicks + 1 } )
}
} , `Clicked ${ clicks } times`
) ;
}
}
ReactDOM . render (
React . createElement ( 'div' , null ,
React . createElement ( Clickable , null , null ) ,
React . createElement ( Clickable , null , null )
) ,
document . body
) ; | function Clickable ( props , state , setState ) {
const { clicks = 0 } = state ;
return H ( 'button' ,
{
onclick ( ) {
setState ( { clicks : clicks + 1 } )
}
} ,
`Clicked ${ clicks } times`
) ;
}
R (
H ( 'div' ,
H ( Clickable ) ,
H ( Clickable )
) ,
document . body
) |
該組件還可以訂閱生命週期事件:
| 反應 | .dom |
|---|---|
class WithLifeCycle extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
mounted : "no"
} ;
}
componentDidMount ( ) {
this . setState ( { mounted : "yes" } )
}
render ( ) {
const { mounted } = this . state ;
return React . createElement (
'div' , null , `mounted = ${ mounted } `
) ;
}
}
ReactDOM . render (
React . createElement ( 'div' , null ,
React . createElement ( WithLifeCycle , null , null ) ,
) ,
document . body
) ; | function WithLifeCycle ( props , state , setState , hooks ) {
const { mounted = "no" } = state ;
hooks . m . push ( ( ) => {
setState ( { mounted : "yes" } )
} ) ;
return H ( 'div' ,
`mounted = ${ mounted } `
) ;
}
R (
H ( 'div' , H ( WithLifeCycle ) ) ,
document . body
) |
鑰匙更新是React的一個有用的對帳功能,它使渲染引擎能夠對要更新的元素做出明智的決定。
一個特別有用的情況是,當您呈現動態元素列表時。由於渲染引擎不了解哪個元素已更改,因此最終以錯誤的更新而結束。
為了解決此問題,Vdom引擎使用唯一標識樹中元素的key屬性。但是,dom通過將元素狀態的副本保留在vdom元素實例本身中來解決。
這意味著您不需要任何key屬性,只需確保與以前返回相同的vdom實例。
如果要創建動態元素(例如,Vdom元素的數組),則可能難以檢測正確的更新順序。
| 反應 | .dom |
|---|---|
class Clickable extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
clicks : 0
} ;
}
render ( ) {
const { clicks } = this . state ;
const { ket } = this . props ;
return React . createElement (
'button' , {
onClick ( ) {
this . setState ( { clicks : clicks + 1 } )
}
} , `clicks= ${ clicks } , key= ${ key } `
) ;
}
}
const list = [ "first" , "second" , "third" ] ;
const components = list . map ( key =>
React . createElement ( Clickable , { key } , null ) ;
ReactDOM . render (
React . createElement ( 'div' , null ,
components
) ,
document . body
) ; | function Clickable ( props , state , setState ) {
const { clicks = 0 } = state ;
const { key } = props ;
return H ( 'button' ,
{
onclick ( ) {
setState ( { clicks : clicks + 1 } )
}
} ,
`clicks= ${ clicks } , key= ${ key } `
) ;
}
const list = [ "first" , "second" , "third" ] ;
const components = list . map ( key =>
H ( Clickable , { key } ) ;
R (
H ( 'div' , components ) ,
document . body
) |
請注意,即使其訂單已更改,上面的解決方案也將正確更新狀態組件。但是,如果您想要更新單個鍵的完整,類似於反應的功能,則可以使用Keyed插件。
function Container ( props , state ) {
const { components } = props ;
// The function `K` accepts the component state and an array of components that
// contain the `key` property, and returns the same array of components, with their
// state correctly manipulated.
return H ( "div" , K ( state , components ) ) ;
} 您可以通過將鉤子對象的.r屬性設置為任何真實值來創建RAW(未恢復)的Vdom節點(例如,攜帶任意HTML內容)。
這將禁用與兒童節點的進一步核對,從而使您的內容保持完整。
function Description ( props , state , setState , hooks ) {
const { html } = props ;
hooks . r = 1 ; // Enable raw mode
return H ( 'div' , {
innerHTML : html
} )
} R( VNode, DOMElement ) R ( H ( 'div' , 'Hello' ) , document . body )將給定的Vnode樹渲染到給定的DOM元素。來自狀態組件的進一步更新只會發生在他們的直系孩子上。
H( tagName | function, [properties], [children ...]) H ( 'tag' )
H ( 'tag' , { prop : "value" } )
H ( 'tag' , H ( 'child' ) )
H ( 'tag' , { prop : "value" } , H ( 'child' ) )
H ( Component , { prop : "value" } )創建一個vnode元素。如果將字符串作為第一個參數傳遞,則將創建HTML元素。如果給出函數,它將創建一個狀態組件。
財產和兒童是可選的,可以省略。
您可以提供一個函數,該函數可以根據某些高級邏輯返回虛擬DOM。此功能具有以下簽名:
const Component = ( props , state , setState , hooks ) {
// Return your Virtual DOM
return div ( ... )
} props屬性包含創建組件時給定的屬性對象。
該state被初始化為一個空對象{} ,並通過調用setState({ newState })方法來更新。後者還將觸發該組件及其孩子的更新。
如果您不想引起更新,也可以直接將屬性分配給state對象。
當您要在組件生命週期方法中註冊處理程序時,可以使用hooks對象。
與反應類似, .dom組成部分具有生命週期:
要訪問生命週期方法,您需要使用有關組件函數的第四個參數。更具體地說,您必須在以下任何一個字段中推動處理功能:
const Component = ( props , state , setState , hooks ) {
hooks . m . push ( ( domElement ) => {
// '.m' is called when the component is mounted
} ) ;
hooks . u . push ( ( ) => {
// `.u` is called when the component is unmounted
} ) ;
hooks . d . push ( ( domElement , previousDomElement ) => {
// `.d` is called when the component is updated
} ) ;
...
}tag( [properties], [children ...] ) const { div , span , a } = H ;
div ( 'hello' , span ( 'world' ) )
div ( 'click' , a ( { href : '#' } , 'Here' ) , 'to continue' )速記函數可以從H函數中提取為屬性。這樣的速記的行為與H完全一樣,但是標籤名稱已經填充。
建議在腳本開始時使用解構分配,以幫助JavaScript minifiers進一步優化結果:
const {div, span, a, button} = H;
tag.class( [properties], [children ...] ) const { h1 , span , p } = H ;
h1 . short ( 'short header' , span . strong ( 'strong text' ) )
button . primary ( { onclick : handleClick } , 'Primary Action' )
p . bold . italic ( twitterPost )您可以將.className assName速記與速記標記方法結合使用,而不是提供className作為屬性。
這與調用div({className: 'className'})相同,並且函數接口與上面完全相同。
注意:您可以通過將多個.class連接到標籤來添加多個類。例如: div.foo.bar與div({className: 'foo bar'})相同。
由於該項目的重點是小規模,因此缺乏理智檢查。這使其容易出現錯誤。請注意以下警告:
您無法使用屬性刪除觸發更新。您必須將新屬性設置為空值。例如:
// Wrong
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { } ) , document . body ) ;
// Correct
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { className : '' } ) , document . body ) ;您切勿在組件中使用名為$的屬性。這樣做,將使屬性對像被視為虛擬DOM節點,並會導致意外結果。
// *NEVER* do this!
R ( H ( MyComponent , { $ : 'Foo' } ) , document . body ) K(state, components)在
plugin-keyed.min.js中
根據其key屬性,確保列表中組件的狀態已同步。這使您能夠進行類似反應的鍵更新之類的信息:
function ValueRenderer ( ... ) {
...
}
function MyComponent ( props , state ) {
const { values } = props ;
const components = values . map ( value => {
H ( ValueRenderer , {
key : value ,
value : value
} ) ;
} )
// Synchronize state of components, based on their key
return H ( 'div' , K ( state , components ) )
} 您有興趣為.dom做出貢獻嗎?您非常歡迎!只需確保遵循指南:
npm install
npm test && npm run build && ls -l dotdom.min.js.gz
如果測試通過,並且dotdom.min.js.gz的大小小於或等於512字節,請創建一個拉請求。否則,減少範圍或考慮其他實現,以使其恢復到512個字節。
確保正確評論您的代碼,因為您很可能必須執行一些極端的JavaScript黑客入侵。 Gudeliens如下:
/**
* Functions are commented as JSDoc blocks
*
* @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render
* ...
*/
global . R = render = (
vnodes , // Flat-code comments start on column 70 and
dom , // wrap after column 120.
/* Logical separations can be commented like this */
...根據Apache許可證獲得許可,版本2.0