PS:根据B站学习视频 :【2020新课程】Webpack从原理到实战完整版-深入浅出,简单易学,前端工程师必学经典内容!学习整理,如有错误,欢迎随时指正,互相学习。
重点:
- 理解前端模块化
- 理解 webpack 打包的核心思路
- 理解 webpack 中的 “关键人物”
前端模块化
模块化的特点:
- 作用域封装
- 复用性
- 解除耦合
从作用域 => 命名空间 => 模块化的过程理解模块化。
作用域概念:决定了代码中变量、函数、对象等其他资源的可见性。
当我们把 js 逻辑代码按功能分成不同的 js 文件时,在 HTML 中引入,后引入的文件就可以访问前者文件的变量、对象等资源,这样可能会造成命名冲突,值覆盖等不可控的结果。
// a.js 文件
var a = 1;
// b.js 文件
var a = 2;
// c.js 文件
console.log(a);// 2
所以我们利用作用域的效果,把每个文件中的 js 代码封装到一个对象中,这样我们在需要取值的时候就可以分离开。
// a.js 文件
var moduleA = {
a: 1
};
// b.js 文件
var moduleB = {
b: 2
};
// c.js 文件
console.log(moduleA.a);// 1
但是当我们需要存储账号密码这种数据时,这样做的话安全性不高,并且容易被其他文件更改,我们并不希望其他文件能访问到我特殊数据,只希望执行我们提供给他的方法。所以我们应该封装到函数中并且返回,这样我们既达到了隐藏特殊数据的效果又达到了功能的复用,并且兼顾了命名冲突产生的可能性。
// a.js 文件
var user = (function() {
var username = 'abc';
var password = 123;
return {
login: function() {
// ...逻辑代码
console.log( username + '登录成功');
}
}
})();
// b.js 文件
user.login();// adc登录成功
但是声明变量是不能通过 delete 操作符删除(还可能更占用内存?),采用更规范的写法:
// a.js 文件
(function(window) {
var username = 'abc';
var password = 123;
function login() {
// ...逻辑代码
console.log( username + '登录成功');
}
window.moduleA = {login};// 挂载到 window 对象上
})(window);// 传入 window
// b.js 文件
module.login();// adc登录成功
从立即执行函数为起点的模块化实现方案
AMD
Asynchronous Module Defineition (异步模块定义)
优点:
- 显示出当前模块所依赖的其他模块有哪些。
- 不用绑定到全局对象上,更近一步增加模块安全性,不担心在其他地方被篡改。
// 求和模块的定义
// define(当前函数ID,当前模块依赖,函数/对象) 函数:利用函数的返回值,导出模块的接口 对象:对象本身就是模块导出值
define('getSum',['math'],function(math) {
return function(a,b) {
console.log('sum:'+ math.sum(a,b));
}
});
CommonJS
最初为了解决服务端的模块化标准,后 Node.js 采用并实现其规范。
在 CommonJS 中,每个文件就是一个模块,并且拥有他自己的作用域和上下文,模块的依赖通过 require 函数引入,通过 exports 将其导出。
同 AMD 一样,强调依赖的显示,方便维护复杂模块时,不用操心各个模块的引入顺序。
// 引入
const math = require('./math');
// 导出
exports.getNum = function(a,b) {
return a + b;
};
ES6 Module
// import 引入
import math from './math';
// export 导出
export function sum(a,b) {
return a + b;
}
npm
- 理解包管理器
- 熟悉 npm 核心特性
- 理解 npm "仓库"与"依赖"的概念
- 理解 npm “语义化版本”
- 掌握使用 npm 自定义工程脚本的方法
package.json 属性说明
- name - 包名
- version - 包的版本号。
- description - 包的描述。
- homepage - 包的官网URL。
- author - 包的作者,它的值是你在 https://npmjs.org 网站的有效账户名,遵循“账户名<邮件>”的规则,例如:zhangsan zhangsan@163.com
- contributors - 包的其他贡献者。
- dependencies / devDependencies - 生产 / 开发 环境依赖包列表。它们将会被安装在 node_module 目录下。
- repository - 包代码的Repo信息,包括type和URL,type可以是git或svn,URL则是包的Repo地址。
- main - main 字段指定了程序的主入口文件,require(‘moduleName’) 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
- keywords - 关键字
- script - 自定义命令
“仓库”
是一个遵循 npm 特定包规范的站点,提供 API 进行下载、上传、获取包信息、管理用户账号等操作。
“依赖”
在开发/生产中所需要用到的外部资源被称为依赖。
“语义化版本”
优势:方便 npm 包的发布者很便捷的把更新的小版本推送到使用者手里。
- ^version:中版本和小版本
- ~version:小版本
- version:特定版本
npm install 的过程
- 寻找包版本信息文件 (package.json),依赖这个文件进行安装
- 查看 package.json 中的依赖,并检查项目中其他的版本信息文件
- 如果发现新包,就更新版本信息文件
webpack 的打包过程
- 从入口文件开始,分析整个应用的依赖树
- 将每个依赖模块包装起来,放到一个数组中等待调用
- 实现模块加载的方法,并把他们放到模块执行的环境中,确保模块间可以互相调用
- 把执行入口文件的逻辑放在一个函数表达式中,并立即执行这个函数
webpack 核心特性
- 使用 webpack 构建简单的工程
- 了解 webpack 配置文件
- 掌握“一切皆模块与loaders”的思想
- 理解 webpack 中的“关键人物”
webpack 构建工程
- 掌握 babel 的用法,理解 babel 的原理
webpack 性能调优
主要从三个方面入手:
- 打包结果优化
- 构建过程优化
- Tree - Shaking
TerserPlugin
const TerserPlugin = require("Terser-webpack-plugin");
optimization:{
minimizer: [
new TerserPlugin({
// 加快构建速度
parallel: true, // 开启多线程处理,加快构建速度
terserOptions: {
compress: { // 剔除发布时没有用的代码的配置
unused:true,
// 剔除 debugger
drop_Debugger: true,
// 剔除 console
drop_console: true,
dead_code: true
}
}
})
]
},
BundleAnalyzerPlugin 打包文件分析工具
通过使用 webpack-bundle-analyzer 可以在打包完成后看到项目各模块的大小,以便于按需优化。
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
plugins: [
new BundleAnalyzerPlugin()
],
happyPack
相关文章:
Happypack 只是作用在 loader 上,使用多个进程同时对文件进行编译。
npm install happypack --save-dev
webpack.config.js
const os = require('os')
const HappyPack = require('happypack');
// 根据 CPU 的数量创建线程池
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
plugins: [
new HappyPack({
id: 'jsx',
threadPool: happyThreadPool,
// 使用时应注意该 loader 是否支持 happyPack
loaders: ['babel-loader']
})
],
thread-loader
使用时,需要将此 loader 放置在其他所有 loader 之前。会将该 loader 之后的 loader 放在一个独立的 worker 池中运行,达到多线程构建的目的。
npm install --save-dev thread-loader
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve('src'),
use: [
"thread-loader",
// 耗时的 loader (例如 babel-loader)
],
},
],
},
};
个人总结
个人的一点小总结,不足以作为参考依据。
- 在测试时,使用 happypack、thread-loader 对项目构建速度提升不明显,甚至可能减缓构建速度,所以在小型项目中没有必要使用。
- 都是通过创建线程池,本质上是通过多进程实现打包加速。进程、线程和协程之间的区别和联系
tree-shaking
DCE 的一种新的实现方式。
本质:消除无用的 js 代码。
理解:通过摇晃一个树,摒弃无用的枯叶,只留下茂盛的树叶和果实。
作用:例如 tool.js 中封装了很多公共方法,其中 A 方法只引入没调用,那么在 development 环境打包结果中你能看到 A 方法的打包结果,但在 production 环境打包结果中就移除了 A 方法。
PS:如有错误,请多多指正!