0


前端【全链路体系优化介绍与落地实践】

1. 技术基建:

是研发团队的技术基础设施建设,是一个团队通用的技术能力沉淀.

(1). 业务、架构、基建理解:

①. 业务支撑是活在当下

②. 技术基建是活在未来

(2). 意义:

①. 技术的价值在于解决业务问题,"业务支撑"和"基础建设"都是为了帮助业务解决问题

②. 不能脱离解决实际场景而基建

③. 基础建设的发起从业务问题中来:
    a. 能帮助业务解决问题
    b. 在建设过程中,提供不同维度的锻炼场景
    c. 在业务问题与场景的识别、方案设计、新技术实践、项目管理和产品化思维方面提供成长空间
    c. 对不同角色进行锻炼和考察,有助于团队梯队建设

2. 基建搞什么?

①. 基建前的痛点:
    a. 靠压榨、纯加班的蛮力方式支持业务
    b. 在这种模式下,业务无法实现跨越式增长的:(1). 业务量突然增长10倍的情况下,研发团队规模也扩充10倍,成本会得到失控(2). 阶段性的忙和加班是不可避免的,如电商的双11大促、toB业务定制的大项目的交付,时间点都是倒排(3). 加班不是重点,只有完不成工作才是重点

②. 面临的困境:
    a. 团队一定要思考,怎么做能更高效?
    b. 如果未来,业务量增长N倍,该如何支撑?
    c. 现在的方式是否能满足?一味的靠堆人不现实

③. 改善的措施:
    a. 靠技术建设去提效降成本
    b. 基建最核心的价值:帮助业务更好的活在未来

④. 基建是内容是什么?
    a. 基建的内容和业务阶段、团队现有建设沉淀有关联
    b. 初创期的团队的建设,往往倾向于基础的技术收益:(1).如脚手架、组件库(2). 打包部署工具等
    c. 越成熟的业务和成熟沉淀的团队,更偏向于获取更多的业务收益:(1).如直接服务于业务的系统(2). 技术提效的同时更能直接带来业务收益

⑤. 团队的阶段性成长:
    a. 起步期和快速爬坡期,主要体现的基建:(1).基于Webpack搞个脚手架(2). 在第三方开源的UI组件库基础上,封装业务组件库
    b. 基础完备、沉淀丰富的BAT团队

(1). 工程化体系定义:

①. 广义上,一切以"提高效率、降低成本、保障质量"为目的的手段,都属于工程化的范畴.

②. 通过一系列的规范、流程、工具达到"研发提效、自动化、保障质量、服务稳定、预警监控"等.

③. 可以借助于Node,将研发链路延伸到整个DevOps中去.

④. 前端工程化指使用软件工程的技术与方法对前端开发的技术、工具、流程、经验、方案等指标标准化:
    a. 模块化
    b. 组件化
    c. 规范化
    d. 自动化

⑤. 目的:
    a. 降低成本
    b. 增加效率

(2). 团队标准:

①. 互联网前端标配:
    组件化、工程化、自动化

②. 规模的团队:
    a. 根据自身业务与梯度来设计符合业务的DevOps流程.

(3). 简单DevOps:

①. 常规基建:
    a. 组件库 + 脚手架 + 工具库 + 模板 +CLI

②. Git Flow:
    a. 通过常规Git Flow工作流,不同branch不同功能 + Code Review

③.CICD:
    a. Webhook +脚本

④. 说明:
    a. 上述DevOps流程,作为小型团队搭建工程化的起点,性价比极高.
    b. 在团队没有制定规则,也没有基础建设时,通常先从最基础的CLI工具开始然后切入到整个工程化的搭建.

(4). 个人发展:

编写业务代码=>使用前端工程化解决生产问题=>前端架构设计=>  技术管理岗晋升

(5). 业务痛点:

随着需求迭代的步伐加速,可能会产生以下问题

①. 构建配置、打包配置、公共组件、工具函数等代码片段,每次新开项目都要复制粘贴

②. 团队成员的编码风格大相径庭,导致从仓库拉取下来的代码运行起来让控制台一片红

③. 团队协作的规范、环境、模块、仓库和文档,太多基建措施导致团队新成员无从入手

④. 随着需求迭代引起项目结构与工程文件不断变化,处理不当让项目直接走向重构道路

