聊一聊前端性能优化

聊一聊前端性能优化性能优化 ,每个工程师跑不掉的一个话题。这里是本人结合Mr.Max的一门课,总结的一些优化手法,希望对大家有所帮助,后续也会继续更新,想要看视频的可以直接去某课网搜。附 演示源码和PPT

前言

性能优化 ,每个工程师跑不掉的一个话题。这里是本人结合Mr.Max的一门课,总结的一些优化手法,希望对大家有所帮助,后续也会继续更新,想要看视频的可以直接去某课网搜。 演示源码和PPT有条件分享(一个赞👍),感谢各位大哥😎

首发个人博客

种一棵树,最好的时机是十年前,其次是现在

演示demo

fe-optimize 源码

聊一聊前端性能优化

演示PPT (一定要看,超帅)

在线预览

fe-ppt 使用HTML做的PPT源码

聊一聊前端性能优化

前端性能的影响(长篇大论警告❗️❗️❗️)

前端性能的一个重要指标是页面加载时间,不仅事关用户体验,也是搜索引擎排名考虑的一个因素。

  • 来自Google的数据表明,一个有10条数据0.4秒能加载完的页面,变成30条数据0.9秒加载完之后,流量和广告收入下降90%
  • Google Map 首页文件大小从100KB减小到70-80KB后,流量在第一周涨了10%,接下来的三周涨了25%
  • 亚马逊的数据表明:加载时间增加100毫秒,销量就下降1%

所以:重铸性能之光,我辈义不容辞😎

一、调试工具

磨刀不误砍柴工,读完大学再打工!

聊一聊前端性能优化 聊一聊前端性能优化

1、Network

聊一聊前端性能优化

这里可以看到资源加载详情,初步评估影响页面性能的因素。鼠标右键可以自定义选项卡,页面底部是当前加载资源的一个概览。DOMContentLoaded DOM渲染完成的时间,Load:当前页面所有资源加载完成的时间

思考:如何判断哪些资源对当前页面加载无用,做对应优化?

shift + cmd + P 调出控制台的扩展工具,添加规则 聊一聊前端性能优化

扩展工具 更多使用姿势

聊一聊前端性能优化

瀑布流waterfall

聊一聊前端性能优化

  • Queueing 浏览器将资源放入队列时间
  • Stalled 因放入队列时间而发生的停滞时间
  • DNS Lookup DNS解析时间
  • Initial connection 建立HTTP连接的时间
  • SSL 浏览器与服务器建立安全性连接的时间
  • TTFB 等待服务端返回数据的时间
  • Content Download 浏览器下载资源的时间

2、Lighthouse

聊一聊前端性能优化

  • First Contentful Paint 首屏渲染时间,1s以内绿色
  • Speed Index 速度指数,4s以内绿色
  • Time to Interactive 到页面可交换的时间

根据chrome的一些策略自动对网站做一个质量评估,并且会给出一些优化的建议。

3、Peformance

聊一聊前端性能优化

对网站最专业的分析~后面会多次讲到

4、webPageTest

可以模拟不同场景下访问的情况,比如模拟不同浏览器、不同国家等等,在线测试地址:webPageTest

聊一聊前端性能优化 聊一聊前端性能优化

5、资源打包分析

webpack-bundle-analyzer

聊一聊前端性能优化

npm install --save-dev webpack-bundle-analyzer
// webpack.config.js 文件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports={
  plugins: [
    new BundleAnalyzerPlugin({
          analyzerMode: 'server',
          analyzerHost: '127.0.0.1',
          analyzerPort: 8889,
          reportFilename: 'report.html',
          defaultSizes: 'parsed',
          openAnalyzer: true,
          generateStatsFile: false,
          statsFilename: 'stats.json',
          statsOptions: null,
          logLevel: 'info'
        }),
  ]
}

// package.json
"analyz": "NODE_ENV=production npm_config_report=true npm run build"

开启source-map

webpack.config.js

module.exports = {
    mode: 'production',
    devtool: 'hidden-source-map',
}

package.json

"analyze": "source-map-explorer 'build/*.js'",

npm run analyze 聊一聊前端性能优化

二、WEB API

工欲善其事,必先利其器。浏览器提供的一些分析API至关重要

1、监听视窗激活状态

大学都刷过慕课吧?只要离开窗口视频就会暂停~

或者一些考试网站,提醒你不能离开当前窗口

再或者,这种效果~ 聊一聊前端性能优化

// 窗口激活状态监听
let vEvent = 'visibilitychange';
if (document.webkitHidden != undefined) {
    vEvent = 'webkitvisibilitychange';
}

