GX博客

分享个人 Full-Stack JavaScript 项目开发经验

使用webpack打包react应用程序

在进行 Javascript Web 应用程序开发中,Grunt 提供了对 js 的合并和混淆压缩等功能插件。这对于一些自编写的 jquery 插件,这种打包方式并没有问题。但对于使用 npm 中诸多依赖模块的项目,我们需要使用 webpack 做更细致的打包,包括代码拆分、源码转换等。

下面结合本博客项目,介绍 webpack 打包 react 应用程序的步骤:


安装 webpack

webpack 依赖于 node.js 环境,还没安装过 node.js 环境的,可以参阅Node.js环境安装

本地安装 webpack 及其 CLI 的开发依赖:

yarn add webpack webpack-cli --dev

创建配置文件

在项目的根目录创建一个 webpack.config.js,这是 webpack 命令默认的配置文件名称(后面会介绍具体的配置例子)。


添加 npm 脚本快捷方式

package.json

{
  // ......
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch",
    "build": "webpack"
  }
  // ......
}

往后就可以执行 npm run build 来打包,执行 npm run watch 来监听并实时打包。


安装 babel 相关的开发依赖模块

安装 webpack 的 babel 源码转换器:

yarn add babel-loader --dev

安装 babel 编译器核心模块:

yarn add @babel/core --dev

安装转换 ES2015+ 的预设模块:

yarn add @babel/preset-env --dev

安装转换 React JSX 格式的预设模块:

yarn add @babel/preset-react --dev

安装类属性 babel 插件:

yarn add @babel/plugin-proposal-class-properties --dev

之所以要使用该类属性插件,是因为我想使用属性初始化器语法来避免在 React 组件中使用繁琐的 bind 来绑定 this:

import React from "react";

class ListTabContentUI extends React.Component {
    constructor(props) {
        super(props);
        // this.handleClick = this.handleClick.bind(this);
    }
    // 可以这样以声明方式绑定 this
    handleClick = () => {
        // ......
    };
}

最后,在项目根目录创建一个 .babelrc 配置文件,定义使用以上的预设模块和插件:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}

babel 在不断的发展,以上说明是基于 babel v7.3.4 的步骤。要查看官方的最新安装方式,请点击这里

webpack 配置例子

以下是一个简单的 webpack 配置文件:

// node.js 的 path 模块,后面使用它的 resolve 方法将一系列路径或路径段解析为绝对路径
const path = require('path');

module.exports = {

    // 告知 webpack 使用相应模式的内置优化,可选值为 'development' 和 'production'
    mode: 'development',

    // 这里定义了两个入口
    entry: {
        backstage: ['./server/src/backstage/index.js'],
        front: ['./server/src/front/index.js']
    },

    // 定义了输出文件的动态名称和输出路径
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'server/public/javascripts/dist')
    },

    // 添加模块处理规则
    module: {
        rules: [
            {
                test: /\.js$/,
                // 声明加载器作用的路径
                include: [
                    path.resolve(__dirname, "server/src"),
                    path.resolve(__dirname, "server/lib")
                ],
                /*exclude: /(node_modules)/,*/
                loader: 'babel-loader',
                query: {
                    presets: ["@babel/preset-env", "@babel/preset-react"],
                    plugins: ["@babel/plugin-proposal-class-properties"]
                }
            }
        ]
    },

    optimization: {
        // 代码拆分方式,用于优化加载
        // 考虑到共享模块可以缓存共享,并且并行加载,所以作出以下拆分方式
        splitChunks: {
            cacheGroups: {
                // 创建一个commons 块,其中包括入口点之间共享的所有代码。
                commons: {
                    name: 'commons',
                    // 仅作用于共享的静态模块
                    chunks: 'initial',
                    // 至少有两个共享模块才作拆分
                    minChunks: 2
                }
            }
        }
    },

    // 生成 .map 文件实现源文件映射。在生产环境可以去掉这项。
    devtool: 'source-map'
};

本博客并没有使用 import() 进行动态加载。因为经过 Nginx 的 gzip 模块进行数据压缩后,使用 Grunt 合并和压缩的第三方脚本库,还有 webpack 打包的 React 脚本所有加起来不到 200K,再利用缓存,已达到很好的加载体现。

版权声明:

本文为博主原创文章,若需转载,须注明出处,添加原文链接。

https://leeguangxing.cn/blog_post_27.html