一、webpack是什么
webpack是前端资源模块化管理和打包工具。它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其打包为合适的格式以供浏览器使用。
二、为什么要使用webpack
在很久以前,纯 HTML + CSS + JavaScript 开发,不需要打包工具,直接把写好的HTML 用浏览器打开就能显示出一个网页。
随着时代的发展,前端出现了很多新技术:
1.前端人不再用 css,而是用 Less、Sass、Stylus 这样的预处理语言;
2.前端不再通过JS或者JQ修改DOM,而是使用各种框架,比如Vue,React;
3.前端人开始尝试语言的新特性,比如 ES6、ES7,现在流行起来使用TypeScript,可是这些新特性并不适用于所有浏览器;
4.为了让代码体积更小,前端人开始对 CSS、JavaScript、HTML、图片等进行压缩处理;
5.项目越来越大,前端人开始追求模块化开发;
从代码的角度来说,webpack 能够把一种代码转换成另一种浏览器能够识别的代码。
三、核心概念
1.入口(entry)
webpack将创建所有应用程序依赖关系图表。入口起点告诉webpack从哪里开始,并遵循着依赖关系图表知道打包什么。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖。
2.出口(output)
将所有资源打包在一起后,告诉webpack将打包资源输出到什么地方,以及如何命名这些文件。
3.加载器(loader)
webpack 只能理解 JavaScript 和 JSON 文件,loader 用于转换webpack不能读取的文件类型。它相当于一个语言的翻译官,比如可以把 less 翻译成 css,把 TypeScript 翻译成 js。
4.插件(plugins)
webpack的插件系统极其强大和可定制,用于执行范围更广的任务。plugin可以监听 webpack 在打包过程中发出的事件,比如 webpack 处理 index.js 这个文件时,会发出事件,plugin 可以接收到这个事件,对 index.js 这个文件进行处理。
5.模式(mode)
打包模式,支持 development(开发模式,方便调试) 和 production(生产模式,会做各种优化处理,比如文件压缩)。
webpack的简单配置如下(webpack.config.js):
// path 为 node 提供的模块,可直接使用
const path = require('path');
// 通过 npm 安装,主要作用自动生成主页index.html
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 设置打包方式,支持 development 和 production
mode: 'development',
// 打包入口文件
entry: './src/index.js',
// 最终打包输出配置
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
},
// 配置 loader,不同文件应用不同的 loader
module: {
rules: [
{
// vue 文件采用 vue-loader 来处理
test: /\.vue$/,
use: ['vue-loader']
},
{
// css 文件,先采用 css-loader 处理,
// 再使用 style-loader 处理,注意顺序
test: /\.css$/,
//css-loader允许在js中import一个css文件,会将css文件当成一个模块引入到js文件中
//style-loader能够在需要载入的html中创建一个<style></style>标签,标签里的内容就是CSS内容
use: ['style-loader', 'css-loader']
}
]
},
//配置插件,为了使用插件,需要require它们,并且添加到plugins数组
plugins: [
//使用new来创建插件的实例
new HtmlWebpackPlugin({
// <%= htmlWebpackPlugin.options.title %>模板语法,模板可使用
title: '自定义',
filename: 'custom.html',
// 指定模板文件
template: 'public/index.html'
})
]
}
四、模块热替换(HMR)
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
HMR是 webpack 提供的最有用的功能之一,此功能可以很大程度提高开发效率。我们要做的就是更新 webpack-dev-server 配置, 然后使用 webpack 内置的 HMR 插件。
module.exports = {
//...
devServer: {
//如果编译报错,会抛出错误,你重新改成正确的,这个时候又会触发重新编译,整个浏览器会重新刷新!
hot: true,
}
}
五、如何写一个webpack插件
一个 js 命名函数。
在原型链上存在一个 apply 方法。
为该插件指定一个 Webpack 的事件钩子函数。
使用 Webpack 内部的实例对象(Compiler 或者 Compilation)具有的属性或者方法。
如果是异步插件,当功能完成以后,需要执行 Webpack 的回调函数。
Compiler 对象: 该对象在 Webpack 启动的时候会被创建,同时该对象也会被传入一些可控的配置,如 Options、Loaders、Plugins。
Compilation 对象:该对象表示本次打包的模块、编译的资源、文件改变和监听的依赖文件的状态。
下面我们写一个自定义插件commentPlugin.js,在打包时去除文件中的注释。
class CommentPlugin {
//Webpack 要求插件必须是一个函数或者是一个包含 apply 方法的类
//apply()会在 Webpack 启动时被调用,接收 compiler 对象参数,这个对象是 Webpack 工作过程中最核心的对象,包含了此次构建的所有配置信息
apply (compiler) {
//通过 compiler 对象的 hooks 属性访问到 emit 钩子,再通过 tap 方法注册一个钩子函数
compiler.hooks.emit.tap('CommentPlugin', compilation => {
// compilation => 可以理解为此次打包的上下文
// compilation对象中的 assets 属性获取即将写入输出目录的资源文件信息
for (const name in compilation.assets) {
if (name.endsWith('.js')) {
//文件内容需要通过遍历的值对象中的 source 方法获取
const contents = compilation.assets[name].source()
const noComments = contents.replace(/\/\*{2,}\/\s?/g, '')
compilation.assets[name] = {
source: () => noComments,
size: () => noComments.length
}
}
}
})
}
}
module.exports=CommentPlugin;
在webpack.config.js文件中配置CommentsPlugin插件:
const CommentsPlugin = require("./comments-plugin.js");
module.exports = {
//...其他配置信息
plugins: [
//使用时通过new操作符创建一个实例对象去使用这个插件
new CommentsPlugin(),
],
};
六、常用插件
见 https://blog.csdn.net/weixin_43720095/article/details/107720200
七、总结
webpack在打包的时候,需要通过 webpack.config.js 这个文件告诉它,打包先从哪个文件开始,比如先从 index.js 文件开始,index.js 文件又引用了 a.js 和 b.js 文件,那么 webpack 在处理的时候会把这些文件合并到一个文件中。最终需要一个 output(出口)告诉 webpack 把最终生成的文件要放到哪个地方。不同的文件要通过相应的loader来处理成webpack能理解的模块,插件主要用来扩展 webpack 的功能。总的来说,webpack 就是一个前端打包工具,它拥有非常多的小工具,可以把代码从开发模式变成线上模式,最终交给浏览器渲染。