function visibilityChanged() {
    if (document.hidden || document.webkitHidden) {
        document.title = '客官,别走啊~'
        console.log("Web page is hidden.")
    } else {
        document.title = '客官,你又回来了呢~'
        console.log("Web page is visible.")
    }
}

document.addEventListener(vEvent, visibilityChanged, false);

其实有很多隐藏的api,这里大家有兴趣的可以去试试看:

2、观察长任务(performance 中Task)

const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        console.log(entry)
    }
})

observer.observe({entryTypes: ['longtask']})

3、监听网络变化

网络变化时给用户反馈网络问题,有时候看直播的时候自己的网络卡顿,直播平台也会提醒你或者自动给你切换清晰度

var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
var type = connection.effectiveType;

function updateConnectionStatus() {
  console.log("Connection type changed from " + type + " to " + connection.effectiveType);
  type = connection.effectiveType;
}

connection.addEventListener('change', updateConnectionStatus);

4、计算DOMContentLoaded时间

window.addEventListener('DOMContentLoaded', (event) => {
    let timing = performance.getEntriesByType('navigation')[0];
    console.log(timing.domInteractive);
    console.log(timing.fetchStart);
    let diff = timing.domInteractive - timing.fetchStart;
    console.log("TTI: " + diff);
})

5、更多计算规则

DNS 解析耗时: domainLookupEnd - domainLookupStart
TCP 连接耗时: connectEnd - connectStart
SSL 安全连接耗时: connectEnd - secureConnectionStart
网络请求耗时 (TTFB): responseStart - requestStart
数据传输耗时: responseEnd - responseStart
DOM 解析耗时: domInteractive - responseEnd
资源加载耗时: loadEventStart - domContentLoadedEventEnd
First Byte时间: responseStart - domainLookupStart
白屏时间: responseEnd - fetchStart
首次可交互时间: domInteractive - fetchStart
DOM Ready 时间: domContentLoadEventEnd - fetchStart
页面完全加载时间: loadEventStart - fetchStart
http 头部大小: transferSize - encodedBodySize
重定向次数:performance.navigation.redirectCount
重定向耗时: redirectEnd - redirectStart

三、老生常谈,雅虎军规

磨好刀了,就该想想往哪里捅比较好了~ 🗡🗡🗡

关于雅虎军规,你知道的有多少条,平时写用到的又有哪些?针对以下规则,我们可以做很多优化工作

聊一聊前端性能优化

1、减少cookie传输

cookie传输会造成带宽浪费,可以:

  • 减少cookie中存储的东西
  • 静态资源不需要cookie,可以采用其他的域名,不会主动带上cookie。

2、避免过多的回流与重绘

连续触发页面回流操作

  let cards = document.getElementsByClassName("MuiPaper-rounded");
  const update = (timestamp) => {
    for (let i = 0; i <cards.length; i++) {
      let top = cards[i].offsetTop;
      cards[i].style.width = ((Math.sin(cards[i].offsetTop + timestamp / 100 + 1) * 500) + 'px')
    }
    window.requestAnimationFrame(update)
  }
  update(1000);

看下效果,很明显的卡顿 聊一聊前端性能优化

performance分析结果,load事件之后存在大量的回流,并且chrome都给标记了红色 聊一聊前端性能优化

使用fastDom进行优化,将对dom的读和写分离,合并

 let cards = document.getElementsByClassName("MuiPaper-rounded");
  const update = (timestamp) => {
    for (let i = 0; i < cards.length; i++) {
      fastdom.measure(() => {
        let top = cards[i].offsetTop;
        fastdom.mutate(() => {
          cards[i].style.width =
            Math.sin(top + timestamp / 100 + 1) * 500 + "px";
        });
      });
    }
    window.requestAnimationFrame(update)
  }
  update(1000);

再看下效果,很流畅~ 聊一聊前端性能优化 performance分析结果,load事件之后也没有了那么多的红色标记 聊一聊前端性能优化

感兴趣的可以去了解一下fastDom:github fastdom 在线预览:fastdom demo

关于任务拆分与组合的思想,react fiber架构做的很牛逼,有兴趣的可以去了解一下调度算法在fiber中的实践

四、压缩

聊一聊前端性能优化

嗯哼哼、确定一下没有走错场子,继续继续!

1、Gzip

开启方式可参考:nginx开启gzip

聊一聊前端性能优化

还有一种方式:打包的时候生成gz文件,上传到服务器端,这样就不需要nginx来压缩了,可以降低服务器压力。 可参考:gzip压缩文件&webPack配置Compression-webpack-plugin

