React项目编译阶段如何获取图片宽高尺寸
问题根源
image-size是Node.js环境专用依赖,基于Node原生fs模块读取本地文件,这类API无法在浏览器端运行。create-react-app(简称CRA)默认webpack配置不会为浏览器端代码polyfill fs等Node原生模块,直接在React组件代码中引入该包必然触发相关报错。
最优实现方案(编译阶段获取尺寸)
该方案完全匹配需求:图片尺寸在应用编译阶段就完成读取,直接写入打包产物,无运行时开销,不需要等浏览器加载图片即可拿到准确像素宽高,最终写法和预期完全一致。
无需eject CRA原生配置,通过自定义webpack loader配合配置覆写工具即可实现:
步骤1:安装必要依赖
npm install @craco/craco image-size --save-dev
步骤2:编写自定义图片处理loader
在项目根目录新建image-size-loader.js文件,在编译环节读取图片尺寸,和图片最终访问路径一起导出:
const sizeOf = require('image-size'); const path = require('path'); module.exports = function imageSizeLoader(source) { const filePath = this.resourcePath; // 读取图片实际像素尺寸 const dimensions = sizeOf(filePath); // 拼接图片编译后的最终访问路径 const assetRelativePath = path.relative(this.rootContext, filePath).replace(/\\/g, '/'); const publicPath = this._compilation.outputOptions.publicPath || ''; const contentHash = this.utils.createHash('md4').update(source).digest('hex').slice(0, 8); const fileExt = path.extname(filePath); const outputSrc = `${publicPath}static/media/${assetRelativePath.split('/').pop().replace(fileExt, '')}.${contentHash}${fileExt}`; // 导出包含路径、宽高的对象 return `export default { src: ${JSON.stringify(outputSrc)}, width: ${dimensions.width}, height: ${dimensions.height} }`; }; // 声明loader接收二进制文件格式 module.exports.raw = true;
步骤3:添加CRA配置覆写文件
在项目根目录新建craco.config.js,将自定义loader匹配到存放本地图片的目录(示例中为src/assets/images,可根据自己的目录结构调整):
const path = require('path'); module.exports = { webpack: { configure: (webpackConfig) => { // 排除CRA默认图片loader对自定义目录的处理 const rules = webpackConfig.module.rules.find(rule => !!rule.oneOf); const defaultImgRule = rules.oneOf.find(item => item.type === 'asset/resource' && item.test && item.test.test('.png')); if (defaultImgRule) { defaultImgRule.exclude = [path.resolve(__dirname, 'src/assets/images')]; } // 新增自定义loader规则 webpackConfig.module.rules.push({ test: /\.(png|jpe?g|gif|webp)$/i, include: [path.resolve(__dirname, 'src/assets/images')], use: [{ loader: path.resolve(__dirname, 'image-size-loader.js') }] }); return webpackConfig; } } };
步骤4:修改项目启动、构建命令
编辑package.json中的scripts字段,将原有的react-scripts命令替换为craco:
{ "scripts": { "start": "craco start", "build": "craco build", "test": "craco test" } }
步骤5:组件中直接使用
导入图片时即可直接拿到编译阶段注入的宽高属性,直接渲染即可:
import demoImg from './assets/images/demo.png'; function App() { return ( <img src={demoImg.src} width={demoImg.width} height={demoImg.height} alt="demo" /> ); }
轻量备选方案(无需修改webpack配置)
如果不想调整webpack编译逻辑,可以通过预执行脚本扫描图片目录生成尺寸映射文件,实现编译阶段拿尺寸的效果:
- 在项目根目录新建
gen-image-size.js脚本:
const fs = require('fs'); const path = require('path'); const sizeOf = require('image-size'); const imageRoot = path.resolve(__dirname, 'src/assets/images'); const sizeMap = {}; function scanDirectory(dir) { const fileList = fs.readdirSync(dir); fileList.forEach(file => { const fullPath = path.join(dir, file); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { scanDirectory(fullPath); } else if (/\.(png|jpe?g|gif|webp)$/i.test(file)) { const { width, height } = sizeOf(fullPath); const relPath = path.relative(__dirname, fullPath).replace(/\\/g, '/'); sizeMap[relPath] = { width, height }; } }); } scanDirectory(imageRoot); fs.writeFileSync( path.resolve(__dirname, 'src/image-size-map.json'), JSON.stringify(sizeMap, null, 2) );
- 在
package.json中添加预执行钩子,保证启动、构建前自动更新尺寸映射:
{ "scripts": { "prestart": "node gen-image-size.js", "prebuild": "node gen-image-size.js", "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test" } }
- 组件中结合映射使用:
import demoImg from './assets/images/demo.png'; import sizeMap from './image-size-map.json'; const { width, height } = sizeMap['src/assets/images/demo.png']; function App() { return <img src={demoImg} width={width} height={height} alt="demo" />; }
若项目配置了非根路径的publicPath,只需调整脚本中路径拼接逻辑即可适配。
内容的提问来源于stack exchange,提问作者Vadim H
相关产品推荐
相关产品推荐

