webpack 本质上是一个打包工具,它会根据代码的内容解析模块依赖,帮助我们把多个模块的代码打包。
一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理,最终会输出浏览器能使用的静态资源。
entry
webpack 构建的入口entry,webpack 会读取这个文件,并从它开始解析依赖,在内部构件一个依赖图,这个依赖图会引用项目中使用到的各个模块,然后进行打包,生成一个或者多个 bundle 文件。
我们常见的项目中,如果是单页面应用,那么入口只有一个;如果是多个页面的项目,那么通常是一个页面会对应一个构建入口。
单⼊⼝:entry 是⼀个字符串,如下代码:
module.exports = { entry: './src/index.js' };
上述代码等价于:
// 上述配置等同于 module.exports = { entry: { main: './src/index.js' } }
多⼊口:entry 是⼀个对象,如下代码:
module.exports = { entry: { app: './src/app.js', home: './src/home.js' } };
还有一种场景比较少用到,即是多个文件作为一个入口来配置,webpack 会解析多个文件的依赖然后打包到一起:
// 使用数组来对多个文件进行打包 module.exports = { entry: { main: [ './src/foo.js', './src/bar.js' ] } }
动态 entry
const path = require('path'); const fs = require('fs'); // src/pages 目录为页面入口的根目录 const pagesRoot = path.resolve(__dirname, './src/pages'); // fs 读取 pages 下的所有文件夹来作为入口,使用 entries 对象记录下来 const entries = fs.readdirSync(pagesRoot).reduce((entries, page) => { // 文件夹名称作为入口名称,值为对应的路径,可以省略 `index.js`,webpack 默认会寻找目录下的 index.js 文件 entries[page] = path.resolve(pagesRoot, page); return entries; }, {}); module.exports = { // 将 entries 对象作为入口配置 entry: entries, // ... };
output
output用来告诉 webpack 如何将编译后的文件输出到磁盘。webpack 构建生成的文件名、路径等都是可以配置的,在配置文件中使用 output
字段来进行设置:
module.exports = { // ... output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, } // 或者使用 entry 的名称 module.exports = { entry: { main: './src/index.js' // main 为 entry 的名称 }, output: { filename: '[name].js', // 使用 [name] 来引用 entry 名称,在这里即为 main path: path.join(__dirname, '/dist/[hash]'), // 路径中使用 hash,每次构建时会有一个不同 hash 值,可以用于避免发布新版本时浏览器缓存导致代码没有更新 // 文件名中也可以使用 hash }, }
loader
我们在前端构建中会遇见需要使用各式各样的文件,例如 css 代码,图片,模板代码等。webpack 中提供一种处理多种文件格式的机制,便是使用 loader。我们可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块。例如我们需要 css-loader 来处理 .css 文件(这里其实还需要 style-loader),最终把不同格式的文件都解析成 js 代码,以便打包后在浏览器中运行。
webpack 开箱即用只支持 JS 和 JSON 两种文件类型,通过 Loaders 去支持其它文件类型并且把它们转化成有效的模块,并且可以添加到依赖图中。
本身是一个函数,接受源文件作为参数,返回转换的结果。
常见的 Loaders 有哪些?
- babel-loader:转换ES6、ES7等JS新特性语法
- css-loader:支持.css文件的加载和解析
- less-loader:将less文件转换成css
- ts-loader:将TS转换成JS
- file-loader:进行图片、字体等的打包
- raw-loader:将文件以字符串的形式导入
- thread-loader:多线程打包JS和CSS
下面我们来配置下处理css的loader以及ES6转ES5的loader,首先npm安装如下loader,
- css-loader:npm i style-loader css-loader -D
- babel-loader:npm install babel-loader @babel/core @babel/preset-env
rules的配置如下:
module: { rules:[ { test: /.css$/, use: ['style-loader','css-loader'] }, { test: /.js$/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀 exclude: /node_modules/, // 排除掉node_modules这个文件夹的js文件 include: [ path.resolve(__dirname,'src') // 指定哪些路径下的文件需要经过 loader 处理 ], use: { // 指定使用的 loader loader: 'babel-loader', // babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码 options: { presets: ['@babel/preset-env'] } } } ] }
plugin
plugin 在 webpack 中的配置只是把实例添加到 plugins
字段的数组中。不过由于需要提供不同的功能,不同的 plugin 本身的配置比较多样化。
常用的plugin:
- CommonsChunkPlugin:将chunks相同的模块代码提取成公共js,webpack4.x版本使用optimization.SplitChunks
- cleanWebpackPlugin:清理构建目录
- ExtractTextWebpackPlugin:将CSS从bundle文件里提取成一个独立的CSS文件(webpack3.x版本),webpack4.x版本使用mini-css-extract-plugin
- CopyWebpackPlugin:将文件或者文件夹拷贝到构建的输出目录
- HtmlWebpackPlugin:创建html文件去承载输出的bundle
- UglifyjsWebpackPlugin:压缩JS
- ZipWebpackPlugin:将打包出的资源生成一个zip包
const path = require('path'); module.exports = { output: { filename: 'bundle.js' }, plugins: [ new HtmlWebpackPlugin({ // 放到plugins数组里 template: './src/index.html' }) ] };
在 webpack 的构建流程中,plugin 用于处理更多其他的一些构建任务。可以这么理解,模块代码转换的工作由 loader 来处理,除此之外的其他任何工作都可以交由 plugin 来完成。
例如,使用 copy-webpack-plugin 来复制其他不需要 loader 处理的文件,只需在配置中通过 plugins 字段添加新的 plugin 即可:
npm install copy-webpack-plugin -D
# 插件通常为第三方的 npm package,都需要安装后才能使用
const CopyPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ new CopyPlugin([ { from: 'src/public', to: 'public' }, ]), ], };
mode
mode,构建模式是 webpack v4 引入的新概念,用于方便快捷地指定一些常用的默认优化配置,mode 可以是 development,production,none 其中的一个,我们之前已经在配置文件中添加了相应的配置:
module.exports = { mode: 'development', // 指定构建模式为 development // ... }
顾名思义,development 模式用于开发时使用,production 模式用于线上生产时使用,none 则是不需要任何默认优化配置时使用。
development 和 production 模式的区别:
- 这两个模式会使用 DefinePlugin 来将 process.env.NODE_ENV 的值分别设置为 development 和 production,方便开发者在项目业务代码中判断当前构建模式。
- production 模式会启用 TerserPlugin来压缩JS代码,让生成的代码文件更小。
- development 模式会启用 devtools: 'eval' 配置,提升构建和再构建的速度。
前端构建基础配置
关联HTML
怎样将HTML引用路径和我们的构建结果关联起来,这个时候我们可以使用 html-webpack-plugin。
html-webpack-plugin 是一个独立的 package,在使用之前我们需要先安装它:
npm install html-webpack-plugin -D
然后在 webpack 配置中,将 html-webpack-plugin 添加到 plugins 列表中:
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // ... plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html', // 配置文件模板 }), ], }
这样,通过 html-webpack-plugin 就可以将我们的页面和构建 JS 关联起来,回归日常,从页面开始开发。如果需要添加多个页面关联,那么实例化多个 html-webpack-plugin, 并将它们都放到 plugins 字段数组中就可以了。
构建 CSS
我们编写 CSS,并且希望使用 webpack 来进行构建,为此,需要在配置中引入 loader 来解析和处理 CSS 文件:
module.exports = { module: { rules: { // ... { test: /.css/, include: [ path.resolve(__dirname, 'src'), ], use: [ 'style-loader', 'css-loader', ], }, }, } }
上面配置的两个loader的作用:
- css-loader 负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url() 等引用外部文件的声明;
- style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。
如果需要单独把 CSS 文件分离出来,我们需要使用 mini-css-extract-plugin 插件。
v4 版本之后才开始使用 mini-css-extract-plugin,之前的版本是使用 extract-text-webpack-plugin。
如下例子:
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { // ... module: { rules: [ { test: /.css/i, use: [ // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader MiniCssExtractPlugin.loader, 'css-loader', ], }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css' // 这里也可以使用 [hash] }), // 将 css 文件单独抽离的 plugin ] };
在上述使用 CSS 的基础上,通常我们会使用 Less/Sass 等 CSS 预处理器,webpack 可以通过添加对应的 loader 来支持,以使用 Less 为例
less-loader 只是 webpack 的转换器,启动 Less 你还需要安装 less 自身,同样地,sass-loader 也是这般。
在 webpack 配置中,添加一个配置来支持解析后缀为 .less 的文件:
module.exports = { // ... module: { rules: [ { test: /.(less|css)$/, use: [ // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader MiniCssExtractPlugin.loader, 'css-loader', 'less-loader', ], }, ], }, // ... }
处理图片文件
在前端项目的样式中总会使用到图片,虽然我们已经提到 css-loader 会解析样式中用 url() 引用的文件路径,但是图片对应的 jpg/png/gif 等文件格式,webpack 处理不了。是的,我们只要添加一个处理图片的 loader 配置就可以了,现有的 file-loader 就是个不错的选择。
file-loader 可以用于处理很多类型的文件,它的主要作用是直接输出文件,把构建后的文件路径返回。配置很简单,在 rules中添加一个字段,增加图片类型文件的解析配置:
module.exports = { // ... module: { rules: [ { test: /.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: {}, }, ], }, ], }, }
使用babel
Babel 是一个让我们能够使用 ES 新特性的 JS 编译工具,我们可以在 webpack 中配置 Babel,以便使用 ES6、ES7 标准来编写 JS 代码。
module.exports = { // ... module: { rules: [ { test: /.jsx?/, // 支持 js 和 jsx 文件,使用 react 时需要 include: [ path.resolve(__dirname, 'src'), // 指定哪些路径下的文件需要经过 loader 处理 ], use: { loader: 'babel-loader', // 指定使用的 loader options: { presets: ['@babel/preset-env'], }, }, }, ], }, }