WEB前端優(yōu)化實(shí)踐

提升首屏的加載速度,是前端性能優(yōu)化中最重要的環(huán)節(jié),這里筆者梳理出一些 常規(guī)且有效 的首屏優(yōu)化建議

目標(biāo):通過(guò)對(duì)比優(yōu)化前后的性能變化翠肘,來(lái)驗(yàn)證方案的有效性,了解并掌握其原理

1商佑、路由懶加載

SPA 項(xiàng)目锯茄,一個(gè)路由對(duì)應(yīng)一個(gè)頁(yè)面,如果不做處理茶没,項(xiàng)目打包后,會(huì)把所有頁(yè)面打包成一個(gè)文件晚碾,當(dāng)用戶打開首頁(yè)時(shí)抓半,會(huì)一次性加載所有的資源,造成首頁(yè)加載很慢格嘁,降低用戶體驗(yàn)笛求。

列一個(gè)實(shí)際項(xiàng)目的打包詳情:

將路由全部改成懶加載

// 通過(guò)webpackChunkName設(shè)置分割后代碼塊的名字
const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue");
const MetricGroup = () => import(/* webpackChunkName: "metricGroup" */ "@/views/metricGroup/index.vue");
…………
const routes = [
    {
       path: "/",
       name: "home",
       component: Home
    },
    {
       path: "/metricGroup",
       name: "metricGroup",
       component: MetricGroup
    },
    …………
 ]

重新打包后,首頁(yè)資源拆分為 app.js 和 home.js糕簿,以及對(duì)應(yīng)的 css 文件

通過(guò)路由懶加載探入,該項(xiàng)目的首頁(yè)資源壓縮約 52%

路由懶加載的原理

懶加載前提的實(shí)現(xiàn):ES6的動(dòng)態(tài)地加載模塊——import()

調(diào)用 import() 之處,被作為分離的模塊起點(diǎn)懂诗,意思是蜂嗽,被請(qǐng)求的模塊和它引用的所有子模塊,會(huì)分離到一個(gè)單獨(dú)的 chunk 中
——摘自《webpack——模塊方法》的import()小節(jié)

要實(shí)現(xiàn)懶加載植旧,就得先將進(jìn)行懶加載的子模塊分離出來(lái)辱揭,打包成一個(gè)單獨(dú)的文件。

webpackChunkName 作用是 webpack 在打包的時(shí)候病附,對(duì)異步引入的庫(kù)代碼(lodash)進(jìn)行代碼分割時(shí)问窃,設(shè)置代碼塊的名字。webpack 會(huì)將任何一個(gè)異步模塊與相同的塊名稱組合到相同的異步塊中完沪。

2域庇、組件懶加載

除了路由的懶加載外,組件的懶加載在很多場(chǎng)景下也有重要的作用

舉個(gè)??:

home 頁(yè)面 和 about 頁(yè)面覆积,都引入了 dialogInfo 彈框組件较剃,該彈框不是一進(jìn)入頁(yè)面就加載,而是需要用戶手動(dòng)觸發(fā)后才展示出來(lái)

home 頁(yè)面示例:

<template>
  <div class="homeView">
    <p>home 頁(yè)面</p>
    <el-button @click="dialogVisible = !dialogVisible">打開彈框</el-button>
    <dialogInfo v-if="dialogVisible" />
  </div>
</template>
<script>
import dialogInfo from '@/components/dialogInfo';
export default {
  name: 'homeView',
  components: {
    dialogInfo
  }
}
</script>

項(xiàng)目打包后技健,發(fā)現(xiàn) home.js 和 about.js 均包括了該彈框組件的代碼(在 dist 文件中搜索dialogInfo彈框組件)

當(dāng)用戶打開 home 頁(yè)時(shí)写穴,會(huì)一次性加載該頁(yè)面所有的資源,我們期望的是用戶觸發(fā)按鈕后雌贱,再加載該彈框組件的資源

這種場(chǎng)景下啊送,就很適合用懶加載的方式引入

彈框組件懶加載:

<script>
const dialogInfo = () => import(/* webpackChunkName: "dialogInfo" */ '@/components/dialogInfo');
export default {
  name: 'homeView',
  components: {
    dialogInfo
  }
}
</script>

