react的SSR(2)

书接上回,在上回的笔记中,已经完成了react的一个最简单的实践,但是这个实践还有很多问题,这次我们来解决一下这些问题。

在这回我们主要解决项目在开发时的热更新问题和css的加载问题,顺便把这个项目目录改一改,让他改的像个正经的项目,而不是用于验证可行性的玩具。

开发中的热更新

在做react的开发的时候,大家肯定非常熟悉webpack-hot-server给我们提供的热更新,他启动了一个node服务,在本地的某个端口可以访问到我们开发的页面,并提供一个监控,在我们的文件发生变动之后,自动打包编译并展示新的内容在相应的端口上。

但是换到我们做SSR的时候,这一套就不能使用了,我们需要用新的一套解决方案。

解决方案分客户端和服务端,毕竟我们其实是开启了两个服务,只不过服务端的热更新我们是用nodemon来实现的

那么nodemon又是个什么东西呢?

nodemon就是一个用来替代node的包,不需要任何的配置(当然可以配置),就可以监控项目文件的变化,并在文件发生改变的时候,自动的重启服务。

是不是感觉特别智能,使用前首先要下载,因为这个东西在所有的项目中都可以用,所以推荐直接全局安装

npm install nodemon -g

然后把通常启动命令由

node [你需要启动的服务入口]

改成

nodemon [你需要启动的服务入口]

然后你会发现当我们改变客户端的时候,这个服务依旧被重启了,但是并没有发生变化,这是为什么呢?

扫描二维码关注公众号,回复: 5427693 查看本文章

因为我们之前走的方式首先服务端读取react的组件,然后输出字符串组装成页面返回给浏览器,然后浏览器读取对打包出来的main.js然后渲染,虽然在重启后我们已经更新了对应的react组件输出的字符串,但是我们并没有改变mian.js这个东西,他仍然会渲染之前的内容。

下面我们是否应该搞客户端的热更新呢?并不是,我们要把nodemon监控的范围圈定在我们的服务端,避开客户端,不然我们每次修改客户端,迎来的都不是热更新,而是服务的重启,虽然并不耽误热更新效果,但是随着项目的变大,重启的时候是越来越长的,我们肯定不能接受改一个css整个项目直接重启了。

当然随着项目的变大,nodemon效率也会越来越低,到时候我们再更换真正的服务端热更新,优化这个东西,最好是在有需求的时候再搞。

好了,说一下nodemon的配置,他的配置其实也很简单,在项目的根目录下新建一个配置文件

mkdir nodemon.json

在文件里写入

{
  "ignore": ["app/"]
}

就可以忽略app文件夹内的所有文件了,后续我们重新整理项目的时候这里也要做相应的改变。

上面的都折腾完了,我们可以开始折腾我们的客户端热更新了,目前常用的解决方案大概是webpack-dev-middleware+webpack-hot-middleware来搞定客户端的热更新

首先我们下载两个依赖

npm install webpack-dev-middleware webpack-hot-middleware --save-dev

然后在我们的server.js文件中做出一些改变

我们要引入一些东西

import webpack from 'webpack';
import webpackConfig from './webpack.config'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'

其中weboack.config就是我们的webpack配置文件

然后添加

let compiler = webpack(webpackConfig)
server.use(webpackDevMiddleware(compiler, {
    publicPath: webpackConfig.output.publicPath,
    quiet: true
}))
server.use(webpackHotMiddleware(compiler));

这里注意到我们使用了webpackConfig.output.publicPath。但是我们好像并没有设置这个地址,这个地址可以理解为内存中的打包文件的访问入口,因为热更新的时候文件并不会真是输出在文件夹内,而是存放在内存中。然后我们去更改我们的webpack配置文件。

output: {
    filename: '[name].js',
    path: path.join(__dirname, 'dist'),
    publicPath: '/dist'
}

然后更改我们的模板文件template.js文件中的js文件地址,更改为/dist/main.js。

好了,现在运行

npm run start

来看看效果吧,更爱hello.js文件的内容看看会不会更新。

下面我们来搞一搞css的loader,这个就不多说了,网上教程一大把。

emmm,直到开始配置css-loader之前我都是这么以为的。然而事实我头疼了两天,一直在报错,始终无法运行css-loader。一直说css-loader找不到@babel/preset-env。

然后我下载这个依赖,依旧如此。

我还没有放弃,把@babel/preset-env下载的全局,还是没有用。最后参考了别人的依赖目录,发现了一个问题,没有人的css-loader的版本是大于2.0的,然后去官网看了看,发现2.0版本更改了很多依赖。

2.0版本他对于babel的依赖全面转到了新的@babel/core中,我懒得重新配一套babel了,就直接把css-loader降级,完美解决。

简单说一下一个最简单的less的实例吧。

首先我们在app文件夹内建立一个home.less的文件,然后填写一些内容比如

.hello{
    font-size:20px;
}

在我们引入这个文件 并给div一个类名

import Hello from './hello.js'
import ReactDOM from 'react-dom'
import React from 'react'
import './home.less';
ReactDOM.hydrate(<Hello />, document.getElementById('root'))
import React from 'react'
export default class Hello extends React.Component {
    render () {
        return <div onClick={this.click} className='hello'>hello worssdsdwldsas</div>
    }
}

less的引入最好是在最外层,这样就不用在每个模块都引入一次样式文件了,并且因为我们的服务端渲染的字符串是直接读取模块信息的,但是目前并没有给他对应的css解析能力,只有js的解析能力。

如果我们把less文件引入在hello模块中,大家可以试着看一下效果。

我们再去webpack里面配置一下css-loader

