什么是 webpack?
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler) 。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph) ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack核心概念
- 入口(entry):入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。
可以通过在 webpack.config.js 中配置 entry 属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src。
- 输出(output):output 属性告诉 webpack 在哪里输出它所创建的
bundles,以及如何命名这些文件,默认值为./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个output字段,来配置这些处理过程。 - loader:
loader让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效 模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
在更高层面,在 webpack 的配置中 loader 有两个目标:
test属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use属性,表示进行转换时,应该使用哪个 loader。
- 插件(plugins):loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。
- 模式
通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化。
用 webpack 搭建一个基于 React+TypeScript 的项目
- 初始化项目:创建一个项目文件夹,运行
npm init -y进行初始化。 - 安装
webpack、webpack-cli
yarn add webpack webpack-cli --dev //(或者使用 npm)
- 新建 lib/index.tsx文件,随便写点什么:
console.log('hi');
- 新建
webpack.config.js,开始 webpack 配置,首先添加入口配置:
module.exports = {
entry: {
index: './lib/index.tsx'
},
}
- 告诉 webpack 如何理解 tsx 文件,添加
awesome-typescript-loader。首先安装:
yarn add awesome-typescript-loader --dev
在 webpack.config.js 中添加配置:
module.exports = {
// ...
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
module: {
rules: [
{
test: /.tsx?$/,
loader: 'awesome-typescript-loader'
}
]
},
}
- 添加出口配置
const path = require('path')
module.exports = {
// ...
output: {
path: path.resolve(__dirname, 'dist/lib'), // 必须是绝对路径
library: 'MyLibrary', // 模块名称
libraryTarget: 'umd', //将 library 暴露为所有的模块定义下都可运行的方式
},
module: {
// ...
},
}
- 添加
mode
module.exports = {
//....
mode: "production",
module: {
//...
}
}
mode 配置项,告知 webpack 使用相应模式的内置优化。
mode 配置项,支持以下两个配置:
development:将process.env.NODE_ENV的值设置为development,启用NamedChunksPlugin和NamedModulesPluginproduction:将process.env.NODE_ENV的值设置为production,启用FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin和UglifyJsPlugin
- 配置
TypeScript和TSlint, 添加tsconfig.json和tslint.json。
// tsconfig.json
{
"compilerOptions": {
"outDir": "dist",
"declaration": true,
"baseUrl": ".",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": ".",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"importHelpers": true,
"strictNullChecks": true,
"esModuleInterop": true,
"noUnusedLocals": true
},
"include": [
"lib/**/*"
],
"exclude": [
"node_modules",
"build",
"dist",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts",
"*.js"
]
}
// tslint.json
{
"extends": ["tslint:recommended", "tslint-react"],
"rules": {
"no-console": [false, "log", "error"],
"jsx-no-multiline-js": false,
"whitespace": false,
"no-empty-interface": false,
"space-before-function-paren": false,
"no-namespace": false,
"label-position": false,
"quotemark": [true, "single", "jsx-double"],
"member-access": false,
"semicolon": [true, "always", "ignore-bound-class-methods"],
"no-unused-expression": [true, "allow-fast-null-checks"],
"member-ordering": false,
"trailing-comma": false,
"arrow-parens": false,
"jsx-self-close": false,
"max-line-length": false,
"interface-name": false,
"no-empty": false,
"comment-format": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"eofline": false,
"jsx-no-lambda": false,
"no-trailing-whitespace": false,
"jsx-alignment": false,
"jsx-wrap-multilines": false,
"no-shadowed-variable": [
false,
{
"class": true,
"enum": true,
"function": false,
"interface": false,
"namespace": true,
"typeAlias": false,
"typeParameter": false
}
]
},
"linterOptions": {
"exclude": [
"config/**/*.js",
"node_modules/**/*.ts",
"coverage/lcov-report/*.js"
]
}
}
现在,执行 npx webpack, 就可以在 dist/lib 文件夹下看到转义好的 index.js 文件了。
- 在浏览器中查看页面
查看页面需要有 html 文件,由于每次生成的 js 文件名可能会不同,总不能每次手动修改 html,所以使用
html-webpack-plugin这个插件来帮我们自动插入script标签。
首先安装 html-webpack-plugin
yarn add html-webpack-plugin --dev
配置 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入插件
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
title: 'React-app',
template: 'index.html'
})
]
}
在根目录新建 index.html
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
- 使用
webpack-dev-server实现自动打包 运行yarn add webpack-dev-server --dev安装webpack-dev-server。
运行 npx webpack-dev-server 即可打开一个端口,该端口请求的 js 是最新编译后的源码字符串(存放在内存中)。
- 对开发环境和生产环境进行分别配置
创建
webpack.config.dev.js和webpack.config.prod.js。
根据项目需求和使用方法,比如本项目是开发一个 React 组件库,那么使用时就不需要再额外下载 React 依赖,那么在生产环境对应的配置中,将 react 加入 externals,可以缩小打包后代码的体积。将生产环境和开发环境共有的配置保留在 webpack.config.js中,然后分别对webpack.config.dev.js和webpack.config.prod.js根据项目需求进行配置。
webpack.config.js
const path = require('path')
module.exports = {
entry: {
index: './lib/index.tsx'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
output: {
path: path.resolve(__dirname, 'dist/lib'),
library: 'MyLibrary',
libraryTarget: 'umd',
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader'
},
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
}
webpack.config.dev.js 用于开发环境
const base = require('./webpack.config') //将共有的配置项合并
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = Object.assign({}, base, {
mode: 'development',
entry: {
example: './example.tsx',
},
plugins: [
new HtmlWebpackPlugin({
title: 'React-app',
template: 'example.html'
})
],
})
webpack.config.prod.js 用于生产环境
const base = require('./webpack.config')
module.exports = Object.assign({}, base, {
mode: 'production',
externals: {
react: {
commonjs: 'react',
commonjs2: 'react',
amd: 'react',
root: 'React',
},
'react-dom': {
commonjs: 'react-dom',
commonjs2: 'react-dom',
amd: 'react-dom',
root: 'ReactDOM',
},
}
})
在package.json文件中添加
"scripts": {
"start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
"test": "cross-env NODE_ENV=test jest --config=jest.config.js --runInBand",
},
就可以通过 yarn start 命令运行 npx webpack-dev-server且指定配置文件为 webpack.config.dev.js;
通过 yarn build 命令运行 npx webpack 且指定配置文件为 webpack.config.prod.js。(cross-env是为了兼容Windows和Mac)。
此次项目的 webpack 配置基本完成,在后续的开发中,只能根据需求和报错随机应变添加相应的配置。只有理解了 webpack,才能根据自己的需求做出最合适的配置。
本文参考: