Webpack 5 + React 布署

Webpack学习

Posted by Pele on July 25, 2022

Webpack 5 + React

intro: 尝试使用Webpack 打包React项目 并添加到HTML中

开发环境

前序知识

Webpack

什么是Webpack?

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

webpack 搭建 react 项目

布署

创建文件夹

创建一个空文件夹

使用npm命令初始化, 生成一个package.json文件

npm init -y

安装webpack

运行命令

npm install --save-dev webpack

安装完成后项目结构如下

|- node_modules
|- package-lock.json
|- package.json

创建config配置文件

在根目录下新建一个config文件夹, 并创建以下三个js文件

  • webpack.common.config.js
  • webpack.prod.config.js
  • webpack.dev.config.js

在根目录下创建一个src文件夹, 并新建一个app.js 文件

+ |- config
+   |- webpack.common.config.js
+   |- webpack.dev.config.js
+   |- webpack.prod.config.js
  |- node_modules
+ |- src
+   |- app.js
	|- package-lock.json
  |- package.json

webpack.common.config.js中添加如下代码

const path = require('path');

module.exports = {
  entry: {
    app: './src/app.js',
  },
  output: {
    filename: 'js/bundle.js',
    path: path.resolve(__dirname, '../dist'),
  },
};

这断代码的作用是指明了入口文件以及打包后的文件路径

package.json配置文件中添加如下属性定义打包指令

   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
+    "start": "webpack --config ./config/webpack.common.config.js"
   },

运行一下试试

npm run start

image-20220725113351428

可以看到生成了一个dist文件夹

  my-project
  |- config
+ |- dist
+   |- js
+     |- bundle.js
  |- node_modules
  |- src
    |- app.js
  |- package.json

此时bundle.js文件夹里还是空的, 因为app.js中还没有添加内容

安装 webpack-merge

npm install --save-dev webpack-merge

配置使用webpack-merge以生成更小的bundle, 更轻量的source map, 更优化的资源, 并改善加载时间, 以满足生产(production)的需求

修改webpack.prod.config.js

const { merge } = require('webpack-merge');
const common = require('./webpack.common.config.js');

module.exports = merge(common, {
  mode: 'production',
});

在根目录下新建一个public文件夹, 并在其中新建一个index.html文件

  |- config
  |- node_modules
+ |- public
+   |- index.html
  |- src
    |- app.js
  |- package-lock.json
  |- package.json

index.html中添加一点内容

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>my-project(webpack + react)</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script src="../dist/js/bundle.js"></script>
</html>

app.js中添加代码

const root = getElementById("root");
root.innerHTML = "<h1>Hello World</h1>";

添加指令到package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack --config ./config/webpack.common.config.js",
+   "build": "webpack --config ./config/webpack.prod.config.js"
  },

执行命令

npm run build

然后用浏览器打开public/index.html, 可以看到app.js中的内容已经成功添加到网页中

image-20220725145433314

安装 React

运行以下命令安装React

npm install react react-dom

安装成功后package.json中会多出项目依赖

+  "dependencies": {
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  }

接下来我们把app.js里的代码替换成React的JSX语法

  1. 在src文件夹中创建一个index.js做为入口文件, 并向文件中添加如下代码\

    // index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import App from "./app";
       
    ReactDOM.render(<App />, document.getElementById("root"));
    
  2. 修改src/app.js中的代码

    - const root = document.getElementById("root");
    - root.innerHTML = "<h1>Hello World</h1>";
       
    + import React from "react";
    +
    + export default function App() {
    +   return <h1>Hello World</h1>;
    + }
    
  3. 替换webpack.common.confid.js中的入口文件

const path = require('path');

module.exports = {
  entry: {
-   app: './src/app.js',
+   index: './src/index.js',
  },
  output: {
    filename: 'js/bundle.js',
    path: path.resolve(__dirname, '../dist')
  }
}

现在运行以下npm run build, 会发现报错了, 原因是缺少讲jsx语法转译为js语言的预处理插件, 所以下面配置Babel

安装 Babel

运行以下命令安装Babel及其依赖

npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime

这些依赖各自的作用如下

  • babel-loader:使用 Babel 和 webpack 来转译 JavaScript 文件。
  • @babel/core:babel 的核心模块
  • @babel/preset-env:转译 ES2015+的语法
  • @babel/preset-react:转译 react 的 JSX
  • @babel/plugin-proposal-class-properties:用来编译类(class)
  • @babel/plugin-transform-runtime:防止污染全局,代码复用和减少打包体积

修改webpack.common.config.js如下以引入这些Babel

const path = require("path");

