我想说的是“照旧”。因为在浏览器端运行的还是spa。入门级的具体见github,至于如何配置得赏心悦目,用起来得心应手,根据项目要求各显神通吧。
服务端打包
和客户端的异同:
同:
需要bable兼容不同版本的js语法
webpack v4+/babel v7+ ... 真香
... 留白
异:
入口文件不一样,出口文件不一样
这里既可以把整个服务端入口app.js作为打包入口,也可以把react路由的起点文件作为打包入口,配置输出为umd模块,再由app.js去require。以后者为例(好处在于升级改造项目时尽可能地降低对原系统的影响,排查问题也方便,断点调试什么的也方便):
// webpack.server.js const webpackConfig = { entry: { server: './src/server/index.js' }, output: { path: path.resolve(__dirname, 'build'), filename: '[name].js', libraryTarget: 'umd' } } // app.js const reactKoaRouter = require('./build/server').default app.use(reactKoaRouter.routes())
css、image资源正常来说服务端无需处理,如何绕开
偷懒,还没开始研究,占个坑
require的是node自带的模块时避免被webpack打包
const serverConfig = { ... target: 'node' }
require第三方模块时如何避免被打包
const serverConfig = { ... externals: [ require('webpack-node-externals')() ]
生产环境代码无需做混淆压缩
... 留白
服务端直出时资源的搜集
服务端输出html时,需要定义好css资源、js资源,让客户端接管后下载使用。如果没啥追求,可以直接把客户端的输出文件全加上去,暴力稳妥,简单方便。但是上面提到的@loadable/component库,实现了路由组件懒加载/code split功能后,也提供了全套服务,配套套装的webpack工具,ssr工具,帮助我们做搜集资源的工作。
// webpack.base.js const webpackConfig = { plugins: [ ..., new LoadablePlugin() ] } // render.js import { ChunkExtractor } from '@loadable/server' const App = () => { return ( <Provider store={store}> <StaticRouter location={ctx.url} context={context}> <Layout /> </StaticRouter> </Provider> ) } const webStats = path.resolve( __dirname, '../public/loadable-stats.json', // 该文件由webpack插件自动生成 ) const webExtractor = new ChunkExtractor({ entrypoints: ['client'], // 为入口文件名 statsFile: webStats }) const jsx = webExtractor.collectChunks(<App />) const html = renderToString(jsx) const scriptTags = webExtractor.getScriptTags() const linkTags = webExtractor.getLinkTags() const styleTags = webExtractor.getStyleTags() const preloadedState = store.getState() const helmet = Helmet.renderStatic() return ` <html> <head> ${helmet.title.toString()} ${helmet.meta.toString()} ${linkTags} ${styleTags} </head> <body> <div>${html}</div> <script> window.STORE = 'love'; window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)}; </script> ${scriptTags} </body> </html> `
SEO信息
上面已经透露了。使用了一个react-helmet库。具体用法可查看官方仓库,信息可直接写在组件上,最后根据优先级提升到head头部。