重新打包后,home.js 和 about.js 中沒有了彈框組件的代碼欣孤,該組件被獨(dú)立打包成 dialogInfo.js馋没,當(dāng)用戶點(diǎn)擊按鈕時(shí),才會(huì)去加載 dialogInfo.js 和 dialogInfo.css

最終降传,使用組件路由懶后篷朵,該項(xiàng)目的首頁(yè)資源進(jìn)一步減少約 11%

組件懶加載的使用場(chǎng)景

有時(shí)資源拆分的過(guò)細(xì)也不好,可能會(huì)造成瀏覽器 http 請(qǐng)求的增多

總結(jié)出三種適合組件懶加載的場(chǎng)景:

1)該頁(yè)面的 JS 文件體積大婆排,導(dǎo)致頁(yè)面打開慢声旺,可以通過(guò)組件懶加載進(jìn)行資源拆分,利用瀏覽器并行下載資源段只,提升下載速度(比如首頁(yè))

2)該組件不是一進(jìn)入頁(yè)面就展示腮猖,需要一定條件下才觸發(fā)(比如彈框組件)

3)該組件復(fù)用性高,很多頁(yè)面都有引入赞枕,利用組件懶加載抽離出該組件澈缺,一方面可以很好利用緩存,同時(shí)也可以減少頁(yè)面的 JS 文件大锌簧簟(比如表格組件姐赡、圖形組件等)

3、合理使用 Tree shaking

Tree shaking 的作用:消除無(wú)用的 JS 代碼柠掂,減少代碼體積

舉個(gè)??:

// util.js
export function targetType(target) {
  return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
}
export function deepClone(target) {
  return JSON.parse(JSON.stringify(target));
}

項(xiàng)目中只使用了 targetType 方法项滑,但未使用 deepClone 方法,項(xiàng)目打包后陪踩,deepClone 方法不會(huì)被打包到項(xiàng)目里

tree-shaking 原理:

依賴于ES6的模塊特性杖们,ES6模塊依賴關(guān)系是確定的悉抵,和運(yùn)行時(shí)的狀態(tài)無(wú)關(guān),可以進(jìn)行可靠的靜態(tài)分析摘完,這就是 tree-shaking 的基礎(chǔ)

靜態(tài)分析就是不需要執(zhí)行代碼姥饰,就可以從字面量上對(duì)代碼進(jìn)行分析。ES6之前的模塊化孝治,比如 CommonJS 是動(dòng)態(tài)加載列粪,只有執(zhí)行后才知道引用的什么模塊,就不能通過(guò)靜態(tài)分析去做優(yōu)化谈飒,正是基于這個(gè)基礎(chǔ)上岂座,才使得 tree-shaking 成為可能

Tree shaking 并不是萬(wàn)能的

并不是說(shuō)所有無(wú)用的代碼都可以被消除,還是上面的代碼杭措,換個(gè)寫法 tree-shaking 就失效了

// util.js
export default {
  targetType(target) {
    return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
  },
  deepClone(target) {
    return JSON.parse(JSON.stringify(target));
  }
};

// 引入并使用
import util from '../util';
util.targetType(null)

同樣的费什,項(xiàng)目中只使用了 targetType 方法,未使用 deepClone 方法手素,項(xiàng)目打包后鸳址,deepClone 方法還是被打包到項(xiàng)目里

在 dist 文件中搜索 deepClone 方法

究其原因,export default 導(dǎo)出的是一個(gè)對(duì)象泉懦,無(wú)法通過(guò)靜態(tài)分析判斷出一個(gè)對(duì)象的哪些變量未被使用稿黍,所以 tree-shaking 只對(duì)使用 export 導(dǎo)出的變量生效

這也是函數(shù)式編程越來(lái)越火的原因,因?yàn)榭梢院芎美?tree-shaking 精簡(jiǎn)項(xiàng)目的體積崩哩,也是 vue3 全面擁抱了函數(shù)式編程的原因之一

4巡球、骨架屏優(yōu)化白屏?xí)r長(zhǎng)

使用骨架屏,可以縮短白屏?xí)r間邓嘹,提升用戶體驗(yàn)酣栈。

國(guó)內(nèi)大多數(shù)的主流網(wǎng)站都使用了骨架屏,特別是手機(jī)端的項(xiàng)目吴超。

