標籤:creat 更改 中介軟體 變化 工作 需要 loader settings length
系統目錄及源碼由此進入
目錄1. 開發前準備
1.1 技術選型
1.2 整體設計
1.3 構建開發
2. 技術點
2.1 react
2.2 redux, react-router
2.3 server-render
3. 總結本文1. 開發前準備1. 1 技術選型
對於個人的部落格系統而言,伺服器計算能力往往不是需要考慮,而其中的 I/O 操作是比較複雜的,同理對前端的互動要求也是較高的,所以這次主要還是圍繞 Node系
,React系
架構進行開發。對於 2016 年後的互連網產品, React
搭建的 SPA ( single page ) 以及 React-Native
搭建的類似 hybrid 的原生應用,這樣的開發模式,會引導今後 3 年產品的開發。
1.2 整體設計
圖描繪的很簡單,簡單來說前端採用 React + React-router + Redux 以 flux 模式進行開發,後端採用了兩個 Node Server 提供 React 伺服器端渲染和資料提供,這裡可以先看為一個伺服器,為啥要採用兩個,後面會提到。如果你有開發前後端分離項目的經驗,你可以將提供 React 伺服器端渲染這一塊與前端合并,我們可以在上面書寫 xtpl , ejs , jade 這些模板,開發起來更為流暢。
由於我的伺服器是在華北地區,加上 React 這一系列的庫又都是非常大的,即使我將 js , css 檔案都打包成一個檔案,訪問起資源載入也是特別慢,所以 CDN 也是必不可少的。
1.3 構建開發
對工程完成大致設計後,我們需要利用前端工具做一些利於開發的構建,可以大致分為以下 3 個任務。
method1: 本機伺服器的熱載入
開發靜態組件階段為了提升開發效率,減少各位小白對 F5 的使用量,我們一般使用 webpack-dev-server
做本地服務,與一般的 webpack 設定檔不同之處也就在於進入點 entry 多加了點東西。
entry: { app: [ ‘webpack-dev-server/client?http://localhost:3000‘, ‘webpack/hot/only-dev-server‘, path.resolve(__dirname, ‘../views/index.js‘) ]}
將 webpack 中的選項傳入到 webpack-dev-server (一個 express 伺服器) 對象
new WebpackDevServer(webpack(webpackConfig), { publicPath: ‘/‘, hot: true, // 熱替換開啟 historyApiFallback: true, // 無刷更改url,配合react-router使用 inline: true, // 熱替換開啟 stats: { colors: true }}).listen(PORT, FUNC);
接著啟動你的伺服器即可完成構建。
method2: 靜態資源分離
通常在開發階段會有 import ./xxx.less
或 require(‘xxx.png‘)
ES5 / ES6 文法來引入靜態資源, 在打包時我們需要把所有靜態檔案和我們的組件解耦在合并成一個檔案,這同樣需要 webpack 來完成,同時要引入名為 extract-text-webpack-plugin 的外掛程式,具體使用如下
var ExtractTextPlugin = require(‘extract-text-webpack-plugin‘);module.export = { module: { loaders: [ ... // other settings { test: /\.css/, loader: ExtractTextPlugin.extract(‘style‘, [‘css‘]) }, { test: /\.less/, loader: ExtractTextPlugin.extract(‘style‘, [‘css‘, ‘less‘]) }, ] }, plugins: [ new ExtractTextPlugin(‘[name].css‘), ... // other settings ]}
method3: 伺服器端渲染
首先,明確一點的是 node 穩定的 4.x 版本並不能支援所有 ES6 的文法,因此採用 ES6 的文法寫 node 伺服器端,需要先轉到 ES5 再執行,一般來說有兩種方法。一是下載 babel-node
到作業系統直接執行,二是利用 webpack 引入 babel-loader
,babel-core
打包(其實兩種方法是一種思想)。所以按照正常 ES6 -> ES5 的構建流程打包即可。
module.export = { node: { fs: ‘empty‘, net: ‘empty‘, // 防止打包錯亂 __filename: true, __dirname: true }}
如果打包過程中出現了大量有關 fs , net 模組的報錯或者打包後出現了檔案引入的錯誤,添加上述 webpack 選項即可解決問題,這兩種都是在 node 端打包經常出現的問題。
2. 開發
先來看看整個工程目錄是怎樣的。
build: 上述中各構建檔案
server: 用於伺服器端渲染的檔案
views: 前端組件及redux狀態管理檔案系統
2.1 react
對於 react 文法這裡就不提了,重點提下檔案的組織方式,以便我們開發和日常維護。若只針對於工作量不大 react ,目錄很簡單,只需區分展示級組件
( compnent ) 和 複合型組件
( containers )。
--- views/
? --- components/ 各展示級組件
? --- containers/各複合型組件
具體呈現形式可在 github 上瞭解
2.2 redux, react-router
剛開始寫 react 的時候總是覺得 redux 和 react-router 是一個嘩眾取寵的存在,直到資料量和邏輯變複雜,後台邏輯愈加複雜,而前端的 react 組件還是如同開始的上手練習一樣,一直做著自頂向下的資料傳遞和渲染,每次更新資料我們都會請求伺服器,這無疑增加了伺服器的壓力(對較大型應用),同時沒有發揮react的長處,所謂的單頁應用更是紙上談兵。
那將三者結合會是怎樣的開發體驗和使用者體驗呢?
redux
是 facebook 提出 flux
架構思想,是專門用於軟體的結構問題的,對於 react 開發它提供一種非常簡單清晰的思想。
View
: 視圖層 ( react 組件)
Action (動作)
: 視圖層發出的訊息(比如 mouseClick )
Dispatcher (派發器)
: 用來接收 Actions 、執行回呼函數
Store (資料層)
: 用來存放應用的狀態,一旦發生變動,就提醒 View 要更新頁面
而 redux 提供了 action , dispatcher( redux 中是 reducer ), store 的實現 ,如何?的呢?就拿個載入某一頁資料做個例子。
action.js : loadThisPage 是一個action creater,每次調用就能建立一個帶有 LOAD_THIS_PAGE 指令的對象給 reducer。
const LOAD_THIS_PAGE = ‘LOAD_THIS_PAGE‘; const loadThisPage = (pN) => { return { type: LOAD_THIS_PAGE, pN } }
reducer.js : 在 reducer 中,通過判斷指令來調用 store 中的資料改變狀態
const LOAD_THIS_PAGE = ‘LOAD_THIS_PAGE‘; const articleReducer = (state = {}, action) => { switch(action.type) { case LOAD_THIS_PAGE: return { allArticles: state.allArticles, articles: state.allArticles.slice(0, action.pN * 5) }; default: return state; } };
store.js : 建立一個 store 對象,可以通過調用這個對象的 dispatch
方法傳入 action。
import { createStore, applyMiddleware} from ‘redux‘; const store = createStore( reducers, applyMiddleware(thunk) // 若需要redux中介軟體才添加 );
這些工作做完後,還需要將 store 和組件進行一個關聯,也就是說組件要能獲得 redux store。
import { Provider, connect } from ‘react-redux‘; // 通過connect串連組件和store const HomeApp = connect( (state) => { return { articles: state.articleReducer.articles, articleLen: state.articleReducer.allArticles.length }; } )(Home); const routes = ( <Route path="/" component={ LeftArea }> <IndexRoute component={ HomeApp } /> </Route> ); // store通過Provider傳入到react組件 render( <Provider store={store} key="index"> <Router history={browserHistory} routes={routes}> </Router> </Provider>, document.getElementById(‘app‘) );
react-router
相比 redux , react-router 理解起來就好多了,每當 url 發生變化,改變的只是組件,而不會重新重新整理頁面。
對於這樣一個SPA,使用者訪問只需載入一次資料,之後的邏輯都由瀏覽器來完成了~所以對互動性來說也是很大的提升。
2.3 server render
為什麼需要去實現 server render ? 也就是說伺服器端渲染比用戶端渲染優在哪裡?
server render vs client render
伺服器端渲染更利於 SEO ,更容易被爬蟲識別
伺服器端渲染可以增加訪問速度,使用者訪問時直接推送已經渲染好的 react 頁面,不同於用戶端渲染的將 react 放入瀏覽器中渲染。同時減少使用者CPU開銷,獲得更好互動性,解決了首屏載入慢的問題。
分離前後端邏輯,利於開發
如何做到 react 伺服器端渲染,其實 facebook 很早就給出瞭解決方案,並推出了 react-dom/server 庫。
import { renderToString } from ‘react-dom/server‘; const html = renderToString( <Provider store={store}> <RoutingContext {...renderProps} /> </Provider> );
最初的一個問題
為什麼會採用兩個node伺服器呢?因為在進行 webpack 對 Node 的打包時,會將資料庫的相關資訊打包到一起,放在網路上是不安全的,所以需要建立另一個伺服器專門用提供持久化資料服務。
3. 總結
這篇博文沒有對技術做太多深入的探究,但提供了一個比較清晰地開發思路,剛從事開發工作或者還是小白的你可能在獨立開發一個工程時會存在很多疑問,從構建到設計,從效能到安全都是需要考慮的。
今天小程式也正式上線了,趕緊去嘗嘗鮮~
基於React伺服器端渲染的部落格系統