module.exports = {
  entry: {
    index: "./src/index.js",
  },
  output: {
    filename: "js/bundle.js",
    path: path.resolve(__dirname, "../dist"),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
            plugins: [
              "@babel/plugin-transform-runtime",
              "@babel/plugin-proposal-class-properties",
            ],
          },
        },
      },
    ],
  },
};

现在再运行npm run build 就可以成功打包了

image-20220725151939444

安装 Html-Webpack-Plugin

之前我们是手动添加的public/index.html文件, 但是在生产中可能会生成多个HTML文件, 所以需要安装html-webpack-plugin插件, 让Webpack在打包时自动创建一个HTML文件

运行以下命令安装html-webpack-plugin

npm install --save-dev html-webpack-plugin

修改webpack.prod.config.js以调用html-webpack-plugin

const { merge } = require('webpack-merge');
const common = require('./webpack.common.config.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      filename: 'index.html',
      inject: 'body',
      minify: {
        removeComments: true,
      },
    }),
  ],
});

解释以下这里的属性

  • template:基于我们自己定义的 html 文件为模板生成 html 文件
  • filename:打包之后的 html 文件名字
  • inject:将 js 文件注入到 body 最底部
  • minify:压缩 html 文件时的配置
    • removeComments:去除注释

还有其他一些属性可以参考 HTML Webpack Plugin 项目文档

修改一下public/index.js, 删去引入../dist/js/bundle.js的这行

 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial- scale=1.0" />
     <title>react_webpack</title>
   </head>
   <body>
     <div id="root"></div>
   </body>
-   <script src="../dist/js/bundle.js"></script>
 </html>

现在再运行npm run build, dist文件夹中就会多出一个index.html文件

|- dist
	|- js
	|- index.html
<!-- dist/index.html  -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>react_webpack</title>
  </head>
  <body>
    <div id="root"></div>
  <script defer="defer" src="js/bundle.js"></script></body>
  <script src="../dist/js/bundle.js"></script>
</html>

修改配置文件以生成不同的HTML文件

如果每次都生成一样的JS文件, 浏览器可能不会发现网页已经更新, 从而直接读取本地的缓存, 所以需要通过修改output中的属性使每次打包后生成不同的JS文件名

只需在webpack.prod.config.js中添加这段代码

 module.exports = merge(common, {
+  output: {
+    filename: "js/[name]-bundle-[hash:6].js",
+  },
   mode: "production",
   ...
  • name: 打包入口的名称
  • hash:6: 生成6位hash码

此时再运行npm run build, 可以发现dist/js文件夹中生成了新的js文件

|- dist
	|- js
		|- bundle.js
+		|- index-bundle-2c81f8.js
	|- index.html

安装 Clean Webpack Plugin

经过上面的配置后每次打包都会生成新的文件, 但是我们发现原有的文件却没有被删除, 这样随着打包次数的增加, 文件也会越来越多, 所以需要安装另一个clean-webpack-plugin在每次打包编译前清理dist目录

运行以下命令安装clean-webpack-plugin

npm install --save-dev clean-webpack-plugin

修改webpack.prod.config.js, 引入clean-webpack-plugin

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

  module.exports = merge(common, {
    output: {
      filename: "js/[name]-bundle-[hash:6].js",
    },
    mode: "production",
    plugins: [
+     new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
      	template: "public/index.html",
      	...

现在在打包编译后旧的文件就会被清除了

安装 Webpack Dev Server

经过上面的配置, 我们已经可以使用npm run build命令来构建并打包React文件到HTML文件中, 但是每次构建完都要手动打开dist/index.html文件来查看效果, 所以需要安装webpack-dev-server, 使项目webpack打包后能创建一个进程运行生成的文件

运行以下命令安装webpack-dev-server

npm install --save-dev webpack-dev-server

配置webpack.dev.config.js

const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.config.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = merge(common, {
  mode: 'development',
  devServer: {
    static: {
    	directory: path.join(__dirname, 'dist'),
    },
    port: 8080,
    compress: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: 'body',
      hash: false,
    }),
  ],
});

  • static.directory: 指定启动文件的目录
  • port: 进程运行的端口号
  • compress: 是否开启gzip压缩

修改package.json中的指令以调用webpack-dev-server

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
-   "start": "webpack --config ./config/webpack.common.config.js",
+   "start": "webpack serve --open --config ./config/webpack.dev.config.js",
    "build": "webpack --config ./config/webpack.prod.config.js"
  },

现在运行npm run start, 就可以打包编译并且在本地的8080端口访问生成的页面啦

image-20220725160514268

参考:

从零配置webpack 5 + React脚手架(一)

HtmlWebpackPlugin

WebpackDevServer

html-webpack-plugin 使用总结