npm init -yyarn add @babel/core @babel/parser @babel/preset-env @babel/traverse./src/index.js
import add from "./add.js";
console.log(add(1 , 2))
./src/add.js
export default (a, b) => a + b
./index.js
const fs = require("fs");
const path = require("path");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const babel = require("@babel/core");
function getModuleInfo(file) {
// 读取文件
const body = fs.readFileSync(file, "utf-8");
// 转化AST语法树
const ast = parser.parse(body, {
sourceType: "module", //表示我们要解析的是ES模块
});
// 依赖收集
const deps = {};
traverse(ast, {
ImportDeclaration({ node }) {
const dirname = path.dirname(file);
const abspath = "./" + path.join(dirname, node.source.value);
// console.log('abs', abspath);
deps[node.source.value] = abspath;
},
});
// ES6转成ES5
const { code } = babel.transformFromAst(ast, null, {
presets: ["@babel/preset-env"],
});
const moduleInfo = { file, deps, code };
return moduleInfo;
}
// const info = getModuleInfo("./src/index.js");
// console.log("info:", info);
// 模块解析
function parseModules(file) {
const entry = getModuleInfo(file);
const temp = [entry];
const depsGraph = {};
getDeps(temp, entry);
temp.forEach((moduleInfo) => {
depsGraph[moduleInfo.file] = {
deps: moduleInfo.deps,
code: moduleInfo.code,
};
});
return depsGraph;
}
// 获取依赖
function getDeps(temp, { deps }) {
Object.keys(deps).forEach((key) => {
const child = getModuleInfo(deps[key]);
temp.push(child);
getDeps(temp, child);
});
}
// 打包
function bundle(file) {
const depsGraph = JSON.stringify(parseModules(file));
return `(function (graph) {
function require(file) {
function absRequire(relPath) {
return require(graph[file].deps[relPath])
}
var exports = {};
(function (require,exports,code) {
eval(code)
})(absRequire,exports,graph[file].code)
return exports
}
require('${file}')
})(${depsGraph})`;
}
const content = bundle("./src/index.js")
console.log('content',content);
// 生成dist文件
!fs.existsSync("./dist") && fs.mkdirSync("./dist");
fs.writeFileSync("./dist/bundle.js", content);
node index.js js文件按照键值对的形式,键为路径值,值为文件内容html文件,所以最开始是需要require函数,导入入口文件的_export,防止文件间相互污染eval去执行对应的code字符串代码code内部含有require函数,则递归调用就行!(function (list) {
function require(file) {
var _export = {}
!(function (_export, code) {
eval(code)
})(_export, list[file])
return _export
}
// 入口
require('./index.js')
})({
'./add.js': '_export.default = function (a, b) {return a + b}',
'./index.js': `
var add = require('./add.js').default
console.log(add(1 , 2))
`,
})

