渲染器示意图javascript
所谓渲染器,简单的说就是将 Virtual DOM 渲染成特定平台下真实 DOM 的工具(就是一个函数,一般叫 render),渲染器的工做流程分为两个阶段:mount 和 patch,若是旧的 VNode 存在,则会使用新的 VNode 与旧的 VNode 进行对比,试图以最小的资源开销完成 DOM 的更新,这个过程就叫 patch,或“打补丁”。若是旧的 VNode 不存在,则直接将新的 VNode 挂载成全新的 DOM,这个过程叫作 mount。html
须要将基于实际框架(vue/react)描述的文档结构用js对象来描述vue
不论是.vue文件仍是jsx文件和咱们在控制台看到的实际dom结构仍是有必定距离的,固然具体的框架会有相应解析,在web层面固然都是咱们所熟悉的dom文档结构。java
为了简化框架的解析过程。咱们目标落在实现由实际的dom结构到保存在内存中由js对象描述的virtual-domnode
// 一个ul-li列表能够以下表示 <ul id='list'> <li class='item'>Item 1</li> <li class='item'>Item 2</li> <li class='item'>Item 3</li> </ul> // 树形数据 var element = { tagName: 'ul', // 节点标签名 props: { // DOM的属性,用一个对象存储键值对 id: 'list' }, children: [ // 该节点的子节点 {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]}, ] }
这里的element就是virtual-dom的样子,只不过实际中从最外层标签开始,结构比这复杂而已!react
为了验证dom文档结构能够抽象成以上的javascript对象,能够实际的遍历dom结构,以DFS遍历树形结构为例,打印当前页面的tagName,classList, 层级git
const DFS = function(node) { if (!node) { return } let deep = arguments[1] || 1 console.log(`${node.nodeName}.${node.classList} ${deep}`) if (!node.children.length) { return } Array.from(node.children).forEach((item) => DFS(item, deep + 1)) } // 在body标签上加了test id属性 var aimNode = document.getElementById('test') DFS(aimNode)
Vue 的 render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node即virtual-dom,体会一下和这里render的区别github
肯定基本的vNode类,web
function Vnode (tagName, props, children) { this.tagName = tagName this.props = props this.children = children } // 添加render方法 Vnode.prototype.render = function () { var el = document.createElement(this.tagName) // 根据tagName构建 var props = this.props for (var propName in props) { // 设置节点的DOM属性 var propValue = props[propName] el.setAttribute(propName, propValue) } var children = this.children || [] children.forEach(function (child) { var childEl = (child instanceof Vnode) ? child.render() // 若是子节点也是虚拟DOM,递归构建DOM节点 : document.createTextNode(child) // 若是字符串,只构建文本节点 el.appendChild(childEl) }) return el } // 实例化ul,是一个virtual-dom对象 var ul = new Vnode('ul', {id: 'list'}, [ new Vnode('li', {class: 'item'}, ['Item 1']), new Vnode('li', {class: 'item'}, ['Item 2']), new Vnode('li', {class: 'item'}, ['Item 3']) ]) // 挂载到body var ulRoot = ul.render() document.body.appendChild(ulRoot)
function render(vnode, container) { // 获取vnode const prevVNode = container.vnode if (prevVNode == null) { if (vnode) { // 没有旧的 VNode,只有新的 VNode。使用 `mount` 函数挂载全新的 VNode mount(vnode, container) // 将新的 VNode 添加到 container.vnode 属性下,这样下一次渲染时旧的 VNode 就存在了 container.vnode = vnode } } else { if (vnode) { // 有旧的 VNode,也有新的 VNode。则调用 `patch` 函数打补丁 patch(prevVNode, vnode, container) // 更新 container.vnode container.vnode = vnode } else { // 有旧的 VNode 可是没有新的 VNode,这说明应该移除 DOM,在浏览器中可使用 removeChild 函数。 container.removeChild(prevVNode.el) container.vnode = null } } }
模拟由vnode到实际dom的过程,见前文render方法算法
var patches = diff(tree, newTree) 这里须要介绍diff算法
前面的例子是Virtual DOM渲染为 Web 平台的真实 DOM,因为面向浏览器,渲染器内部须要调用浏览器提供的 DOM 编程接口
为了实现多端渲染,render方法不须要再强依赖DOM 编程接口
相应的操做节点的接口由具体平台暴露,知足相似与dom节点的增删改查
节点:能够理解成对应平台的展现单元,如web端展现的是dom
function specialRenderer(options) { const { hanlde: { createElement: platformCreateElement, appendChild: platformAppendChild, insertBefore: platformInsertBefore, removeChild: platformRemoveChild, parentNode: platformParentNode, nextSibling: platformNextSibling, querySelector: platformQuerySelector } } = options }
Vue3 提供了一个叫作 @vue/runtime-test 的包,其做用是方便开发者在无 DOM 环境时有能力对组件的渲染内容进行测试。
Taro 是一套遵循 React 语法规范的 多端开发 解决方案。现现在市面上端的形态多种多样,Web、React-Native、微信小程序等各类端大行其道,当业务要求同时在不一样的端都要求有所表现的时候,针对不一样的端去编写多套代码的成本显然很是高,这时候只编写一套代码就可以适配到多端的能力就显得极为须要。
使用 Taro,咱们能够只书写一套代码,再经过 Taro 的编译工具,将源代码分别编译出能够在不一样端(微信/百度/支付宝/字节跳动/QQ小程序、快应用、H五、React-Native 等)运行的代码。
Taro多端实现猜测
too simple sometimes naive
基于渲染层的一点认识开始去调研市面上多端渲染框架的原理,结果实现截然不同。
探讨:基于react的Taro是如何实现一套代码,多端运行?
业务代码统一约束,借助babel输出多端代码
Chameleon:在各个端运行时分别实现了 Framework 统一,在各个端尽可能使用原有框架,方便利用其生态,这样不少组件能够直接用起来。(folk?)
再次回顾一下这张图,基本思想就是借助Virtual DOM 带来了 分层设计,每一步的单独处理均可以自成一家之言。框架多端编译的概念层出不穷,
站在开发者的角度知道背后实际是作了哪些改动就能够根据本身的兴趣选择方向。
文中对一些方法和操做作了简化,目的在与梳理流程,知识点包括不限于:
VirtualDOM和基本DFS:https://zhuanlan.zhihu.com/p/64187708
渲染器解读:http://hcysun.me/vue-design/zh/renderer-advanced.html