如何用 55 行代碼實現一個簡單的 View 框架
整天用各種視圖框架 React, Vue 有時候也想到底是怎麼實現的,源碼太多太長沒太多時間翻看,正好今天不加班,本來準備嘗試下 Chrome 61 的 es6 module 支持,然後就停不下來了。花了半個小時順手寫了這個。。所以代碼按照 es6 模塊拆分,沒有使用 webpack 打包,需要像 Chrome 61 這樣的支持原生 es6 module 的瀏覽器才能查看 demo。主要實現參考 Vue 的使用方式,所以這裡不再對使用方面作詳細說明。
demo:GView
GitHub 地址:gaoxiaoliangz/gview
Reactive Object
首先,我們需要一個反應式的 Object。用過 Vue 的都知道,就是說當你修改組件內部狀態的時候,對應的視圖會刷新,那麼這個是如何實現的呢?其實,說到底就是在 Object 對應的 key 的 setter 裡面調用了一個回調函數,從而知道組件狀態里的某個值被修改了,需要更新視圖。
下面我們就來定義一個函數,將一個普通的 Object 轉化為一個 Reactive Object
function makeReactive(obj, callback) { const reactiveObj = {} Object.keys(obj).forEach((key) => { Object.defineProperty(reactiveObj, key, { get() { return obj[key] }, set(value) { obj[key] = value if (callback) { callback(key, value) } } }) }) return reactiveObj}
看代碼就很清楚了,遍歷每個 key,然後用 defineProperty 定義 getter 和 setter,setter 被觸發時調用回調函數,傳入 key 和 value。
Template
template 的處理很簡單,只做了解析數據的支持,用 {} 包裹 key,然後通過正則匹配並注入,組件狀態對應的 key 的值。
Methods
這個其實就是定義的一些函數,然後在構造函數裡面將每一個方法都綁定實例的 this。
Render
由於只是一個超輕量級的實現,所以這部分就簡單粗暴的設置 innerHTML 來實現渲染,所以如果實際用這種方法來開發,一是會造成新能問題,另外 mounted 會在每一次組件狀態更新時觸發。
生命周期函數
目前只寫了 mounted,並且由於 Render 的簡單粗暴,實際會在每一次視圖重繪後觸發。
代碼
export default class GView { constructor(config) { const { el, template, data, methods, mounted } = config this.config = config this.$root = document.getElementById(el) this.$state = makeReactive(this.getAllData(), this.render.bind(this)) if (methods) { Object.keys(methods).forEach(key => { methods[key] = methods[key].bind(this) }) this.$methods = methods } this.render() } getAllData() { return this.config.data() } render() { const html = this.config.template.replace(/{(.*?)}/g, val => { const content = val.substr(1, val.length - 2) const value = this.$state[content] if (value === undefined) { console.error(`${content} is not defined!`) return } return value }) this.$root.innerHTML = html if (this.config.mounted) { this.config.mounted.call(this) } }}
這是具體的 GView 類的代碼實現。
總結
雖然寫的匆忙代碼看起來也很簡陋,但是整個思想還是很清晰的。本來其實就想玩玩 Chrome 61 的 es6 module,稍稍有點過了,不過這種對 es6 module 的原生支持也許會在後面的開發工具上帶來一些變革,比如開發的時候可能不需要用 webpack 之類的打包工具,只是在發布的時候打包一下,並且可以通過漸進增強的思想讓支持 es6 module 的客戶端直接用原生的。扯遠了。。隨著現在 mvvm 這種形式的框架越來越多的被使用,其實搞清楚其內部的原理,對於開發來說,不僅能提升自身對於框架的理解,另一方面,也能提供一種更高層次的解決問題的視角。
推薦閱讀:
※2017年度最流行的十大中國開源軟體
※從用緩存優化函數性能說說第三方框架的使用
※Qt用emit隨時隨地發送信號
※測試弱密碼
※框架到底是個什麼東西?