外卖,根据 React.js 和 Node.js 的SSR完成计划,diy

根底概念

  1. SSR:即服务端烘托(Server Side Render)
  2. 传统的服务端烘托能够运用Java,php 等开发言语来完结,跟着 Node.js 和相关前端范畴技能的不断进步,前端同学也能够根据此完结独立的服务端烘托。
  3. 进程:浏览器发送恳求 -> 服务器运转 React代码生成页面 -> 服务器回来页面 -> 浏览器下载HTML文档 -> 页面预备就绪
  4. 即:当时页面的内容是服务器生成好给到浏览器的。
  5. 对应CSR:即客户端烘托(Client Side Render)
  6. 进程:浏览器发送恳求 外卖,依据 React.js 和 Node.js 的SSR完结计划,diy-> 服务器回来空白 HTML(HTML里包括一个root节点和js文件) -> 浏览器下载js文件 -> 浏览器运转react代码 -> 页面预备就绪
  7. 即:当时页面的内容是JavaScript 烘托出来。
  8. 怎么差异页面是否服务端烘托:
  9. 右键点击 -> 显现网页源代码,假如页面上的内容在HTML文档里,是服务端烘托,不然便是客户端烘托。
  10. 比照
  • CSR:首屏烘托时刻长,react代码运转在浏览器,耗费的是浏览器的功用
  • SSR:首屏烘托时刻短,react代码运转在服务器,耗费的是服务器的功用

为什么要用服务端烘托

  • 首屏加载时刻优化,因为SSR是直接回来生成好内容的HTML,而一般的CSR是先回来空白的HTML,再由浏览器动态加载JavaScript脚本并烘托好后页面才有内容;所以SSR首屏加载更快、削减白屏的时刻、用户体会更好。
  • SEO (查找引擎优化),查找关键词的时分排名,对大多数查找引擎,不辨认JavaScript 内容,只辨认 HTML 内容。
  • (注:原则上能够不必服务端烘托时最好不必,所以假如只要 SEO 要求,能够用预烘托等技能去替代)

构建一个服务端烘托的项目

(1) 使程琳老共用 Node.js 作为服务端和客户端的中间层,承当 proxy署理,处理cookie等操作。

(2) hydrate 的运用:在有服务端烘托情况下,运用hydrate替代render,它的效果主要是将相关的事情灌水进HTML页面中(即:让React组件的数据跟着HTML文档一同传递给浏览器网页),这样能够坚持服务端数据和浏览器端共同,防止闪屏,使第一次加载体会更高效流通。

1 ReactDom.hydrate(, document.getElementById('root'));

(3) 服务端代码webpa10万左右的车排行榜ck编译:一般会建一个webpack.server.js文件,除了惯例的参数装备外,还需要设置target参外卖,依据 React.js 和 Node.js 的SSR完结计划,diy数为'node'。

 1const serverConfig = {
2 target: 'node',
3 entry: './src说唱脸谱/server/index.js',
4 output: {
5 filename: 'bundle.js',
6 path: path.resolve(__dirname, '../dist')
7 },
8 externals: [nodeExternals()],
9 module: {
10 rules: [{
11 test: /\.js?$/,
12 loader: 'babel-loader',
13 exclude: [
14 path.j张龄心oin(__dirname, './node_modules')
15 ]
16 }
17 ...
18 ]
19 }
20 (此处省掉款式打包,代码紧缩,运穿越未来之男人不好当行坏境装备等等...)
21 ...
22};

(4) 运用react-dom/server下的 renderToString办法在服务器上把各种杂乱的组件和代码转化成 HTML 字符串回来到浏览器,并在初始恳求时发送符号以加速页面加载速度,并答应查找引擎抓取页面以完结SEO意图。

 1const render = (store, routes, req, context) => {
2 const content = renderToString((
3
4
5

6 {renderRoutes(实在阅历routes)}
7

8

9

10 ));骆冰银传
11 return `
12
13
14
15
16
17
${content}

18
19
20
21 `;
22}
23app.get('*', function (req, res) {
24 ...
25 const html = render(store, routes, req, context);
26 res.send(html);
27});

与 renderToString相似功用的还有:

i. renderToStaticMarkup:差异在于renderToStaticMarkup 烘托出的是不带data-reactid的纯HTML,在JavaScript加载完结后因为不认识之前服务端烘托的内容导致从头烘托(或许页面会闪一下)。

ii. renderToNodeStream:将React元素烘托为其初始HTML,回来一个输出HTML字符串的可读流。

iii. renderToStaticNodeStream:与renderToNodeStream此相似,除了这不会创立React在内部运用的额定外卖,依据 React.js 和 Node.js 的SSR完结计划,diyDOM特点,例如data-reactroot。

(5) 运用redux 承当数据预备,状况保护的责任,一般调配react-redux, redux-thunk(中间件:发异步恳求用到action)运用。(本猿现在运用比较多是便是Redux和Mobx,这儿以Redux为例)。

