render(element, vnodes)
描述
将虚拟 DOM 渲染到指定的 DOM 元素中。
m.render(document.body, 'hello');
// <body>hello</body>
签名
m.render(element, vnodes, redraw)
参数名 | 类型 | 是否必需 | 描述 |
---|---|---|---|
element | Element | 是 | 作为子树父节点的 DOM 元素。 |
vnodes | Array<Vnode>|Vnode | 是 | 要渲染的 虚拟节点 (vnodes)。 |
redraw | () -> any | 否 | 一个回调函数,每次调用子树中的事件处理程序时都会执行。 |
返回值 | 返回 undefined 。 |
工作原理
m.render(element, vnodes)
方法接收一个虚拟 DOM 树(通常使用 m()
hyperscript 函数 创建),生成对应的 DOM 树并将其挂载到 element
上。如果 element
之前已经通过 m.render()
挂载了一个 DOM 树,则会将新的 vnodes
树与之前的树进行比较,并仅修改现有 DOM 树中需要更新的部分。未发生更改的 DOM 节点将保持不变。
如果提供了可选的 redraw
参数,则每次调用子树中任何位置的事件处理程序时,都会执行该回调函数。m.mount
和 m.redraw
使用此机制来实现 自动重绘 功能。同时,它也暴露出来,以支持更高级的用例,例如与第三方框架集成。
m.render
是同步执行的。
为什么使用虚拟 DOM
在每次重绘时生成一个 vnode 树似乎效率不高,但实际上,创建和比较 JavaScript 数据结构比直接读取和修改 DOM 的开销要小得多。
操作 DOM 可能会非常耗费资源,原因如下:频繁地交替读取和写入 DOM 可能会导致浏览器频繁重绘,从而影响性能。而比较虚拟 DOM 树可以将多次写入操作合并为单次重绘。此外,各种 DOM 操作的性能特征在不同浏览器实现中存在差异,难以针对所有浏览器进行学习和优化。例如,在某些实现中,读取 childNodes.length
的复杂度为 O(n);在某些实现中,读取 parentNode
可能会导致重绘。
相比之下,遍历 JavaScript 数据结构具有更可预测和稳定的性能特征。此外,vnode 树的实现方式使得现代 JavaScript 引擎能够应用诸如隐藏类等优化,从而获得更好的性能。
与其他 API 方法的区别
m.render()
方法在内部被 m.mount()
、m.route()
、m.redraw()
和 m.request()
调用。它不会在 流更新 后被调用。
与 m.mount()
和 m.route()
不同,通过 m.render()
渲染的 vnode 树不会自动响应视图事件、m.redraw()
调用或 m.request()
调用而重绘。这是一种底层机制,适用于希望手动控制渲染过程,而不是依赖 Mithril.js 内置的自动重绘系统的库开发者。
另一个区别是 m.render
方法期望一个 虚拟节点 (vnode) 或一个 vnodes 数组作为其第二个参数,而 m.mount()
和 m.route()
期望一个组件。
独立使用
var render = require("mithril/render")
m.render
模块在功能上类似于 Knockout、React 和 Vue 等视图库。它实现了一个虚拟 DOM 差异比对引擎,具有现代的搜索空间缩减算法和 DOM 回收机制,从而提供一流的性能,无论是在初始页面加载还是重新渲染时。除了通过 require("mithril/render/vnode")
暴露的规范化之外,它不依赖于 Mithril.js 的其他部分,并且可以作为独立库使用。
尽管体积相对较小,但 render 模块功能齐全且自给自足。它支持您可能期望的一切:SVG、自定义元素以及所有有效的属性和事件,没有任何奇怪的大小写敏感的边缘情况或异常。当然,它也完全支持 组件 和 生命周期方法。
当将 m.render
用作独立模块时,导入 hyperscript 函数
来创建要渲染的 vnodes 会很有帮助。