IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    從 Redux 1 升級到 3

    Tommy Chen发表于 2022-03-26 09:52:57
    love 0

    好久不見,在 資服創新競賽 結束後,我耍廢了好一段時間,更準確來說,從校內專題比賽結束後就開始耍廢了 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 的過程了。

    @connect(state => ({  // props}), {  // actions})

    另一點則是 Provider class,原本的寫法實在不怎麼優雅,this.props.children 傳入的是一個函數,而現在可以直接傳入 React element。

    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-router 和 redux-simple-router 最大的不同就在於:前者把完整的 Router 狀態都存到了 Redux store,而後者只存了路徑,在 Dehydration/Rehydration 時,根本沒辦法處理 redux-router 的資料,只能忽略。
    • redux-router 的觸發時機早於 Rehydration,因此有些資料尚未初始化而發生錯誤。
    • redux-router 的 getRoutes 參數半壞,你如果想把 Redux store 傳進 routes 裡的話,自己實作比較保險。
    • redux-router 的原始碼不知道在複雜幾點的,而 redux-simple-router 的原始碼不到 100 行,雖然功能比較少,但是好用多了。

    接著比較實際的程式碼吧:

    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'));
    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 元件。由於後者非常簡單,也減少了錯誤發生的機率。

    redux-devtools

    這東西真的很炫,它可以在頁面中顯示側欄來顯示 action 的動態,還可以復原某些 action 對 store 的變更,使用起來非常簡單,讓我愛不釋手。

    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 Transform

    原本我使用的是 React Hot Loader,這種方法其實用起來也沒什麼大問題,只是得另外開個 webpack-dev-server。不過原作者決定廢棄 React Hot Loader,又另外開了個 React Transform,所以我也只好轉移到 React Transform 了。相較上面幾個大改變來說,這只是編譯過程的改變而已,只需要改 Webpack 和 Babel 的配置即可。

    首先把 webpack-dev-server 相關的東西都移除掉,安裝:

    npm install babel-plugin-react-transform --save-devnpm install react-transform-catch-errors --save-devnpm install react-transform-hmr --save-devnpm install redbox-react --save-devnpm install webpack-dev-middleware --save-devnpm install webpack-hot-middleware --save-dev

    以下程式碼使用的是 Express,如果你用的是 Koa 的話,請把 webpack-dev-middleware 改成 koa-webpack-dev-middleware,webpack-hot-middleware 改成 koa-webpack-hot-middleware,這兩個 middleware 的使用方式會有些差異,不過大致上是差不多的。

    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));
    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

    目前動畫除了有些設定變更和大☆崩★壞的第五話以外,日常場景都挺不錯的,看來動畫工房的百合日常比較穩定。



沪ICP备19023445号-2号
友情链接