vue中動態引入圖片為什麼要是require, 你不知道的那些事

2022-10-30 06:03:35

相信用過vue的小夥伴,肯定被面試官問過這樣一個問題:在vue中動態的引入圖片為什麼要使用require

有些小夥伴,可能會輕蔑一笑:呵,就這,因為動態新增src被當做靜態資源處理了,沒有進行編譯,所以要加上require, 我倒著都能背出來......

emmm... 乍一看好像說的很有道理啊,但是仔細一看,這句話說的到底是個啥?針對上面的回答,我不禁有如下幾個疑問:

  1. 什麼是靜態資源?
  2. 為什麼動態新增的src會被當做的靜態的資源?
  3. 沒有進行編譯,是指為是什麼沒有被編譯?
  4. 加上require為什麼能正確的引入資源,是因為加上require就能編譯了?

當我產生最後一個疑問的時候,發現上面的答案看似說了些啥,但好像又什麼都沒說...... 如果各位看官老爺也有如上幾個疑問,那就讓我給大家一一解惑

1.什麼是靜態資源

與靜態資源相對應的還有一個動態資源,先讓我們看看網上的各位大佬們怎麼解釋的。

靜態資源:一般使用者端傳送請求到web伺服器,web伺服器從記憶體在取到相應的檔案,返回給使用者端,使用者端解析並渲染顯示出來。

動態資源:一般使用者端請求的動態資源,先將請求交於web容器,web容器連線資料庫,資料庫處理資料之後,將內容交給web伺服器,web伺服器返回給使用者端解析渲染處理。

其實上面的總結已經很清晰了。站在一個vue專案的角度,我們可以簡單的理解為:

靜態資源就是直接存放在專案中的資源,這些資源不需要我們傳送專門的請求進行獲取。比如assets目錄下面的圖片,視訊,音訊,字型檔案,css樣式表等。

動態資源就是需要傳送請求獲取到的資源。比如我們刷淘寶的時候,不同的商品資訊是傳送的專門的請求獲取到的,就可以稱之為動態資源。

2. 為什麼動態新增的src會被當做的靜態的資源?

回答這個問題之前,我們需要了解一下,瀏覽器是怎麼能執行一個vue專案的。

我們知道瀏覽器開啟一個網頁,實際上執行的是html,css,js三種型別的檔案。當我們本地啟動一個vue專案的時候,實際上是先將vue專案進行打包,打包的過程就是將專案中的一個個vue檔案轉編譯成html,css,js檔案的過程,而後再在瀏覽器上執行的。

那動態新增的src如果我們沒有使用require引入,最終會打包成什麼樣子呢,我帶大家實驗一波。

// vue檔案中動態引入一張圖片
<template>
  <div class="home">
      <!-- 通過v-bind引入資源的方式就稱之為動態新增 -->
    <img :src="'../assets/logo.png'">

可以發現,編譯過後的靜態地址確實是和dist下編譯後圖片地址是一致的,從而驗證我們的想法。

到這裡我們其實就可以解釋上面的問題了:動態新增的src,被編譯過後的靜態路徑為什麼無法正確的引入資源?

因為動態的新增的src編譯過後的地址,與圖片資源編譯過後的資源地址不一致, 導致無法正確的引入資源

  編譯過後的src地址:../assets/logo.png
  編譯過後的圖片資源地址:/img/logo.6c137b82.png

那要怎麼解決上述的問題呢,答案就是:require

4. 加上require為什麼能正確的引入資源,是因為加上require就能編譯了?

針對這個問題,首先就要否定後半句,無論加不加require,vue檔案中引入一張圖片都會被編譯。

接著我們再來好好了解一下,require。

4.1 require是什麼: 是一個node方法,用於引入模組,JSON或本地檔案

4.2 呼叫require方法引入一張圖片之後發生了什麼:

在回答這個問題之前,容我先對問題3中的內容進行一定的補充。其實如果真的有小夥伴跟著問題三中的操作進行驗證,估計就要開噴了:為什麼我靜態引入的圖片最終編譯的地址和你的不一樣,是個base64,而且打包之後dist下面也沒有生成新的圖片。大概就是下面這樣的情況。

// vue檔案中靜態的引入一張圖片
<template>
<div class="home">
    <!-- 直接引入圖片靜態地址, 不再使用v-bind -->
  <img src="../assets/logo.png">

上圖就是vue中webpack預設的圖片打包規則。設定 type: 'asset',預設的,對於小於8k的圖片,會將圖片轉成base64 直接插入圖片,不會再在dist目錄生成新圖片。對於大於8k的圖片,會打包進dist目錄,之後將新圖片地址返回給src。

而我在上述測試中使用的圖片,是vue-cli自帶的一張logo圖片,大小是6.69k。按照預設的打包規則,是會轉成base64,嵌入圖片中的。所以為了講述方便,我在vue.config.js中修改了其預設的設定,設定如下:

