系統目錄及源碼由此進入css
1.1 技術選型
1.2 總體設計
1.3 構建開發html
2.1 react
2.2 redux, react-router
2.3 server-render前端
對於我的的博客系統而言,服務器計算能力每每不是須要考慮,而其中的 I/O 操做是比較複雜的,同理對前端的交互要求也是較高的,因此此次主要仍是圍繞 Node系
,React系
框架進行開發。對於 2016 年後的互聯網產品, React
搭建的 SPA ( single page ) 以及 React-Native
搭建的相似 hybrid 的原生應用,這樣的開發模式,會引導從此 3 年產品的開發。node
圖描繪的很簡單,簡單來講前端採用 React + React-router + Redux 以 flux 模式進行開發,後端採用了兩個 Node Server 提供 React 服務器端渲染和數據提供,這裏能夠先看爲一個服務器,爲啥要採用兩個,後面會提到。若是你有開發先後端分離項目的經驗,你能夠將提供 React 服務器端渲染這一塊與前端合併,咱們能夠在上面書寫 xtpl , ejs , jade 這些模板,開發起來更爲流暢。react
因爲個人服務器是在華北地區,加上 React 這一系列的庫又都是很是大的,即便我將 js , css 文件都打包成一個文件,訪問起資源加載也是特別慢,因此 CDN 也是必不可少的。webpack
對工程完成大體設計後,咱們須要利用前端工具作一些利於開發的構建,能夠大體分爲如下 3 個任務。git
method1: 本地服務器的熱加載
github
開發靜態組件階段爲了提高開發效率,減小各位小白對 F5 的使用量,咱們通常使用 webpack-dev-server
作本地服務,與通常的 webpack 配置文件不一樣之處也就在於入口點 entry 多加了點東西。web
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 端打包常常出現的問題。
先來看看整個工程目錄是怎樣的。
build: 上述中各構建文件
server: 用於服務器端渲染的文件
views: 前端組件及redux狀態管理文件系統
對於 react 語法這裏就不提了,重點提下文件的組織方式,以便咱們開發和平常維護。若只針對於工做量不大 react ,目錄很簡單,只需區分展現級組件
( compnent ) 和 複合型組件
( containers )。
--- views/
--- components/ 各展現級組件
--- containers/各複合型組件
具體呈現形式可在 github 上了解
剛開始寫 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,用戶訪問只需加載一次數據,以後的邏輯都由瀏覽器來完成了~因此對交互性來講也是很大的提高。
爲何須要去實現 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 的打包時,會將數據庫的相關信息打包到一塊兒,放在網絡上是不安全的,因此須要建立另外一個服務器專門用提供持久化數據服務。
這篇博文沒有對技術作太多深刻的探究,但提供了一個比較清晰地開發思路,剛從事開發工做或者仍是小白的你可能在獨立開發一個工程時會存在不少疑問,從構建到設計,從性能到安全都是須要考慮的。
今天微信小程序也正式上線了,趕忙去嚐嚐鮮~