webpack前端already

动态引入图片-require()和import()

2022-04-02  本文已影响0人  wu0228

现如今的前端项目用webpack打包已经成为了行业趋势,然而在此模式的前提下,图片的引入是我们不可避免的问题。正常的图片引入是用img标签或者元素背景图的方式,采用这种方式的图片,webpack都能正常打包并显示。但是如果直接在js文件中定义图片路径,并赋给图片元素的话不能正常显示的,这是因为webpack打包后,会将静态资源文件放在dist/static/img下,我们的网站实际上以dist目录作为根目录,并由此加载该目录下的index.html所需的css、js、img等。而当我们在js文件中动态引入图片时url-loader是无法探测到图片路径的。我们build后发现,图片根本不会打包输出到dist目录(webpack是按需打包的)。

一、require

// html
<img :src="imgList[0]" />
// xxx.js
let imgList = [
    require('../images/a.png'),
    require('../images/b.jpg')
]

官方文档:如果你的 request 含有表达式(expressions),会创建一个上下文(context),因为在编译时(compile time)并不清楚具体是哪一个模块被导入。

错误引用

上述意思即是不能通过以下这种方式加载图片,这种方式下,webpack找不到具体是哪个模块(图片)被引入,故而无法将图片hash并输出到dist文件下。

let imgUrlStr = '../images/a.png'; 
let imgUrl = require(imgUrlStr);

正确引用

鉴于require在纯变量的情况下找不到模块,所以我们至少要在require参数中写明一个目录,这样的话,虽然不知道具体的模块,但是webpack也会为我们做些分析工作:

但是此种情况下,webpack生成的上下文模块(context module)。它包含目录下的所有模块的引用,是通过一个 request 解析出来的正则表达式,去匹配目录下所有符合的模块,然后都 require 进来。此 context module 包含一个 map 对象,会把 request 中所有模块翻译成对应的模块 id。这意味着 webpack 能够支持动态地 require,但会导致所有可能用到的模块都包含在 bundle 中。

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');  // 目录 + 文件名 + 后缀

require.context

此方法可理解为require方法的详细实现,用require.context() 函数来创建自己的 context。可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。

// 语法
require.context(directory, useSubdirectories = false, regExp = /^.//);

// example
// 创建出一个 context,其中所有文件都来自父文件夹及其所有子级文件夹,request 以 `.png` 结尾。
require.context('../images', true, /.png$/);

require.context返回值

一个 context module 会导出一个(require)函数,此函数可以接收一个参数:request。
此导出函数有三个属性:resolve, keys, id。

图片预加载

动态加载文件夹下所有图片实例:

// 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++;
        };
    });
}

二、import

require是运行时加载模块,但import命令会被javascript引擎静态分析,先于模块内其他模块执行,做不到运行时加载,因此为了实现类似于require的动态加载,就提出了实现一个import()函数方法,

import(specifier);

上面代码中,import函数的参数specifier,指定所要加载的模块的位置。import命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载。

import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,也会加载指定的模块。另外,import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。

import() 特性依赖于内置的 Promise。如果想在低版本浏览器使用 import(),记得使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库,来预先填充(shim) Promise 环境。

// example
let imgUrl = '';

// 与require参数类似,不能通过纯参数的方式引入模块。正确的引入方式可查看以上require的引入方式
import('../assets/tree/tree.png').then(res => {
    imgUrl = res;
});
上一篇 下一篇

猜你喜欢

热点阅读