webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)
需要整理的
- 打包体积缩减
- 分包
- DLL
- 手动配两个环境,三个配置文件(base、dev、prod)
- 编译加速
- dev-server + mock
webpack介绍
当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack功能
- 代码转换
- 文件优化
- 代码分割
- 模块合并
- 自动刷新
- 代码校验
- 自动发布
有用的链接
link:Babel中文文档
webpack + vue脚手架
常用配置项
- 配置文件
- 多种配置类型
- 入口
- 输出
- hash、chunkhash、contenthash
- 模式
- 自动生成页面
- loader
- css-loader和style-loader
- sass-loader和less-loader
- postcss-loader 自动加-moz、-ms、-webkit等浏览器私有前缀
- babel-loader
- file-loader和url-loader
- html-withimg-loader
- vue-loader
- 搭建开发环境
- plugins
- clean-webpack-plugin
- mini-css-extract-plugin
- optimize-css-assets-webpack-plugin
- copy-webpack-plugin
- ProvidePlugin
- loader和plugin的区别
手写webpack核心打包过程
1. 打包的主要流程
- 需要读到入口文件里面的内容
- 分析入口文件,递归的去读取模块所依赖的文件内容,生成AST语法树
- 根据AST语法树,生成浏览器能够运行的代码
2. 打包过程的步骤
- 获取模块内容
- 分析模块
- 收集依赖
- ES6转成ES5(AST)
- 递归获取所有依赖
- 处理两个关键字(import、exports)
- 输出打包结果代码
3. 实现打包的步骤
- 获取主模块内容
- 分析模块
- 安装@babel/parser包(转AST)
- 对模块内容进行处理
- 安装@babel/traverse包(遍历AST收集依赖)
- 安装@babel/core和@babel/preset-env包 (es6转ES5)
- 递归所有模块
- 生成最终代码
4. 代码
/**
* 手写webpack核心打包流程
*/
// 获取主入口文件
const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core');
// 解析单个文件,获取文件路径,依赖文件列表,编译成es5的代码
const getModuleInfo = (file) => {
const body = fs.readFileSync(path.resolve(__dirname, file), 'utf-8');
// 新增代码
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);
deps[node.source.value] = abspath;
}
});
const {code} = babel.transformFromAst(ast, null, {
presets: ['@babel/preset-env']
});
const moduleInfo = {file, deps, code};
return moduleInfo;
};
// 编译入口文件,非递归遍历AST依赖树,将所有文件解析后生成平铺的映射对象
const parseModules = (file) => {
const entry = getModuleInfo(file);
const temp = [entry];
const depsGraph = {};
for (let i = 0; i < temp.length; i++) {
const deps = temp[i].deps;
if (deps) {
for (const key in deps) {
if (deps.hasOwnProperty(key)) {
temp.push(getModuleInfo(deps[key]));
}
}
}
}
temp.forEach(moduleInfo => {
depsGraph[moduleInfo.file] = {
deps: moduleInfo.deps,
code: moduleInfo.code
};
});
return depsGraph;
};
// 打包,生成递归遍历执行依赖关系树的执行器和AST依赖关系树
const bundle = (file) => {
const depsGraph = JSON.stringify(parseModules(file), null, 4);
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 entry = './src/index.js';
const content = bundle(entry);
console.log(content);
// 输出代码
if (!fs.existsSync('./dist')) {
fs.mkdirSync('./dist');
}
fs.writeFileSync('./dist/bundle.js', content);