SPA 單頁(yè)應(yīng)用钉嘹,無(wú)論 vue 還是 react,最初的 html 都是空白的鲸阻,需要通過(guò)加載 JS 將內(nèi)容掛載到根節(jié)點(diǎn)上,這套機(jī)制的副作用:會(huì)造成長(zhǎng)時(shí)間的白屏缨睡。

常見的骨架屏插件就是基于這種原理鸟悴,在項(xiàng)目打包時(shí)將骨架屏的內(nèi)容直接放到 html 文件的根節(jié)點(diǎn)中。

使用骨架屏插件奖年,打包后的 html 文件(根節(jié)點(diǎn)內(nèi)部為骨架屏):

同一項(xiàng)目细诸,對(duì)比使用骨架屏前后的 FP 白屏?xí)r間:

  • 無(wú)骨架屏:白屏?xí)r間 1063ms

  • 有骨架屏:白屏?xí)r間 144ms

骨架屏確實(shí)是優(yōu)化白屏的不二選擇,白屏?xí)r間縮短了 86%

骨架屏插件

這里以 vue-skeleton-webpack-plugin 插件為例陋守,該插件的亮點(diǎn)是可以給不同的頁(yè)面設(shè)置不同的骨架屏震贵,這點(diǎn)確實(shí)很酷

1)安裝

npm i vue-skeleton-webpack-plugin

2)vue.config.js 配置

// 骨架屏
const SkeletonWebpackPlugin = require("vue-skeleton-webpack-plugin");
module.exports = {
   configureWebpack: {
      plugins: [
       new SkeletonWebpackPlugin({
        // 實(shí)例化插件對(duì)象
        webpackConfig: {
          entry: {
            app: path.join(__dirname, './src/skeleton.js') // 引入骨架屏入口文件
          }
        },
        minimize: true, // SPA 下是否需要壓縮注入 HTML 的 JS 代碼
        quiet: true, // 在服務(wù)端渲染時(shí)是否需要輸出信息到控制臺(tái)
        router: {
          mode: 'hash', // 路由模式
          routes: [
            // 不同頁(yè)面可以配置不同骨架屏
            // 對(duì)應(yīng)路徑所需要的骨架屏組件id利赋,id的定義在入口文件內(nèi)
            { path: /^\/home(?:\/)?/i, skeletonId: 'homeSkeleton' },
            { path: /^\/detail(?:\/)?/i, skeletonId: 'detailSkeleton' }
          ]
        }
      })        
      ]
   }
}

3)新建 skeleton.js 入口文件

// skeleton.js
import Vue from "vue";
// 引入對(duì)應(yīng)的骨架屏頁(yè)面
import homeSkeleton from "./views/homeSkeleton";
import detailSkeleton from "./views/detailSkeleton";

export default new Vue({
    components: {
        homeSkeleton,
        detailSkeleton,
    },
    template: `
    <div>
      <homeSkeleton id="homeSkeleton" style="display:none;" />
      <detailSkeleton id="detailSkeleton" style="display:none;" />
    </div>
  `,
});

5、長(zhǎng)列表虛擬滾動(dòng)

首頁(yè)中不乏有需要渲染長(zhǎng)列表的場(chǎng)景猩系,當(dāng)渲染條數(shù)過(guò)多時(shí)媚送,所需要的渲染時(shí)間會(huì)很長(zhǎng),滾動(dòng)時(shí)還會(huì)造成頁(yè)面卡頓寇甸,整體體驗(yàn)非常不好

虛擬滾動(dòng)——指的是只渲染可視區(qū)域的列表項(xiàng)塘偎,非可見區(qū)域的不渲染,在滾動(dòng)時(shí)動(dòng)態(tài)更新可視區(qū)域拿霉,該方案在優(yōu)化大量數(shù)據(jù)渲染時(shí)效果是很明顯的

虛擬滾動(dòng)圖例:

虛擬滾動(dòng)基本原理:

計(jì)算出 totalHeight 列表總高度吟秩,并在觸發(fā)時(shí)滾動(dòng)事件時(shí)根據(jù) scrollTop 值不斷更新 startIndex 以及 endIndex ,以此從列表數(shù)據(jù) listData 中截取對(duì)應(yīng)元素

虛擬滾動(dòng)性能對(duì)比:

使用虛擬滾動(dòng)使性能提升了 78%