2、服务端压缩

server.js

const express = require('express');
const app = express();
const fs = require('fs');
const compression = require('compression');
const path = require('path');


app.use(compression());
app.use(express.static('build'));

app.get('*', (req,res) =>{
    res.sendFile(path.join(__dirname+'/build/index.html'));
});

const listener = app.listen(process.env.PORT || 3000, function () {
    console.log(`Listening on port ${listener.address().port}`);
});

package.json

"start": "npm run build && node server.js",

聊一聊前端性能优化

3、JavaScript、Css、Html压缩

工程化项目中直接使用对应的插件即可,webpack的主要有下面三个:

  • UglifyJS
  • webpack-parallel-uglify-plugin
  • terser-webpack-plugin 具体优缺点可参考:webpack常用的三种JS压缩插件压缩原理简单的讲就是去除一些空格、换行、注释,借助es6模块化的功能,做了一些tree-shaking的优化。同时做了一些代码混淆,一方面是为了更小的体积,另一方面也是为了源码的安全性。

css压缩主要是mini-css-extract-plugin,当然前面的js压缩插件也会给你做好css压缩。使用姿势:

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins:[
 new MiniCssExtractPlugin({
       filename: "[name].css",
       chunkFilename: "[id].css"
   })
]

html压缩可以用HtmlWebpackPlugin,单页项目就一个index.html,性能提升微乎其微~

4、http2首部压缩

http2的特点

  • 二进制分帧
  • 首部压缩
  • 流量控制
  • 多路复用
  • 请求优先级
  • 服务器推送http2_push: 'xxx.jpg' 具体升级方式也很简单,修改一下nginx配置,方法请自行Google

五、webpack优化

上文中也提到了部分webpack插件,下面我再来看看还有哪些~

1、DllPlugin 提升构建速度

通过DllPlugin插件,将一些比较大的,基本很少升级的包拆分出来,生成xx.dll.js文件,通过manifest.json引用

webpack.dll.config.js

const path = require("path");
const webpack = require("webpack");
module.exports = {
    mode: "production",
    entry: {
        react: ["react", "react-dom"],
    },
    output: {
        filename: "[name].dll.js",
        path: path.resolve(__dirname, "dll"),
        library: "[name]"
    },
    plugins: [
        new webpack.DllPlugin({
            name: "[name]",
            path: path.resolve(__dirname, "dll/[name].manifest.json")
        })
    ]
};

package.json

"scripts": {
    "dll-build": "NODE_ENV=production webpack --config webpack.dll.config.js",
  },

2、splitChunks 拆包

optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    name: 'vendor',
                    test: /[\\/]node_modules[\\/]/,
                    minSize: 0,
                    minChunks: 1,
                    priority: 10,
                    chunks: 'initial'
                },
                common: {
                    name: 'common',
                    test: /[\\/]src[\\/]/,
                    chunks: 'all',
                    minSize: 0,
                    minChunks: 2
                }
            }
        }
    },

六、骨架屏

用css提前占好位置,当资源加载完成即可填充,减少页面的回流与重绘,同时还能给用户最直接的反馈。 图中使用插件:react-placeholder 聊一聊前端性能优化

关于实现骨架屏还有很多种方案,用Puppeteer服务端渲染的挺多的

使用css伪类:只要css就能实现的骨架屏方案

等等

七、窗口化

原理:只加载当前窗口能显示的DOM元素,当视图变化时,删除隐藏的,添加要显示的DOM就可以保证页面上存在的dom元素数量永远不多,页面就不会卡顿

图中使用的插件:react-window 聊一聊前端性能优化

安装:npm i react-window

引入:import { FixedSizeList as List } from 'react-window';

使用:

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

八、缓存

1、http缓存

keep-alive

判断是否开启:看response headers中有没有Connection: keep-alive 。开启以后,看network的瀑布流中就没有 Initial connection耗时了

nginx设置keep-alive(默认开启)

# 0 为关闭
#keepalive_timeout 0;
# 65s无连接 关闭
keepalive_timeout 65;
# 连接数,达到100断开
keepalive_requests 100;

Cache-Control / Expires / Max-Age

设置资源是否缓存,以及缓存时间

Etag / If-None-Match

资源唯一标识作对比,如果有变化,从服务器拉取资源。如果没变化则取缓存资源,状态码304,也就是协商缓存

Last-Modified / If-Modified-Since

通过对比时间的差异来觉得要不要从服务器获取资源

