优化

86 阅读10分钟

前端性能优化

为什么出现白屏

spa单页面加载过程

首先是html:FP 阶段:首次渲染

其次是css,js FCP阶段:首次内容渲染

最后是FMP:页面的主要内容出现在屏幕上

总结:FP->FMP之间的会出现白屏,header背景色会出现在屏幕上,但是数据会在ajax结束后解析放到html中去

如何解决白屏

  • webpack预渲染:没有ajax的交互
  • 同构渲染:一套代码多端使用 vue->json->vue-sever-render->html
  • SSR:服务端渲染 服务端把所有数据渲染完成然后返回到客户端 ssr->请求->node->解析->客户端
    • seo 首屏优化
  • 路由懒加载:
    • 在需要的时候去加载路由
    • 按照路由将页面进行划分,一个路由文件分成若干个文件
    • const app = () => import('')
    • 1、异步组件:vue-router配置路由
      • { path: '/problem', name: 'problem', component: resolve => require(['../pages/home/problemList'], resolve) }
    • 2、es6 标准语法 import
      • { path: '/vipcard', component: ()=> import('../views/vipcard/VipCard.vue') }
    • 3、webpack的require.ensure()
  • quicklink:浏览器在空闲时加载数据
  • Gzip压缩:后端开启gzip 前端:npm i compression-webpack-plugin -D test匹配压缩文件的类型
  • 外链css,js文件
  • webpack-entry:将单页改成多页应用,比如一些组件中,vue.js vue-router等插件已经在某个页面使用了,然后给它缓存起来,下次就无需加载
  • 骨架屏:
  • loading:

首屏加载慢

原因:

1、网络延时问题

2、资源文件体积是否过大

3、资源是否重复发送请求去加载了

4、加载脚本的时候,渲染内容堵塞了

如何首屏优化

路由懒加载
打包文件中去掉map文件:找到config文件夹下index.js文件,把这个build里面的productionSourceMap改成false即可
减小入口文件体积
静态资源本地缓存
UI框架按需加载
图片资源的压缩
组件重复打包
开启GZip压缩
使用SSR
预渲染

做过哪些vue的性能优化

-   对象层级不要过深,否则性能就会差
-   不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
-   v-if 和 v-show 区分使用场景
-   computed 和 watch 区分使用场景
-   v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
-   大数据列表和表格性能优化-虚拟列表/虚拟表格
-   防止内部泄漏,组件销毁后把全局变量和事件销毁
-   图片懒加载
-   路由懒加载
-   第三方插件的按需引入
-   适当采用 keep-alive 缓存组件
-   防抖、节流运用
-   服务端渲染 SSR or 预渲染

做过什么前端性能优化

从页面的渲染过程来建立自己的一个前端性能优化的体系

要优化一个网站的性能,首先需要学会如何衡量一个网站的性能。

rail 模型:response animation load idie

以用户为中心的性能指标
-   First Paint 首次绘制(FP)

-   First contentful paint 首次内容绘制 (FCP)

-   核心1、Largest contentful paint 最大内容绘制 (LCP)

-   核心2、First input delay 首次输入延迟 (FID)渲染完成到交互耗时

-   Time to Interactive 可交互时间 (TTI)首次可交互时间

-   Total blocking time 总阻塞时间 (TBT)

-   核心3、Cumulative layout shift 累积布局偏移 (CLS)

1、Lighthouse

2、web-vitals-extension

你可以通过安装  web-vitals-extension 插件来获取三大核心指标

3、web-vitals库

>使用:
import {getCLS, getFID, getLCP} from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);

4、Chrome

页面渲染过程中优化

网页输入网址按下回车键发生了什么

输入网址
解析url:协议 主机 端口 路径 参数 锚点6部分
查找浏览器缓存:强缓存和协商缓存
    开启浏览器缓存:cache-control 
    -   浏览器发送请求前,根据请求头的`expires`和`cache-control`判断是否命中(包括是否过期)强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。
    -   没有命中强缓存规则,浏览器会发送请求,根据请求头的`If-Modified-Since`和`If-None-Match`判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则进入下一步。
    -   如果前两步都没有命中,则直接从服务端获取资源。

    第三方库公共模块抽取
    -   如何提取第三方库呢?在 webpack4.x 中, SplitChunksPlugin 插件取代了 CommonsChunkPlugin 插件来进行公共模块抽取,我们可以对SplitChunksPlugin 进行配置进行 **拆包** 操作
    