虛擬滾動(dòng)插件

虛擬滾動(dòng)的插件有很多绽淘,比如 vue-virtual-scroller涵防、vue-virtual-scroll-list、react-tiny-virtual-list沪铭、react-virtualized 等

這里簡(jiǎn)單介紹 vue-virtual-scroller 的使用

// 安裝插件
npm install vue-virtual-scroller

// main.js
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

Vue.use(VueVirtualScroller)

// 使用
<template> 
  <RecycleScroller 
    class="scroller" 
    :items="list" 
    :item-size="32" 
    key-field="id" 
    v-slot="{ item }"> 
      <div class="user"> {{ item.name }} </div>
  </RecycleScroller> 
</template>

該插件主要有 RecycleScroller.vue壮池、DynamicScroller.vue 這兩個(gè)組件,其中 RecycleScroller 需要 item 的高度為靜態(tài)的伦意,也就是列表每個(gè) item 的高度都是一致的火窒,而 DynamicScroller 可以兼容 item 的高度為動(dòng)態(tài)的情況

6狮暑、Web Worker 優(yōu)化長(zhǎng)任務(wù)

由于瀏覽器 GUI 渲染線程與 JS 引擎線程是互斥的關(guān)系聪全,當(dāng)頁(yè)面中有很多長(zhǎng)任務(wù)時(shí),會(huì)造成頁(yè)面 UI 阻塞孝凌,出現(xiàn)界面卡頓离钝、掉幀等情況

查看頁(yè)面的長(zhǎng)任務(wù):

打開控制臺(tái)票编,選擇 Performance 工具,點(diǎn)擊 Start 按鈕卵渴,展開 Main 選項(xiàng)慧域,會(huì)發(fā)現(xiàn)有很多紅色的三角,這些就屬于長(zhǎng)任務(wù)(長(zhǎng)任務(wù):執(zhí)行時(shí)間超過(guò)50ms的任務(wù))

測(cè)試實(shí)驗(yàn):

如果直接把下面這段代碼直接丟到主線程中浪读,計(jì)算過(guò)程中頁(yè)面一直處于卡死狀態(tài)昔榴,無(wú)法操作

let sum = 0;
for (let i = 0; i < 200000; i++) {
    for (let i = 0; i < 10000; i++) {
      sum += Math.random()
    }
  }

使用 Web Worker 執(zhí)行上述代碼時(shí),計(jì)算過(guò)程中頁(yè)面正车忾伲可操作互订、無(wú)卡頓

// worker.js
onmessage = function (e) {
  // onmessage獲取傳入的初始值
  let sum = e.data;
  for (let i = 0; i < 200000; i++) {
    for (let i = 0; i < 10000; i++) {
      sum += Math.random()
    }
  }
  // 將計(jì)算的結(jié)果傳遞出去
  postMessage(sum);
}

Web Worker 具體的使用與案例,詳情見 一文徹底了解Web Worker痘拆,十萬(wàn)仰禽、百萬(wàn)條數(shù)據(jù)都是弟弟??

Web Worker 的通信時(shí)長(zhǎng)

并不是執(zhí)行時(shí)間超過(guò) 50ms 的任務(wù),就可以使用 Web Worker,還要先考慮通信時(shí)長(zhǎng)的問(wèn)題

假如一個(gè)運(yùn)算執(zhí)行時(shí)長(zhǎng)為 100ms吐葵,但是通信時(shí)長(zhǎng)為 300ms规揪, 用了 Web Worker可能會(huì)更慢

比如新建一個(gè) web worker, 瀏覽器會(huì)加載對(duì)應(yīng)的 worker.js 資源,下圖中的 Time 是這個(gè)資源的通信時(shí)長(zhǎng)(也叫加載時(shí)長(zhǎng))

當(dāng)任務(wù)的運(yùn)算時(shí)長(zhǎng) - 通信時(shí)長(zhǎng) > 50ms温峭,推薦使用Web Worker

7猛铅、requestAnimationFrame 制作動(dòng)畫

requestAnimationFrame 是瀏覽器專門為動(dòng)畫提供的 API,它的刷新頻率與顯示器的頻率保持一致诚镰,使用該 api 可以解決用 setTimeout/setInterval 制作動(dòng)畫卡頓的情況

