Skip to content

yuwanlin/react-webpack-SSR

Repository files navigation

1.publicPath

output: {
    filename: '[name]-[hash].js',
    path: path.join(__dirname, '../dist'),
    publicPath:'/public/'
},

client配置publicPath旨在添加为所有资源添加一个资源目录。 client使用html-webpack-plugin生成页面,页面自动引用webpack打包后生成的js文件,路径包括publib父级目录。

2.babel 使用babel处理文件。根目录添加.babelrc文件。babel-core包括核心api。babel-preset-env处理es6/7/8文件、babel-preset-react处理jsx。babel-loader整合任务。

3.server webpack.config.server.js需要就行额外配置。

target: 'node'
libraryTarget: 'commonjs2'

4.webpack生成的文件在dist目录。

5.通过在命令中添加cross-env NODE_ENV=development设置开发环境。

const serverEntry = require('../dist/server-entry').default;
app.use('/public', express.static(path.join(__dirname, '../dist')));

var template = fs.readFileSync(path.join(__dirname, '../dist/index.html'), 'utf8');

app.get('*', function(req, res) {
    const appString = reactSSR.renderToString(serverEntry)
    res.send(template.replace('<!-- app -->', appString));
})

由于webpack打包生成的文件都在dist目录下,html-webpack-plugin中的js文件会引用public目录。所以通过express对url中的public目录进行映射到dist目录。

服务端渲染需要先获取dist下的index.html文件,然后将其中的替换成appString. webpack.config.server.js打包后的产物。服务端渲染生成的html和页面中js操作后生成的相同。 这是对于生成了dist目录的情况。

6.HMR (npm run dev:client localhost:8888)

  • .babelrc文件中添加"plugins": ["react-hot-loader/babel"]
  • webpack.config.client.js的entry添加'react-hot-loader/patch',
  • webpack.config.client.js的plugins添加webpack.HotModuleReplacementPlugin()
  • devServer中的hot设置为true
  • 入口jsx组件添加处理。
if(module.hot) {
    module.hot.accept('./App.jsx', () => {
        const NextAPP = require('./App.jsx').default;
        render(NextAPP);
    })
}

7.如果用到了webpack-dev-server,那么生成的文件都在内存中这时候要做ssr较为麻烦。主要处理文件在util/dev-statis.js中。

const axios = require('axios');
const webpack = require('webpack');
const MemoryFs = require('memory-fs');
const reactSSR = require('react-dom/server');
const proxy = require('http-proxy-middleware');
const path = require('path');
const serverConfig = require('../../build/webpack.config.server');

const getTemplate = () => {
    return new Promise((resolve, reject) => {
        axios.get('http://localhost:8888/public/index.html') //获取生成的html
            .then(res => {
                resolve(res.data);
            })
            .catch(reject);
    })
}

const mfs = new MemoryFs(); //memory-fs和fs接口相同,只不过用于读取内存文件
const Module = module.constructor; //黑科技
let serverBundle;
const serverCompile = webpack(serverConfig); //服务端配置文件->生成处理后的文件
serverCompile.outputFileSystem = mfs;
serverCompile.watch({}, (err, stats) => { // 监听entry
    if(err) throw err;
    stats = stats.toJson();
    stats.errors.forEach(err => console.error(err));
    stats.warnings.forEach(warn => console.warn(warn));

    const bundlePath = path.join( // 打包后生成的js路径
        serverConfig.output.path,
        serverConfig.output.filename
    );

    const bundle = mfs.readFileSync(bundlePath, 'utf8'); // bundle是string
    const m = new Module();
    m._compile(bundle, 'server-entry.js'); // compile the string生成占用内存server-entry.js的文件
    serverBundle = m.exports.default; // 导出的就是生成的js的bundle
})

module.exports = function(app) {

    app.use('/public', proxy({
        target: 'http://localhost:8888' //启动服务器后,访问的文件时3333端口的,代理到8888端口,因为文件时通过webpack生成的,webpack服务器端口是8888
    }))

    app.get('*', function(req, res) {
        getTemplate().then(template => {
            const appString = reactSSR.renderToString(serverBundle);
            res.send(template.replace('<!-- app -->', appString));
        })
    })
}

这时候仍可以使用hmr(npm run dev:client npm run dev:server localhost:3333)。

8.husky在scripts中添加了'precommit'命令。在commit时会执行这个命令。 9.serve-favicon用来配置页面icon。

10.使用mobx和mobx-react,需要用到装饰器。安装babel-preset-stage-1,babel-plugin-trnasform-decorators-legacy.

11.body-parser用于解析请求的参数。网络请求使用网络库axios。

12.路由使用react-router-dom。

13.webpack.config.client.js中devServer.proxy设置'/api'的请求代理到服务器'http:localhost:3333'

14。由于前端使用了router和store,所以server-side-render需要作出改变。需要用到StaticRouter.

15.react-async-bootstrapper这个库可以在服务端渲染组件之前通过执行bootstrap方法修改store。会导致server端store数据和client端不一样,所以需要server提供store数据给client。这里使用了server.template.ejs模板。

new HTMLPlugin({
  template: '!!ejs-compiled-loader!' + path.join(__dirname, '../client/server.template.ejs'),
  filename: 'server.ejs'
})

由于htmlWebpackPlugin会解析ejs,所以模板文件中防止ejs被解析。随后使用ejs-compiled-loader来编译,得到的template使用ejs.render来编译。

const html = ejs.render(template, {
  appString: content,
  initialState: serialize(state)
})

因为state是一个对象,传给前端会变成[object Object]。所以需要转换为字符串,这里使用的是serialize-javascript库。

16.webpack.config.server.js添加externals后,dev-static中m._compile(bundle, 'server-entry.js'); 编译得到的文件中无法require模块

const getModuleFromString = (bundle, filename) => {
  const m = { exports: {} }
  const wrapper = NativeModule.wrap(bundle) // `(function(exports, require, module, __filename, __dirname){ ... bundle })`
  const script = new vm.Script(wrapper, {
    filename: filename,
    displayErrors: true
  })

  const result = script.runInThisContext();
  result.call(m.exports, m.exports, require, m); // 提供require

  return m;
}

通过helmet解决seo。

<div>
        <Helmet>
          <title>This is topic list</title>
          <meta name="discription" content="this is description" />
        </Helmet>
        <input type="text" onChange={this.changeName} />
        {this.props.appState.msg}
      </div>
Helmet = require('react-helmet').default;
const helmet = Helmet.rewind(); // HelmetExport.renderStatic = HelmetExport.rewind;
const content = reactDomServer.renderToString(app);
// res.send(template.replace('<!-- app -->', content));
// content是内容,template是模板文件
const html = ejs.render(template, {
  appString: content,
  initialState: serialize(state),
  meta: helmet.meta.toString(),
  title: helmet.title.toString(),
  style: helmet.style.toString(),
  link: helmet.link.toString(),
})

17.chapter 4-1 react-fiber react 16

  1. chapter 4-2
  • @material-ui/core
    • @material-ui/core/styles
    • @material-ui/core/colors
  • @meterial-ui/icons

react-jss(css in js with react)

18.chapter 4.234

  • material-ui ssr

  • withStyles

import { withStyles } from '@material-ui/core/styles';
export default withStyles(styles)(MainAppBar);

About

the fullest reacat-webpack-ssr

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published