渲染器示意圖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