前端工程化的开发思维与解决方案应用到项目中,解决非业务需求,为业务降本增效.

①. 前端工程化不是某个具体的工具:
    a. 对项目的整体架构与整体规划,使开发者能在未来可判时间内动态规划发展走向,以提升整个项目对用户的服务周期

②. 闭环:
    a. 理解项目的完整流程
    b. 在复杂的流程中快速定位并解决问题
    c. 根据知识储备制定一些可扩展流程
    d. 预见项目的未来发展方向

前端工程化体系:

①. 明确前后端任务分离的能力:
    a. 任务属于前端还是后端,利于前端工程化的接入
    b. 基于前端工程化解决问题的基础

②. 核心特性:
    a. 模块化、组件化、规范化和自动化
    b. 如何实现?各自的标准是什么?

③. 前端工程化领域实践:
    a. 利用工程架构的知识重构项目
    b. 脚手架、组件库、工具库、多包仓库、私有仓库、接口系统、文档系统、监控系统、CI/CD、可移植容器
    c. 从手动处理流程转换为自动处理流程,让其它成员更专注于自身业务需求

前端工程化的意义:

①. 前后分离:
    a. 前后端自成体系,且与后端分离
    b. 不限于规范、服务、环境、构建、组织和部署方面

②. 技术选型:
    a. 不能以一个框架满足所有业务场景
    b. 制定多套框架解决方案避免技术瓶颈的出现

③. 重构封装:
    a. 新生技术不断涌现就要避免改头换面式的重构
    b. 重复需求不断出现就要学会举一反三的封装

④. 工程设计:
    a. 解决方案要合理分层且互相独立,随时应对各种变化
    b. 任何一层可低成本被替换与淘汰

⑤. 所有的基建都是要依托业务才能发挥最大的作用

在这里插入图片描述

2. 如何开发一个前端脚手架?

①. 功能:
    a. 脚手架是一套命令集,不只用来创建项目.
    b. 解耦 - 脚手架与模板分离:(1). 脚手架负责构建流程,通过命令行与用户交互,获取项目信息(2). 脚手架需要检测模板的版本是否有更新,支持模板的删除与新建(3). 模板负责统一项目结构、工作流程、依赖项管理

②. 作用:
    a. 减少重复工作,不需要复制其它项目再删除无关代码,或从零创建一个项目和文件.
    b. 根据交互动态生成项目结构和配置文件.

在这里插入图片描述

1. CLI工具集:

①. 构建:
    a. 提供本地构建功能
    b. 接管发布构建

②. 质量:
    a. 自动化测试
    b. Eslint校验

③. 模板:
    a. 创建模板
    b. 创建区块

④. 工具合集:
    a. 其他可以内置的工具类

(1). 构建:

①. 小团队构建流程:
    a. 在一套或多套模板中使用webpack或rollup构建工具,配置多个文件,如.env.production、.env.development、.env.staging
    b. 通过Shell脚本来构建项目
    c. 进行部署,实现了简单、通用的CI/CD流程

②. 构建过程不可控:
    a. 团队的开发成员都可以修改发布配置项
    b. 误操作,如选择的是dev模式,没有对构建代码压缩混淆、没有注入一些全局统一方法等.

③. 优化:
    a. 构建配置和项目模板分离:(1). 将构建配置、过程从项目模板中抽离出来,统一使用CLI管理构建流程:(2).不再读取项目中的配置(3). 通过CLI使用统一配置(每一类项目都可以自定义一套标准构建配置)进行构建.
    b. 避免业务开发同学修改了错误配置而导致的生产问题.

(2). 质量:

①. 通用的自动化测试、常规的格式校验统一:
    a. 如每个开发的习惯不同,导致ESLINT校验规则不同.
    b. 同一个团队必须使用同一套代码校验规则最好.

②. 优化:
    a. 将自动化测试、校验从项目中剥离,使用CLI接管,从而保证整个团队同一类项目代码格式的统一性.

(3). 模板:

①. 可以快速、便捷初始化一个项目或代码片段.

②. Cli工具产出最高、收益最明显的功能模块.

(4). 工具合集:

①. 通用的工具类:
    a.图片压缩(png压缩)、上传CDN等
    b.项目升级(如通用配置更新了,提供一键升级模板的功能)
    c. 项目部署、发布npm包等操作

