模块化就是根据文件划分的形式,将每个功能及其相关的数据放在一个JS
中,每个JS
文件就是一个单独的模块,在需要时再去引入。
这样的模块化有一个缺点就是:模块都是在全局中,容易造成全局变量污染,命名冲突;而且模块与模块之间并没有依赖关系,维护困难。
webpack
是一个前端应用程序静态打包工具,它的目标是实现前端项目的模块化,皆在更高效地管理和维护项目中的每一个资源
静态模块是指开发阶段可以被webpack直接引用的资源(可以直接被获取打包进bundle.js
的资源)。当webpack
处理项目时,它会在内部构建一个依赖图,此依赖图对应项目所需的每个模块,并生成一个或多个bundle
webpack
的构建流程是一个串行的过程,它的工作流程就是将各个插件串联起来。
webpack
的构建流程分为以下三步:
Shell
语句中读取参数、合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数。Entry
触发,针对每个Module
串行调用对应的Loader
编译文件内容,再找到该Module
依赖的Module
,递归地进行编译处理。Module
组合成Chunk
,把Chunk
转换成文件,输出到文件系统var path = require('path');
var node_modules = path.resolve(__dirname, 'node_modules');
var pathToReact = path.resolve(node_modules, 'react/dist/react.min.js');
module.exports = {
// 入口文件,是模块构建的起点,同时每一个入口文件对应最后生成的一个 chunk。
entry: './path/to/my/entry/file.js',
// 文件路径指向(可加快打包过程)。
resolve: {
alias: {
'react': pathToReact
}
},
// 生成文件,是模块构建的终点,包括输出文件与输出路径。
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
},
// 这里配置了处理各模块的 loader ,包括 css 预处理 loader ,es6 编译 loader,图片处理 loader。
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}
],
noParse: [pathToReact]
},
// webpack 各插件对象,在 webpack 的事件流中执行对应的方法。
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
Shell
命令中读取、合并参数,写到Options
对象中Compiler
对象,加载所有配置的插件,调用Compiler
的run
方法来启动webpack
的构建流程entry
,确定入口文件Module
调用对应的Loader
编译文件内容,再找到该Module
依赖的Module
,递归进行编译,得到每个Module
之间的依赖关系Chunk
,再把每个Chunk
转换成一个单独的文件加入到输出列表在上述过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果。
对于不同类型的资源,webpack
有对应模块加载器loader
。
css-loader
:加载CSS
。只负责加载CSS
文件,如果样式生效,还需要style-loader
配合style-loader
:将css-loader生成的内容用style
标签注入到页面的head
中module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
},
{ loader: 'sass-loader' }
]
}
]
}
};
多个`loader`的加载方式是从右到左,从下到上
less-loader
/sass-loader
:解析less
、sass
为css
ts-loader
:将TS
转换为JS
babel-laoder
:将ES6
解析为ES5
eslint-loader
:检查JS
代码规范file-loader
:将文件输出到指定目录,并将该资源的地址返回url-loader
:与file-loader
类似,但遇到图片格式的文件可以选择性的把图片转成base64格式的字符串,并打包到js中。对小体积的图片合适,对大图片不合适。provider-plugin
:将指定的模块暴露到全局,使用时就不需要再import或者requirehtml-webpack-plugin
:在打包结束后,自动生成一个html
文件,将打包生成的js
模块引入到该html
中hot-module-replacement-plugin
:热更新。通过监听本地代码的变化,自动构建,自动刷新页面uglifyjs-webpack-plugin
:压缩JS代码,去掉console.log()、注释等代码,减少文件的字节数,提升构建效率mini-css-extract-plugin
:提取CSS,实现按需加载webpack-parallel-uglify-plugin
:多核压缩,提升压缩速度loader
运行在打包文件之前。plugin
在整个编译周期内都起作用。webpack
会在特定的时间点广播特定的事件,plugin
会监听这些事件,对感兴趣的事件进行执行。plugin
还可以调用wepack
提供的API
来改变webpack
的执行结果。loader
是文件加载器,能够加载资源文件,并对这些文件进行处理plugin
扩展了webpack
的功能,例如打包优化、压缩代码等module
:webpack可以解析的模块,多个module
之间存在依赖关系chunk
:包含一个或多个module
,代码分割加载的就是chunk
bundle
:包含一个或多个chunk
,是webpack
最终打包出来的代码文件我们写的源文件module
传送到webpack
进行打包时,webpack
会根据module
之间的依赖关系生成chunk
文件,webpack
会对这些chunk
文件进行操作,比如实现按需加载。webpack
处理好chunk后最终输出bundle
文件,bundle
由一个或多个chunk
文件构成,bundle
包含了经过加载和编译最终生成的源文件,可以直接在浏览器中运行。
AST
,分为两个阶段,词法分析和语法分析
tokens流
tokens流
转换成AST
@babel/traverse
方法对AST
进行遍历,完成对AST
节点的替换、删除或增加操作,返回转换后的AST
@babel/generator
将AST转换成字符串形式的代码Tree-Shaking
主要从以下三个方面进行优化:
首先,Tree-Shaking
分为以下两步:
Make
阶段:收集导出的变量并记录到模块依赖图**ModuleGraph
**中Seal
阶段:遍历**ModuleGraph
**标记模块导出有没有被使用Terser
删除掉没有被用到的导出语句webpack Complier
对象将源代码与HMR Runtime
一起编译成bundle
文件,HMR Runtime
是一个socket
服务器,会被注入到浏览器,更新文件的变化websocket
连接,客户端运行的HRM Runtime
,服务器端运行的是HRM Server
webpack
监听文件的变化,如果发生变化,重新编译打包后再推送给客户端,编译生成唯一的hash
值,这个hash
值用来作为下一次热更新的标识manifest
(包含了hash
和chunkId
,用来说明变化的内容)和chunk.js
hash
ajax
请求,根据hash
获取包含变化内容的manifest
文件manifest
替换掉旧的内容实现局部更新UglifyJsPlugin
和 ParallelUglifyPlugin
来压缩JS
⽂件css-minimizer-webpack-plugin
压缩css
CDN
上对应的路径,提高对静态资源的访问速度chunk
分为更小的chunk
,对css
提取到单独的文件中,控制资源加载的优先级,实现按需加载,提高首屏渲染速度import
加载,将import
放到异步回调函数中,异步函数执行后再去加载webpack
的打包webpack-uglify-parallel
来提升 uglifyPlugin
的压缩速度。 原理上 webpack-uglify-parallel
采⽤了多核并⾏压缩来提升压缩速度Tree-shaking
来剔除多余代码webpack.cache
、happypack.cache
、babel-loader.cacheDirctory
都可以提高二次构建的速度hash
一般结合CDN
缓存来使用,通过webpack
构建之后,生成对应文件名自动带上对应的MD5值
,如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML
引用的URL地址
也会发生改变,触发CDN回源
,进行更新CDN节点
缓存。
chunkhash
是根据chunk
来生成hash
值的,chunkhash
实现了模块的异步加载。
chunkhash
存在一个问题,就是当在一个JS
中引入CSS
,编译后它们的hash
是相同的,而且只要js
发生改变 ,关联css
的hash
也会改变。
这个时候可以使用mini-css-extract-plugin
里的contenthash
值,保证css
所处的模块里就算其他文件内容改变,只要css
内容不变,那么不会重复构建。
CSS Modules是以模块化的形式引入CSS,使用CSS Modules可以防止样式覆盖,防止CSS命名冲突,做到样式隔离。
{
test: /\.css$/,
loaders: [
'style-loader',
'css-loader?modules'
}
.color {
color: red;
}
// 整体引入
import modules from './modules.css'
// 按需引入
import { color } from './modules.css'
class Cssmodules extends React.Component {
render() {
return (
基本用法
这是整体引入的红色文字
这是通过按需引入红色文字
)
}
}
作用域分为局部作用域和全局作用域。像上面那样不管是modules.color
还是color
都属于局部作用。在DOM
中,对应的类名会变成一个唯一的hash
字符串,只有在当前作用域才有效,这也是一个模块化的体现。
如果想要申明全局作用域,需要在前面加上:global
关键字