module.exports = {
    // 使用configureWebpack物件,下面可以直接按照webpack中的寫法進行編寫
    // 編寫的內容,最終會被webpack-merge外掛合併到webpack.config.js主組態檔中
  configureWebpack: { 
    module: {
      rules: [
        {
          test: /\.(png|jpe?g|gif|webp|avif)(\?.*)?$/,
          type: 'asset',
          parser: {
            dataUrlCondition: {
             // 這裡我將預設的大小限制改成6k。
              // 當圖片小於6k時候,使用base64引入圖片;大於6k時,打包到dist目錄下再進行引入
              maxSize: 1024 * 6
            }
          }
        }
      ]
    }
  }
}

那上面說了這麼多,和require有啥關係,自然是有滴。

我們現在知道vue最終是通過webpack打包,並且會在webpack組態檔中編寫一系列打包規則。而webpack中的打包規則,針對的其實是一個一個模組,換而言之webpack只會對模組進行打包。那webpack怎麼將圖片當成一個模組呢,這就要用到我們的正主require。

當我們使用require方法引入一張圖片的時候,webpack會將這張圖片當成一個模組,並根據組態檔中的規則進行打包。我們可以將require當成一個橋樑,使用了require方法引入的資源,該資源就會當成模組並根據組態檔進行打包,並返回最終的打包結果。

回到問題4.2:呼叫require方法引入一張圖片之後發生了什麼

1.如果這張圖片小於專案中設定的資源限制大小,則會返回圖片的base64插入到require方法的呼叫處

2.如果這張圖片大於專案中設定的資源限制大小,則會將這個圖片編譯成一個新的圖片資源。require方法返回新的圖片資源路徑及檔名

回到問題4:為什麼加上require能正確的引入資源

因為通過require方法拿到的檔案地址,是資原始檔編譯過後的檔案地址(dist下生成的檔案或base64檔案),因此可以找對應的檔案,從而成功引入資源。

答案就是這麼簡單,來驗證一波

// vue檔案中使用require動態的引入一張圖片
<template>
  <div class="home">
      <!-- 使用require動態引入圖片 -->
      <img :src="require('../assets/logo.png')">

有問題嗎,沒有問題。到這裡,不妨再對我們的標準答案進行一次優化:

因為動態新增的src,編譯過後的檔案地址和被編譯過後的資原始檔地址不一致,從而無法正確引入資源。而使用require,返回的就是資原始檔被編譯後的檔案地址,從而可以正確的引入資源

看到這,估計還是有一些小夥伴有一些疑問,我再擴充套件一波:

6. 問題3中,靜態的引入一張圖片,沒有使用require,為什麼返回的依然是編譯過後的檔案地址?

答:在webpack編譯的vue檔案的時候,遇見src等屬性會預設的使用require引入資源路徑。參照vue-cli官方的一段原話

當你在 JavaScript、CSS 或 *.vue 檔案中使用相對路徑 (必須以 . 開頭) 參照一個靜態資源時,該資源將會被包含進入 webpack 的依賴圖中。在其編譯過程中,所有諸如 <img src="...">background: url(...) 和 CSS @import 的資源 URL 都會被解析為一個模組依賴

例如,url(./image.png) 會被翻譯為 require('./image.png'),而:

<img src="./image.png">

將會被編譯到:

h('img', { attrs: { src: require('./image.png') }})

7. 按照問題6中所說,那麼動態新增src的時候也會使用require引入,為什麼src編譯過後的地址,與圖片資源編譯過後的資源地址不一致

答:因為動態引入一張圖片的時候,src後面的屬性值,實際上是一個變數。webpack會根據v-bind指令去解析src後面的屬性值。並不會通過reuqire引入資源路徑。這也是為什麼需要手動的新增require。

8.據說public下面的檔案不會被編譯,那我們使用靜態路徑去引入資源的時候,也會預設的使用require引入嗎?

官方的原文是這樣子的:

任何放置在 public 資料夾的靜態資源都會被簡單的複製,而不經過 webpack。你需要通過絕對路徑來參照它們。

答:不會,使用require引入資源的前提的該資源是webpack解析的模組,而public下的檔案壓根就不會走編譯,也就不會使用到require。

9.為什麼使用public下的資源一定要絕對路徑

答:因為雖然public檔案不會被編譯,但是src下的檔案都會被編譯。由於引入的是public下的資源,不會走require,會直接返回程式碼中的定義的檔案地址,該地址無法在編譯後的檔案目錄(dist目錄)下找到對應的檔案,會導致引入資源失敗。

10.上檔案中提到的webpack,為什麼引入資源的時候要有base64和打包到dist目錄下兩種的方式,全部打包到的dist目錄下,他不香嗎?

答:為了減少http請求。頁面中通過路徑引入的圖片,實際上都會向伺服器傳送一個請求拿到這張圖片。對於資源較小的檔案,設定成base64,既可以減少請求,也不會影響到頁面的載入效能。

以上就是今天的全部內容啦,謝謝各位看官老爺的觀看。不好的地方,還請包涵。不對的地方,還請指正。