在接觸 React 時候,我只瞭解到經過 babel 能夠把 JSX 轉成 VNode(經過調用 React.createElement 方法),可是對其具體是如何轉換的卻不瞭解。html
很明顯,回答失敗。經過 github 上搜索 template+vnode 的關鍵詞,讓我搜到了htm
庫,發現簡直就是我想要的。讓咱們看下用法:vue
const htm = require("htm"); function h(type, props, ...children) { return { type, props, children }; } const html = htm.bind(h); html` <div>Hello World</div> `; // 返回: { type: 'div', props: null, children: ['Hello World'] } 複製代碼
htm 的大概思路是經過一個個字符遍歷 template 字符串,並設置狀態類型,當遇到<>表示進入元素狀態,遇到="'則表示屬性狀態。子元素的關係經過數組的 push 和 slice 某一位來肯定。 更詳細能夠看看這篇文章如何解析 template 成 VNODEnode
我想這裏應該是經過比較 VNode 和 DOM,並給出 VNode 的優點和 DOM 的不足。git
當前 Vue 和 React 都使用了 VNode,是出於什麼緣由,讓兩大目前最火熱的框架都選擇使用了 VNode 呢?github
這裏咱們直接看下寫的比較好的文章吧. 深度剖析:如何實現一個 Virtual DOM 算法面試
瞭解到上面知識的大體原理後,回顧了下 React 的 JSX 寫法:算法
render() {
return ( <ul> { list.map(item => <li>item</li>) } </ul> ) } 複製代碼
render() {
return ( <p>{{ msg }}</p> ) } 複製代碼
思考了下,若是結合 ejs 等模板引擎(這些模板引擎大體的思路是結合 template+data->html->設置到 DOM 的 innerHTML),先把數據填充進去,轉變成 html 字符串。npm
以後使用htm
轉成 VNode,再使用 Virtual Dom,使用 Virtual Dom 的 diff 和 patch,即可以實現了簡單的 MVVM 體驗。數組
沒錯,就是這麼簡單,廢話很少說,開幹吧。babel
<!-- 好比咱們須要渲染數組列表: --> <ul> <% for (let item of list) { %> <li></li> <% } %> </ul> <!-- 好比咱們須要條件渲染 --> <% if (condition) { %> <span>open</span> <% } else { %> <span>close</span> <% } %> <!-- 好比咱們須要渲染數據 --> <p><%= msg %></p> 複製代碼
個人思路的先處理邏輯運算如:(for,if 等), 經過正則/<%[^=]([^%]*)%>/g
來匹配,並經過str += 匹配內容
, 由於 exec 會含有 index 屬性,因此匹配以前的 html 經過 slice 來獲取,並拼接到 str。
let _str = 'let str = "";\n'; let exec; let index = 0; let content; while ((exec = REG.exec(str))) { content = str_format(str.slice(index, exec.index)); if (content) { _str += `str += '${content}';\n`; } _str += `${str_format(exec[1])}\n`; index = exec.index + exec[0].length; } // some code 複製代碼
處理完邏輯的代碼,經過正則/<%=([^%]*)%>/g
直接對上面的字符串進行 replace 操做替換。
具體代碼: template.js
這裏咱們使用simple-virtual-dom庫來實現虛擬 DOM 處理,咱們對上面函數 h 作一點調整。
import { el } from "simple-virtual-dom"; import htm from "htm"; function h(tagName, props, ...children) { return new el(tagName, props, children); } const html = htm.bind(h); const vnode = html([html_str]); 複製代碼
這裏咱們就實現了template+data
-> html str
-> VNode
的轉換。使用 VNode 庫提供的 render 轉成具體的 DOM 並掛載到 document 上。
可是咱們貌似尚未對事件進行處理,這裏我使用了事件委託機制,也就是掛載事件到 window 對象上進行監聽處理。因此這裏須要對simple-virtual-dom
庫的 element.js 作一點小調整.
// 惟一Id let uid = 0; function Element(tagName, props, children) { // 給每一個VNode增長uid this.uid = uid++; } Element.prototype.render = function() { for (var propName in props) { var propValue = props[propName]; // 這裏模仿vue的事件綁定 if (propName.startsWith("@")) { // 事件處理 const callback = (vm.$methods[propValue] || function() {}).bind(vm); delegate(window, `[dance-el-${this.uid}]`, propName.slice(1), callback); continue; } } // 添加uid屬性, 爲了事件代理 _.setAttr(el, "dance-el-" + this.uid, ""); }; 複製代碼
這樣,事件處理咱們也解決好了,哦對了,對 delegate 實現原理感興趣的能夠閱讀delegate源碼
這裏我加入了 React 中的 setState,當咱們調用這個方法,咱們會獲得新的 data 數據,這個時候再次觸發template+data
-> html str
-> VNode
的轉換.
而後使用 virtual dom 的 diff 和 patch 差別比較,修改只需改變的 DOM 元素。
你們能夠點擊這裏進行查看MVVM
若是能夠,還請給個 star,star 是面試加分項。😂
水平有限,不免有不對之處,還請指出,謝謝.😄