②. 其它一些重复性的操作

3. 生成最简化脚手架:

(1). 初始化package.json文件:

yarn init

(2). 在package.json中,新增bin属性:

{"name":"cli","main":"index.js","bin":{"gl-cli":"./index.js"// gl-cli表示脚手架的名称}}

(3). 根目录下新增cli.js文件:

#!/usr/bin/env node// Node CLI 应用入口文件必须要有这样的文件头,用于指定脚本的解释程序

console.log('gl-cli working!')注:
①. Linux或maxOS,需要修改此文件的读写权限为755:
    chmod 755 cli.js

(4). 把本项目/应用链接到yarn全局缓存(链接到全局),只是方便开发调试:

yarn link      // 在当前根目录执行,yarn unlink可卸载注:
①. 检查当前yarn的bin位置:
    a. yarn global bin=>/Users/xxx/.yarn/bin=> 有一个gl-cli执行命令

②. 检查当前 yarn 的 全局安装位置
    a. yarn global dir=>/Users/xxx/.config/yarn/global=>  下面有一个link文件

(5). 测试执行本cli命令:

gl-cli

①. 当打包引入的第三方库时,vender.js会很大:
    a. 一些常用固定的第三方库,不会改动源代码,不会每次都发生变化.
    b. 导致加载时空白页时间过长.
    c. 没必要每次都生成hash值,让用户重新加载.同时还会消耗带宽流量.

②. webpack提供的externals属性:
    a. externals可以将依赖的第三方库从打包文件剔除
    b. 大大减小了文件包大小,同时大幅提升编译效率.

(1). 工作原理:

①. externals配置在所创建bundle时:
    a.会依赖于用户环境(consumer's environment)中的依赖,防止将某些import的包(package)打包到bundle中
    b.在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

②. webpack会检测这些组件是否在externals中注册,如果注册则不会将其打包到app.js中

③. 修改了记得重启webpack

④. 在需要使用它的时候,可以通过CMD、AMD、或window全局方式访问

2. 哪些第三方库适合?

①. vue、vue-router、axios、element-ui、qs、crypto-js、vuex、moment、highlight.js

②. 要考虑大小不超过500kb,如果用到ueditor大型工具库需要单独打包.

(2). element-ui分析:

①. 都会把element-ui打包进去,每次修改都会下载element-ui.

②. 独立出去用cdn加载,用户下一次就有缓存.

③. 后边随便怎么改,只要有缓存就不会在下element-ui.

(3). 例子:

// externals中的key是后面需要require的名字,value是第三方库暴露出来的方法名// 'alias': 'ObjName'// 简单的配置如上,alias 是项目内使用时的组件名称,ObjName 是某外部组件对外暴露的名称。// 比如 vue 的 window 全局名称是 Vue// 比如 vue-router的 window 全局名称是 VueRouter// 比如 jquery 的 window 全局名称是 Jquery
module.exports ={externals:{'vue':'Vue','vue-router':'VueRouter','axios':'axios','element-ui':'Element','qs':'Qs'}}

(1). 优化vue.js:

①. 修改vue.config.js:const isProd = process.env.NODE_ENV==='production'constgetProdExternals=()=>{return{'vue':'Vue',// 'vue-router': 'VueRouter',// 'vuex': 'Vuex'}}
    module.exports ={...configureWebpack:{...externals: isProd ?getProdExternals():{}}}

②. 在public/index.html文件中引入vue cdn路径:<script src="//cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    a. 不写协议前缀,会与网站的协议相同.所以,可以不写https.

②.'vue':'Vue'说明:
    a. key是node模块名称,value是项目中对模块的引用
    b. 前面的vue是代码中importAfromB中的B
    c. 后面的Vue是引入的cdn暴露的变量:(1). 可以在console控制台打印window,会发现window.Vue(2). 这个Vue就是需要的变量名称

(2). 优化index.html写法:

vue.config.js:

const cdn ={css:[],js:[// 与package.json里面的版本对应'//cdn.bootcss.com/vue/2.6.10/vue.min.js','//cdn.bootcss.com/vue-router/3.0.6/vue-router.min.js','//cdn.bootcss.com/vuex/3.1.0/vuex.min.js']}
module.exports ={chainWebpack(config){...
    config.plugins.delete('prefetch')// 加载配置
    config.plugin('html').tap(args=>{if(process.env.NODE_ENV==='production'){
        args[0].cdn = cdn
      }return args
    })...}}

在这里插入图片描述

index.html:

<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<linkhref="<%= htmlWebpackPlugin.options.cdn.css[i] %>"rel="external nofollow"rel="external nofollow"rel="preload"as="style"><linkhref="<%= htmlWebpackPlugin.options.cdn.css[i] %>"rel="external nofollow"rel="external nofollow"rel="stylesheet">
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
  <!-- <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="external nofollow" rel="preload" as="script"> --><scriptsrc="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>

1. 全局引入:

①. 安装:
    npm i element-ui -S

②. main.js引入:import ElementUI from'element-ui';import'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);

③. 弊端:
    a. 打包的文件过大.

2. 按需引入:

①. 安装组件:
    yarn add babel-plugin-component -D

②. 修改babel.config.js:{"plugins":[["component",{"libraryName":"element-ui","styleLibraryName":"theme-chalk"}]]}

③. main.js引入(下面有项目实战):import{ Button, Select }from'element-ui';import App from'./App.vue';// 方式一
    Vue.component(Button.name, Button);
    Vue.component(Select.name, Select);// 方式二
    Vue.use(Button)
    Vue.use(Select)

(2). 项目中完整组件列表和引入方式 - src/core/lazy_use.js:

import Vue from'vue'import{
  Pagination,
  Dialog,// Autocomplete,// Dropdown,// DropdownMenu,// DropdownItem,// Menu,// Submenu,// MenuItem,// MenuItemGroup,
  Input,// InputNumber,// Radio,// RadioGroup,// RadioButton,// Checkbox,// CheckboxButton,// CheckboxGroup,
  Switch,
  Select,
  Option,// OptionGroup,
  Button,// ButtonGroup,
  Table,
  TableColumn,
  DatePicker,// TimeSelect,// TimePicker,// Popover,// Tooltip,// Breadcrumb,// BreadcrumbItem,
  Form,
  FormItem,// Tabs,// TabPane,// Tag,// Tree,
  Alert,// Slider,// Icon,
  Row,
  Col,// Upload,// Progress,// Spinner,// Badge,
  Card,// Rate,
  Steps,
  Step,// Carousel,// CarouselItem,// Collapse,// CollapseItem,// Cascader,// ColorPicker,// Transfer,// Container,// Header,// Aside,// Main,// Footer,// Timeline,// TimelineItem,// Link,// Divider,// Image,// Calendar,// Backtop,// PageHeader,// CascaderPanel,// Loading,
  MessageBox,
  Message,// Notification,
  Drawer
}from'element-ui'const maps ={
  Pagination,
  Dialog,// Autocomplete,// Dropdown,// DropdownMenu,// DropdownItem,// Menu,// Submenu,// MenuItem,// MenuItemGroup,
  Input,// InputNumber,// Radio,// RadioGroup,// RadioButton,// Checkbox,// CheckboxButton,// CheckboxGroup,
  Switch,
  Select,
  Option,// OptionGroup,
  Button,// ButtonGroup,
  Table,
  TableColumn,
  DatePicker,// TimeSelect,// TimePicker,// Popover,// Tooltip,// Breadcrumb,// BreadcrumbItem,
  Form,
  FormItem,// Tabs,// TabPane,// Tag,// Tree,
  Alert,// Slider,// Icon,
  Row,
  Col,// Upload,// Progress,// Spinner,// Badge,
  Card,// Rate,
  Steps,
  Step,// Carousel,// CarouselItem,// Collapse,// CollapseItem,// Cascader,// ColorPicker,// Transfer,// Container,// Header,// Aside,// Main,// Footer,// Timeline,// TimelineItem,// Link,// Divider,// Image,// Calendar,// Backtop,// PageHeader,// CascaderPanel,
  Drawer
}// 只有一部分组件是use引入
Object.keys(maps).forEach(item=>{
  Vue.use(maps[item])})// Vue.use(Loading.directive)// Vue.prototype.$loading = Loading.serviceVue.prototype.$msgbox = MessageBox
Vue.prototype.$alert = MessageBox.alert
// Vue.prototype.$confirm = MessageBox.confirm// Vue.prototype.$prompt = MessageBox.prompt// Vue.prototype.$notify = NotificationVue.prototype.$message = Message

(3). 项目中main.js引入:

import'./core/lazy_use'// 之前的全部注释掉// import ElementUI from 'element-ui'// import 'element-ui/lib/theme-chalk/index.css'// import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n// set ElementUI lang to EN// Vue.use(ElementUI, { locale })// 如果想要中文版 element-ui,按如下方式声明// Vue.use(ElementUI)

在这里插入图片描述

(4). 打包出来chunck可以放到cdn上.


3. 在index.html中指定版本cdn加载:

<!-- 引入样式 --><link rel="stylesheet" href="https://unpkg.com/[email protected]/lib/theme-chalk/index.css"><!-- 引入组件库 --><script src="https://unpkg.com/[email protected]/lib/index.js"></script>

(1). 安装插件:

$ yarn add babel-plugin-transform-remove-console -D

(2). 修改babel.config.js文件:

宸汐项目

constIS_PROD=['production','prod'].includes(process.env.NODE_ENV)const plugins =[['component',{'libraryName':'element-ui','styleLibraryName':'theme-chalk'}]]// 只有生产环境去掉console.logif(IS_PROD){
  plugins.push('transform-remove-console')}

module.exports ={...
  plugins
}

(3). 修改babel.config.js文件(vue-cli4):

疾控项目

module.exports ={env:{development:{plugins:["dynamic-import-node"]},production:{plugins:["transform-remove-console"]}}}

在这里插入图片描述

少了1kb左右,在源码中也找不到console.log

(4). 缺点:

①. 自己写的console去除了.

②. index.html内联的runtime代码没去除console,自己单独分离的chunk也没去除.

1. why?

①. 如果存在很多过大文件时,会导致可能阻塞后面的进程.

②. 减少包的大小:
    a. 更快的加载速度以及更好的用户体验.

(1). gzip:

①. 是一种 http 请求优化方式:
    a. 通过减少文件体积来提高加载速度
    b. 对于用户量多的网站,开启 gizp 压缩会大大降低服务器压力,提高加载速度、降低服务器流量成本.
    c. 节省了服务器的网络带宽,节约的流量非常可观.

②. 必须浏览器与服务器都支持gzip.

③. gzip算法特性:
    a. 代码相似率越大压缩效率越高.

(2). 工作原理图:

在这里插入图片描述

①. 浏览器发送请求:
    a. 在 request header 中设置属性 accept-encoding:gzip
    b. 表示浏览器支持 gzip.

②. 服务器收到请求后:
    a. 判断浏览器是否支持 gzip:(1). 如果支持 gzip,则向浏览器传送压缩过的内容.(2). 不支持则向浏览器发送未经压缩的内容.
    b. Response headers返回包含 content-encoding:gzip.

③. 浏览器接收响应后判断内容是否被压缩,如果被压缩则解压缩显示页面内容:
    a. 浏览器先解压再使用,对于用户是无感知的.

2. 两种 gzip 压缩方式:

①. webpack打包生成 .gz 文件:
    a. 通过 webpack 配置生成对应的 .gz 文件.
    b. 浏览器请求 xx.js/css 等文件时,服务器返回对应的 xxx.js.gz 文件.

②. 服务器实时在线将请求 xx.js 文件进行gzip压缩后传输给浏览器:
    a. 压缩文件过程本身有额外开销.
    b. 服务器压缩的时间开销和 CPU开销(及浏览器解析压缩文件的开销)为代价,来节省传输过程中的时间开销.

1. 配置:

(1). 安装插件:

①. 安装 compression-webpack-plugin:
    yarn add [email protected]

②. 新版本 7.x 会报错:
    a. Cannot read property 'tapPromise'ofundefined

(2). 在 vue.config.js 中配置:

const CompressionPlugin =require('compression-webpack-plugin');

module.exports ={chainWebpack(config){...// 方式一:
        config
            .when(process.env.NODE_ENV==='production',config=>{
                    config
                        .plugin('compression').use(CompressionPlugin).tap(()=>[{test:/\.js$|\.html$|\.css$/,// 匹配文件名,开启js、css压缩filename:'[path].gz[query]',// 压缩后的文件名(保持原文件名,后缀加.gz)minRatio:1,// 压缩率小于1才会压缩threshold:10240,// 对超过10k的数据压缩deleteOriginalAssets:false// 是否删除未压缩的源文件(不设置或设置为false)// 保留非gzip的资源,删除打包后的gz后还可以加载到原始资源文件,建议不要设置为true}])})// 方式二:if(process.env.NODE_ENV==='production'){
            config.plugin('compression-webpack-plugin').use(newCompressionPlugin({test:/\.js$|\.html$|\.css/,threshold:10240,deleteOriginalAssets:false}))}}}

①. test 另种写法:const productionGzipExtensions =['html','js','css']test:newRegExp('\\.('+ productionGzipExtensions.join('|')+')$')

②. 打包后目录会多出 .gz 文件:-rw-r--r--1 xx  staff   4275632823:01 app.9c5d6e51.js
    -rw-r--r--1 xx  staff   1449532823:01 app.9c5d6e51.js.gz
    -rw-r--r--1 xx  staff   1407232823:01 chunk-19edcdf1.2e318185.js
    -rw-r--r--1 xx  staff    479132823:01 chunk-19edcdf1.2e318185.js.gz
    // 有些没有gz是因为大小没有超过设定的10k-rw-r--r--1 xx  staff      1132823:01 chunk-47179b48.01af0134.js
    ...

③. 打包只有一个没有名称的 .gz 文件,并提示:
    warning
    Conflict: Multiple assets emit different content to the same filename static/js/.gz
    ...-rw-r--r--1 xx  staff    48K  32910:39.gz     // 没有名字的gz文件-rw-r--r--1 xx  staff    39K  32910:39 app.3c690d0c.js
    a. 要修改 filename 的设置为 filename ,老版本为'[path].gz[query]'.

(3). 服务器开启 gzip:

server {// 表示静态加载本地的gz文件// 浏览器请求xx.js/css等文件时,服务器返回对应的xxx.js.gz文件// 服务器会根据Request Headers的Accept-Encoding标签进行鉴别,如果支持gzip就返回.gz文件.// gzip_static开启后,nginx就会读取预先压缩的gz文件,可以减少每次请求进行gzip压缩的CPU资源消耗
    gzip_static on;
    gzip_http_version 1.1;}

(4). 检查是否开启Gzip成功:

curl -I-H"accept-encoding: gzip, deflate""https://admin.chaidoudou.cn/static/css/chunk-elementUI.a8b08852.css"HTTP/2200server: nginx/1.14.0(Ubuntu)date: Tue,30 Mar 202115:59:15GMT
content-type: text/css
content-length:33216
last-modified: Tue,30 Mar 202108:15:15GMTetag:"6062de13-81c0"
content-encoding: gzip

(4). 看Network:

如果发现两个大小不一样,表示Gzip压缩过


2. 分析:

(1). gzip 压缩比率:

①. 压缩前:
    a. 整个页面加载完是 8.89s.
    b. 最大的 js 文件加载是 8.31s ,大小为 593k.

②. 压缩后:
    a. 整个页面加载完是 2.21s.
    b. 最大的 js 文件加载是 1.75s,大小为 146k.

③. gzip 压缩比率在 4 倍左右.

压缩前:

在这里插入图片描述

压缩后:

在这里插入图片描述

(2). Request、Response 比对:

①. Request Headers:
    a. Accept-Encoding: gzip,deflate:(1). 表示用户浏览器支持二种压缩,包括 gzip 的压缩方式.(2). deflate 与 gzip 使用的压缩算法几乎相同.

压缩前的 request:

在这里插入图片描述

压缩后的 request:

在这里插入图片描述

(3). 其它:

②. nginx 配置了静态 gz 加载后,请求文件变小不会导致请求卡线程.

③. 保留了源文件,当删除 gz 后,浏览器会自动去请求原始文件,不会导致界面出现任何问题.

④. 静态加载 gz 文件的响应头:
    Content-Encoding: gzip

在这里插入图片描述


本文转载自: https://blog.csdn.net/wanmeijuhao/article/details/136051526
版权归原作者 完美句号 所有, 如有侵权,请联系我们删除。

“前端【全链路体系优化介绍与落地实践】”的评论:

还没有评论