前言
这是这个系列的第三篇了,这篇讲讲一些Webpack配置,还有常见的扩展
Webpack配置项
entry入口
入口起点指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,Webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的,并进行相应的解析和打包操作。
1 2 3 4 5 6 7 8 9 10 11 12 13
| module.exports = { entry: './path/to/my/entry/file.js' };
module.exports = { entry: { app: './src/app.js', adminApp: './src/adminApp.js' } };
|
output出口
配置打包后的输出文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| module.exports = { output: { filename: 'bundle.js', } };
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name].js', path: __dirname + '/dist' } };
|
mode
告知 webpack 使用的打包模式,有三个可取值
- development
- production
- none
其实就是三个模式下webpack有不同的配置而已,比如production模式下会使用一些代码压缩和混淆的插件。
context
设置context会影响入口和loaders的解析,入口和loaders的相对路径会以context的配置作为基准路径。这样,你的配置会独立于CWD(current working directory 当前执行路径)
当前执行路径是什么呢,就是你打开控制台时的那一串文件路径
1
| E:\Workspaces\Project\WebPro\WebpackTest>
|
比如现在我的webpack.config.js是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| let path = require("path"); let MyPlugin = require("./src/plugins/MyPlugin"); let FileListPlugin = require("./src/plugins/FileListPlugin"); module.exports = { entry: "./src/index.js", mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js" }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000 }, module: { rules: [{ test: /\.css$/, use: ["./src/loaders/style-loader"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [{ loader: "./src/loaders/img-loader.js", options: { limit: 3000, filename: "img-[contenthash:5].[ext]" } }] }] }, plugins: [ new MyPlugin(), new FileListPlugin() ] }
|
在使用了context后,就可以对入口和loader的文件路径进行一些改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| let path = require("path"); let MyPlugin = require("./src/plugins/MyPlugin"); let FileListPlugin = require("./src/plugins/FileListPlugin"); module.exports = { entry: "./index.js", mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js" }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000 }, context: path.resolve(__dirname, "src"), module: { rules: [{ test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [{ loader: "./loaders/img-loader.js", options: { limit: 3000, filename: "img-[contenthash:5].[ext]" } }] }] }, plugins: [ new MyPlugin(), new FileListPlugin() ] }
|
library和libraryTarget
这两个变量用于暴露自执行函数的执行结果,配置在output下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| let path = require("path"); let MyPlugin = require("./src/plugins/MyPlugin"); let FileListPlugin = require("./src/plugins/FileListPlugin"); module.exports = { entry: "./index.js", mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js", library: "indexVal", libraryTarget: "var" }, }
|
打包结果暴露到indexVal上,使用var暴露
其他可用的值有:
target
设置打包结果最终要运行的环境,常用值有
- web: 打包后的代码运行在web环境中
- node:打包后的代码运行在node环境中
改改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| let path = require("path"); let MyPlugin = require("./src/plugins/MyPlugin"); let FileListPlugin = require("./src/plugins/FileListPlugin"); module.exports = { entry: "./index.js", mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js", library: "indexVal", libraryTarget: "var" }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000 }, context: path.resolve(__dirname, "src"), target: "web" }
|
在index.js里加一行
1 2
| import fs from "fs"; console.log(fs);
|
运行webpack进行打包就会报错
1 2
| ERROR in ./index.js 4:0-20 Module not found: Error: Can't resolve 'fs' in 'E:\Workspaces\Project\WebPro\WebpackTest\src'
|
这个配置会影响一些内置模块的解析,如果设置的target是web,是不能使用node的内置模块的
如果要对这段代码进行打包,需要把target改成node
打包结果
就是非常普通的导出了一个require(“fs”)
noParse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| module.exports = { module: { rules: [ { test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [{ loader: "./loaders/img-loader.js", options: { limit: 3000, filename: "img-[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, }
|
不解析正则表达式匹配的模块,通常用它来忽略那些大型的单模块库,以提高构建性能
简单来说,就是匹配到的模块会在读取文件内容后直接把内容添加到最终生成的assets中,跳过AST解析和依赖查找等步骤,对一些已经是打包后代码的大型库可以使用这个选项
resolve
resolve的相关配置主要用于控制模块解析过程,详情可以看这里
modules
1
| modules: ["node_modules"]
|
这个配置告诉webpack在使用下面的导入语句时该去哪里查找依赖
extensions
这个配置告诉webpack在导入模块时没有后缀该怎么自动补全后缀
1
| extensions: [".js", ".json"]
|
1
| import add from "./util/add";
|
这里不需要写后缀就是因为webpack会尝试进行后缀补全,如果能匹配到对应的文件就会进行相应的读取解析操作
alias
alias是别名的意思,用于代替一个路径
1 2 3 4 5 6 7 8 9 10
| module.exports = { entry: "./index.js", mode: "development", resolve: { alias: { "@": path.resolve(__dirname, 'src') } } }
|
在index.js中就可以使用@了
1
| import add from "@/add.js";
|
在大型系统中,源码结构往往比较深和复杂,别名配置可以让我们更加方便的导入依赖
externals
有时一些外部库我们是通过CDN的方式来引入,那么我们就不希望让这些库出现在bundle中,这时候我们就要使用这个配置了
修改dist/index.html
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> </head> <body> <script src="./bundle.js"></script> </body> </html>
|
src/index.js
1 2
| import jquery from "jquery"; console.log(jquery);
|
webpack.config.js
1 2 3 4 5 6
| module.exports = { externals: { jquery : "$" } }
|
康康打包结果
在使用了这个配置后,不会再把整个jquery打包到打包结果里了,而是直接导出一个$,这个$就是通过cdn引入jquery时绑定在页面上的值
常用扩展
clean-webpack-plugin
A webpack plugin to remove/clean your build folder(s).
这个插件用于清除你的output文件夹,一般用于清除上次打包的文件
html-webpack-plugin
Plugin that simplifies creation of HTML files to serve your bundles
这个插件可以生成一个html,其实就是自动生成一个html引用了打包结果而已
修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| const path = require("path"); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { index : "./index.js" }, mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "[name].[chunkhash:5].js", }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000 }, context: path.resolve(__dirname, "src"), target: "node", module: { rules: [ { test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [{ loader: "./loaders/img-loader.js", options: { limit: 3000, filename: "img-[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html", filename: "index.html", chunks: ["index"] }) ], resolve: { alias: { "@": path.resolve(__dirname, 'src') } }, externals: { jquery : "$" } }
|
新建public/index.html
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> </head> <body> </body> </html>
|
打包,会在dist目录下生成一个index.html文件
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> <script defer src="index.1f60d.js"></script></head> <body> </body> </html>
|
顺带一提,如果你报了这个错误
1
| The 'compilation' argument must be an instance of Compilation
|
那是因为html-webpack-plugin旧版本对webpack5不兼容,可以安装5.0.0-alpha.9
这个版本来解决
copy-webpack-plugin
Copies individual files or entire directories, which already exist, to the build directory.
这个插件用于复制静态文件到output文件夹里,参考下面的例子
src/public/index.html
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> </head> <body> <img src="../assets/img.png"> </body> </html>
|
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| const path = require("path"); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { entry: { index: "./index.js" }, mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "js/[name].[chunkhash:5].js", }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000, open : true }, context: path.resolve(__dirname, "src"), target: "node", module: { rules: [ { test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [{ loader: "./loaders/img-loader.js", options: { limit: 3000, filename: "img-[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html", filename: "html/index.html", chunks: ["index"] }), new CopyWebpackPlugin({ patterns: [ { from: "./assets", to: "./assets" }, ], }), ], resolve: { alias: { "@": path.resolve(__dirname, 'src') }}, externals: { jquery : "$" } }
|
打包后生成的dist/html/index.html
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> <script defer src="../js/index.1f60d.js"></script></head> <body> <img src="../assets/img.png"> </body> </html>
|
dist目录如下
如果没有使用copy-webpack-plugin,就不会生成assets文件夹
本地开发服务器
想必你已经注意到了,在开发阶段,从编写代码到查看效果的过程非常繁琐,有下面几个步骤
- 编写代码
- 控制台运行命令完成打包
- 刷新页面查看效果
而且有时候,我们希望把最终生成的代码和页面部署到服务器环境上来模拟真实环境
为了解决这些问题,webpack官方提供了一个库webpack-dev-server
,它不是plugin也不是loader,而是一个单独的库
安装
1
| cnpm install webpack-dev-server -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const path = require("path"); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000, open : true }, }
|
启动本地开发服务器
访问http://localhost:9000/html/index.html
,就可以看到效果了
dist/html/index.html
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> <script defer src="../js/index.1f60d.js"></script></head> <body> <img src="../assets/img.png"> </body> </html>
|
文件处理
这里我们介绍file-loader和url-loader
file-loader
The file-loader
resolves import
/require()
on a file into a url and emits the file into the output directory.
url-loader
A loader for webpack which transforms files into base64 URIs.
安装file-loader和url-loader
1
| cnpm install file-loader url-loader -D
|
配置一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| const path = require("path"); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { entry: { index: "./index.js" }, mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "js/[name].[chunkhash:5].js", }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000, open : true, }, context: path.resolve(__dirname, "src"), target: "node", module: { rules: [ { test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(gif)|(jpg)$/, use: [{ loader: "url-loader", options: { limit: 10 * 1024, name: "imgs/[name].[hash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html", filename: "html/index.html", chunks: ["index"] }), new CopyWebpackPlugin({ patterns: [ { from: "./assets", to: "./assets" }, ], }), ], resolve: { alias: { "@": path.resolve(__dirname, 'src') }}, externals: { jquery : "$" } }
|
修改src/index.js
1 2 3 4 5 6 7 8 9
| import src from "./assets/img.png"; let img = document.createElement("img"); img.src = src; document.body.appendChild(img); console.log(img); import jquery from "jquery"; console.log(jquery);
|
打开页面,你会发现报了这样一个错
那是图片没有打包成功吗,其实不是,我们康康文件目录,然后看看img的src是什么
文件目录
看出问题在哪了嘛,在index.js中有这样一句话
1
| import src from "./assets/img.png";
|
这个图片的解析是通过url-loader的,url-loader会把这个图片输出到img/
下,然后返回对应的路径
这一点可以通过打包文件验证
可以看到这个文件最后被转化成了一个文件路径,值是
1
| __webpack_require__.p + "imgs/img.e7819.png"
|
所以这就是问题所在了,我们打包后的js文件在这个路径下被执行
1
| http://localhost:9000/html/index.html
|
所以__webpack_require__.p + "imgs/img.e7819.png"
和http://localhost:9000/html/index.html
一拼接,就变成了了这样(__webpack_require__.p
默认是””)
1
| http://localhost:9000/html/imgs/img.e7819.png
|
这种问题发生的根本原因就是模块中的路径来自于某个loader或plugin,当产生路径时,loader或plugin只有相对于dist目录的路径,并不知道该路径将在哪个资源中使用,从而无法确定最终正确的路径
那么这个问题怎么解决呢,其实也挺简单,看到那个__webpack_require__.p
了嘛,我们修改它就可以了
publicPath
官方文档看这里
webpack 提供一个非常有用的配置,该配置能帮助你为项目中的所有资源指定一个基础路径,它被称为公共路径(publicPath)。
publishPath只是配置了一个字符串,但是一些loader和plugin会在有它存在时,对路径进行相应的处理,比如说上面的url-loader会把它拼在生成的文件名的前面
我们现在配置publishPath为”/“
打包结果中多了这样一个东西
1 2 3
| (() => { __webpack_require__.p = "/"; })();
|
现在这一个路径的结果变成了
1
| "/" + "imgs/img.e7819.png"
|
访问正常√
webpack内置插件
DefinePlugin
全局常量定义插件,使用该插件通常定义一些常量值,这样一来,在源码中,我们可以直接使用插件中提供的常量,当webpack编译完成后,会自动替换为常量的值
BannerPlugin
它可以为每个chunk生成的文件头部添加一行注释,一般用于添加作者、公司、版权等信息
ProvidePlugin
自动加载模块,而不必到处 import 或 require
修改index.js
1 2 3 4
| console.log(PI); console.log(VERSION); console.log(DOMAIN); console.log($);
|
修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| const path = require("path"); const {CleanWebpackPlugin} = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpack = require("webpack"); module.exports = { entry: { index: "./index.js" }, mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "js/[name].[chunkhash:5].js", publicPath: "/" }, devServer: { contentBase: path.resolve(__dirname, 'dist'), port: 9000, open : true, }, context: path.resolve(__dirname, "src"), target: "node", module: { rules: [ { test: /\.css$/, use: ["./loaders/style-loader"] }, { test: /\.(png)|(gif)|(jpg)$/, use: [{ loader: "url-loader", options: { limit: 10 * 1024, name: "imgs/[name].[hash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html", filename: "html/index.html", chunks: ["index"] }), new CopyWebpackPlugin({ patterns: [ { from: "./assets", to: "./assets" }, ], }), new webpack.DefinePlugin({ PI: `Math.PI`, VERSION: `"1.0.0"`, DOMAIN: JSON.stringify("https://www.sakura-snow.com/") }), new webpack.BannerPlugin({ banner: ` hash : [hash] chunkhash : [chunkhash] name : [name] author : SakuraSnow ` }), new webpack.ProvidePlugin({ $: 'jquery' }) ], resolve: { alias: { "@": path.resolve(__dirname, 'src') }}, externals: { jquery : "$" } }
|
看看打包结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var __webpack_modules__ = ( { "./index.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__); var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "jquery"); var jquery__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__); var $ = __webpack_require__( "jquery"); console.log(Math.PI); console.log("1.0.0"); console.log("https://www.sakura-snow.com/"); console.log($);
}), "jquery": ((module) => { eval("module.exports = $;\n\n//# sourceURL=webpack:///external_%22$%22?"); })
|
还是挺容易看懂的,而且莫得啥用orz,康康就行
后记
哇写的真是头秃,下篇写css相关的配置orz,希望不咕