脉络
-
webpack 内部模块机制 webpack 构建原理 tapable tapable 构建流程管控
-
抽象语法树 状态机、正则匹配 AST 代码编译流程 手写一个
-
bundle、bundleless tree shaking
gulp vs webpack gulp 根据配置项构建 流控制 webpack根据模块引用路径构建依赖
- 变量 方法 名称 冲突 ==> 模块(作用域)隔离 ==> function(){} => 闭包
- 产生依赖之后,保证模块执行顺序 数组 对webpack模块的执行顺序,就是对依赖树进行深度优先遍历 函数执行的过程就是一个深度优先遍历的过程
// module仓库
(function (modules) {
/**
* 1. 按照顺序执行模块
* 2. 存值
* 3. 执行
* 4. 取值
*/
var installedModules = {}; // 存值
function require(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId];
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {} // 当前模块存的值
};
modules[moduleId].call(module.exports, module, require);
module.l = true;
return module.exports;
}
return require(0);
})([
function b(module, require) {
// a.js
// import { name } from 'b.js';
var name = require(1);
function getName() {
console.log(name);
}
getName();
},
function a(module, require) {
// b.js
var name = 'Nolan';
// export name
module.exports = name;
},
]);
webpack config ==>
- 初始化配置 内部默认配置 合并用户的配置 ==> webpack config
- 初始化 文件读写 分析路径 配置webpack wepback 内部 管理构建流程
不同的阶段 做不同的事情 通知系统 tapable 发出通知
webpack内部 插件 tapable 管理整个webpack的执行流程
- 不同的电话 不同的处理逻辑 串行 异步 并行
- 不同阶段 执行不同通知
- 执行任务 tap => 订阅对应阶段的过程 call => 调用对应阶段的过程 编译 性能和控制流程的优化
const {
SyncHook,
SyncBailHook, // return 返回 直接结束
SyncWaterfallHook, // 流式串形
SyncLoopHook, // 循环逻辑
AsyncParalleHook, // 异步并行执行回调
AsyncParallelBaiHook, // 异步并行执行回调,return返回直接结束
AsyncSeriesHook, // 异步按顺序执行
AsyncSeriesBailHook, // 异步按顺序执行 return 返回直接结束
AsyncSeriesAterfallHook, // 异步按顺序执行 参数传递到下一个
AsyncHook,
} = require('tapable') // 25.1k(gzipped 7.1k)
let hooks = new SyncHook();
hooks.tap('WarningLampPlugin'), () => {
console.log('brake plugin');
});
hooks.tap({
name: 'warpPlufin',
// stage: -1 // 控制顺序,正负数表示执行前后次序
}, () => {
console.log('brake plugin1');
});
hooks.tap({
name: 'warpPlufin2',
}, () => {
console.log('brake, plugin12');
});
hook.call();
一、环境差异
- 开发环境
- 需要生成sourcemap文件
- 需要打印debug信息
- 需要live reload 或者 hot reload的功能
- 生产环境
- 可能需要分离css成单独文件、以便多个文件共享同一个css文件
- 需要压缩HTML/CSS/JS代码
- 需要压缩图片
二、区分环境
- --mode 用来设置模块内的 process.env.NODE_ENV
- --env 用来设置webpack配置文件的函数参数
- cross-env用来设置node环境的process.env.NODE_ENV
- dotenv 可以按需加载不同的环境变量文件
- define-plugin 用来配置在 编译时候 用的 全局变量
Prefetch 使用预先拉取,表示该模块可能会被用到,浏览器在空闲时间下下载该模块 import(/* webpackCunkName: 'xx', webpackPrefetch: true */'xx.js').then() --->
Preload 预加载
-
此资源肯定会用到,优先较高,需要体检获取,要慎用!
-
preload通常用与本页要用到的关键资源,包括关键js、字体、css文件
-
preload将会把资源的下载顺序权重提高,使得关键数据提前下载好,优化页面打开速度。
-
在资源上添加预先加载的注释,你指明该模块需要立即被使用
-
在一个资源的加载优先级被分为五个级别,分别是:
- Hightest 最高
- High 高
- Medium 中等
- Low 低
- Lowest 最低
-
异步、延迟插入的脚本(无论在什么位置)在网络上优先级中是Low
-
module: 就是js的模块化webpack支持commonJS/ES6等模块化规范,简单来说就是你通过import语句引入的代码
-
chunk: chunk是webpack根据功能拆分出来的,包括三种情况
- 你的项目入口(entry)
- 通过import()动态引入的代码
- 通过splitChunks拆分出来的代码
-
bundle:bundle是webpack大饱之后的各个文件,一般就是和chunk是一对一的关系,bundle就是对chunk进行编译压缩打包等处理之后的产出
三、Babel
-
预设 @babel/preset-env
.babelrc { "presets": ["@babel/preset-env"], }
.browserslistrc last 2 Chrome versions
-
polyfill
- @babel/preset-env 默认只转换新的javascript语法,而不专换新的API,比如Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise等全局对象,以及一些在全局对象上的方法(比如Object.assign)都不转码。
- 他会通过全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find方法,引入polyfill,我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染。
- V7.4.0版本开始, @babel/polyfill 已经废弃
use: { loader: 'babel-loader', options: { presets: [ ["@babel/preset-env"], { useBuiltIns: 'entry', // true 表示加载全部 corejs: 3, targets: "last 2 Chrome versions" } ] }, }
corejs如果是3,需要 在index.js中引入 import 'core-js/stable'; import 'regenerator-runtime/runtime' 按需引入: useBuiltIns: 'usage'
-
babel-runtime 不污染全局环境,适合开发共用库,但是需要手工引入
-
@babel/plugin-transform-runtime
plugins: [ [ "@babel/plugin-transform-runtime", { corejs: 3, // 如果用到了Promise,会自动引入对应的polyfill helpers: false, // 移除内联的babel helpers 并自动引入babel-runtime/helpers regenerator: true } ] ]
插件先执行预设后执行 插件是从前往后 预设是从后往前
- sourcemap [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map: 外部 错误代码的准确信息和源代码的错误位置 inline-source-map: 内联(只生成一个) 与上面一致 hideen-source-map: 外部 错误代码的信息,但是没有错误位置,不能追踪到源代码的错误,只能提示构建后代码位置 eval-source-map: 内联(每一个文件都生成,都在evallimain1) 与source-map一致 nosources-source-map: 外部 错误代码信息有,没有任何源代码信息 cheap-source-map: 外部 提示错误信息,只精确到行 cheap-module-source-map: 外部 提示错误信息,只精确到行, module会将loader的source map加入
内联 和 外部的区别: 1.外部生成了文件,内联没有。 2.内联更快
-
开发环境: 速度快、调试更友好 速度: eval>inline>cheap>... eval-cheap-source-map eval-source-map 调试更友好 source-map cheap-module-source-map cheap-source-map
结论: eval-source-map
-
生产环境: 源代码要不要隐藏?调试要不要更友好 内联会让代码提价变大 nosources-source-map 全部隐藏 hidden-source-map 只隐藏源代码 --> source-map/cheap-module-source-map