看到微博上 vue-loader
開始支持代碼熱替換的消息
真讓人坐不住, 趕忙翻代碼看下, 結果看不懂
現實的壓力仍是在的, react-hot-loader
已經不推薦使用了
做者搞了 React Transform, 並且針對 Babel 優化, 整套新的東西
而簡聊用 actions-recorder
加 react-hot-loader
多少有些風險
讓 actions-recorder
更好調試是頗有必要的css
然而問題的重點是, Webpack 是跟很強大但文檔挺難懂的項目
週末我大體翻了一些文檔, 晚上作了寫嘗試, 大體達成了基本的功能
https://github.com/webpack/docs/wiki/hot-module-replacement
http://jlongster.com/Backend-Apps-with-Webpack--Part-III
實際上一個 API 就能解決的事情, 太簡單了.. 只是門檻真高啊html
細節強烈建議讀上邊兩篇文檔, 到底仍是花了很長篇幅解釋的
簡單的彷佛, 大體上就是 Webpack 當中模塊其實是 module tree
這個和 DOM tree 很相似, update 在其中就是一個個事件
update 事件會冒泡, 能夠經過代碼捕捉和中止冒泡
若是冒泡到根節點, 那麼說明代碼沒有處理 update 事件, 因而刷新頁面vue
module.hot.accept
react
Wiki 上的這個服務端例子, 我以爲最能說清 Webpack 是怎麼作的
不過這個例子怎麼運行我就徹底不知道了... 文檔沒寫清楚
注意其中的 module.hot
和 module.hot.accept
兩個 API
前者只是狀態, 無視, 然後者就是用來捕捉 update
事件進行處理的所有webpack
var requestHandler = require("./handler.js"); var server = require("http").createServer(); server.on("request", requestHandler); server.listen(8080); // check if HMR is enabled if (module.hot) { // accept update of dependency module.hot.accept("./handler.js", function() { // replace request handler of server server.removeListener("request", requestHandler); requestHandler = require("./handler.js"); server.on("request", requestHandler); }); }
能夠看到代碼裏指明瞭 ./handle.js
模塊的更新須要被接受
而後, update 發生時, 原有的舊代碼 requestHandler
先移除掉
而後從新調用 require
加載新的代碼, 而後再從新綁定一次
內部的操做, Webpack 自動完成的, 我按照這個寫法也基本沒遇到問題
總之這樣就完成了模塊熱替換的實現git
module.hot.dispose
github
這個 API 說實話我還不會用, 如今只知道大概的意思
單單上邊的方案只適合處理純函數的代碼, 也就是沒有內部狀態
若是一個模塊, 好比說會往 DOM 上掛載節點的,
那麼模塊移除的時候它就應該自動把掛上去的節點帶走銷燬.dispose()
方法大體上就是處理這樣一個場景用的, 看代碼web
// addStyleTag(css: string) => HTMLStyleElement var addStyleTag = require("./addStyleTag"); var element = addStyleTag(".rule { attr: name }"); module.exports = null; // check if HMR is enabled if(module.hot) { // accept itself module.hot.accept(); // removeStyleTag(element: HTMLStyleElement) => void var removeStyleTag = require("./removeStyleTag"); // dispose handler module.hot.dispose(function() { // revoke the side effect removeStyleTag(element); }); }
固然, 實際嘗試處理 dispose
我估計還會遇到各類坑, 不像折騰了
還好這個 API 尤雨溪大神也不用, 我就先不要趟渾水了
還好 React 裏的東西主要都是一些純函數的編碼風格主導的, 私有狀態很少app
此外在 module.hot
還綁定了一些能訪問內部狀態和操做的 API
具體狀況比較複雜, 我如今徹底不知道如何下手
若是頗有興趣或者很無聊, 能夠繼續看看兩個著名的 loader 怎麼搞的:
https://github.com/webpack/style-loader/blob/master/index.js
https://github.com/gaearon/react-hot-loader/blob/master/index.jsless
上邊的代碼會有個不足, 就是 module.hot
相關的代碼直接在源碼裏了
其實不屬於應用的代碼, 只是專門處理模塊熱替換的, 有點難看
前面這些 loader 考慮到了這一點, 因而會自動生成代碼進去
也就是好比一個 React 組件的 module.exports
是個組件,
那麼這個組件前面後面就爆包裹一段生成的代碼, 用來調用 accept
方法
確實是個頗有效果的方法
不用這樣的辦法, 我在 Amok 的文檔上見到一種
由於 Amok 是經過 Remote Debugging API 動態替換 JavaScript 的
就是說任何狀態很大程度上可以保留的, 只是更新函數代碼
因此它能夠在代碼替換完成後, 直接頂層調用一次 render
搞定
http://amokjs.com/getting_started.html
window.addEventListener('patch', function(event) { React.render(app); });
我在現有的 actions-recorder
方案中模擬了一下
隱隱會有一些問題, 就是組件內部狀態我未必能確保
這個更多取決於 React 內部機制怎樣處理 render
過程具體的更新
反過來看 react-hot-loader
的方案會是更有保障的
就是每一個組件在更新時類方法都單獨更新了一遍, 同時手動維護好 state
只是代碼量真心不小, 也沒有講解, 我並不知道具體細節如何
actions-recorder
中爲了支持熱替換, 我增長了一個方法hotSetup
, 和 setup
相似傳入對象配置 schema
和 updater
代碼我覺直接複製文件了, 應該能看懂... 也就是代碼多複製了一遍嘛:
React = require 'react' recorder = require 'actions-recorder' ReactDOM = require 'react-dom' Immutable = require 'immutable' require('volubile-ui/ui/index.less') # 正常引用 schema 和 updater schema = require './schema' updater = require './updater' Page = React.createFactory require './app/page' # 第一次初始化 recorder.setup initial: schema.store updater: updater # 第一次生成 render 函數, 以及作綁定 render = (store, core) -> ReactDOM.render Page({store, core}), document.querySelector('.demo') recorder.request render recorder.subscribe render # 開始處理模塊熱替換, 特徵代碼, 很容易認出來吧 if module.hot # 聲明兩個入口文件 module.hot.accept ['./updater', './schema'], -> # 從新調用新的模塊 schema = require './schema' updater = require './updater' # 此次用 hotSetup 傳入參數, hotSetup 會強制建立新的 store recorder.hotSetup initial: schema.store updater: updater # 組件也要處理一下, 跟上邊邏輯有點區別 module.hot.accept ['./app/page'], -> # 從新調用新的組件 Page = React.createFactory require './app/page' # 先解綁舊的 render 函數, 同時生成新的 render 函數 # ... 考慮到變量是能夠修改的, 下面引用的 Page 是新的也許不用寫那麼多 recorder.unsubscribe render render = (store, core) -> ReactDOM.render Page({store, core}), document.querySelector('.demo') # 新的 render 函數先調用一次更新下界面, 而後從新監聽 recorder.request render recorder.subscribe render
這樣的代碼, 實現的效果就是, updater schema Page
代碼能夠熱替換updater
代碼的更新在熱替換後能夠立刻從回調的 store 中體現schema
更新會更新 actions-recorder
初始狀態, 也是更新 storePage
更新會引發整個 DOM tree 從新渲染, 就是 DOM 的局部更新
初步的 Demo 已經運行正常, 複雜場景還須要多加試驗
而後 actions
可能涉及到很多的代碼, 我發現也能夠試着改一下
首先 todo.coffee
文件裏寫把 actions
須要的方法寫好
而後用這個文件... 其實就是寫兩遍啦, 都不須要解釋了
因爲 JavaScript 數據可變, 後面的 exports
就都能訪問到新的代碼了
todo = require './todo' exports.create = todo.create exports.update = todo.update exports.toggle = todo.toggle exports.archive = todo.archive if module.hot module.hot.accept ['./todo'], -> todo = require './todo' exports.create = todo.create exports.update = todo.update exports.toggle = todo.toggle exports.archive = todo.archive
那麼用視頻演示下:
http://www.tudou.com/programs/view/3zCKPo6a1ZU/?phd=99
大體就這樣吧, 後面的開發當中我再想法子深刻一下
哪位有資源但願也能共享, Webpack 這個文檔看着太累了
目前代碼熱替換比較驚人的, 除了 Webpack 還有 Amok 和 Elm, Figwheel
Amok 是用 Chrome DevTools 內部 API, 對現成的大項目項目支持度存疑
後邊兩個是函數式社區超前並且高大上的玩意, 我只能看看視頻瞭解下
另外 Redux 做者搞的那一整套東西, 我也就望而生畏一下了
首先熱替換帶來的界面開發的效率提升是有目共睹的, 回想下改 CSS
另外一方面, 函數式的理念讓代碼熱替換變得可行甚至實用
我想這也會極大的改變你們理解程序, 理解圖形應用的方式
對於程序的抽象, 能夠遠遠超出二進制指令用 subroutine 整合的抽象
而是程序執行的 tree 能夠分爲函數, 狀態, 還有 IO 幾個變和不變的部分,
某一時刻代碼修改了, 這棵 tree 其實能夠快速更新爲等效的新的 tree
就是 React 更新 DOM 同樣, 某些場景理解對了, 更新就很是巧妙
開發效率不是件小事, 後續事情還不少, 我文章先結尾了
照例加個公司招聘郵箱 <hr@teambition.com>