下面的案例演示了兩者制作進(jìn)度條的對(duì)比(運(yùn)行按鈕可點(diǎn)擊)

可以看到使用定時(shí)器制作的動(dòng)畫奕坟,卡頓還是比較明顯的

jcode

setTimeout/setInterval、requestAnimationFrame 三者的區(qū)別:

1)引擎層面

setTimeout/setInterval 屬于 JS引擎清笨,requestAnimationFrame 屬于 GUI引擎

JS引擎與GUI引擎是互斥的月杉,也就是說(shuō) GUI 引擎在渲染時(shí)會(huì)阻塞 JS 引擎的計(jì)算

2)時(shí)間是否準(zhǔn)確

requestAnimationFrame 刷新頻率是固定且準(zhǔn)確的,但 setTimeout/setInterval 是宏任務(wù)抠艾,根據(jù)事件輪詢機(jī)制苛萎,其他任務(wù)會(huì)阻塞或延遲js任務(wù)的執(zhí)行,會(huì)出現(xiàn)定時(shí)器不準(zhǔn)的情況

3)性能層面

當(dāng)頁(yè)面被隱藏或最小化時(shí)检号,setTimeout/setInterval 定時(shí)器仍會(huì)在后臺(tái)執(zhí)行動(dòng)畫任務(wù)腌歉,而使用 requestAnimationFrame 當(dāng)頁(yè)面處于未激活的狀態(tài)下,屏幕刷新任務(wù)會(huì)被系統(tǒng)暫停

8齐苛、JS 的6種加載方式

1)正常模式

<script src="index.js"></script>

這種情況下 JS 會(huì)阻塞 dom 渲染翘盖,瀏覽器必須等待 index.js 加載和執(zhí)行完成后才能去做其它事情

2)async 模式

<script async src="index.js"></script>

async 模式下,它的加載是異步的凹蜂,JS 不會(huì)阻塞 DOM 的渲染馍驯,async 加載是無(wú)順序的,當(dāng)它加載結(jié)束玛痊,JS 會(huì)立即執(zhí)行

使用場(chǎng)景:若該 JS 資源與 DOM 元素沒有依賴關(guān)系汰瘫,也不會(huì)產(chǎn)生其他資源所需要的數(shù)據(jù)時(shí),可以使用async 模式擂煞,比如埋點(diǎn)統(tǒng)計(jì)

3)defer 模式

<script defer src="index.js"></script>

defer 模式下混弥,JS 的加載也是異步的,defer 資源會(huì)在 DOMContentLoaded 執(zhí)行之前对省,并且 defer 是有順序的加載

如果有多個(gè)設(shè)置了 defer 的 script 標(biāo)簽存在蝗拿,則會(huì)按照引入的前后順序執(zhí)行,即便是后面的 script 資源先返回

所以 defer 可以用來(lái)控制 JS 文件的執(zhí)行順序蒿涎,比如 element-ui.js 和 vue.js蛹磺,因?yàn)?element-ui.js 依賴于 vue,所以必須先引入 vue.js同仆,再引入 element-ui.js

<script defer src="vue.js"></script>
<script defer src="element-ui.js"></script>

defer 使用場(chǎng)景:一般情況下都可以使用 defer,特別是需要控制資源加載順序時(shí)

4)module 模式

<script type="module">import { a } from './a.js'</script>

在主流的現(xiàn)代瀏覽器中裙品,script 標(biāo)簽的屬性可以加上 type="module"俗批,瀏覽器會(huì)對(duì)其內(nèi)部的 import 引用發(fā)起 HTTP 請(qǐng)求俗或,獲取模塊內(nèi)容。這時(shí) script 的行為會(huì)像是 defer 一樣岁忘,在后臺(tái)下載辛慰,并且等待 DOM 解析

Vite 就是利用瀏覽器支持原生的 es module 模塊,開發(fā)時(shí)跳過(guò)打包的過(guò)程干像,提升編譯效率

5) preload

<link rel="preload" as="script" href="index.js">

link 標(biāo)簽的 preload 屬性:用于提前加載一些需要的依賴帅腌,這些資源會(huì)優(yōu)先加載(如下圖紅框)

vue2 項(xiàng)目打包生成的 index.html 文件,會(huì)自動(dòng)給首頁(yè)所需要的資源麻汰,全部添加 preload速客,實(shí)現(xiàn)關(guān)鍵資源的提前加載