,但是老浏览器不支持,为了都支持,所以需要webpack打包一下entry)output)loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。loader 能够import导入任何类型的模块(例如.css文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。webpack的配置中 loader 有两个目标:
test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use 属性,表示进行转换时,应该使用哪个 loader。 module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用 raw-loader `转换一下。”webpack 配置中定义loader时,要定义在 module.rules 中,而不是 rules。然而,在定义错误时 webpack 会给出严重的警告。为了使你受益于此,如果没有按照正确方式去做,webpack 会“给出严重的警告”plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。webpack.config.js 配置如下:webpack 提供许多开箱可用的插件!查阅我们的插件列表获取更多信息。webpack 配置中使用插件是简单直接的,然而也有很多值得我们进一步探讨的用例。const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
前端网页功能丰富,网页资源相互穿插应用,错综复杂,webpack可以帮我们去理清这种文件依赖关系。
前端开发目前面临哪些复杂问题?
日常使用:
gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。gulp是基于任务和流(Task、Stream)的。
类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
gulp强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task处理的事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,来让gulp执行这些task,从而构建项目的整个前端开发流程。
webpack是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。
gulp与webpack上是互补的,还是可替换的,取决于你项目的需求。如果只是个vue或react的单页应用,webpack也就够用;如果webpack某些功能使用起来麻烦甚至没有(雪碧图就没有),那就可以结合gulp一起用。

如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。
多入口情况下,使用CommonsChunkPlugin来提取公共代码
通过externals配置来提取常用库
利用DllPlugin和DllReferencePlugin预编译资源模块通过DllPlugin来对那些我们
引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
使用Happypack 实现多线程加速编译
使用webpack-uglify-paralle来提升uglifyPlugin的压缩速度。
原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
使用Tree-shaking和Scope Hoisting来剔除多余代码
Npm是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。
你可能只是JS模块的使用者,但是有些情况你也会去选择上传自己开发的模块。
关于NPM模块上传的方法可以去官网上进行学习,这里只讲解如何利用webpack来构建。
NPM模块需要注意以下问题:
代码层面:
研发流程层面:
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:\
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。\
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述
多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。多页应用中要注意的是:
webpack默认只能打包js文件,配置里的module.rules数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换打包成js。
注意:
use属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如css-loader?minimize中的minimize告诉css-loader要开启 CSS 压缩。
css-loader读取 合并CSS 文件
style-loader把 CSS 内容注入到 JavaScript 里
sass-loader 解析sass文件(安装sass-loader,node-sass)
postcss-loader自动添加浏览器兼容前缀(postcss.config配置)
url-loader将文件转换为base64 URI。
vue-loader处理vue文件。\
Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。
Webpack 是通过plugins属性来配置需要使用的插件列表的。plugins属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。
bundle:是由webpack打包出来的文件
chunk:是指webpack在进行模块依赖分析的时候,代码分割出来的代码块
module:是开发中的单个模块
HtmlWbpackPlugin自动在打包结束后生成html文件,并引入bundle.js
cleanwebPackPlugin打包自动删除上次打包文件\
ExtractTextPlugin插件的作用是提取出 JavaScript 代码里的 CSS 到一个单独的文件。
对此你可以通过插件的filename属性,告诉插件输出的 CSS 文件名称是通过[name]_[contenthash:8].css字符串模版生成的,里面的[name]代表文件名称,[contenthash:8]代表根据文件内容算出的8位 hash 值, 还有很多配置选项可以在ExtractTextPlugin的主页上查到。\
是一个映射关系,将打包后的文件隐射到源代码,用于定位报错位置
配置方式:
例如:devtool:‘source-map’
加不同前缀意义:\
最佳实践:
开发环境:cheap-module-eval-source-map
线上环境:cheap-mudole-source-map\
借助webpack.HotModuleReplacementPlugin(),devServer开启hot\
场景1:实现只刷新css,不影响js
场景2:js中实现热更新,只更新指定js模块
if (module.hot) { module.hot.accept(’./library.js’, function() { // Do something with the updated library module… });}
entry: { home: resolve(__dirname, "src/home/index.js"), about: resolve(__dirname, "src/about/index.js")}
用于描述入口的对象。你可以使用如下属性:
1.polyfill 以及 runtime 区别
babel-polyfill 的原理是当运行环境中并没有实现的一些方法,babel-polyfill会做兼容。
babel-runtime 它是将es6编译成es5去执行。我们使用es6的语法来编写,最终会通过babel-runtime编译成es5.也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5.所以就有很多冗余的代码。
babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染。
babel-runtime: 它不会污染全局对象和内置对象的原型,比如说我们需要Promise,我们只需要import Promise from 'babel-runtime/core-js/promise’即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。
2.stage-x:指处于某一阶段的js语言提案
Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
Stage 1 - 建议(Proposal):这是值得跟进的。
Stage 2 - 草案(Draft):初始规范。
Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
3. 理解 babel-preset-env
babel-preset-es2015: 可以将es6的代码编译成es5.
babel-preset-es2016: 可以将es7的代码编译为es6.
babel-preset-es2017: 可以将es8的代码编译为es7.
babel-preset-latest: 支持现有所有ECMAScript版本的新特性
模块热更新是webpack的一个功能,它可以使得代码修改之后,不用刷新浏览器就可以更新。
在应用过程中替换添加删出模块,无需重新加载整个页面,是高级版的自动刷新浏览器。
优点:只更新变更内容,以节省宝贵的开发时间。调整样式更加快速,几乎相当于在浏览器中更改样式\
借助import()语法异步引入组件,实现文件懒加载:prefetch,preloading
webpack提倡多写异步代码,提升代码利用率,从而提升页面性能
先加载主业务文件,prefetch利用网络空闲时间,异步加载组件
import(/* webpackPrefetch: true / ‘LoginModal’);
preload和主业务文件一起加载,异步加载组件
import(/ webpackPreload: true */ ‘ChartingLibrary’);
浏览器在用户访问页面的时候,为了加快加载速度,会对用户访问的静态资源进行存储,但是每一次代码升级或者更新,都需要浏览器去下载新的代码,最方便和最简单的更新方式就是引入新的文件名称。
在webpack中,可以在output给出输出的文件制定chunkhash,并且分离经常更新的代码和框架代码,通过NameModulesPlugin或者HashedModulesPlugin使再次打包文件名不变。
什么是Tree-shaking
JavaScript的Tree Shaking:
webpack实现Tree Shaking,两种方案:
usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的;psideEffects:跳过整个模块/文件,直接查看该文件是否有副作用;usedExports
sideEffects



综上,Webpack中tree shaking的设置
css的Tree Shaking:
npm install purgecss-webpack-plugin -D


npm install compression-webpack-plugin -D

npm install react-dev-utils -D
Library : [ˈlaɪbrəri] 图书馆,软件库




webpack-dev-server使用内存来存储webpack开发环境下的打包文件,并且可以使用模块热更新,比传统的http服务对开发更加有效。
mode/–mode参数,新增了mode/–mode参数来表示是开发还是生产(development/production)production 侧重于打包后的文件大小,development侧重于goujiansud移除loaders,必须使用rules(在3版本的时候loaders和rules 是共存的但是到4的时候只允许使用rules)移除了CommonsChunkPlugin (提取公共代码),用optimization.splitChunks和optimization.runtimeChunk来代替支持es6的方式导入JSON文件,并且可以过滤无用的代码
webpack包 和 webpack-cli包,需要都安装,但是webpack-cli不是必须的,如vue源码,就替换为了自己的cli,不需用webpack-cli
webpack依赖关系图
webpack是不知道如何加载css模块的,我们需要借助loader来完成该功能,yarn add css-loader
css-loader的使用方案(webpack官网提供的loader使用方案):
import "css-loader!../css/index.css" 开发中不会这样使用
webpack --module-bind 'css=css-loader' --config wk.config.js 开发不会这样使用
loader存在简写方式,如图css-loader

处理less样式的 yarn add less -D,可以npx less ./src/css/component.less > component.css,但是在webpack中用需要装less-loader,yarn add less-loader -D
综合上面的代码如下:(loader链式调用,从后往前逆序调用)
不同浏览器,css样式存在兼容性写法,browserlist的配置包共享出不同版本浏览器列表,方便其他包去统一使用(单独的.browserlistrc文件,或者package.json内部配置,创建vue项目时候有给选择)
browserlist包是如何查询的
postcss的使用,yarn add postcss -D,为了想在命令行上使用命令需要额外安装postcss-cli,yarn add postcss-cli -D,为了使得webpack能使用postcss,于是需要额外安装postcss-loader,yarn add postcss-loader -D

自动给css样式添加基于配置.browserlist的兼容代码,使用autoprefixer,对于一般的css代码,autoprefixer足以做兼容处理,但是color: #12345678;,颜色8位数,有些浏览器不识别,那么需要转换为rgba格式,这种转换autoprefixer做不到,这里有个新的包postcss-preset-env去替换它
npx postcss --use autoprefixer -o result.css ./src/css/test.css 命令行输出
上面写的postcss-loader,匹配less文件,又要重复写一套代码配置,如何提取放到公共的位置,在根目录下新建postcss.config.js文件,内容为module.exports = { plugins: [ 'postcss-preset-env' ] },不用再重复写postcss-loader的options配置了,代码可以简写为:(这里只写了css的use,less的类似,这里就不重复写了)
处理图片
webpack5之前,加载资源需要一些loader,比如url-loader
webpack之后,使用资源模块类型 (asset module type),来替代上面的loader
认识plugins
每次打包删除之前的包,yarn add clean-webpack-plugin -D
生成html文件,基于template的html模板有,打包会报BASE_URL 错误,需要设置DefinePlugin

复制文件,template的html模板生成html,内部可能有引入的资源,需要将资源文件复制一下
yarn add webpack-dev-server
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)
模块热替换是指在 应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可;在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading;
但是如果要热更新math.js文件,仍然需要如下代码:
webpack热更新原理
将css提取到一个独立的css文件中yarn add mini-css-extract-plugin -D
直接用插件,不需要手动导入axios,直接用get函数和axios函数
// 导出文件
const dateFormat = (date) => {
return "3000-12-12";
}
const priceFormat = (price) => {
return "100.00";
}
module.exports = {
dateFormat,
priceFormat
}
// 导入文件
const { dateFormat, priceFormat } = require('./js/format');
console.log(dateFormat("abc"));
console.log(priceFormat("abc"));
打包之后的代码:
// 定义了一个对象
// 模块的路径(key): 函数(value)
var __webpack_modules__ = {
"./src/js/format.js":
(function (module) {
const dateFormat = (date) => {
return "3000-12-12";
}
const priceFormat = (price) => {
return "100.00";
}
// 将我们要导出的变量, 放入到module对象中的exports对象
module.exports = {
dateFormat,
priceFormat
}
})
}
// 定义一个对象, 作为加载模块的缓存
var __webpack_module_cache__ = {};
// 是一个函数, 当我们加载一个模块时, 都会通过这个函数来加载
function __webpack_require__(moduleId) {
// 1.判断缓存中是否已经加载过
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
// 2.给module变量和__webpack_module_cache__[moduleId]赋值了同一个对象
var module = __webpack_module_cache__[moduleId] = { exports: {} };
// 3.加载执行模块
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// 4.导出module.exports {dateFormat: function, priceForamt: function}
return module.exports;
}
// 具体开始执行代码逻辑
// 1.加载./src/js/format.js
const { dateFormat, priceFormat } = __webpack_require__("./src/js/format.js");
console.log(dateFormat("abc"));
console.log(priceFormat("abc"));
// 导出模块
export const sum = (num1, num2) => {
return num1 + num2;
}
export const mul = (num1, num2) => {
return num1 * num2;
}
// 导入模块
import { sum, mul } from "./js/math";
console.log(mul(20, 30));
console.log(sum(20, 30));
webpack打包之后的代码:
// 1.定义了一个对象, 对象里面放的是我们的模块映射
var __webpack_modules__ = {
"./src/es_index.js":
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
// 调用r的目的是记录时一个__esModule -> true
__webpack_require__.r(__webpack_exports__);
// _js_math__WEBPACK_IMPORTED_MODULE_0__ == exports
var _js_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/math.js");
console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.mul(20, 30));
console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.sum(20, 30));
}),
"./src/js/math.js":
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
// 调用了d函数: 给exports设置了一个代理definition
// exports对象中本身是没有对应的函数
__webpack_require__.d(__webpack_exports__, {
"sum": function () { return sum; },
"mul": function () { return mul; }
});
const sum = (num1, num2) => {
return num1 + num2;
}
const mul = (num1, num2) => {
return num1 * num2;
}
})
};
// 2.模块的缓存
var __webpack_module_cache__ = {};
// 3.require函数的实现(加载模块)
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
// __webpack_require__这个函数对象添加了一个属性: d -> 值function
__webpack_require__.d = function (exports, definition) {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
// __webpack_require__这个函数对象添加了一个属性: o -> 值function
__webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
// __webpack_require__这个函数对象添加了一个属性: r -> 值function
__webpack_require__.r = function (exports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
__webpack_require__("./src/es_index.js");
// commonjs导出
const dateFormat = (date) => {
return "3000-12-12";
}
const priceFormat = (price) => {
return "100.00";
}
module.exports = {
dateFormat,
priceFormat
}
// esmodule 导出
export const sum = (num1, num2) => {
return num1 + num2;
}
export const mul = (num1, num2) => {
return num1 * num2;
}
// es module导出内容, CommonJS导入内容
const { sum, mul } = require("./js/math");
// CommonJS导出内容, es module导入内容
import { dateFormat, priceFormat } from "./js/format";
console.log(sum(20, 30));
console.log(mul(20, 30));
console.log(dateFormat("aaa"));
console.log(priceFormat("bbb"));
打包后的代码:
var __webpack_modules__ = ({
"./src/index.js":
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _js_format__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/format.js");
var _js_format__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_js_format__WEBPACK_IMPORTED_MODULE_0__);
// es module导出内容, CommonJS导入内容
const math = __webpack_require__("./src/js/math.js");
// CommonJS导出内容, es module导入内容
console.log(math.sum(20, 30));
console.log(math.mul(20, 30));
console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().dateFormat("aaa"));
console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().priceFormat("bbb"));
}),
"./src/js/format.js":
(function (module) {
const dateFormat = (date) => {
return "2020-12-12";
}
const priceFormat = (price) => {
return "100.00";
}
module.exports = {
dateFormat,
priceFormat
}
}),
"./src/js/math.js":
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
"sum": function () { return sum; },
"mul": function () { return mul; }
});
const sum = (num1, num2) => {
return num1 + num2;
}
const mul = (num1, num2) => {
return num1 * num2;
}
})
});
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function () { return module['default']; } :
function () { return module; };
__webpack_require__.d(getter, { a: getter });
return getter;
};
__webpack_require__.d = function (exports, definition) {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
__webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
__webpack_require__.r = function (exports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
__webpack_require__("./src/index.js");
source-map 比 eval信息更加的全,不仅仅展示出错误的源文件,而且把源文件的目录都给展示出来,这样方便用户查看项目结构。


@babel/core:babel的核心代码,必须安装;
@babel/cli:可以让我们在命令行使用babel;
yarn add @babel/cli @babel/core
输入命令:
src:是源文件的目录;
–out-dir:指定要输出的文件夹dist;
npx babel src --out-dir dist
使用插件帮助解析:
yarn add @babel/plugin-transform-arrow-functions -D // 箭头函数 -> function
yarn add @babel/plugin-transform-block-scoping -D // const -> var
输入命令:
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping ,@babel/plugin-transform-arrow-functions
对应的webpack配置:

如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):
yarn add @babel/preset-env -D

目标版本优先级:
多个预设联用:

stage-x:
babel编译器原理:

babel的配置文件:

module.exports = {
presets: [
["@babel/preset-env", {
// false: 不用任何的polyfill相关的代码
// usage: 代码中需要哪些polyfill, 就引用相关的api
// entry: 手动在入口文件中导入 core-js/regenerator-runtime, 根据目标浏览器引入所有对应的polyfill
useBuiltIns: "entry",
corejs: 3
}],
["@babel/preset-react"]
],
// plugins: [
// ["@babel/plugin-transform-runtime", {
// corejs: 3
// }]
// ]
}
promise和generator,symbol转换不了,需要装polyfill

多入口起点:

Entry Dependencies(入口依赖):

加粗样式:

SplitChunks自定义配置:


影响 npm run build 打包时间:node版本,电脑性能。
默认情况下,打包完成展示,打包的总时间,并没有告诉我,每个插件使用所消耗的时间
希望看到每个loader,每个plugin消耗的打包时间,可以借助一个插件:speed-measure-webpack-plugin,该插件有webpack版本兼容问题
npm install speed-measure-webpack-plugin
有部分插件不支持,可能不支持,需要给注释一下
方法一:官方提供的
"scripts": {
"stats": "webpack --config ./config/webpack.common.js --env production --profile --json=stats.json"
},
--profile --json=stats.json 加了这个 生成stats.json文件,把该文件上传到
https://webpack.github.io/analyse/网站上,可以看到可视化分析
方法二:插件提供的
npm install webapck webpack-cli --save-dev
"scripts": { "build": "webpack --config wk.config.js" }
当在命令行中执行 npm run build 时,会执行node_modules/.bin下的webpack可执行文件
我们看下上面webpack文件的内容
从代码中可以看到,会执行node_modules/webpack/bin/ 目录下的webpack.js,该文件主要代码如下:
该文件最重要的函数就是 runCli ,该函数可以执行 webpack-cli 包中bin目录下的cli.js 文件,也就是说在此之前的步骤只是为了找到cli.js文件,在此之后,webpack-cli 才发挥作用。
同时,webpack.js 文件也做了一些辅助判断,首先查看你是否安装了webpack-cli,如果没有安装,就会询问你是否安装(或手动安装)该包,如果选择不安装,那么程序运行到这就停止了。
接着打开webpack-cli/bin/cli.js
该文件的主要函数为 runCLI,而 runCLI 又来自 bootstrap.js 文件,打开 bootstrap.js 文件
new WebpackCLI(),然后继续调用实例的run方法
在constructor内,获取了webpack包的函数库
注意,到了这里才真正用到了 webpack-cli 暴露出的接口,cli.run(args) 用来处理命令行参数,此时args参数为:
最终,从以上整个过程,我们可以知道 webpack-cli 是用来处理命令行参数,并通过参数构建 compiler 对象,然后才是对代码进行打包的过程。
所以说,webpack-cli对于文件打包不是必需的。
既然 webpack-cli只是为了处理命令行参数,那我们同样可以构建自己的cli来处理参数,比如 lyx-cli。在第三方框架中,React 和 Vue(未使用Vite的版本)也没有使用 webpack-cli.
自己写代码替换webpack-cli,咱们在webpack源码下做实验
zanlan/src/utils/math.js
zanlan/src/main.js
zanlan/build.js 
zanlan/webpack.config.js
执行node zanlan/build.js,可以看到在zanlan文件夹下生成了build文件夹,在内部有bundle.js,bundle.js.map两个文件。
webpack启动流程图

git clone --branch v5.72.1 https://github.com/webpack/webpack.git
npm install