配置前我们还是要去下载对应的依赖

npm install less less-loader css-loaser

我们只需要在webpack的rule中添加以下代码

{
    test:/\.less$/,
    use:['style-loader','css-loader','less-loader']
},

我是不打算在项目中写css和sass,所以我只做了less的解析。

好了这些都搞完了,我们的css应该可以实时的更新了,但是现在的更新是css都写在mian.js里面的,但是在实际的项目中,我们通常不会这么做,因为浏览器的规则规定,js必须全部下载完才会执行,而js通常会放到整个页面的最后面,所以在项目巨大的时候,这样的状态会让页面出现光秃秃的html的情况,所以我们要把他们区分出来。让js归js,css归css。但是这样并不影响我们开发,那么我们既要让开发和生产分离了。

开发生产分离

刚刚写热更新的我们先写js,分离的时候我们反过来,先搞css,因为css相对于js是更看重分离的。

既然搞分离,那么我们最早要搞的其实应该是webpack。

那么webpack应该怎么分离呢,首先看我们之前的packjson.json里的内容,scripts里的内容就是我们平时执行的脚本,我们分离出来start和build。

"scripts": {
    "start": "nodemon index.js",
    "build": "webpack",
  },

这是我们之前的启动脚本,启动服务端时候回自动启动webpack的热更新,但是这个功能在生产环境并没有用,所以我们要使用环境变量区分出来生产环境还开发环境。

这里我们使用的是cross-env,如果不使用这个,在mac环境下,和linux环境下也没什么问题,但是win下可能就会无法识别环境变量,我们并不能要求所有人都统一开发环境,所以最好做一下兼容。

npm i cross-env --save-dev

然后将启动脚本修改为

"start": "cross-env NODE_ENV=development nodemon index.js",

在我们所有的文件中都可以通过

process.env.NODE_ENV

来访问这个变量,对应的,我们也可以设置一个脚本启动生产环境。

"startprod": "cross-env NODE_ENV=project node index.js",

这里用node启动,因为我们有其他的东西守护进程。

说完了服务端,我们再来看客户端,客户端在开发环境下是不需要我们自己去启动的,我们需要的是生产环境的打包

之前的脚本是

"build": "webpack",

他只执行了webpack,并没有指定文件,所以他回去搜索默认的文件webpack.config.js,来启动webpack。但是我们并不想用他,并且我们要把webpack也分离出来。

因为生产环境的webpack和开发环境的webpack其实在很多地方都是相同,我们需要把相同的东西分离出来,这样我们在改两者都可以用到的东西的时候只需要改一处,不会出现改了一个忘了另一个的问题,不然这种问题一旦爆发,就是爆发在生产环境了。

既然分离webpack,那就感觉单独建立一个文件夹用来放置设置项的东西,所以我们新建一个config的文件,并在文件夹里新建三个文件。

mkdir config 
touch webpack.base.js webpack.prod.js webpack.dev.js

说道分离,我们就要确认那些东西是通用,那些是特有的,目前似乎除了css都是通用的,所以我们分离

var path = require('path');

const WebpackBase = {
    mode: 'production',
    entry: {
        main: './app/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.join(__dirname, '../dist'),
        publicPath: '/dist'
    },
    resolve: {
        extensions: ['.js', '.jsx']
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                loaders: ['babel-loader'],
            },
        ]
    },
}
module.exports = WebpackBase;

对于css来说,我们的开发可以不用变,生产环境用extract-text-webpack-plugin来分离css

npm i extract-text-webpack-plugin --save-dev

然后在webpack.prod.js中引入

var ExtractTextPlugin = require('extract-text-webpack-plugin');

并把这个文件中的less规则替换为

{
    test:/\.less$/,
    use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
            'css-loader',
            'less-loader'
        ]
    })
},

然后在这一个文件的plugins中添加一个插件

new ExtractTextPlugin('main.css')

这个mian.css就是你输出的css文件了,好了上面就是关于css配置改,下面我们接着说我们的webpack分离后的文件应该是什么样子。

因为我们webpack分离了一下,在使用的时候我们还要合并他们

npm install webpack-merge --save-dev 

所以对应的两个文件分别为

var base = require('./webpack.base');
var merge = require('webpack-merge');
const webpackConfig = merge(base,{
    module: {
        rules: [
            {
                test:/\.less$/,
                use:['style-loader','css-loader','less-loader']
            },
        ]
    }
})
module.exports = webpackConfig
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var base = require('./webpack.base');
var merge = require('webpack-merge');
const webpackConfig = merge(base,{
    module: {
        rules: [
            {
                test:/\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader',
                        'less-loader'
                    ]
                })
            },
        ]
    },
    plugins:[
        require('autoprefixer'),
        new ExtractTextPlugin('main.css')
    ]
})
module.exports = webpackConfig

说道这里我们webpack的分离就写完了,但是记得把我们服务加载的webpack地址更改到相应的地址。还有脚本,不要拿忘记我们为什么分离他。

最后的脚本改成

"build": "cross-env NODE_ENV=project webpack --config config/webpack.prod.js",

设置他的NODE_ENV变量为project,并使用config文件夹下的webpack.prod.js作为配置文件启动webpack打包。

笔记第二章到此位置,这一章里面我们实现了开发阶段的热更新,方便了我们开发,下一章我们就要研究研究react服务端渲染的路由问题了。

哦对了,还有一些小修改我并没有在这里展示出来,因为不太重要,或者是我在写的时候忘记了,可以对照着我github看,里面每一章都会有对应的分支。

猜你喜欢

转载自blog.csdn.net/qq_18417627/article/details/88180382
ssr
今日推荐