前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >react 同构初步(1)

react 同构初步(1)

作者头像
一粒小麦
发布2019-12-19 14:43:24
发布2019-12-19 14:43:24
1.6K20
代码可运行
举报
文章被收录于专栏:一Li小麦一Li小麦
运行总次数:0
代码可运行
react 同构初步(1)

这是一个即时短课程的系列笔记。

单页面应用(SPA)在传统的实现)上,面临着首页白屏加载时间过长,seo难以优化的难题。解决这个问题的思路之一就是ssr(服务端渲染)。

无论react或vue,代码都包括组件,store,component等。共同通向一个app.js,我们把app.js通过webpack分成两个bundle,一个是服务端的html(ssr),一个是客户端定义交互操作的js(csr),这个过程称之为同构。

react-dom提供了server的渲染api:renderToString,它可以把react组件解析为html。因为在服务端渲染,而服务端本身是不支持jsx的。使用babel-loader,可以帮助在服务端解析jsx。

环境搭建

初始化npm

代码语言:javascript
代码运行次数:0
复制
mkdir ssr
cd ssr
npm init -y

在项目中新建src,下面新建App.js

代码语言:javascript
代码运行次数:0
复制
import React,{useState} from 'react';

function App(props){
    const [count,setCount]=useState(1);
    return <div>
        <h1>{props.title}:react ssr</h1>
        <span>{count}</span><br/>
        <button onClick={()=>{setCount(count+1)}}>+</button>
    </div>
}

export default <App title="djtao" />;

在项目根目录新建webpack.server.js:

代码语言:javascript
代码运行次数:0
复制
// 服务端webpack
const path=require('path');
const nodeExternals=require('webpack-node-externals');

module.exports={
    target:'node',
    mode:'development',
    entry:'./server/index.js',//服务端要打包的入口
    externals:[nodeExternals()],
    output:{ // 定义输出目录和文件名
        filename:'bundle.js',
        path:path.resolve(__dirname,'build')
    },
    module:{
        rules:[
            {
                test:/\.js$/, // 规则
                loader:'babel-loader', // 使用babel-loader解析
                exclude:/node_modules/,
                options:{
                      // 支持jsx和最新的js写法
                    presets:['@babel/preset-react',['@babel/preset-env']]
                }
            }
        ]
    }
}

然后在命令行安装用到的库:

代码语言:javascript
代码运行次数:0
复制
npm i webpack webpack-cli webpack-node-externals @babel/core @babel/preset-env @babel/preset-react -D

安装完之后,开始写服务端。

服务端渲染(SSR)

在package.json增加两条指令

代码语言:javascript
代码运行次数:0
复制
  "scripts": {
    "dev:server": "webpack --config webpack.server.js --watch",
    "dev:start": "node --watch build --exec node \"./build/bundle.js\""
  },

npm run dev:server执行打包服务端的bundle。

npm run dev:start则是启动你的node服务。

理论上不管服务端选用哪种框架,只要是node环境即可。在此处以express为例.在根目录创建server/index.js

因为已经支持babel,所以可以使用import和jsx了。

代码语言:javascript
代码运行次数:0
复制
import React from 'react';
import {renderToString} from 'react-dom/server';
import express from 'express';
import App from '../src/App';

const app=express();
// 把public作为网站跟路由
app.use(express.static('public'));

app.get('/',(req,res)=>{
    // react组件解析为dom
    const content=renderToString(App);
      // 直接返回一个html模板
    res.send(`
    <html>
        <head>
            <meta charset="UTF-8">
            <title>react ssr</title>
            <body>
                <div id="root">${content}</div>
            </body>
        </head>
    </html>
    `)
});
// 监听9000端口
app.listen(9000,()=>{
    console.log('server is runing..')
});

写完之后安装一下用到的库:

代码语言:javascript
代码运行次数:0
复制
npm i react react-dom express -S

安装好后,ssr就初步完成了。

代码语言:javascript
代码运行次数:0
复制
# 打包服务端bundle.js
npm run dev:server 
# 运行node
npm run dev:start

这时你打开http://localhost:9000,就看到页面了

然而你的计数器是不能用的。点击➕,始终不会有反应

客户端注水:CSR

想要真的能交互,离不开客户端js的加载。怎么做呢?我们也在根目录配置一个webpack.client.js——用于在浏览器执行的js:

代码语言:javascript
代码运行次数:0
复制
const path=require('path');

module.exports={
    mode:'development',
    entry:'./client/index.js', //入口
    output:{
        filename:'bundle.js', //期望在public下创建bundle
        path:path.resolve(__dirname,'public')
    },
    module:{
        rules:[
            {
                test:/\.js$/,
                loader:'babel-loader',
                exclude:/node_modules/,
                options:{
                    presets:['@babel/preset-react',['@babel/preset-env']]
                }
            }
        ]
    }
}

相对于服务端的webpack.server.js,此处省去了很多node才有的配置。

在上面的代码中,我们制定了客户端js的入口,所以在根目录下创建/client/index.js

在这里,我们通过hydrate(react服务端渲染方法,替代旧有的reactDom.render)完成注水工作:

代码语言:javascript
代码运行次数:0
复制
// /client/index.js
import React from 'react';
import ReacDom from 'react-dom';
import App from '../src/App';

// 客户端
// 注水:不需render
ReacDom.hydrate(App,document.querySelector('#root'));

然后增加一条打包/client/index.js的指令:

代码语言:javascript
代码运行次数:0
复制
  "scripts": {
    "dev:client": "webpack --config webpack.client.js --watch",
    "dev:server": "webpack --config webpack.server.js --watch",
    "dev:start": "node --watch build --exec node \"./build/bundle.js\""
  },

执行npm run dev:client,就生成了一个public文件夹,下有你打包好的客户端bundle.js

组合

你的应用想要使用客户端的bundle.js可以在node服务中这么写:

代码语言:javascript
代码运行次数:0
复制
// ...
const app=express();
// 把public作为网站跟路由
app.use(express.static('public'));
app.get('/',(req,res)=>{
    // react组件解析为dom
    const content=renderToString(App);
      // 直接返回一个html模板,带上你的bundle引用!
    res.send(`
    <html>
        <head>
            <meta charset="UTF-8">
            <title>react ssr</title>
            <body>
                <div id="root">${content}</div>
                                <script src="bundle.js"></script>
            </body>
        </head>
    </html>
    `)
});

现在可以运行看看了,

代码语言:javascript
代码运行次数:0
复制
# 分别执行客户端和服务端打包
npm run dev:server
npm run dev:client

# 启node服务
npm run dev:start

然后在9000端口,就可以看到计数器了。

如果我想支持更多的服务端渲染,比如router和redux,应该怎么操作呢?请期待下期分解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境搭建
  • 服务端渲染(SSR)
  • 客户端注水:CSR
  • 组合
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档