React 同构 - 同步学习

531 阅读3分钟

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的问题;

地址www.npmjs.com/package/rea…

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

地址www.npmjs.com/package/rea…

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

地址 www.npmjs.com/package/rea…

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

地址www.npmjs.com/package/use…

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/'); // 自动打开默认浏览器

四、总结