webpack 基本配置(可展开 && 收缩)
const os = require('os')
const { resolve } = require('path')
const webpack = require('webpack')
const HappyPack = require('happypack')
const WebpackBar = require('webpackbar')
const CopyPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const CircularDependencyPlugin = require('circular-dependency-plugin')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
const source = resolve(__dirname, '..', 'src')
module.exports = {
context: source,
entry: {
app: './index.tsx',
},
watchOptions: {
// 不监听的 node_modules 目录下的文件
ignored: /node_modules/,
},
stats: {
assets: true,
builtAt: true,
colors: true,
chunks: false,
children: false,
env: true,
entrypoints: false,
errors: true,
errorDetails: true,
hash: true,
modules: false,
moduleTrace: true,
performance: true,
publicPath: true,
timings: true,
version: true,
warnings: true,
},
resolve: {
unsafeCache: true,
mainFiles: ['index'],
mainFields: ['main'],
extensions: ['.ts', '.tsx', '.js'],
modules: [source, 'node_modules'],
alias: {
'@': source,
'react-dom': '@hot-loader/react-dom',
moment: 'dayjs',
'@components': resolve(__dirname, '../src/components'),
'@ant-design/icons/lib/dist$': resolve(__dirname, '../src/assets/icons.ts'),
},
},
module: {
rules: [
{
test: /\.((ts|js)(x?))$/,
exclude: /node_modules/,
include: [source],
use: 'happypack/loader?id=babel',
},
{
test: /\.(jpe?g|png|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8 * 1024,
outputPath: 'images',
name: '[path][name].[ext]',
},
},
],
},
],
},
plugins: [
new WebpackBar(),
new CopyPlugin([
{
from: resolve(__dirname, '../static'),
ignore: ['dll/*'],
to: resolve(__dirname, '../dist'),
},
]),
new HtmlWebpackPlugin({
title: '新际通管理平台',
filename: 'index.html',
template: resolve(__dirname, '../public/index.html'),
hash: true,
minify: {
removeRedundantAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
collapseBooleanAttributes: true,
},
// favicon: resolve(__dirname, '../public/favicon.ico'),
}),
new HappyPack({
id: 'babel',
threadPool: happyThreadPool,
loaders: [
'cache-loader',
{
loader: 'babel-loader',
query: {
cacheDirectory: './node_modules/webpack_cache/',
},
},
'eslint-loader',
],
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /dayjs$/),
new HardSourceWebpackPlugin(),
new CircularDependencyPlugin({
exclude: /node_modules/,
include: /src/,
failOnError: true,
allowAsyncCycles: false,
cwd: process.cwd(),
}),
],
}
复制代码
webpack 模块打包原理
下面谈谈我的理解
javascript 万物皆对象,每个实例都有一个proto属性,指向了他构造函数的原型对象
每个构造函数都有一个 prototype 属性,指向了他的原型对象
实例对象创建之间判断指向关系有 proto 代表指向关系 指向了原型对象
function A() {} // A 构造函数
var a = new A(); // a 实例对象
A.prototype; // 原型对象
a.__proto__ === A.prototype;
复制代码
所有函数都是由 Function 创建
A.__proto__ === Function.prototype; // true
Function.__proto__ === Function.prototype; // true
复制代码
Object 刚讲的是顶级函数,所以也是函数:(所有的鱼都归猫管哈哈哈哈哈)
Object.__proto__ === Function.prototype;
复制代码
所有的对象都是由 Object 构造函数创建的, 所以对象proto指向了 Object 的构造函数:
A.prototype.__proto__ === Object.prototype;
复制代码
Object.prototype 也是对象,比较特殊,指向了 null
Object.prototype.__proto__ === null;
复制代码
webpack 热更新原理
webpack 如何自定义loader,实现思路如何
webpack 如何自定义plugin,实现思路是如何
如何题提升webpack构建速度
-
使用高版本的webpack和nodejs
-
启用多进程构建(happypack/thead-loader)
-
压缩代码
-
使用mini-css-extract-plugin 提取公共css
-
terser-webpack-plugin 开启js多进程压缩
-
缩小打包作用域
-
exclude/include (确定 loader 规则范围)
-
resolve.modules 指定解析第三方包的目录位置 (减少不必要的查找)
-
resolve.extensions 尽可能减少后缀尝试的可能性
-
合理使用alias,简化导入
-
提取页面公共资源
-
使用 SplitChunksPlugin 进行(公共脚本、基础包、页面公共文件)分离(Webpack4内置) ,替代了 CommonsChunkPlugin 插件
-
提取公共js资源 => splitChunks
module.exports = { optimization: { splitChunks: { cacheGroups: { utils: { chunks: 'initial', minSize: 0, minChunks: 2 } } } } }; 复制代码
-
利用缓存提升二次加载速度
-
hard-source-webpack-plugin
常见的loader有哪些,使用过哪些loader
- file-loader
- css-loader
- image-loader
- less-loader
- babel-loader
- ts-loader
- eslint-loader
有哪些常用插件, 使用过哪些
- html-webpack-plugin 简化html创建
- mini-css-extract-plugin 提取css文件
- define-plugin 定义环境变量 (Webpack4 之后指定 mode 会自动配置)
- hardsource-webpack-plugin 启用缓存
- happypack 多进程构建
- webpack-bundle-analyzer 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)
webpack中loader和plugin区别
Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作
Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果
Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。
webpack构建流程简述下
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说
- 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
- 编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
- 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
webpack如何文件监听
webpack 开启监听有2种方式
- 启动命令行添加 –watch
- webpack 配置文件中 添加 watch: true
babel编译原理
- 解析:将代码转换成 AST
- 转换:访问 AST 的节点进行变换操作生产新的 AST
- 生成:以新的 AST 为基础生成代码