搬运工之优化篇

以下的内容全是搬运,甚至大块的搬运,没办法,我也创造不出来,我就是看着写得好,我又理解了,就想手抄一下,加深印象?

部署优化

docker优化

最近读光神的小册子,他提到了一个docker多阶段构建,我去搜了搜。

找到了我以前看过的山月大佬的docker部署系列。

【前端部署第四篇】使用 Docker 构建缓存及多阶段构建优化单页应用

他在构建阶段的优化提到了两点,一个是时间,一个是空间。

  • 时间上:可以利用yarn缓存,如果 yarn.lock 内容没有变化,则不会重新依赖安装,当然这一步是利用缓存还不涉及多阶段构建

      FROM node:14-alpine as builder
    
      WORKDIR /code
    
    
      # 单独分离 package.json,是为了安装依赖可最大限度利用缓存
      ADD package.json yarn.lock /code/
      
      # 此时,yarn 可以利用缓存,如果 yarn.lock 内容没有变化,则不会重新依赖安装
      RUN yarn
    
      ADD . /code
      RUN npm run build
    
    
      CMD npx serve -s build
      EXPOSE 3000
    
    

    提到yarn.lock文件,就想起npm、yarn、pnpm的区别,知识真的会串起来

  • 空间上:
    我们的目标静态资源,完全不需要依赖于 node.js 环境进行服务化,而 node.js 环境将造成极大的资源浪费。

    我们可以使用多阶段构建进行优化(只保留最后一个阶段构建好的镜像),最终使用 nginx 进行服务化。

    1. 第一阶段 Node 镜像: 使用 node 镜像对单页应用进行构建,生成静态资源
    2. 第二阶段 Nginx 镜像: 使用 nginx 镜像对单页应用的静态资源进行服务化
    # 第一步利用node镜像构建出静态资源
    FROM node:14-alpine as builder 
    # 工作目录为 code
    WORKDIR /code

    ADD package.json yarn.lock /code/
    # 在 code中生成 node_modules
    RUN yarn
    # 将文件价复制到code中
    ADD . /code
    # 打包出静态资源
    RUN npm run build


    # 选择更小体积的基础镜像!
    FROM nginx:alpine
    # 将静态资源挂载到 nginx工作目录下面
    COPY --from=builder code/build /usr/share/nginx/html

图片加载优化

多图片如何加载?

懒加载

推荐文章 前端性能优化之图片懒加载

这是我第一次知道懒加载原理。

懒加载其实就是看到哪里才开始加载。

主要就是用到两个api,一个是 document.body.clientHeight获取视口高度、一个是getBoundingClientRect获取元素相对于视口的位置(x,y)

   function lazyload() {
     let viewHeight = document.body.clientHeight //获取可视区高度
     let imgs = document.querySelectorAll('img[data-src]')
     imgs.forEach((item, index) => {
        if (item.dataset.src === '') return

        // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
        let rect = item.getBoundingClientRect()


        // 如果元素的底部距离视口的高度大于0(我理解的就是图片有高度)
        // 并且图片的顶部距离视口的顶部小于视口的高度,也就是图片出现当前视口中
        if (rect.bottom >= 0 && rect.top < viewHeight) {
            item.src = item.dataset.src
            item.removeAttribute('data-src')
        }
     })
   }

作者还帮我们复习了一下节流函数,来逐行解释一下。

function throttle(fn, delay) {
  let timer
  let prevTime
  // 经典闭包 timer 和  prevTime 的值不会被销毁
  return function (...args) {
    // 每次函数被执行 都要重新赋值当前的currTime
    const currTime = Date.now()
    // 赋值当前的上下文(此处应该是windows)
    const context = this
    // 第一次执行当前函数时,会执行这个if语句,也就是将当前的currTime赋值过去
    if (!prevTime) prevTime = currTime
    // 每次进入都要清除上一个定时器,防止开启多个定时器以及内存泄漏
    clearTimeout(timer)
    
    // 如果说已经过了200ms,就重新执行里面的函数,否则进不来
    if (currTime - prevTime > delay) {
      // 重新执行的时候重新赋值 prevTime 为后面的判断做准备
      prevTime = currTime
      // 执行fn函数 
      fn.apply(context, args)
      // 清除掉上一个定时器  (但我觉得这里没必要?)
      clearTimeout(timer)
      return
    }
    // 每次触发都是等待200ms后执行
    timer = setTimeout(function () {
      // 重新赋值 prevTime 为后面的判断做准备
      prevTime = Date.now()
      // 清除当前定时器
      timer = null
      // 执行函数
      fn.apply(context, args)
    }, delay)
  }
}

== 我又晕了 逐行解释完我也不理解这玩意,反正就是200ms内只执行第一次就完了

IntersectionObserver

原来有个API能很好的帮助我们去实现图片的懒加载
具体文章看这里?

探秘神奇的IntersectionObserver:释放网页性能的黑科技! – 掘金 (juejin.cn)

   
   // 获取所有的需要监听的对象
   const imgs = document.querySelectorAll("img[data-src]");


    const config = {
      rootMargin: "0px",
      threshold: 0,
    };


    // 初始化一个 IntersectionObserver 实例
    let observer = new IntersectionObserver((entries, self) => {
      // entries 就是所有的 被当前实例observe的对象组成的数组,这里也就是我们12张图片
      entries.forEach((entry) => {
        // 被观察的对象会自动新增isIntersecting,如果是true,就说明在可视区域内
        if (entry.isIntersecting) {
          let img = entry.target;
          let src = img.dataset.src;
          // 渲染图片
          if (src) {
            img.src = src;
            img.removeAttribute("data-src");
          }
          // 停止观察
          self.unobserve(entry.target);
        }
      });
    });
    
    // 观察要监听的对象
    imgs.forEach((image) => {
      observer.observe(image);
    });

