分享个人 Full-Stack JavaScript 项目开发经验
在进行 Javascript Web 应用程序开发中,Grunt 提供了对 js 的合并和混淆压缩等功能插件。这对于一些自编写的 jquery 插件,这种打包方式并没有问题。但对于使用 npm 中诸多依赖模块的项目,我们需要使用 webpack 做更细致的打包,包括代码拆分、源码转换等。
下面结合本博客项目,介绍 webpack 打包 react 应用程序的步骤:
webpack 依赖于 node.js 环境,还没安装过 node.js 环境的,可以参阅Node.js环境安装。
本地安装 webpack 及其 CLI 的开发依赖:
yarn add webpack webpack-cli --dev
在项目的根目录创建一个 webpack.config.js,这是 webpack 命令默认的配置文件名称(后面会介绍具体的配置例子)。
package.json
{
// ......
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"build": "webpack"
}
// ......
}
往后就可以执行 npm run build 来打包,执行 npm run watch 来监听并实时打包。
安装 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 配置文件:
// 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,再利用缓存,已达到很好的加载体现。