## 1. 項目初始化javascript
npm install easywebpack-cli -g
命令行,而後就可使用 easywebpack
或 easy
命令easywebpack init
npm install
git clone https://github.com/hubcarl/egg-react-webpack-boilerplate.git npm install
初始化的項目提供多頁面和SPA(react-router/react-redux)服務端渲染實例,能夠直接運行。css
npm start
npm start 作了以下三件事情html
npm run build 或 easywebpack build prod
app/view
目錄public
目錄buildConfig.json
和 manifest.json
放到 config
目錄gitignore
的,部署時請注意把這些文件打包進去啓動應用前, 請設置 EGG_SERVER_ENV
環境變量,測試環境設置 test
, 正式環境設置 prod
前端
npm start
easywebpack-cli
統一構建,支持 dev,test,prod 模式構建easywebpack-cli
經過項目根目錄下的 webpack.config.js
配置文件構造出 Webpack 實際的配置文件,配置項請見 webpack.config.js webpackConfigList.length
啓動對應個數的 Webpack 編譯實例,這裏會同時啓動兩個 Webpack 構建服務, 客戶端jsbundle構建,端口9000, 服務端jsbundle構建端口9001。默認端口爲9000, 端口依次遞增。// config/config.local.js 本地 npm start 使用 const EasyWebpack = require('easywebpack-react'); exports.webpack = { webpackConfigList:EasyWebpack.getWebpackConfig() };
app/web/page
目錄中全部 .jsx 文件看成 Webpack 構建入口是採用 app/web/framework/entry/loader.js 模板實現的,這個須要結合 webpack.config.js
下的 entry.loader 使用。entry: { include: ['app/web/page', { layout: 'app/web/view/layout.jsx?loader=false' }, { 'spa/redux': 'app/web/page/spa/redux.jsx?loader=false' }, { 'spa/client': 'app/web/page/spa/client.jsx?loader=false' }, { 'spa/ssr': 'app/web/page/spa/ssr.jsx?loader=false' } ], exclude: ['app/web/page/test'], loader: { client: 'app/web/framework/entry/loader.js' } }
上面 { 'app/app': 'app/web/page/app/app.js?loader=false' }
這個 loader=false
的含義表示 app/web/page
目錄下的 app/app.js
不使用 entry.loader 模板。由於這個app/app.js是一個SPA服務端渲染Example,實現邏輯與其餘普通的頁面不同,不能用 entry.loader 模板, 這個功能在自定義entry文件構建規範時使用。vue
webpack.config.js
entry.exclude 排除 .jsx 文件。
支持多頁面/單頁面服務端渲染, 前端渲染, 靜態頁面三種方式.java
在app/web/page 目錄下面建立home目錄, home.jsx 文件, Webpack自動根據.jsx 文件建立entry入口, 具體實現請見webpack.config.jsreact
import React, { Component } from 'react'; import Header from 'component/layout/standard/header/header.jsx'; import List from 'component/home/list.jsx'; import './home.css'; export default class Home extends Component { componentDidMount() { console.log('----componentDidMount-----'); } render() { return <div> <Header></Header> <div className="main"> <div className="page-container page-component"> <List list={this.props.list}></List> </div> </div> </div>; }
egg-view-react-ssr
插件 render
方法實現exports.index = function* (ctx) { yield ctx.render('home/home.js', Model.getPage(1, 10)); };
app.get('/home', app.controller.home.home.index);
egg-view-react-ssr
插件 renderClient
方法實現exports.client = function* (ctx) { yield ctx.renderClient('home/home.js', Model.getPage(1, 10)); };
app.get('/client', app.controller.home.home.client);
webpack.config.js
配置和 app/web/page/html
代碼實現egg-static
靜態文件訪問HTML文件在app/web/page 目錄下面建立app目錄, spa/ssr.jsx 文件.webpack
import React, { Component } from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import {match, RouterContext} from 'react-router' import { BrowserRouter, StaticRouter } from 'react-router-dom'; import { matchRoutes, renderRoutes } from 'react-router-config' import Header from 'component/layout/standard/header/header'; import SSR from 'component/spa/ssr/ssr'; import { create } from 'component/spa/ssr/store'; import routes from 'component/spa/ssr/routes' import './spa.css'; if (typeof window === 'object') { // 前端渲染構建 const store = create(window.__INITIAL_STATE__); const url = store.getState().url; ReactDOM.render( <div> <Header></Header> <Provider store={ store }> <BrowserRouter> <SSR url={ url }/> </BrowserRouter> </Provider> </div>, document.getElementById('app') ); } else { // 服務端渲染構建和render入口, 這裏 export 函數,服務端會負責處理 module.exports = (context, options) => { const url = context.state.url; const branch = matchRoutes(routes, url); // 獲取組件數據 const promises = branch.map(({route}) => { const fetch = route.component.fetch; return fetch instanceof Function ? fetch() : Promise.resolve(null) }); return Promise.all(promises).then(data => { // 初始化store數據 const initState = context.state; data.forEach(item => { Object.assign(initState, item); }); context.state = Object.assign({}, context.state, initState); const store = create(initState); return () =>( <div> <Header></Header> <Provider store={store}> <StaticRouter location={url} context={{}}> <SSR url={url}/> </StaticRouter> </Provider> </div> ) }); }; }
exports.ssr = function* (ctx) { yield ctx.render('spa/ssr.js', { url: this.url }); };
app.get('/spa/ssr', app.controller.spa.ssr);
spa 單頁面實現複雜,不能使用 entry.loader, 因此須要在 webpack.config.js
配置git
{ entry: { include: ['app/web/page', { layout: 'app/web/view/layout.jsx?loader=false' }, { 'spa/redux': 'app/web/page/spa/redux.jsx?loader=false' }, { 'spa/client': 'app/web/page/spa/client.jsx?loader=false' }, { 'spa/ssr': 'app/web/page/spa/ssr.jsx?loader=false' } ], exclude: ['app/web/page/test'], loader: { client: 'app/web/framework/entry/loader.js' } }, }
詳細代碼請參考骨架項目實現github
EGG_SERVER_ENV=prod
環境變量, 更多請見運行環境 app/view
目錄, public
目錄以及 buildConfig.json
和 manifest.json
等文件, 都是 gitignore
的,部署時請注意把這些文件打包進去。