预加载

推荐文章

preload、prefetch、preconnect 和 dns-prefetch 知多少 – 掘金 (juejin.cn)

preload

提前加载当前页面需要的内容,不会阻塞onload事件

应用案例:

一、 提前加载css中的font字体

当页面中使用了自定义字体的时候,就必须在 CSS 中引入该字体,而由于字体必须要等到浏览器下载完且解析该 CSS 文件的时候才开始下载,所以对应页面上该字体处可能会出现闪动的现象,为了避免这种现象的出现,就可以使用 preload 来提前加载字体,type 可以用来指定具体的字体类型,加载字体必须指定 crossorigin 属性,否则会导致字体被加载两次

<link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">

二、 预加载css文件

作者介绍了两个css文件,一个是critical.css,另一个是non-critical.css。加载首屏时,我们可以把首屏可视区域的样式抽离出来,写到内置的style标签中。这样就可以让首屏快速的进行样式渲染。让不那么重要的css,进行预加载(proload),这样加载完成后也可以马上渲染到页面上。

三、 结合媒体查询预加载响应式图片

preload 甚至还可以结合媒体查询加载对应尺寸下的资源,对于以下代码当可视区域尺寸小于 600px 的时候会提前加载这张图片。

<link rel="preload" as="image" href="someimage.jpg" media="(max-width: 600px)">

四、结合 Webpack 预加载 JS 模块

import(/* webpackPreload: true */ 'ChartingLibrary');
prefetch

preload 用于提前加载用于当前页面的资源,而 prefetch 则是用于加载未来(比如下一个页面)会用到的资源,并且告诉浏览器在空闲的时候去下载,它会将下载资源的优先级降到最低。

比如在首页配置如下代码:

<link rel="prefetch" as="script" href="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js">

我们会在页面中看到该脚本的下载优先级已经被降低为 Lowest

image.png

当资源被下载完成后,会被存到浏览器缓存中,当从首页跳转到页面 A 的时候,假如页面 A 中引入了该脚本,那么浏览器会直接从 prefetch cache 中读取该资源,从而实现资源加载优化。

image.png

网络优化

当浏览器向服务器请求一个资源的时候,需要建立连接,而建立一个安全的连接需要经历以下 3 个步骤:

  • 查询域名并将其解析成 IP 地址(DNS Lookup);
  • 建立和服务器的连接(Initial connection);
  • 加密连接以确保安全(SSL);

以上 3 个步骤浏览器都需要和服务器进行通信,而这一来一往的请求和响应势必会耗费不少时间。

image.png

preconnect

当我们的站点需要对别的域下的资源进行请求的时候,就需要和那个域建立连接,然后才能开始下载资源,如果我都已经知道了是和哪个域进行通信,就可以先建立连接,然后等需要进行资源请求的时候就可以直接进行下载了。

假设当前站点是 https://a.com,这个站点的主页需要请求 https://b.com/b.js 这个资源。对比正常请求和配置了 preconnect 时候的请求,它们在请求时间轴上看到的表现是不一样的:

image.png

通过如下配置可以提前建立和 https://b.com 这个域的连接:

<link rel="preconnect" href="https://b.com">

通过 preconnect 提早建立和第三方源的连接,可以将资源的加载时间缩短 100ms ~ 500ms,这个时间虽然看起来微不足道,但是它是实实在在的优化了页面的性能,提升了用户的体验。

通过 preconnect 和别的域建立连接后,应该尽快的使用它,因为浏览器会关闭所有在 10 秒内未使用的连接。不必要的预连接会延迟其他重要资源,因此要限制 preconnect 连接域的数量

应用场景

场景一:

当知道资源是来源于哪个源下,但是对于加载哪个资源不是很明确的时候,比如对于如下这些资源:

它们要嘛是动态的,要嘛是根据不同环境携带不同参数,所以它们很适合用 preconnect 进行加载。

场景二:

如果页面上有流媒体,但是没那么快播放,又希望当按下播放按钮的时候可以越快开始越好,此时就可以使用 preconnect 预建立连接,节省一段时间。

如果用 preconnect 预建立连接的资源是一个字体文件,那么也是需要加上 crossorigin 属性。

dns-prefetch

通常我们记住一个网站都是通过它的域名,但是对于服务器来说,它是通过 IP 来记住它们的。浏览器使用 DNS 来将站点转成 IP 地址,这个是建立连接的第一步,而这一步骤通常需要花费的时间大概是 20ms ~ 120ms。因此,可以通过 dns-prefetch 来节省这一步骤的时间。

居然能通过 preconnect 来减少整个建立连接的时间,那为什么还需要 dns-prefetch 来减少建立连接中第一步 DNS 查找解析的时间呢?

假如页面引入了许多第三方域下的资源,而如果它们都通过 preconnect 来预建立连接,其实这样的优化效果反而不好,甚至可能变差,所以这个时候就有另外一个方案,那就是对于最关键的连接使用 preconnect,而其他的则可以用 dns-prefetch

可以按照如下方式配置 dns-prefetch

<link rel="dns-prefetch" href="https://cdn.bootcss.com">

就记录这些应付下面试吧

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYe3RDCc' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片