preload 特點(diǎn):

1)preload 加載的資源是在瀏覽器渲染機(jī)制之前進(jìn)行處理的,并且不會(huì)阻塞 onload 事件五鲫;

2)preload 加載的 JS 腳本其加載和執(zhí)行的過(guò)程是分離的溺职,即 preload 會(huì)預(yù)加載相應(yīng)的腳本代碼,待到需要時(shí)自行調(diào)用位喂;

6)prefetch

<link rel="prefetch" as="script" href="index.js">

prefetch 是利用瀏覽器的空閑時(shí)間浪耘,加載頁(yè)面將來(lái)可能用到的資源的一種機(jī)制;通乘苎拢可以用于加載其他頁(yè)面(非首頁(yè))所需要的資源七冲,以便加快后續(xù)頁(yè)面的打開速度

prefetch 特點(diǎn):

1)pretch 加載的資源可以獲取非當(dāng)前頁(yè)面所需要的資源,并且將其放入緩存至少5分鐘(無(wú)論資源是否可以緩存)

2)當(dāng)頁(yè)面跳轉(zhuǎn)時(shí)规婆,未完成的 prefetch 請(qǐng)求不會(huì)被中斷

加載方式總結(jié)

async澜躺、defer 是 script 標(biāo)簽的專屬屬性,對(duì)于網(wǎng)頁(yè)中的其他資源聋呢,可以通過(guò) link 的 preload苗踪、prefetch 屬性來(lái)預(yù)加載

如今現(xiàn)代框架已經(jīng)將 preload、prefetch 添加到打包流程中了削锰,通過(guò)靈活的配置通铲,去使用這些預(yù)加載功能,同時(shí)我們也可以審時(shí)度勢(shì)地向 script 標(biāo)簽添加 async器贩、defer 屬性去處理資源颅夺,這樣可以顯著提升性能

9、圖片的優(yōu)化

平常大部分性能優(yōu)化工作都集中在 JS 方面蛹稍,但圖片也是頁(yè)面上非常重要的部分

特別是對(duì)于移動(dòng)端來(lái)說(shuō)吧黄,完全沒有必要去加載原圖,浪費(fèi)帶寬唆姐。如何去壓縮圖片拗慨,讓圖片更快的展示出來(lái),有很多優(yōu)化工作可以做

淘寶首頁(yè)的圖片資源都很小:

圖片的動(dòng)態(tài)裁剪

很多云服務(wù)赵抢,比如阿里云或七牛云剧蹂,都提供了圖片的動(dòng)態(tài)裁剪功能,效果很棒烦却,確實(shí)是錢沒有白花

只需在圖片的url地址上動(dòng)態(tài)添加參數(shù)宠叼,就可以得到你所需要的尺寸大小,比如:http://7xkv1q.com1.z0.glb.clouddn.com/grape.jpg?imageView2/1/w/200/h/200

圖片瘦身前后對(duì)比:

  • 原圖:1.8M

  • 裁剪后:12.8KB

經(jīng)過(guò)動(dòng)態(tài)裁剪后的圖片其爵,加載速度會(huì)有非常明顯的提升

圖片的懶加載

對(duì)于一些圖片量比較大的首頁(yè)冒冬,用戶打開頁(yè)面后,只需要呈現(xiàn)出在屏幕可視區(qū)域內(nèi)的圖片摩渺,當(dāng)用戶滑動(dòng)頁(yè)面時(shí)简烤,再去加載出現(xiàn)在屏幕內(nèi)的圖片,以優(yōu)化圖片的加載效果

圖片懶加載實(shí)現(xiàn)原理:

由于瀏覽器會(huì)自動(dòng)對(duì)頁(yè)面中的 img 標(biāo)簽的 src 屬性發(fā)送請(qǐng)求并下載圖片证逻,可以通過(guò) html5 自定義屬性 data-xxx 先暫存 src 的值乐埠,然后在圖片出現(xiàn)在屏幕可視區(qū)域的時(shí)候,再將 data-xxx 的值重新賦值到 img 的 src 屬性即可

<img src="" alt="" data-src="./images/1.jpg">
<img src="" alt="" data-src="./images/2.jpg">

這里以 vue-lazyload 插件為例

// 安裝 
npm install vue-lazyload 
    
