Hyperscript——React 的隐藏语言
JSX 是起点
React 使用 JSX 来简化开发者的工作。所以当你写类似这样的代码时。
<div id="foo">
Hello!
</div>
带有反应预设的 Babel 将其转换为此。
React.createElement("div", {
id: "foo"
}, "Hello!");
看看Babel REPL中的这个例子。React.createElement
这是一个创建虚拟节点的函数。
这是一个众所周知的事实,你可能已经知道了。那么它的意义何在呢?
Preact 方式
如果您以前使用过 Preact,您可能会注意到它的源代码中有一个不明显的导出。
export {
createElement,
createElement as h,
} from './create-element';
为了更清楚地说明,createElement
Preact 中的函数与 满足相同的需求React.createElement
。那么问题是,为什么它也被导出为h
呢?
原因很简单。它被导出为 ,h
因为它是一个 hypescript 函数。
那么 hypescript 到底是什么呢?
Hyperscript 是关键
Hypescript 是一种用 JavaScript 创建超文本的语言,由 Dominic Tarr 于 2012 年创立。他的灵感来源于markaby,一种用纯 Ruby 编写 HTML 的“简短代码”。Markaby
可以实现类似的功能。
require 'markaby'
mab = Markaby::Builder.new
mab.html do
head { title "Boats.com" }
body do
h1 "Boats.com has great deals"
ul do
li "$49 for a canoe"
li "$39 for a raft"
li "$29 for a huge boot that floats and can fit 5 people"
end
end
end
puts mab.to_s
该h
函数允许做本质上相同的事情,但使用不同的语法。
h = require("hyperscript")
h("div#foo", "Hello!")
它还支持嵌套和 CSS 属性。
h = require("hyperscript")
h("div#foo",
h("h1", "Hello from H1!", { style: { 'color': 'coral' } })
)
查看交互式演示来了解其工作原理。
亲自动手
现在,我们知道了该h
函数的作用以及为什么需要它,我们就可以编写自己的版本了。完整的示例可以在codesanbox上找到。
首先,让我们编写一个render
函数,从虚拟节点创建真实的 DOM 元素。
const render = ({type, children, props}) => {
const element = document.createElement(type);
if (props) {
for (const prop in props) {
element.setAttribute(prop, props[prop]);
}
}
if (children) {
if (Array.isArray(children)) {
children.forEach(child => {
if (typeof child === 'string') {
element.innerText = child;
} else {
element.appendChild(render(child));
}
})
} else if (typeof children === 'string') {
element.innerText = children;
} else {
element.appendChild(render(children));
}
}
return element;
}
然后,让我们创建这个h
函数。
const h = (type, children, props) => {
let handledType = typeof type === 'string' ? type : 'div';
return {
type: handledType,
props,
children
}
}
最后,让我们用我们的h
函数创建一个实际的内容,用我们的函数渲染它render
并将结果安装到 DOM。
const div = render(
h('div',
[
h('h1', 'Hello!', { id: 'foo' }),
h('h2', 'World!', { class: 'bar' })
],
)
);
document.querySelector('#app').appendChild(div);