dns解析:域名解析系统
    优化:DNS负载均衡技术 访问量过大 应答dns查询 对每个请求返回不同的结果 引到不同的服务器上去
    
tcp/ip连接:osi7️⃣层模型 
    三次握手:建立连接
            客户端syn=1 seq=1 告诉我要发请求了,看你能收到吗
            服务端收到 发送ack=seq+1 syn=1 表达我收到了
            客户端验证ack 和 seq的值 发送ack 服务端的seq+1(告诉服务器我马上要发了,准备接受),服务端收到验证完建立连接

http请求:https区别 
    优化:采用http2
    优点:多路复用:提升效率 流的概念 二进制数据比文本效率高 头部信息压缩:减少带宽的浪费
    
服务器响应:
浏览器渲染页面:
    字节(编码)→ 字符(令牌化) → 令牌(词法分析转成对象) → 节点 → 对象模型(dom构建)。
    DOM+CSSOM -> Layout Tree
    渲染流程:
        1、DOM分割为多个图层
        2、对每个图层的节点计算样式结果
        3、每个节点生成图形和位置(重排、回流)
        4、每个节点绘制填充到图层位中(重绘)
        5、图层作为纹理上传至GPU
        6、组合多个图层到页面上生成最终图像
    如何引起的回流和重绘
        1、页面首次渲染
        2、浏览器窗口变化
        3、元素尺寸或者位置变化
        4、元素内容字体大小变化
        5、添加删除元素
        6、激活css伪类
        7、查询属性或者调用某些方法
    优化:
        1、静态资源使用CDN:让用户离服务器更近 减少请求的时间
        2、防止脚本阻塞:css头部 js尾部延迟加载
        3、图片优化:压缩图片 延迟加载图片 webp格式图片 响应式图片
            -  使用url-loader
            
        4、使用webpack压缩文件:
            -   JavaScript:UglifyPlugin
            -   CSS :MiniCssExtractPlugin
            -   HTML:HtmlWebpackPlugin
           或者gzip 压缩:向 HTTP 请求头中的 Accept-Encoding 头添加 gzip 标识来开启这一功能。服务器也得支持这一功能。
        5、减少回流和重绘
            css
           -   减少使用table布局
           -   避免设置深层次样式
           -   避免使用css表达式
           -   dom的末端修改css
            js
           -   避免重复操作样式 
           -   使用文档碎片 操作dom 结束后将他插入到页面
           -   动画元素使用定位 脱离文档流
           -   使用display:none;元素操作完成后显示出来
        6、代码分割
            组件和路由
        7、tree-shaking
            去除死代码无用代码
            可以通过在启动 webpack 时追加参数 --optimize-minimize 来实现
        8、骨架屏
        9、服务端渲染
断开连接:四次挥手
    两者都属于establish状态
    1、客户端发送关闭连接请求 fin报文(含序列号) 处于-fin_wait状态
    2、收到fin,ack=序列号+1,close_wait状态 表示我收到请求了
    3、服务端数据处理完成,也想断开连接了,发送syn报文序列号,处于last_ack状态
    4、客户端收到了,ack=服务端的ack+1 time_wait状态,等一会确保服务端收到报文之后进入closed状态

webpack优化性能

优化的是webpack的打包结果 时间层面和体积层面

时间层面:webpack的构建速度

  1. 定向查找:resolve.modules,resolve.extensions

  2. 减少执行构建的模块:合理配置loader的includes,excludes

  3. 开启缓存:babel-loader,cache-loader

  4. 并行构建:happypack

  5. 并行压缩:uglifywebpackplugin开启paralle

体积层面:构建结果

  1. 压缩代码:uglifyJsPlugin parannelUglifyJsPlugin 压缩js文件,cssnano(css-loader)压缩css文件
  • html:html-webpack-plugin
