React 同构 - 同步学习
一、技术选型
A、在create-react-app中使用use
B、配置两套webpack.config文件监听 –watch
该方法不支持而热更 文件更新后会重新打包 ,打包后手动刷新页面
本地开发时 nodemon启动 后台服务 并且监听两端的代码
线上运行时 webpack打包两端代码 nodemon启动 后台服务
C、起两个服务node server监听 web端口
该方法解决热更 web与server 各自起服务 server做两部处理
1、本地开发时监听端口数据进行缓存更新
2、正式环境的时候进行静态文件处理即可
二、同构框架
1、helmet header的问题;
a) 使用react-helmet-async
b) 前后端最外层添加 HelmetProvider 层级一样都在routes外层即可
c) 当异步加载完成后可以拿到 context = {helmetContext}
d) helmetContext.helmet.tltle(link).toString()
2、路由懒加载;
地址loadable-components.com/docs/api-lo…
a) 客服端
import loadable from‘@loadable-component’
const Home = loadable(() => import(/* Home */‘./Home’)})
b) 服务端
import { ChunkExtractor } from '@loadable/server'
const statsFile = path.resolve(__dirname, 'loadable-stats.json文件路径')
const extractor = new ChunkExtractor({ statsFile })
const jsx = extractor.collectChunks()
const content = renderToString(jsx);
const scriptTags = extractor.getScriptTags()
const linkTags = extractor.getLinkTags()
const styleTags = extractor.getStyleTags()
c) Webpack
const LoadablePlugin = require('@loadable/webpack-plugin')
plugins:[new LoadablePlugin()], // 对应生成loadable-stats.json文件
d) Babel
在.babelrc文件中添加"plugins": ["@loadable/babel-plugin"]
3、服务端路由用StaticRouter、客户端路由用BrowserRouter
e) 客服端与服务端使用同一套路由
f) 客服端使用BrowserRouter
g) 服务端使用StaticRouter 并且外层添加renderToString
import {matchRoutes, renderRoutes} from "react-router-config";
matchRoutes 可以获取到组件的一下信息
h) 服务端使用react-router-config promises= matchRoutes所有routes ;Promise.all(promises)执行结果中使用renderRoutes
4、数据处理 与3联合使用
Server端
a) Store
import {matchRoutes, renderRoutes} from "react-router-config";
const promises = matchRouters(Router, req.path).map(({route}) => { /* 处理store */ retuen router })
Promise.all(promises).then(route)
b) Hook
import { createServerContext } from "use-sse";
const { ServerDataContext, resolveData}=createServerContext();
在renderToString后调用
const data = await resolveData();
在次调用renderToString
调用后的字符串中自动加入数据
Web端
a)Store
renderRoutes(Routes)
在路由组件中 返回需要初始化的store 的函数 该函数接受store 调用action
b)Hook
路由中
import { createBroswerContext } from "use-sse";
const BroswerDataContext = createBroswerContext();
BroswerDataContext 包裹路由
组件中
import { useSSE } from "use-sse";
const [data, error] = useSSE(() => {
return axios.get("").then((res) => res.data)
}, []);
5、数据脱水与注水
通过 INITIAL_STATE 全局变量把后台请求到的数据存起来。客户端创建 store 时,当做初始化的 state 使用即可:
Server端
export const getClientStore = ()=>{
const defaultState = window.INITIAL_STATE;
return createStore(reducer,defaultState,applyMiddleware(thunk));
}
Web端
window.INITIAL_STATE = ${JSON.stringify(store.getState())}
7、CSS
两端解析css使用相同的lader配置打包后两端可共用
客服端
1、外部链接link的形式使用mini-css-extract-plugin(webpack4.0以后) 或者 extract-text-webpack-plugin(webpack4.0以前)
2、style的形式 普通style-loader 的形式向后端参数注入代码的形式
style-loader
this.props.staticContext.css.push(styles._getCss());
服务端
this.props.staticContext.css.push(styles._getCss());
1、按需加载的时候引入链接的形式
const styleTags = extractor.getStyleTags()
2、客服端注入的形式
与客服端配合使用 isomorphic-style-loader
三、一些常见的报错
使用async awsit时的报错
安装babel-polyfill依赖并在入口文件引入
import "babel-polyfill"
node 子进程可以打开一个地址
const cp = require('child_process'); // 可自动打开浏览器模块
cp.exec('open http://127.0.0.1:3000/'); // 自动打开默认浏览器
四、总结