好久不見,在 資服創新競賽 結束後,我耍廢了好一段時間,更準確來說,從校內專題比賽結束後就開始耍廢了 XD,Hexo 的開發停滯這麼久真是不好意思 てへぺろ(・ω<)。
我在暑假時開始進入 Dcard,利用 React + Redux + React Router 寫了一個和現行網站完全分離的行動版網站,那時候 Redux 的文件還非常不齊全,很多東西都得自己摸索,不過拜 @tomchentw 所賜,解決了很多架構上的問題。
然而在我跑去寫 V2 API 的這兩個月,Redux 竟然從 1.0.0 升級到 3.0.4 了!React Router 也終於推出 1.0.0 了!原本以為新版 Admin panel 也能沿用相同的配置,沒想到還是有一些部分得推倒重來,不禁令人感嘆前端變化之快。
Redux
先說說 Redux 究竟有什麼差別吧,其實 Redux 本身的修改倒還好,只要看 Changelog 就 OK 的程度,但是它周邊的東西就麻煩了。react-redux 從 0.2.1 跳到 4.0.0,此外還多了一堆伙伴,Redux 生態圈的形成也他媽的太快了吧!
react-redux
react-redux 本身的 API 很簡單,但是 Changelog 卻能出現好幾次的 Breaking Change,這東西是有這麼容易 Break 逆!
主要的差別在於 connect
function 變得更好用了,過去只能用來綁定 props,現在也能綁定 action,如此一來就能省去 bindActionCreators
的過程了。
1 2 3 4 5
| @connect(state => ({ }), { })
|
另一點則是 Provider
class,原本的寫法實在不怎麼優雅,this.props.children
傳入的是一個函數,而現在可以直接傳入 React element。
1 2 3 4 5
| React.render( <Provider store={store}> <App/> </Provider> , document.getElementById('root'));
|
redux-router
redux-router 是用來整合 Redux 和 React Router 的,他做的事情很簡單,就是把 Router 的資料存在 Redux store 裡,但是實際上用起來卻有不少問題,所以我先說結論:
改用 redux-simple-router,不然就不要把 Router 狀態存在 Redux store 裡。
你一定會很好奇我怎麼會去推薦一個版號連 0.1 都不到的超新星套件,但是 redux-router 這貨真的很坑,為了你的肝指數著想,請等到 1.0.0 正式版推出後再考慮它吧。
說了這麼多,這東西究竟有什麼問題呢?咱們來一一列舉吧:
接著比較實際的程式碼吧:
redux-router1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import React from 'react'; import {render} from 'react-dom'; import {createStore, compose} from 'redux'; import {ReduxRouter, reduxReactRouter} from 'redux-router'; import {createHistory} from 'history';
const store = compose( middlewares, reduxReactRouter({ routes, createHistory }) )(createStore)(rootReducer, initialState);
render( <Provider store={store}> <ReduxRouter/> </Provider> , document.getElementById('root'));
|
redux-simple-router1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import React from 'react'; import {render} from 'react-dom'; import {createStore, compose} from 'redux'; import {createHistory} from 'history'; import {syncReduxAndRouter} from 'redux-simple-router'; import {Router} from 'react-router';
const history = createHistory(); const store = compose( middlewares )(createStore)(rootReducer, initialState);
syncReduxAndRouter(history, store);
render( <Provider store={store}> <Router routes={routes} history={history}/> </Provider> , document.getElementById('root'));
|
redux-router 需要在 createStore 時就加入 reduxReactRouter
middleware,而 redux-simple-router 則是在 store 創建完成後才同步 router 狀態;redux-router render 時使用 ReduxRouter
元件,而 redux-simple-router 直接使用 React Router 的 Router
元件。由於後者非常簡單,也減少了錯誤發生的機率。
這東西真的很炫,它可以在頁面中顯示側欄來顯示 action 的動態,還可以復原某些 action 對 store 的變更,使用起來非常簡單,讓我愛不釋手。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React from 'react'; import {render} from 'react-dom'; import {createStore, compose} from 'redux'; import {persistState} from 'redux-devtools'; import {createDevTools} from 'redux-devtools'; import LogMonitor from 'redux-devtools-log-monitor'; import DockMonitor from 'redux-devtools-dock-monitor';
const DevTools = createDevTools( <DockMonitor toggleVisibilityKey='H' changePositionKey='Q'> <LogMonitor/> </DockMonitor> );
const store = compose( DevTools.instrument(), persistState( window.location.href.match( /[?&]debug_session=([^&]+)\b/ ) ) )(createStore)(rootReducer, initialState);
render( <Provider store={store}> <DevTools/> </Provider> , document.getElementById('root'));
|
React Router
React Router 從 0.13 到 1.0 的差別非常大:
- Named route 被移除了,原本
Link
元件能從 named route 自動產生完整的路徑,現在必須自己來
- 不一定得用 JSX 來寫 route,現在也能用 plain object 了,這還挺方便的
willTransitionTo
=> onEnter
, willTransitionFrom
=> onLeave
- 支援 Async child routes,現在能更方便的切頁面了
我從暑假時因為 React context 的關係就開始用 React 0.14 + React Router 1.0 beta 了,所以轉換起來比較無痛,但是 1.0 beta 和正式版還是有些 API 差別的,因為一言難盡,還是看 Changelog 吧。
原本我使用的是 React Hot Loader,這種方法其實用起來也沒什麼大問題,只是得另外開個 webpack-dev-server。不過原作者決定廢棄 React Hot Loader,又另外開了個 React Transform,所以我也只好轉移到 React Transform 了。相較上面幾個大改變來說,這只是編譯過程的改變而已,只需要改 Webpack 和 Babel 的配置即可。
首先把 webpack-dev-server 相關的東西都移除掉,安裝:
1 2 3 4 5 6
| $ npm install babel-plugin-react-transform --save-dev $ npm install react-transform-catch-errors --save-dev $ npm install react-transform-hmr --save-dev $ npm install redbox-react --save-dev $ npm install webpack-dev-middleware --save-dev $ npm install webpack-hot-middleware --save-dev
|
以下程式碼使用的是 Express,如果你用的是 Koa 的話,請把 webpack-dev-middleware
改成 koa-webpack-dev-middleware
,webpack-hot-middleware
改成 koa-webpack-hot-middleware
,這兩個 middleware 的使用方式會有些差異,不過大致上是差不多的。
server.js1 2 3 4 5 6 7 8 9 10 11 12 13
| import webpack from 'webpack'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware'; import webpackConfig from './config';
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
|
webpack.config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import webpack from 'webpack';
export default { entry: { main: [ 'webpack-hot-middleware/client', './src/main' ] }, module: { loaders: [ { test: /\.jsx?$/, loaders: ['babel'], exclude: /node_modules/, query: { plugins: ['react-transform'], extra: { 'react-transform': [ { transform: 'react-transform-hmr', imports: ['react'], locals: ['module'] }, { transform: 'react-transform-catch-errors', imports: ['react', 'redbox-react'] } ] } } } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin() ] };
|
Babel
Babel 在上個月也從 5 升級到 6 了,但現在還有很多問題:
- 眾多功能拆成 Plugin 後,由於執行順序的問題,可能互相衝突,例如 Decorator 怪怪的
import *
的行為和過去不同,這有可能是上一點的問題
- 最重要的是,React Transform 尚未支援
如果你現在 Babel 5 用得好好的,那就別折騰了。
後記
你可以在 tommy351/redux-example 看到完整的 Universal Redux example。
前端的變化這麼快實在太可怕了,明年說不定又有新玩意了,我還是回去寫後端壓壓驚吧。
題圖是本季動畫「緋弾のアリアAA」,我原本以為這是和本傳差不多的動畫,沒想到一看就讓我震驚了,花了兩天把原作漫畫讀完,在某些方面來說,這的確和本傳差不多:
- 主角「間宮明里」,和金次一樣有吸引後宮的體質,只是明里專門吸引サイコレズ,這世界就沒半個正常人吧。
- 明里在平常就是個低等廢物,但是危急的時候總是能用祖傳炫泡絕技打爆敵人來收後宮。
- 白雪(本傳)和志乃(AA)都非常喜歡主角,也都對亞莉亞抱有敵意www
目前動畫除了有些設定變更和大☆崩★壞的第五話以外,日常場景都挺不錯的,看來動畫工房的百合日常比較穩定。