module.export = {
  plugins: [
    new HtmlWebpackPlugin({
      // 动态生成 html 文件
      template: "./index.html",
      minify: {
        // 压缩HTML
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空⽩符与换⾏符
        minifyCSS: true // 压缩内联css
      },
    })
  ]
}
// webpack5
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: ["babel-loader"],
        exclude: /node_modules/, //排除 node_modules 目录
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
        exclude: /node_modules/, //排除 node_modules 目录
      },
    ]
  },
  plugins: [
    new MiniCssExtractPlugin()
  ],
  optimization: {
    // 是否需要压缩
    minimize: true, // 开发环境需要开启
    // 配置压缩工具
    minimizer: [
      // 添加 css 压缩配置
      new OptimizeCssAssetsPlugin({}), // 需要安装
    ],
  },
}
//entry文件
const names = ["randy", "jack"];
const say = (_name) => {
  console.log(_name);
};
say(Math.random() > 0.5 ? names[1] : names[0]);
// 配置
const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); // 需要安装
module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: ["babel-loader"],
        exclude: /node_modules/, //排除 node_modules 目录
      },
    ]
  },
  optimization: {
    // 是否需要压缩
    minimize: true, // 开发环境需要开启
    // 配置压缩工具
    minimizer: [
      new UglifyJsPlugin({})
    ],
  },
}
  • image: file-loader之后加上image-webpack-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif|jpeg|webp|svg)$/,
        use: [
          "file-loader",
          {
            loader: "image-webpack-loader",
            options: {
              mozjpeg: {
                progressive: true,
              },
              // optipng.enabled: false will disable optipng
              optipng: {
                enabled: false,
              },
              pngquant: {
                quality: [0.65, 0.9],
                speed: 4,
              },
              gifsicle: {
                interlaced: false,
              },
            },
          },
        ],
        exclude: /node_modules/, //排除 node_modules 目录
      },
    ]
  },
}

  1. 按需加载:路由懒加载或者组件
  2. 提前加载:prefetch:true preload
  3. tree shaking:去除死代码 js插件
// webpack.config.js
optimization: {
  usedExports: true
},
// package.json
"sideEffects": false

css插件:purgecss-webpack-plugin

const path = require("path");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const glob = require("glob"); // 文件匹配模式
plugins: [
  // ...
  new PurgecssPlugin({
    // 这里我的样式在根目录下的index.html里面使用,所以配置这个路径
    paths: glob.sync(`${path.join(__dirname)}/index.html`, { nodir: true }),
  }),
]
  1. 代码分割:SplitChunksPlugin
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async', // 值有 `all`,`async` 和 `initial`
      minSize: 20000, // 生成 chunk 的最小体积(以 bytes 为单位)。
      minRemainingSize: 0,
      minChunks: 1, // 拆分前必须共享模块的最小 chunks 数。
      maxAsyncRequests: 30, // 按需加载时的最大并行请求数。
      maxInitialRequests: 30, // 入口点的最大并行请求数。
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

  1. 提取第三方库
  2. cdn加速
  3. gzip:安装compression-webpack-plugin 并在plugins配置

前端除了在打包的时候将无用的代码或者 console、注释剔除之外。我们还可以使用 Gzip 对资源进行进一步压缩。Gzip 原本是 UNIX 系统的文件压缩,后来逐步成为 web 领域主流的压缩工具。那么浏览器和服务端是如何通信来支持 Gzip 呢?

  1. 当用户访问 web 站点的时候,会在 request header 中设置 accept-encoding:gzip,表明浏览器是否支持 Gzip

  2. 服务器在收到请求后,判断如果需要返回 Gzip 压缩后的文件那么服务器就会先将我们的 JS\CSS 等其他资源文件进行 Gzip 压缩后再传输到客户端,同时将 response headers 设置 content-encoding:gzip。反之,则返回源文件。

  3. 浏览器在接收到服务器返回的文件后,判断服务端返回的内容是否为压缩过的内容,是的话则进行解压操作。

作者:苏苏同学
链接:juejin.cn/post/724481…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

const CompressionWebpackPlugin = require("compression-webpack-plugin"); // 需要安装
module.exports = {
  plugins: [
    new CompressionWebpackPlugin()
  ]
}

常用分析工具:

  1. 时间: speed-measure-webpack-plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); // 需要安装
const smp = new SpeedMeasurePlugin();

module.exports = () => smp.wrap(config); // 使用smp包裹webpack的配置

  1. 构建结果: webpack-bundle-analyze
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 需要安装

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}