這篇文章來說解來配置server side render
,咱們先從最簡單的方法開始,用cli的方式模擬實現SSR。css
demo在這裏html
主要內容:node
添加webpack的server render配置react
使用CLI的方式測試SSR輸出webpack
以前我是考慮在node端直接require
源碼,例如:git
//hook require require("babel-register")({ babelrc: "false", presets: ['react'], plugins: [ "transform-decorators-legacy", "transform-es2015-modules-commonjs" ] }); //直接引入源碼 const IndexBundle = require("./src/index/Index.jsx"); //do server side render...
這樣少編譯一套代碼,以爲這樣維護起來更方便,可是後來實踐發現有幾個問題:github
import "xxx.styl"
,引入樣式文件會報錯。web
這種模式下須要使用babel-register,babel
編譯速度較慢,開發模式下每次修改文件再重啓服務器耗時太長。ajax
影響生產環境下執行效率。npm
最後權衡下,仍是決定使用如今多一套ssr編譯配置的方案。
在webpack.config.js添加如下代碼
let serverConfig = {}; Object.assign(serverConfig, browserConfig, { output: { path: path.join(__dirname, 'build_server'), filename: "[name].bundle.js", libraryTarget: 'commonjs2' //設置導出類型,web端默認是var,node須要module.exports = xxx的形式 }, module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader", query: { //node端的babel編譯配置能夠簡化不少 babelrc: "false", presets: ['react'], plugins: [ "transform-decorators-legacy", "transform-es2015-modules-commonjs" //若是不轉換成require,import 'xxx.styl'會報錯 ] } }, { test: /\.(styl|css)$/, //node端不能 require('xx.css'),會報錯 loader: 'null' }, ] }, plugins: [ new webpack.ProvidePlugin({ React: 'react', ReactDOM: 'react-dom', fetch: 'isomorphic-fetch', promise: 'promise' }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) || JSON.stringify('development') }), ], target: 'node', externals: [nodeExternals()], //不把node_modules中的文件打包 });
由於serverConfig
的配置和browserConfig
類似,我就使用Object.assign
來複制一份,同時作下修改。
nodejs
啓用 --harmony
參數就能夠支持絕大部分的ES6,ES7語法,如async
等,所以只須要編譯JSX
語法和import
語法。babel的編譯速度也所以能夠提升不少。babelrc: "false"
是爲了屏蔽項目目錄下的babel.rc
文件,那是給瀏覽器端編譯使用的。
同時,在node環境不支持直接引入CSS文件的,如require('xx.css')
,所以在打包的時候要忽略樣式文件和資源文件,不然會報錯。
這裏我使用了webpack-node-externals插件,這個插件的原理是利用了webapck中的externals配置項,來剔除node_modules
文件的,由於默認webapck會把全部用到的js文件通通打包,而咱們因爲是在node端,所以不須要把用到的庫也打包了。
執行試試
npm run watch
若是不用webpack-node-externals,打包出的文件體積會大不少
其實使用React的ssr很簡單,熟悉下面兩個API便可:
這裏簡單解釋下,React.createElement
把React類
進行實例化,實例化後的組件就能夠進行mount操做了,在瀏覽器環境咱們是使用ReactDOM.render()
來進行掛載操做的。
而ReactDOMServer.renderToString
則是把React實例渲染成HTML標籤。
這裏咱們先不搭建HTTP server,暫時用cli的方式模擬一下,方便你們理解。
新建cli.js,寫入如下內容(以Index.jsx
爲例),注意:.defalut
不能少。
/** * Created by chenchen on 2017/2/4. * * React server render 命令行測試 */ //以Index.jsx爲例 const IndexBundle = require("../build_server/index.bundle.js"); const React = require("react"); const ReactDOMServer = require("react-dom/server"); let {renderToString} = ReactDOMServer; let initialData = {todoList: ['11', '22', '33']}; let instance = React.createElement(IndexBundle.default, initialData); //.defalut不能少 let str = renderToString(instance); console.log(str);
咱們添加一條npm script
"test-ssr": "node --harmony test/cli.js"
執行後效果如圖
能夠看到咱們已經成功輸出了組件渲染後的HTML文本了。
下一篇文章我將講解如何搭建一個簡單的Koa server
,並結合這邊文章內容,實現真正意義上的server side render
^_^。
React組件的聲明週期只會到componentWillMount
,所以你不能在componentWillMount
及其以前的生命週期鉤子中寫瀏覽器環境下的代碼,如$.ajax(...)
,會報錯。
要注意瀏覽器端和服務器端的數據要一致,不然會出現HTML重用失敗的錯誤:
可能有人會疑惑,在瀏覽器編譯的代碼是:
//初始數據,用於和server render數據同步 let initialData = window._SERVER_DATA || {}; let store = createStore(reducers, initialData, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); let App = connect(_ => _)(Layout);//用connect包裝一下,這裏只用到mapStateToProps,並且不對state加以過濾 ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('wrap'));
而server端的編譯沒有和Redux沾邊,由於Provider
和connect(...)(Layout)
是functional component
,自己不會多渲染出來HTML,所以能夠不用Redux參與渲染。