更多HTTP缓存参数可参考:使用 HTTP 缓存:Etag, Last-Modified 与 Cache-Control

2、Service Worker

借助webpack插件WorkboxWebpackPluginManifestPlugin,加载serviceWorker.js,通过serviceWorker.register()注册

new WorkboxWebpackPlugin.GenerateSW({
    clientsClaim: true,
    exclude: [/\.map$/, /asset-manifest\.json$/],
    importWorkboxFrom: 'cdn',
    navigateFallback: paths.publicUrlOrPath + 'index.html',
    navigateFallbackBlacklist: [
        new RegExp('^/_'),
        new RegExp('/[^/?]+\\.[^/]+$'),
    ],
}),

new ManifestPlugin({
    fileName: 'asset-manifest.json',
    publicPath: paths.publicUrlOrPath,
    generate: (seed, files, entrypoints) => {
        const manifestFiles = files.reduce((manifest, file) => {
            manifest[file.name] = file.path;
            return manifest;
        }, seed);
        const entrypointFiles = entrypoints.app.filter(
            fileName => !fileName.endsWith('.map')
        );

        return {
            files: manifestFiles,
            entrypoints: entrypointFiles,
        };
    },
}),

聊一聊前端性能优化

九、预加载 && 懒加载

1、preload

就拿demo中的字体举例,正常情况下的加载顺序是这样的: 聊一聊前端性能优化

加入preload:

<link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.119.woff2" as="font" crossorigin="anonymous"/> 
<link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.118.woff2" as="font" crossorigin="anonymous"/> 
<link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.116.woff2" as="font" crossorigin="anonymous"/> 

聊一聊前端性能优化

2、prefetch

场景:首页不需要这样的字体文件,下个页面需要:首页会以最低优先级Lowest来提前加载

加入prefetch:

<link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.113.woff2" as="font"/> 
<link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.118.woff2" as="font"/> 
<link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.117.woff2" as="font"/> 

需要的页面,从prefetch cache中取 聊一聊前端性能优化

webpack也是支持这两个属性的:webpackPrefetch 和 webpackPreload

3、懒加载

图片

机械图片 聊一聊前端性能优化

渐进式图片(类似高斯模糊) 需要UI小姐姐出稿的时候指定这种格式 聊一聊前端性能优化

响应式图片

原生模式:<img src="./img/index.jpg" sizes="100vw" srcset="./img/dog.jpg 800w, ./img/index.jpg 1200w"/> 聊一聊前端性能优化

路由懒加载

通过函数 + import实现

const Page404 = () => import(/* webpackChunkName: "error" */'@views/errorPage/404');

十、ssr && react-snap

  • 服务端渲染SSR,vue使用nuxt.js,react使用next.js
  • react-snap可以借助Puppeteer实现先渲染单页,然后保留DOM,发送到客户端

十一、体验优化

白屏loading

聊一聊前端性能优化 loading.html 需要自取哦,还有种方式,使用webpack插件HtmlWebpackPlugin将loading资源插入到页面中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Loading</title>
    <style> body { margin: 0; } #loadding { position: fixed; top: 0; bottom: 0; display: flex; width: 100%; align-items: center; justify-content: center; } #loadding > span { display: inline-block; width: 8px; height: 100%; margin-right: 5px; border-radius: 4px; -webkit-animation: load 1.04s ease infinite; animation: load 1.04s ease infinite; } @keyframes load { 0%, 100% { height: 40px; background: #98beff; } 50% { height: 60px; margin-top: -20px; background: #3e7fee; } } </style>
  </head>

  <body>
    <div id="loadding">
      <span></span>
      <span style="animation-delay: 0.13s"></span>
      <span style="animation-delay: 0.26s"></span>
      <span style="animation-delay: 0.39s"></span>
      <span style="animation-delay: 0.52s"></span>
    </div>
  </body>
  <script> window.addEventListener("DOMContentLoaded", () => { const $loadding = document.getElementById("loadding"); if (!$loadding) { return; } $loadding.style.display = "none"; $loadding.parentNode.removeChild($loadding); }); </script>
</html>

招聘小广告

  • 微医集团 医院支撑组
  • 坐标:浙江杭州
  • 社招3年+ 或者 21届实习
  • 简历邮箱: wujj@wedoctor.com

更多性能优化,敬请期待~

学习 + 整理这些花了很长时间,路过的大哥们、小姐姐们还请给个赞👍,有问题的也可留言交流哈😝

聊一聊前端性能优化

参考文章:

今天的文章聊一聊前端性能优化分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16443.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注