動態引入(require, import)

文章轉自:https://www.dazhuanlan.com/2019/12/15/5df63790ae230/?__cf_chl_jschl_tk__=eacc74b5ed8c2f307461492753ace6f066ea65ba-1584858192-0-AXUV-b_QbPVyyf7UVyr4aiknQnDOv1jnzM5rCD2AcILNh3Xy2Wqp1DzBA_rIaarYlTHqQs0754JYY8VdPV7nmEYcotAO3JS1UjY8skRbUL68PURK_zTAphqjKqMRxux9KDe2Kr5AuIGoZEdZay3zpDag_2nV1gqHZyXT8XKB1FxEeVDxu0bdAvSQgtqnZg9QxoxWFeU2b7tpak_LwiyJsQpRXOgC-e-hCUiS8D1-tc2fPY-AjVuuItBgMJ4UT2wfmS4uFz3gmYReRvvsItXTvyq-cLcHVuzN1fgR63dw2Wx7lc3COjKQxLRSD9ey3mBrTg

本篇基於圖片加載的基礎來簡單介紹require, require.context和import的區別!

現如今的前端項目用webpack打包已經成爲了行業趨勢,然而在此模式的前提下,圖片的引入是我們不可避免的問題。正常的圖片引入是用img標籤或者元素背景圖的方式,採用這種方式的圖片,webpack都能正常打包並顯示。但是如果直接在js文件中定義圖片路徑,並賦給圖片元素的話不能正常顯示的,這是因爲webpack打包後,會將靜態資源文件放在dist/static/img下,我們的網站實際上以dist目錄作爲根目錄,並由此加載該目錄下的index.html所需的css、js、img等。而當我們在js文件中動態引入圖片時url-loader是無法探測到圖片路徑的。我們build後發現,圖片根本不會打包輸出到dist目錄(webpack是按需打包的)。

此處介紹了三種動態引入圖片的方式:

1. require

1
2
// html
<img :src="imgList[0]" />
1
2
3
4
5
6
7

// xxx.js
let imgList = [
    require('../images/a.png'),
    require('../images/b.jpg')
]

官方文檔:如果你的 request 含有表達式(expressions),會創建一個上下文(context),因爲在編譯時(compile time)並不清楚具體是哪一個模塊被導入。

原因:

  • webpack本身是一個預編譯路徑 不能require純變量的打包工具,無法預測未知變量路徑
  • require(path) ,path 至少要有三部分組成, 目錄,文件名和後綴
  • 目錄:webpack 才知道從哪裏開始查找
  • 後綴 文件後綴,必須要加上,不然會報錯
  • 文件名:可用變量表示

1. 錯誤引用

上述意思即是不能通過以下這種方式加載圖片,這種方式下,webpack找不到具體是哪個模塊(圖片)被引入,故而無法將圖片hash並輸出到dist文件下。

1
2
3
let imgUrlStr = '../images/a.png'; 

let imgUrl = require(imgUrlStr);

2. 正確引用

鑑於require在純變量的情況下找不到模塊,所以我們至少要在require參數中寫明一個目錄(如下邊代碼中的example 2和example 3),這樣的話,雖然不知道具體的模塊,但是webpack也會爲我們做些分析工作:

    1. 分析目錄: ‘../images’
    1. 提取正則表達式 : ‘/^.*.png$/‘

但是此種情況下,webpack生成的上下文模塊(context module)。它包含目錄下的所有模塊的引用,是通過一個 request 解析出來的正則表達式,去匹配目錄下所有符合的模塊,然後都 require 進來。此 context module 包含一個 map 對象,會把 request 中所有模塊翻譯成對應的模塊 id。這意味着 webpack 能夠支持動態地 require,但會導致所有可能用到的模塊都包含在 bundle 中。

1
2
3
4
5
6
7
8
9
let imgName = 'a'; 
let imgAllName = 'a.png';

// example 1
let imgUrl = require('../images/a.png');                // 純字符串
// example 2
let imgUrl = require('../images/' + imgAllName);        // 目錄 + 文件全名
// example 3
let imgUrl = require('../images/' + imgName + '.png');  // 目錄 + 文件名 + 後綴

2. require.context

此方法可理解爲require方法的詳細實現,用require.context() 函數來創建自己的 context。可以給這個函數傳入三個參數:一個要搜索的目錄,一個標記表示是否還搜索其子目錄, 以及一個匹配文件的正則表達式。

1
2
3
4
5
6
// 語法
require.context(directory, useSubdirectories = false, regExp = /^.//);

// example
// 創建出一個 context,其中所有文件都來自父文件夾及其所有子級文件夾,request 以 `.png` 結尾。
require.context('../images', true, /.png$/);

1. require.context返回值

一個 context module 會導出一個(require)函數,此函數可以接收一個參數:request。
此導出函數有三個屬性:resolve, keys, id。

  • resolve 是一個函數,它返回 request 被解析後得到的模塊 id。
  • keys 也是一個函數,它返回一個數組,由所有可能被此 context module 處理的請求(譯者注:參考下面第二段代碼中的 key)組成。
  • id 是 context module 裏面所包含的模塊 id. 它可能在你使用 module.hot.accept 時會用到。

require-context.pnguploading.4e448015.gif轉存失敗重新上傳取消require.context返回值

2. 圖片預加載

動態加載文件夾下所有圖片實例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// example 
// 圖片預加載, 
preloadAllImages () {
    let imgCounts = 0;      // 已加載圖片計數,可實現真實進度條 
    let imgsFun = require.context('../images', true, /.(png|jpg)$/);
    let imgKeys = imgsFun.keys();

    imgKeys.forEach(item => {
        let Img = new Image();
        Img.src = imgsFun(item);
        Img.onload = function () {
            imgCounts++;
        }
        Img.onerror = function () {
            imgCounts++;
        };
    });
}

3. import

require是運行時加載模塊,但import命令會被javascript引擎靜態分析,先於模塊內其他模塊執行,做不到運行時加載,因此爲了實現類似於require的動態加載,就提出了實現一個import()函數方法,

1
import(specifier);

上面代碼中,import函數的參數specifier,指定所要加載的模塊的位置。import命令能夠接受什麼參數,import()函數就能接受什麼參數,兩者區別主要是後者爲動態加載。

import()函數可以用在任何地方,不僅僅是模塊,非模塊的腳本也可以使用。它是運行時執行,也就是說,什麼時候運行到這一句,也會加載指定的模塊。另外,import()函數與所加載的模塊沒有靜態連接關係,這點也是與import語句不相同。

import() 特性依賴於內置的 Promise。如果想在低版本瀏覽器使用 import(),記得使用像 es6-promise 或者 promise-polyfill 這樣 polyfill 庫,來預先填充(shim) Promise 環境。

1
2
3
4
5
6
7
// example
let imgUrl = '';

// 與require參數類似,不能通過純參數的方式引入模塊。正確的引入方式可查看以上require的引入方式
import('../assets/tree/tree.png').then(res => {
    imgUrl = res;
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章