前端基础-一步步搭建webpack4(react篇 )二

1,940 阅读4分钟

开场

上一篇前端基础-一步步搭建webpack4(react篇)中,已经成功搭建了一个能运行起来的react项目。这一篇我们来学习新的东西。

正片

在我们正式工作中,前端项目分为:正式环境(production)、测试环境(development),在webpack4中,直接使用 mode(环境)就可以了,production:生产模式,打包的代码会被压缩,不开启代码调试,development:开发模式,打包的代码不会被压缩,开启代码调试。

那么我们先来说说development开发模式,在我们使用开发模式的时候,会搭配热更新一起来使用。但是webpack-dev-serverhot,在更新时,会刷新整个页面。在react开发时,刷新了页面,会丢失以前的数据流,需要我们重新再去操作。这样很麻烦。所以我们就需要一个新的插件,react-hot-loaderreact-hot-loader不会刷新整个页面,它只是替换了修改的代码,做到了页面的局部刷新。

那么我们先来使用一下react-hot-loader

react-hot-loader

首先,安装一下react-hot-loader

yarn add react-hot-loader -D

使用方法很简单。首先在.babelrc中添加一个plugins

// .babelrc
"plugins" : [ "react-hot-loader/babel" ]

之后在webpack.config.js中entry中添加一行'react-hot-loader/patch',然后在plugins中开启HMR(热替换功能,替换更新部分,不重载页面)

//  webpack.config.js
// entry
entry : [ "react-hot-loader/patch" , "./src/index.js" ]
// plugins 添加
plugins : [
    new webpack.HotModuleReplacementPlugin(),
    // ....其他的plugins
]

接下来,我们需要修改一下index.js。

// src/index.js
import React from "react";
import ReactDom from "react-dom";
import { AppContainer } from "react-hot-loader";
import App from "./App";

// 初始化
renderWithHotReload(App);

// 热更新
if (module.hot) {
  module.hot.accept("./App.js", function() {
    const NextApp = require("./App.js").default;
    renderWithHotReload(NextApp);
  });
}

function renderWithHotReload(Index) {
  ReactDom.render(
    <AppContainer>
      <Index />
    </AppContainer>,
    document.getElementById("app")
  );
}

module.hot:如果已经通过HotModuleReplacementPlugin启用了模块热替换(Hot Module Replacement),则它的接口将被暴露在module.hot属性下面。通常,用户先要检查这个接口是否可访问,然后再开始使用它。 module.hot.accept:接受给定依赖模块的更新,并触发一个回掉函数来对这些更新作出相应

因为使用了JavaScript中新的特性,所有需要安装下@babel/plugin-proposal-class-properties,来支持一下这些特性。

安装成功后,我们在.babelrc中添加plugins

// .babelrc
"plugins": [
    "react-hot-loader/babel",
    "@babel/plugin-proposal-class-properties"
  ]

好了,一切准备就绪了,我们现在来修改下src/App.js,来检查一下,react-hot-loader是否已经启用,是否达到了我们要的效果

import React from "react";
class App extends React.Component {
  state = {
    num: 0
  };
  changeNum = () => {
    this.setState({
      num: this.state.num + 1
    });
  };

  render() {
    const { num } = this.state;
    return (
      <div>
        {num}
        <button onClick={this.changeNum}>add num</button>
      </div>
    );
  }
}

export default App;

当我们按下add num时,num改变成了1,然后修改add num为add num 1。num的值还是为1。所以证明了react-hot-loader成功了。

生产环境,开发环境的配置

好了。接下来,在我们正式开发的情况,我们是要分为正式环境和开发环境的。一般我们的命令为

npm run dev 开发环境,有react-hot-loader功能、debug等等的测试、断点的功能

npm run build 正式环境,打包一份文件夹,上传到服务器。这里是没有测试、断点之类的功能的。

那我们就来写一下区分环境的代码吧

首先我们需要创建三个文件,config/webpack.commom.js config/webpack.development.js config/webpack.production.js

接下来我们安装一下我们需要的包webpack-merge yarn add webpack-merge 这是用来合并webpack配置的。我们需要一个公共的webpack.js方法,存放的是development和production公共的配置。然后使用webpack-merge合并。

接下来看一下,config/webpack.common.js里的代码吧

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: ["./src/index.js"],
  output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "bundle.js"
  },
  resolve: {
    extensions: [".js", ".jsx"]
  },
  module: {
    rules: [
      // 也就是以前的loader
      {
        test: /\.jsx?$/, // 正则表达式,匹配编译的文件
        exclude: /node_modules/, // 排除特定条件,如通常会写node_modules,即把某些目录/文件过滤掉
        use: [
          {
            loader: "babel-loader" // loader 必需要有它,它相当于是一个test匹配到的文件对应的解析起,babel-loader、style-loader、sass-loader等等
          }
        ]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: "index.html", // 最终生成的文件名
      template: path.join(__dirname, "..", "index.html")
    })
  ],
  performance: false // 关闭性能提示
};

接下来我们先把前面写好的带有react-hot-loader的webpack提炼一些代码到config/webpack.development.js

// config/webpack.development.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: ["./src/index.js"],
  output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "bundle.js"
  },
  resolve: {
    extensions: [".js", ".jsx"]
  },
  module: {
    rules: [
      // 也就是以前的loader
      {
        test: /\.jsx?$/, // 正则表达式,匹配编译的文件
        exclude: /node_modules/, // 排除特定条件,如通常会写node_modules,即把某些目录/文件过滤掉
        use: [
          {
            loader: "babel-loader" // loader 必需要有它,它相当于是一个test匹配到的文件对应的解析起,babel-loader、style-loader、sass-loader等等
          }
        ]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: "index.html", // 最终生成的文件名
      template: path.join(__dirname, "..", "index.html")
    })
  ],
  performance: false // 关闭性能提示
};

我们把package.json中的script中的代码改动一下

 "dev": "webpack-dev-server --config ./config/webpack.develop.js --color --propgress",

这样我们运行npm run dev,就可以启动项目了

我们接下来在webpack.production.js中添加一下打包的代码。

// config/webpack.production.js
const path = require("path");
const webpack = require("webpack");
const merge = require("webpack-merge");
const commonConfig = require("./webpack.common");

module.exports = merge(commonConfig, {
  mode: "production",
  output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "js/[name].js",
    chunkFilename: "js/[name].[chunkhash:8].js"
  },
  devtool: "cheap-module-source-map",
  optimization: {
    splitChunks: {
      chunks: "all", // 所有的 chunks 代码公共的部分分离出来成为一个单独的文件
      cacheGroups: {
        // 公共代码打包分组配置
        vvendors: {
          test: "/[\\/]node_modules[\\/]/",
          name: "vendors"
        }
      }
    }
  }
});

我们在package.json中的script中的添加一行

 "build": "webpack --config ./config/webpack.production.js --color --propgress"

现在我们运行npm run build就已经生成了生产环境所对应的代码了。