A. 创立store(服务器每次恳求都要创立一次,客户端只创立一次):

 1const reducer = combineReducers({
2 home: homeReducer,
3 page1: page1Reducer,
4 page2: page2Reducer
5});
6
7export const getStore = (req) => {
8 return createStore(reducer, applyMiddleware(thunk.withExtraArgume马力nt(bingbarserverAxios(req))));
9}
10
11export const getClientStore = () => {
12 return createStore(reducer, window.STATE_FROM_SERVER, applyMiddleware(thunk.w军事博物馆ithExtraArgument(clientAxios)));
13}

B. action: 担任把数据从运用传到store,是store数据的仅有来历

 1export const ge外卖,依据 React.js 和 Node.js 的SSR完结计划,diytData = () => {
2 return (dispatch, getState, axiosInstance)外卖,依据 React.js 和 Node.js 的SSR完结计划,diy => {
3 return axiosInstance.get('interfaceUrl/xxx')
4 .then((res) => {
5 dispatch({
6 type: 'HOME_LIST',
7 list: res.list
8 })
9 });
10 }
11}

C. reducer:接纳旧的state和action,回来新的state,呼应actions并发送到store。

 1export de郑智薰fault (state = { list: [] }, action) 千层蛋糕=> {
2 switch(action.type) {
3 case 'HOME_LfishIST':
4 return {
5 ...state,
6 list: action.list
7 }
8 default:
9 return state;
10 }
11}
1外卖,依据 React.js 和 Node.js 的SSR完结计划,diy2export default (state = { list: [] }, act商业计划书ion) => {
13 switch(action.type) {
14 case 'HOME_LIST':
15 retu自学网rn 外卖,依据 React.js 和 Node.js 的SSR完结计划,diy{
16 ...state,
17 list: action.list
18 }
19 default:
20 return state;
21 }
22}

D. 运用react-redux的connect,Provider把组件和store连接起来

  • Provider 将之前创立的store作为prop传给Pr布衣官道ovider
1const content = renderToString((
2
3
4

5 {renderRoutes(routes)}
6

7

8

9));
  • connect([mapStateToProps],[mapDispatchToProps],[mergeProps], [options])接纳四个参数,常用的是前两个特点
  • mapStateToProps 函数答应咱们将store中的数据作为 props 绑定到组件上
  • mapDispatchToProps 将action作为props绑定到组件上
1 connect(mapStateToProps(),mapDispatchToProps())(MyComponent)

(6) 运用驻港部队与飞虎队沟通react-router承当路由责任

服务端路由不同于客户端,它是无状况的。React 供给了一个无状况的组件StaticRouter,向StaticRouter传递当时URL,调用ReactDOMServer.renderToString() 就能匹配到路由视图。

服务端

1import { StaticRouter } from聚美 'react-router-dom';
2import { renderRoutes } from 'react-router-config'
3import routes from './router.js'
4
5
6{renderRoutes(routes)}
7

8

浏览器端

1import { BrowserRouter } from 'react-router-dom';
2import { renderRoutes } from 'react-router-config'
3import routes from './router.js'
4
5
6 {renderRoutes(routes)}
7

8
1const routes = [{ component: Root,
2 routes: [
3 { path: '/',
4 exact: true,
5 component: Home,
6 loadData: Home.loadData
7 },
8 { path: '/child/:id',
9 component: Child,
10 loadData: Child.loadData
11 routes: [
12 path: '/child/:id/grand-child',
13 component: GrandChild,
14 loadData: GrandChild.loadData
15 ]
16 }
17 ]
18}];
1import { matchRoutes } from 'react-router-config'
2 const loadBranchData = (location) => {
3 const branch = matchRoutes(routes, location.pathname)
4
5 const promises = branch.map(({ route, match }) => {
6 return route.loadData
7 ? route.loadData(match)
8 : Promise.resolve(null)
9 })
10
11 return Promise.all(promises)
12}

(7) 写组件留意代码同构(即:一套React代码在服务端履行一次,在客户端再履行一次)

因为服务器端绑定事情是无效的,所以服务器回来的只要页面款式(&灌水的数据),一起回来JavaScript文件,在浏览器上下载并履行JavaScript时才能把事情绑上,而咱们期望这个进程只需编写一次代码,这个时分就会用到同构,服务端渲k7713染出款式,在客户端履行时绑上事情。

长处: 共用前端代码,节约开发时刻

坏处: 因为服务器端和浏览器环境差异,会带来一些问题,如document等目标找不到,DOM核算报错,前端烘托和服务端烘托内容不共同等;前端能够做非常杂乱的恳求合并和推迟处理,但为了同构,所有这些恳求都在预先拿到成果才会烘托。

根据 React.js 和 Node.js 的SSR完结计划