嵌入式项目的微小(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