webpack是什么?
webpack是前端一个有效的打包工作,主要就是处理依赖,形成模块,压缩文件,管理插件。
webpack的5个核心概念
module.exports = {
mode: '',
entry: '',
output: '',
plugins: [],
module: [],
resolve: '',
devServer: {}
}
entry: 指示wepack从哪个文件为入口开始打包,分析构建内部的依赖图
ouput: 指示webpack打包后的资源输出到哪里去,以及如何命名
loader: loader能够使得wbpack处理非js文件,比如:css、图片、文件
plugin: plugin可以使得webpack的功能更加强大,从打包优化到压缩
mode: 指示webpack使用相应的模块配置 development:开发环境,代码在本地运行的环境 production:生产环境,代码优化后的上线环境 development打包后,一些没有依赖的方法、变量、文件会保留,production则会移除。 production打包后,代码会进行压缩,比development的文件小
常用的loader
style-loader:创建style标签,将css-loader解析后的css引入style中
css-loader:可以解析import、require、@import、url引入的内容
less-loader:处理less语法
sass-loader:处理sass语法
postcss-loader:用postcss来处理css
file-loader:将以import和requrie()方式的资源解析成url,并且把文件放到输出的文件夹下
url-loader:和file-loader类似,处理图片文件,当文件在限制内,可以把文件转成base64,当超过限制,会自动交给file-loader处理
html-minify-loader:压缩html
babel-loader:用babel来将ES6转换成ES5
raw-loader:加载文件原始内容(utf-8)
常用的plugin
plugin和loader区别
loader 加载器
loader使得webpack能够处理除了js以外的文件(webpack默认能够处理js文件和json文件),比如css、图片、html等。其他类型loader举例:比如加载css(css-loader)、图片和字体文件(file-loader)、加载html(html-loader)、最新的js语法转成ES5(babel-loader)
//test定义了需要转换的文件或者文件类型,use定义了对该文件进行转换的loader类型,
该配置是告诉webpack,当遇到txt类型的文件,先使用raw-loader转换一下该文件在把他打包进bundle
module.exports = {
module: {
rules: [
{
test: /.txt$/,
use: 'raw-loader'
}
]
}
};
plugin 插件
plugin使得webpack的功能更加强大,plugin是组成webpack的关键,webpack自身也是基于plugin搭建的,webpack有丰富的内置插件和外置插件,同时要想使用,必须先引入插件,插件举例:new HotModuleReplacementPlugin(),new HtmlWebpackPlugin()
区别
loader是转换和加载特定类型的文件,所有loader的执行层面是单个源文件
plugin可以监听webpack处理过程中的关键事件,深入集成到webpack的编译器,可以说plugin的执行层面是整个构建过程。
实战体会
css-loader
css-loader会处理 import/require() @import/url 引入的内容。
// style.css
html,
body {
color: red;
}
// index.js
require("./assets/style.css");
css-loader处理之后导出的是
这个是没有办法满足需求的,所以需要借助style-loader
style-loader
1、创建style标签
2、 将css引入标签内
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"], // use底层是一个栈结构,后进先出,后面的loader先执行
},
],
},
};
最后css文件打包才会成功
file-loader
// index.js
const src = require("./test.jpg");
// 图片
const app = document.getElementById("imgContent");
const img = new Image();
img.src = src;
app.appendChild(img);
module: {
rules: [
{
test: /\.(jpg|png)$/,
loader: "file-loader",
options: {
esModule: false,
},
},
],
},
HtmlWebpackPlugin
官方回复简单创建一个html,服务器端访问,说白了,看不懂
每次打包完成,dist文件夹中都有一个index.html,其实这个就是HtmlWebpackPlugin功劳了
1、可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口
2、为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 主要多了这一行
module.exports = {
entry: "./src/index.js", // 入口文件
output: {
path: path.resolve(__dirname, "dist"), // 输出到哪个文件夹
filename: "output.js", // 输出的文件名
},
mode: "development", // 开发模式
module: {
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
},
plugins: [
// 主要多了这一行
new HtmlWebpackPlugin({
filename: "webpack.html", // 打包出去的文件名
template: "./src/index.html", // 依赖哪个文件做模板
}),
],
};
HotModuleReplacementPlugin
传送门webpack|体会webpack热更新和热更新原理🔥🔥
RemoveCommentsPlugin
封装一个去掉打包文件中注释的插件
思路:webpack提供了各种钩子函数,选择合适的钩子函数进行操作,emit:输出 asset 到 output 目录之前执行。这个钩子不会被复制到子编译器;在输出之前,把js文件中的注释去掉 ,返回去掉注释的文件
const pluginName = "RemoveCommentsPlugin";
module.exports = class RemoveCommentsPlugin {
apply(compiler) {
compiler.hooks.emit.tap(pluginName, (compilation) => {
//compilation是本次打包的执行上下文,它包含所有打包中产生的结果
// assets 包含打包要输出的所有文件
for (const name in compilation.assets) {
// 找到后缀是js文件
if (name.endsWith("js")) {
// source()可以获取文件内容
let content = compilation.assets[name].source();
// 匹配注释替换成空字段串
/**
* \/ => /
* \* => *
* {2,} 至少匹配2次
* \/ => /
* \s => 匹配任何空白字符
* ? => 匹配前面的子表达式零次或一次
* /g => 查找所有匹配项
* 综上所述这个正则的意思是 /**....../
*/
let noComments = content.replace(/\/\*{2,}\/\s?/g, "");
compilation.assets[name] = {
source: () => noComments,
// 去掉注释,文件大小肯定会有变化
size: () => noComments.length,
};
}
}
});
}
};
webapck引入自己封装的插件即可
const RemoveCommentsPlugin = require("./remove-comments-plugin");
module.exports = {
plugins: [
new RemoveCommentsPlugin(),
],
};
可以看一下打包前后的对比
webpack编译原理
初始化: 解析配置参数,主要是shell命令(npm install 类似的命令)和webpack.config.js文件的配置信息,进行融合,输出最终的配置信息
编译:
1.创建chunk
chunk 代码块 bundle 打包 modules 模块
chunk表示通过入口找到所有依赖的总称,简单总结来说,webpack的编译是基于入口的,通过入口文件找出每个文件的依赖,形成chunk(块),比方说:入口模块(./src/index.js)依赖一种模块(./src/a.js),a模块又依赖b模块(./src/b.js),通过一个入口模块分析依赖关系,可以找到三个模块,那么【index.js、a.js、b.js】这三个统称为一个chunk,根据入口模块,创建一个chunk,每一个块都有自己的名字。 chunk至少有2个属性{name:'main(默认)',id:'唯一编号'}
2.构建所有依赖模块
(1)先检查模块是否加载过,去模块记录表中查看
(2)如果有记录,说明加载过,如果没有,进行下一步
(3)读取模块中的内容
(4)对内容进行分析树形结构遍历,找到所有依赖,生成AST抽象语法树
(5)将分析出来的依赖加到dependencies数组中
(6)替换依赖函数
(7)将替换后的模块代码,保存到模块记录表中
(8)比如index.js模块处理完成,由于index.js依赖其它模块,所以递归循环保存在dependencies数组中的依赖,开始分析./src/a.js模块,假设a模块依赖./src/b.js模块,那么等a模块处理完成后,再处理a模块所依赖的b模块,再最后处理index模块所依赖的b模块,此时会发现b模块在处理a模块所依赖的b模块已经加载过了,那么index模块所依赖的b模块是不会进行下一步处理,直接结束。 最后的目的就是为了形成一个模块记录表
3.产生chunk assets
chunk中的模块记录表,webpack会根据配置为chunk生成一个资源列表,就是chunk assets
4.合并chunk assets
将多个chunk assets合成一个,并生成一个总的hash ,将生成的总的assets,通过nodejs的fs模块,生成相应的文件
总结
当敲下webapck打包命令时,文件开始初始化,各个参数进行融合,形成最终的配置对象,然后把配置对象交给编译器进行编译,通过入口模块找到互相依赖的模块形成模块列表,接下来webpack根据配置为chunk生成资源列表,然后将每一个chunk生成的资源合并成一个完整的资源,并且生成一个完成的hash值,最终根据完成的资源列表输出到文件。
思考题
思考💡:vue.config.js 和webpack.config.js 的关系?区别?
webpack.config.js是webpack的配置文件,vue和react都可以使用
vue.config.js是基于vue的webpack的配置文件
vue-cli 2.0时代,webpack的配置文件写在config/index.js 文件中
vue-cli 3.0时代,没有了config文件夹,vue.config.js写在根目录下
vue-cli3创建的时候并不会自动创建vue.config.js,因为这个是可选项,所以一般都是修改webpack的时候才会自己创建一个vue.config.js
再然后因为vue-cli3内部高度集成了webpack,一般来说使用者不需要再去webpack做了什么,所以没有暴露webpack的配置文件,但你依然可以创建vue.config.js去修改默认的webpack
思考💡:vue和vue-cli的关系区别?
vue是一个渐进式框架,可以在html引入vue.js 使用vue的数据绑定,也可以基于vue使用vue的全家桶,构建一个前端项目
vue-cli是一个脚手架,也就是一个代码生成器,vue-cli = vue + 一堆js 代码
vue-cli 4.5以下对应的是vue2, 4.5以上对应的是vue3
思考💡:vue-cli3.0高度集成的webpack怎么理解?
vue-cli3.0 里面自带webpack,已经具有一些基本的loader,可以通过vue inspect > output.js查看webpack的配置,这个命令会在根目录下生成一个js文件
简单理解就是vue-cli提供了一个inspect命令,可以把webpack配置、包括链式访问规则和插件的提示打印到一个根目录下的output.js文件中,我们只需要在项目根目录下命令行中输入该命令,即可在根目录下得到output.js,该文件中即是webpack的所有配置,方便我们参考,但是仅仅是方便我们看的,我们对其进行更改不会生效。
思考💡:如果需要配置webpack,怎么做 ?
调整 webpack 配置最简单的方式就是在 vue.config.js(项目中没有的话,在根目录下创建一个)中的 configureWebpack 选项提供一个对象
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
//你想加的配置写在下面,以一个对象的形式写到参数里。
//例如:new MyWebpackPlugin({template: './src/index.html'})
//随便举的例子,实际开发请写自己需要加的配置!
new MyAwesomeWebpackPlugin()
]
}
}
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
参考👀
前端性能优化-Vue-cli 3.0项目中webpack配置