美文网首页webpack前端already
动态引入图片-require()和import()

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

作者: wu0228 | 来源:发表于2022-04-02 11:54 被阅读0次

    现如今的前端项目用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本身是一个预编译路径 不能require纯变量的打包工具,无法预测未知变量路径
    • require(path) ,path 至少要有三部分组成, 目录,文件名和后缀
    • 目录:webpack 才知道从哪里开始查找
    • 后缀 文件后缀,必须要加上,不然会报错
    • 文件名:可用变量表示

    错误引用

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

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

    正确引用

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

    • 分析目录: ‘../images’
    • 提取正则表达式 : ‘/^.*.png$/‘

    但是此种情况下,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。

    • resolve 是一个函数,它返回 request 被解析后得到的模块 id。
    • keys 也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请求(译者注:参考下面第二段代码中的 key)组成。
    • id 是 context module 里面所包含的模块 id. 它可能在你使用 module.hot.accept 时会用到。

    图片预加载

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

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

    相关文章

      网友评论

        本文标题:动态引入图片-require()和import()

        本文链接:https://www.haomeiwen.com/subject/tzdtsrtx.html