如何动态加载本地图片
# 0.前言
在刚开始接触 React
项目中,静态资源必须使用以 import
的方式导入。
可是,在之前的静态网页开发过程中,我们只需要以 相对路径
的方式引入静态资源:
<img src="./assets/demo.png">
但是,当使用 npm run dev
启动本地服务器,可以发现图片资源无法得到正确的引入,甚至还有可能报跨域错误。
其实,以上的问题主要是在 React
框架中已经内置了create-react-app(cra)
,在 cra
中集成了通用性的webpack
配置,这就是造成静态资源的导入失败的原因。
# 1.静态资源存放位置
首先以 相对路径 的方式引入图片等静态资源到底读取的是什么资源?
一般图片等静态资源存放的位置有两处:public/assets
、src/assets
。
两者的主要区别:
两者的区别主要是在是否可以被
webpack
所处理,public
文件下的内容会被原封不动地copy
至dist
文件夹,而src
文件下的内容则会被webpack
处理,常见的webpack处理有:
js
文件或者css
会被压缩并捆绑在一起,以避免额外的网络请求。缺少文件会导致编译错误,而不是用户的404错误。
webpack
会对文件进行rename
,如原有路径'@/assets/img/banner.png'
,转换成了'/static/img/banner.[chunk].png'
。// webpack.config.js module.exports = { output: { filename: '[name]_[hash].js' //其余占位符:Hash Chunkhash Contenthash } }
1
2
3
4
5
6
7比如图片,如果你的图片小于你在
webpack
中的loader
下设置的limit
大小(可配置),它会被编译成base64
写在行内,从而在实际项目中减少http
请求。Ps: 但是随着HTTP协议不断发展,
HTTP1.1
中已经默认开启了Keep-alive
长连接,HTTP2.0
又具有多路复用的特性,目前已经不是很有必要再做这种操作了。// 使用 url-loader 设置图片的大小 module:{ test: /.(jpg|png|gif)$/, use: { options: { name: '[name]_[hash].[ext]' limit: 2048 } } }
1
2
3
4
5
6
7
8
9
10
# 2.图片加载方案
# 2.1 若文件存在public
文件下,可以直接使用相对路径读取
<img src="./assets/demo.png"> // 读取 public/assets/demo.png
其原理是:public
下的文件会被原封不动地拷贝到build
下,而src
下的文件则被压缩至build/static
,且会被rename,所以不能使用相对路径的方式导入src下的文件。
# 2.2 若图片存在src
下,将图片等静态资源当做模块加载
常用的两种模块化方式 require
(CMD语法)和 import
(ES6语法)
关于
CMD
、ES6
、CommonJS
、AMD
的请见:https://juejin.cn/post/6844903576309858318
// import 引入静态资源
import png from './assets/demo.png' // 该文件在src下
<img src={png} alt=""></img>
// require
<img src={require("./assets/demo.png").default} alt="" ></img>
2
3
4
5
6
点击查看
额外补充的知识点:
impot
有两种使用方式:1.作为函数 2.关键字比较常见的是作为关键字,但是仍有几个需要注意的点,以下是几个错误示例:
// 1. import 作为关键字,必须作用在顶部作用域 function tryImport(){ import flag from "./example.js" } //2. import 的变量是只读属性,无法再修改,类似加了const import {num} from "./example.js" num = 1 // 报错
1
2
3
4
5
6
7import的第二种用法是作为函数,其返回的是Promise对象
import('./assets/demo.png').then(module=>{ console.log(module) }) // 输出:Module {default:'/static/media/demo.0a407da5.png',}
1
2
3
4
5从上可以看出,原本的
src
目录下的assets/demo.png
被拷贝到public/static/media
下,且图片的名称以[name].[hash].png
的方式被rename
。此外,在打印的时候仍需注意的:
console.log('<<<',import('./assets/aaa.png').then(module=>{console.log(module)})) console.log(123)
1
2打印次序为:
<<< Promise {<pending>} 123 Module {default:'/static/media/demo.0a407da5.png',}
1
2
3⭐️
import
关键字被设计为静态的,以便让诸如文本编辑器之类的工具能轻易判断模块有哪些信息可 用。--《深入理解ES6》
# 2.3 当使用 require
动态引入图片,会导致cannot find module
的问题
现在要实现的一个大的集合中遍历去加载每一项的图片,因此我将数据存放在一个json文件中,data.json
的内容如下:
[{"imagePath": "@/assets/demo.png"},
{"imagePath": "@/assets/demo.png"},
{"imagePath": "@/assets/demo.png"}]
2
3
若仍用 require
的方式读入:
import picJson from './data.json'
<img src={require(picJson.imagePath).default}>
2
则会提示:can't find moudle "@/assets/demo.png"
,但require("@/assets/demo.png")
是能够正常导入的。
▶️ 原因是:require
是在 动态运行
时引入资源的,且加载的是webpack预编译后的文件路径;当 require
使用变量来引入图片时,React 在渲染时实际传给require 的是'@/assets/demo.png'
。因此在打包结果中,根本就找不到这个图片的模块。可以用下面两个方式对其进行改进:
使用模板字符串的方式引入
<img src={require(`${picJson.imagePath}`).default}></img>
1技巧:加上一个空字符串
<img src={require(picJson.imagePath+"").default}></img>
1
# 2.4 使用 %PUBLIC_URL%
拼接为绝对路径
这个方法有些看不懂,暂时只记录看的懂得部分:https://juejin.cn/post/6892950616977948680
html:
⭐️在
public
目录中有个index.html
是单页面应用的基本模板,所有react生成的代码都会注入到此HTML中。所以此处可以添加一些cdn脚本或者全局的html。在公共目录下,你可以放字体文件、图片、svg等文件,访问这些文件最好添加
%PUBLIC_URL%
作为根目录。<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
Javascript:
process.env.PUBLIC_URL
# 本篇blog仍未解决的问题:
- 需要再复习一下webpack的配置
npm run serve
和build
以后再启动live-server的区别?与file://
的方式打开html?有什么区别?- 在测试的过程中,
import
和require
编译后的html
为啥有区别?
# 参考资料:
- require 动态引入图片,导致
cannot find module
见:https://juejin.cn/post/6892950616977948680 - public文件夹的使用见:https://juejin.cn/post/6892950616977948680
- Vue如何动态加载本地图片:https://www.cnblogs.com/lvonve/p/15044160.html
# 扩展阅读:
webpack学习笔记--按需加载:https://www.cnblogs.com/joyco773/p/9051401.html
按需加载2:https://www.cnblogs.com/mamimi/p/7646358.html
webpack打包的多页模式(本身就是懒加载?)与单页模式,见:https://www.cnblogs.com/nangezi/p/9201226.html
任务:看完要能说出多页模式和单页模式的优缺点?
react-scripts流程及源码分析,见:https://juejin.cn/post/6844903951893004296
这篇文章实在是太难理解了,需要预先学习好多知识点,图做的超级棒👍🏻。
阮一峰写的ES6的module模块引入:https://es6.ruanyifeng.com/#docs/module-loader
掘金中总结的很好的模块化文章:https://juejin.cn/post/6844903576309858318 5和6待总结成一篇文章