里程碑2 前端工程化

4 阅读4分钟

本文来源于 哲玄前端(抖音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请求。可以统一对异常状态进行拦截提示,可以统一对接口进行签名等需要对接口请求/返回统一处理的都可以在此处添加。