// main.js 注冊(cè)
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// 配置項(xiàng)
Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'dist/error.png', // 圖片加載失敗時(shí)的占位圖
  loading: 'dist/loading.gif', // 圖片加載中時(shí)的占位圖
  attempt: 1
})

// 通過(guò) v-lazy 指令使用
<ul>  
    <li v-for="img in list">
        <img v-lazy="img.src" :key="img.src" >
    </li>
</ul>

使用字體圖標(biāo)

字體圖標(biāo)是頁(yè)面使用小圖標(biāo)的不二選擇囚企,最常用的就是 iconfont

字體圖標(biāo)的優(yōu)點(diǎn):

1)輕量級(jí):一個(gè)圖標(biāo)字體要比一系列的圖像要小丈咐。一旦字體加載了,圖標(biāo)就會(huì)馬上渲染出來(lái)龙宏,減少了 http 請(qǐng)求

2)靈活性:可以隨意的改變顏色棵逊、產(chǎn)生陰影、透明效果银酗、旋轉(zhuǎn)等

3)兼容性:幾乎支持所有的瀏覽器辆影,請(qǐng)放心使用

圖片轉(zhuǎn) base64 格式

將小圖片轉(zhuǎn)換為 base64 編碼字符串,并寫入 HTML 或者 CSS 中黍特,減少 http 請(qǐng)求

轉(zhuǎn) base64 格式的優(yōu)缺點(diǎn):

1)它處理的往往是非常小的圖片蛙讥,因?yàn)?Base64 編碼后,圖片大小會(huì)膨脹為原文件的 4/3灭衷,如果對(duì)大圖也使用 Base64 編碼次慢,后者的體積會(huì)明顯增加,即便減少了 http 請(qǐng)求翔曲,也無(wú)法彌補(bǔ)這龐大的體積帶來(lái)的性能開銷迫像,得不償失

2)在傳輸非常小的圖片的時(shí)候,Base64 帶來(lái)的文件體積膨脹瞳遍、以及瀏覽器解析 Base64 的時(shí)間開銷闻妓,與它節(jié)省掉的 http 請(qǐng)求開銷相比,可以忽略不計(jì)掠械,這時(shí)候才能真正體現(xiàn)出它在性能方面的優(yōu)勢(shì)

項(xiàng)目可以使用 url-loader 將圖片轉(zhuǎn) base64:

// 安裝
npm install url-loader --save-dev
    
// 配置
module.exports = {
  module: {
    rules: [{
        test: /.(png|jpg|gif)$/i,
        use: [{
            loader: 'url-loader',
            options: {
              // 小于 10kb 的圖片轉(zhuǎn)化為 base64
              limit: 1024 * 10
            }
        }]
     }]
  }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末由缆,一起剝皮案震驚了整個(gè)濱河市注祖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌犁功,老刑警劉巖氓轰,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異浸卦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)案糙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門限嫌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人时捌,你說(shuō)我怎么就攤上這事怒医。” “怎么了奢讨?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵稚叹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拿诸,道長(zhǎng)扒袖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任亩码,我火速辦了婚禮季率,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘描沟。我一直安慰自己飒泻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布吏廉。 她就那樣靜靜地躺著泞遗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪席覆。 梳的紋絲不亂的頭發(fā)上史辙,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音娜睛,去河邊找鬼髓霞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛畦戒,可吹牛的內(nèi)容都是我干的方库。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼障斋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纵潦!你這毒婦竟也來(lái)了徐鹤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邀层,失蹤者是張志新(化名)和其女友劉穎返敬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寥院,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劲赠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秸谢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛澎。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖估蹄,靈堂內(nèi)的尸體忽然破棺而出塑煎,到底是詐尸還是另有隱情,我是刑警寧澤臭蚁,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布最铁,位于F島的核電站,受9級(jí)特大地震影響垮兑,放射性物質(zhì)發(fā)生泄漏冷尉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一甥角、第九天 我趴在偏房一處隱蔽的房頂上張望网严。 院中可真熱鬧,春花似錦嗤无、人聲如沸震束。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垢村。三九已至,卻和暖如春嚎卫,著一層夾襖步出監(jiān)牢的瞬間嘉栓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工拓诸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侵佃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓奠支,卻偏偏與公主長(zhǎng)得像馋辈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子倍谜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345