webpack是啥?
静态模块打包工具。
会从入口文件开始,递归的分析模块以及依赖模块,然后进行模块路径解析,loader处理,plugin处理等等,最终产出静态资源:js
css
images
fonts
等等。
配置文件概述
一切从webpack.config.js
配置文件开始。
webpack
早期的版本,模糊不清的文档被大家所诟病,好在目前webpack4
的文档已经非常清晰,而且社区生态也是非常的活跃和强大。
module.exports = {
mode: xxx,
// 入口
entry:xxx,
output: xxx,
module: {
rules: []
},
plugins: [],
resolve: {
modules: [],
extensions: [],
alias: [],
},
devtool: xxx,
devServer: {},
....
}
特点
- 配置型
- 所有的静态资源都可以视为模块
- 支持HMR
- 扩展性良好: 可以通过自定义
loader
和plugins
实现特定的业务需求。 - 目前为止,社区活跃、强大,很多前端技术栈体系都是依靠webpack来组织和构建,比如流行的
react
,vue
等。
注意点
-
webpack
已经内置支持了各种的模块记载机制(es2015、AMD、commonjs等),其内部实现 会将代码进行transpile
,实现了旧版本浏览器也可以执行。
常用的点
- 一般使用
webpack-dev-server
来搭建开发环境,默认提供了livereload
功能。 -
webpack-dev-server
内部实现依赖的webpack-dev-middleware
-
webpack-dev-server
可以通过配置hot
配置项来开启HMR
特性, 但是具体的实现需要自己来,好在强大的社区已经为我们实现了相关资源模块的热更新替换, 比如css-loader
就为我们提供了css样式的HMR
功能。
webpack编译过程
- 收集配置项
- 实例化
Compiler
对象,调用run 或者是 watch
开始编译 -
Compiler
继承自Tabable
类,Tabable
类实现了订阅发布模式。 - 各个
hooks
就是事件列表,tap tapPromise tapAsync
是添加事件,call promise callAsync
就是执行事件列表。
hooks -
webpack
内部不同的hooks
部署到webpack
编译的各个阶段。 - 所以我们实现自己插件的时候,定义了一个类,有一个
apply
方法,在apply
方法中对编译的不同阶段 添加处理流程。
插件
webpack 自己也是基于plugin的机制进行实现。
-
plugin
的作用 - 实现自己的
plugin
loader
-
loader
的作用 -
实现自己的
loader
所谓 loader 只是一个导出为函数的 JavaScript 模块。内部 webpack会调用它。
const {getOptions} = require('loader-utils');
const schemaUtils = require('schema-utils');
module.exports = function (source) {
const options = getOptions(this);
console.log(options, "options");
console.log('-----------');
console.log(source);
schemaUtils(schema, options, 'Example Loader');
// Apply some transformations to the source...
return source;
}
学习代码的一种最好的方式就是学习开源的优秀lib,所以我clone
了 file-loader
css-loader
style-loader
我们常用的几个loader
来学习。
先看下file-loader
:
import path from 'path';
// 引入常用的loader辅助工具
import loaderUtils from 'loader-utils';
// 引入loader 配置项校验工具
import validateOptions from 'schema-utils';
// 配置项的校验格式要求
import schema from './options.json';
// 暴露一个函数 给webpack使用
export default function loader(content) {
// 获取到配置loader的配置项
const options = loaderUtils.getOptions(this) || {};
// 校验配置项
validateOptions(schema, options, {
name: 'File Loader',
baseDataPath: 'options',
});
const context = options.context || this.rootContext;
// 调用了loader-utils的辅助方法
const url = loaderUtils.interpolateName(
this,
options.name || '[contenthash].[ext]',
{
context,
content,
regExp: options.regExp,
}
);
let outputPath = url;
if (options.outputPath) {
if (typeof options.outputPath === 'function') {
outputPath = options.outputPath(url, this.resourcePath, context);
} else {
outputPath = path.posix.join(options.outputPath, url);
}
}
let publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`;
if (options.publicPath) {
if (typeof options.publicPath === 'function') {
publicPath = options.publicPath(url, this.resourcePath, context);
} else {
publicPath = `${
options.publicPath.endsWith('/')
? options.publicPath
: `${options.publicPath}/`
}${url}`;
}
publicPath = JSON.stringify(publicPath);
}
if (options.postTransformPublicPath) {
publicPath = options.postTransformPublicPath(publicPath);
}
// 产出文件
if (typeof options.emitFile === 'undefined' || options.emitFile) {
this.emitFile(outputPath, content);
}
const esModule =
typeof options.esModule !== 'undefined' ? options.esModule : true;
// 返回路径
return `${esModule ? 'export default' : 'module.exports ='} ${publicPath};`;
}
export const raw = true;
网友评论