hyperscript 是一个帮助构建dom的js类库,可以运行于服务端以及客户端,在virtual-dom模块中略加修改用它来生成虚拟DOM。
但这个模块本身是生成真是DOM的。
它的典型用法是:
h('h1', 'Hello World!')
会返回一个标准dom节点:
<h1>Hello World!</h1>
hyprscript 类库并不大,源代码总共160+行。
以下为源码解读:
// 一个跨浏览器的split实现
var split = require('browser-split')
// 跨浏览器的class list实现
var ClassList = require('class-list')
// 如果不是客户端 就使用 shim(一个HTMLElement实现,支持一些方法比如createElement)
var w = typeof window === 'undefined' ? require('html-element') : window
var document = w.document
var Text = w.Text
以上一段是为了支持server端可用使用的基本方法(因为client端方法都有不需要额外引入)。
紧接着就到了核心函数实现 既h('h1', 'Hello World!')
实现:
// 用例
// 基本结构生成
h('h1', 'Hello World!')
// 带attribute的dom
h('a', {href: 'hyperscript'}, 'hyperscript')
// 带事件的dom
h('a', {href: '#',
onclick: function (e) {
alert('you are 1,000,000th visitor!')
e.preventDefault()
}
}, 'click here to win a prize')
// 带style的dom
h('h1.fun', {style: {'font-family': 'Comic Sans MS'}}, 'Happy Birthday!')
// child生成
h('h1', [1,2,3,4].map(k=>{
return h('span',k)
}))
以上为该函数的用例,基本上实现方式就是围绕这个来构建的。
function h() {
// 参数解析
var args = [].slice.call(arguments),
// element 提前声明
e = null
// dom生成核心函数
function item(l){
///
}
// 将每个参数传递给 item来生成dom结构
while (args.length)
item(args.shift())
return e
}
// 接下来看 item实现
function item(l) {
// 结果存储 提前声明
var r ;
// 解析dom string里面的选择器 class or id ,
// 并生成element `e = document.createElement(xx) `
// 或者 在element上增加选择器属性 ` e.setAttribute('id',xx)`
function parseClass(){
// some code
}
// 接下来开始解析参数
// 如果没参数 不作处理
if (l == null)
;
// 如果是字符串 开始解析
else if ('string' === typeof l) {
// 如果element不存在 生成element
if (!e)
parseClass(l)
else
// 如果存在 说明这个是子级文字节点
e.appendChild(r = document.createTextNode(l))
}
// 如果是 数字、布尔、date、正则,都解析为文字节点
else if ('number' === typeof l
|| 'boolean' === typeof l
|| l instanceof Date
|| l instanceof RegExp) {
e.appendChild(r = document.createTextNode(l.toString()))
}
// 如果是数据,则遍历处理
else if (isArray(l))
forEach(l, item)
// 如果是node ,插入节点
else if (isNode(l))
e.appendChild(r = l)
// 如果是文字 ,插入节点
else if (l instanceof Text)
e.appendChild(r = l)
// 如果是对象
else if ('object' === typeof l) {
for (var k in l) {
if ('function' === typeof l[k]) {
// 绑定事件
if (/^on\w+/.test(k)) {
} else {
// 生成时候可执行函数 。后面可用cleanup清除
// observable
e[k] = l[k]()
cleanupFuncs.push(l[k](function (v) {
e[k] = v
}))
}
}
}
// 样式
else if (k === 'style') {
}
// 属性
else if (k === 'attrs') {
}
// data标准属性
else if (k.substr(0, 5) === "data-") {
}
// 最后补偿措施 一些属性直接存储dom上
else {
e[k] = l[k]
}
}
// 最后是function 一般用户批量生成子节点
else if ('function' === typeof l) {
//assume it's an observable!
var v = l()
e.appendChild(r = isNode(v) ? v : document.createTextNode(v))
cleanupFuncs.push(l(function (v) {
if (isNode(v) && r.parentElement)
r.parentElement.replaceChild(v, r), r = v
else
r.textContent = v
}))
}
return r;
}