本文来源于 哲玄前端(抖音ID:44622831736)大前端全栈实践课程 ,记录里程碑2 前端工程化的学习笔记。课程内容非常值得,刚入行的小白也可以听懂,哲哥用js语言带你快速入门全栈!强推。
参考文档
webpack5:webpack.docschina.org/concepts/
原理
-
业务文件 (.vue、.js、.css、.less、.png、.woff、.tpl、node_modules)
-
解析引擎
-
解析编译
- 入口->依赖分析 (require ,import ,ast)
- 编译 .vue->vue-loader | .less->less-loader | .css -> css-loader | .js ->babel-loader | .png -> file-loader (解析出浏览器可识别的文件 )
- 输出 xx.js | xx.css | xx.png | entry.tpl
-
模块分包
- 模块分析
- 模块拆分 abc.js ->a.js | b.js | c.js (减少不必要的引用)
- 输出 若干xx.js | 若干 xx.css | xx.png | entry.tpl
-
压缩优化
- 环境分流
- 生产环境 压缩混合js | 压缩css | 输出文件产物
- 开发环境 资源注入内容 | 本地服务-热更新
-
-
产物文件(xxx.min.js、xxx.min.css、 xxx.min.png、xxx.tpl)
-
Koa->产物文件->输出页面
webpack打包流程
plugin通过Tapable 钩子系统注册到不同的生命周期,作用于各阶段。
- 初始化阶段 读取配置、初始化插件、创建 Compiler 对象 (build)
- 编译阶段 从入口文件开始,递归解析模块依赖,使用 loader 转换文件 (module.rules)
- 生成阶段 将模块组合成 Chunk,构建模块依赖图,生成资源 (entry, output)
- 优化阶段 代码压缩、Tree Shaking、Scope Hoisting 等 (optimization )
- 输出阶段 将生成的资源写入到 output 目录(output.path)
webpack配置
基础配置:webpack.base.js,最终配置存储在WebBaseConfig,最后通过build.js文件中 webpack方法传入WebBaseConfig进行打包。多页面应用时,需要自动写入webpack配置中的entry和plugin中的HtmlWebpackPlugin,确定生成页面的入口以及打包后的渲染文件。根据环境的不同,对生产/开发环境配置两套webpack配置
Entry
入口配置,以下为多页面渲染的打包示例:
entry:{
'entry.page1':'./app/pages/page1/entry.page1.js',
'entry.page2':'./app/pages/page2/entry.page2.js',
},
以第一条数据为例: Webpack 从 ./app/pages/page1/entry.page1.js 开始构建依赖图,将所有依赖打包成一个独立的 bundle。
entry.page1 与plugins中的HtmlWebpackPlugin 配合使用,指定bundle要注入的代码块。
import {createApp} from 'vue';
import page1 from './page1.vue';
const app = createApp(page1);
app.mount('#root');
Module
模块解析配置,决定了要加载解析哪些模块,以及用什么方式去解析
rules:[
{
test:/.vue$/,
use:{
loader:'vue-loader'
}
},{
test:/.js$/,
include:[
// 只对业务代码进行 babel 转译,加快webpack打包速度
path.resolve(process.cwd(),'./app/pages')
],
use:{
loader:'babel-loader'
}
},
]
对文件名进行正则匹配test,匹配成功的文件通过use中的loader工具进行解析,解析后的文件输出位置由output控制。
Output
控制产物输出的路径
output:{
// 打包后的js文件名格式
filename:'js/[name]_[chunkhash:8].bundle.js',
// 打包后的文件输出目录(绝对路径)
path:path.join(process.cwd(),'./app/public/dist/prod'),
// 浏览器访问打包资源时的 URL 前缀,因为配置过koaStatic('.../app/public'),所以这里只写 dist/prod/
publicPath:'/dist/prod/',
// 允许跨域加载脚本(用于错误追踪等场景)
crossOriginLoading:'anonymous',
},
Resolve
配置模块解析的具体行为,webpack 在打包时,如何找到并解析模块的路径。主要便于开发。
resolve:{
//省略后缀时,自动解析扩展名,根据这个查找,找不到再报错
extensions:['.js','.vue','.less','.css'],
//路径别名 import xxx from '$pages/xxx/xx',类比平时项目用的@
alias:{
$pages:path.resolve(process.cwd(),'./app/pages'),
}
},
Plugin
webpack插件
plugins:[
// 处理 .vue 文件 ,这个插件是必须的
// 它的只能是将你定义过的其他规则复制并应用到 .vue文件中里
// 例如,有一条匹配规则 /.js$/ 的规则,那么它会应用到 .vue 文件中的 <script> 模块中
new VueLoaderPlugin(),
// 把第三方库暴露到 window context 下
new webpack.ProvidePlugin({
Vue: 'vue',
}),
// 定义全局常量
new webpack.DefinePlugin({
// 支持 Vue 解析 optionsApi
__VUE_OPTIONS_API__: 'true',
// 禁用 Vue 调试工具
__VUE_PROD_DEVTOOLS__: 'false',
// 禁用生产环境显示"水合"信息
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
}),
// 构造最终渲染的页面模板
// 在template中注入chunks,输出到filename
new HtmlWebpackPlugin({
// 产物(最终模板)输出路径
filename:path.resolve(process.cwd(),'./app/public/dist/','entry.page1.tpl'),
// 指定要使用的模板文件
template: path.resolve(process.cwd(), 'app/view/entry.tpl'),
// 要注入的代码块
chunks: ['entry.page1'],
}),
new HtmlWebpackPlugin({
// 产物(最终模板)输出路径
filename:path.resolve(process.cwd(),'./app/public/dist/','entry.page2.tpl'),
// 指定要使用的模板文件
template: path.resolve(process.cwd(), 'app/view/entry.tpl'),
// 要注入的代码块
chunks: ['entry.page2'],
}),
],
Optimization
配置打包输出优化(配置 代码分割,模块合并,缓存,TreeShaing, 压缩等优化策略 )
分包策略可根据自己需求进行修改,此处以使用频率为例子,具体可查文档。
optimization:{
/**
* 分包策略
* 把 js 文件打包成3种类型
* 1. vendor:第三方 lib 库,基本不会改动,触发依赖版本升级
* 2. common: 业务组件代码的公共部分抽取出来,改动较少
* 3. entry.{page}: 不同页面entry 里的业务组件代码的差异部分,会经常改动
* 目的:把改动和引用频率不一样的 js 区分出来,以达到更好利用浏览器缓存的效果
*/
splitChunks:{
chunks:'all',//对同步和异步模块进行分割
maxAsyncRequests:10, //每次异步加载的最大并行请求数
maxInitialRequests:10, //入口点的最大并行请求数
cacheGroups:{
vendor:{ //第三方依赖库
test:/[\/]node_modules[\/]/,//打包node_module 中的文件
name:'vendor',//模块名称
priority: 20 ,//优先级,数字越大,优先级越高
enforce:true,//强制执行
reuseExistingChunk:true,//复用已有的公共chunk
},
common:{ //公共模块
name:'common', //模块名称
minChunks:2 ,//被两处引用即被归为公共模块
minSize: 1,// 最小分割文件大小 1字节
priority:10 ,//优先级
reuseExistingChunk:true,//复用已有的公共chunk
}
}
},
// 将webpack运行时生产的代码打包到runtime.js
runtimeChunk:true
},
生产环境
注重打包速度以及打包后的文件大小
plugin
- 设置多线程打包工具(happypack)
- 构建前删除dist文件夹(CleanWebpackPlugin)
- 提取css公共部分,避免页面重复请求相同的资源,利用缓存(MiniCssExtractPlugin)
- 优化压缩css文件(CSSMinimizerPlugin)
- 浏览器在请求资源时不发送用户的身份凭证(HtmlWebpackInjectAttributesPlugin)
optimization
- 多核压缩代码,并且清楚调试代码console.log (TerserWebpackPlugin)
开发环境
热更新原理:
启动一个服务(devServer),监听着业务代码的变化(devMiddleware),若发生变化,通知工具解析改动的部分生成js,css,html文件存于该服务的内存中,再通知页面(eventSource)重新来请求该服务中的最新模块代码(hotMiddleware),最后执行模块替换。 即 监听文件变化->解析编译->模块分包->压缩优化->形成编译产物->通知浏览器->浏览器重新加载资源
- 配置服务器设置(devServer)
- 调整baseConfig中的entry属性,配置hmr的注入路径 (
webpack-hot-middleware/client?path=http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/${DEV_SERVER_CONFIG.HMR_PATH}&timeout=${DEV_SERVER_CONFIG.TIMEOUT}&reload=true) - 调整output的资源获取路径,要去devServer服务器中获取(
http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/public/dist/dev/) - 配置devtool,使得源文件和打包后的文件进行映射,便于调试。
- 在build.js文件中,开启devServer服务器,引入devMiddleware和hotMiddleware中间件。
前端基础建设
各页面的入口页通过统一文件启动(boot.js),在boot中,对资源(公共css,组件库,pinia)进行统一引用,通过传参提供对 路由和第三方库的支持。
通过axios封装请求接口:主要思路,调用时传入必要的请求参数,通过封装的curl将参数转换为axios可识别的参数进行http请求。可以统一对异常状态进行拦截提示,可以统一对接口进行签名等需要对接口请求/返回统一处理的都可以在此处添加。