设置方法
mini 中提供了 imageUrlLoaderOption 和 postcss.url 。
其中:
config.limit 和 imageUrlLoaderOption.limit 服务于 Taro 的 MiniWebpackModule.js , 值的写法要 ()KB * 1024。
config.maxSize 服务于 postcss-url 的 inline.js , 值的写法要 ()KB。
关于为什么 limit 和 maxSize 的写法不同?
因为在源码层:
limit 在使用时是判断 limit 和 2 * 1024 的值: maxSize: options.limit || 2 * 1024 ;
maxSize 在使用时是判断 maxSize 和 0 ,再乘以 1024 : const maxSize = (options.maxSize || 0) * 1024; ;
所以在配置时要注意区分。
const config = {// ...mini: {// ...imageUrlLoaderOption: {limit: num * 1024,},postcss: {// ...url: {enable: true / false,config: {limit: num * 1024,maxSize: num,},},// ...},},// ...
};// ...
Base64 转换上限值分以下 12 种情况去配置:
| url | config | imageUrlLoaderOption | 转成 Base64 的图片上限 |
| url.enable 为 true | config.limit 和 config.maxSize 都存在 | 没有 imageUrlLoaderOption.limit | config.limit 和 maxSize的最大值 |
| 有 imageUrlLoaderOption.limit | config.limit、imageUrlLoaderOption.limit 、 maxSize的最大值 | ||
| config.maxSize 不存在, config.limit 存在 | 没有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 | |
| 有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 | ||
| config.limit 不存在, config.maxSize 存在 | 没有 imageUrlLoaderOption.limit | 当 maxSize > 10 ,以 maxSize 为主;否则小于 10KB 的图片被转成 Base64 | |
| 有 imageUrlLoaderOption.limit | imageUrlLoaderOption.limit 和 maxSize的最大值 | ||
| config.limit 和 config.maxSize 都不存在 | 没有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 | |
| 有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 | ||
| url.enable 为 false | - | 没有 imageUrlLoaderOption.limit | 2KB |
| 有 imageUrlLoaderOption.limit | imageUrlLoaderOption.limit | ||
| 不存在 url | - | 没有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 |
| 有 imageUrlLoaderOption.limit | 全部图片都被转成 Base64 |
最实用的配置:
不使用 postcss 插件,通过 Taro 提供的 imageUrlLoaderOption 来设置转换上限值
// ...
const config = {// ...mini: {// ...imageUrlLoaderOption: {limit: -1,},postcss: {// ...url: {enable: false,},// ...},},// ...
};
// ...
关于 limit
config.limit 和 imageUrlLoaderOption.limit 的替换关系如图所示

源码解析
Taro 部分
class MiniWebpackModule 是 Taro 框架中用于处理和封装 Webpack 构建模块的类。它负责配置、加载和编译与各类文件格式相关的模块,通常在 Taro 生成项目构建配置时发挥作用。它的目标是将开发者的源代码转换成适用于小程序、 H5 、 React Native 等平台的最终代码。
getModules
getModules 方法的核心作用是配置和返回不同类型模块的处理规则,包括对 Sass 、 Scss 、 Less 、 Stylus 等样式文件的处理,并将其与 CSS 、 PostCSS 等工具结合起来,最终生成适合各种小程序平台的构建规则。
parsePostCSSOptions() 解析 PostCSS 的配置选项,返回 postcssOption 、postcssUrlOption 和 cssModuleOption 等。
通过 getDefaultPostcssConfig 方法获取 PostCSS 的默认配置,返回给 this.__postcssOption 进行保存。
调用 getImageRule 方法对 image 进行配置,并将配置存入 rule 对象中。
getModules() {const { appPath, config, sourceRoot, fileType } = this.combination;const { buildAdapter, sassLoaderOption, lessLoaderOption, stylusLoaderOption, designWidth, deviceRatio } = config;const { postcssOption, postcssUrlOption, cssModuleOption } = this.parsePostCSSOptions();this.__postcssOption = (0, postcss_mini_1.getDefaultPostcssConfig)({designWidth,deviceRatio,postcssOption,alias: config.alias,});const rule = {image: this.getImageRule(postcssUrlOption)};return { rule };
}
parsePostCSSOptions
defaultUrlOption 确实默认启用 postcss-url,并设置 limit 为 10KB(10240 字节)。代码中 postcssOption.url 会通过 recursiveMerge 方法与 defaultUrlOption 合并,如果配置中提供了 postcss-url 的自定义配置,将会覆盖默认值。
postcssUrlOption 不会赋值,即 config/index.ts 中配置的 config 不会被使用或存储。
parsePostCSSOptions() {const { postcss: postcssOption = {} } = this.combination.config;const defaultUrlOption = {enable: true,config: {limit: 10 * 1024 // limit 10k base on document}};const urlOptions = (0, helper_1.recursiveMerge)({}, defaultUrlOption, postcssOption.url);let postcssUrlOption = {};if (urlOptions.enable) {postcssUrlOption = urlOptions.config;}return {postcssOption,postcssUrlOption};
}
getDefaultPostcssConfig
接收 postcssOption ,从中提取 url 配置,并通过 recursiveMerge 将其与 defaultUrlOption 合并为 urlOption 。如果 postcssOption.url 存在,就会用自定义配置来替代或合并默认配置。
const getDefaultPostcssConfig = function ({ postcssOption = {} }) {const { url } = postcssOption,options = __rest(postcssOption, ["url"]);const urlOption = (0, helper_1.recursiveMerge)({}, defaultUrlOption, url);return [["postcss-url", urlOption, require("postcss-url")],...Object.entries(options),];
};
getImageRule
当调用 getImageRule 时,postcssOptions 和 imageUrlLoaderOption 合并生成新的 options ,并传递给 WebpackModule.getImageRule(),limit 的优先级以 imageUrlLoaderOption.limit 为主导。
getImageRule(postcssOptions) {const sourceRoot = this.combination.sourceRoot;const { imageUrlLoaderOption } = this.combination.config;const options = Object.assign({}, postcssOptions, imageUrlLoaderOption);return WebpackModule_1.WebpackModule.getImageRule(sourceRoot, options);
}
WebpackModule_1.WebpackModule.getImageRule
如果 postcss.url.config.limit 没有设置,系统应有逻辑保证 limit 的默认值为 2KB。
static getImageRule(sourceRoot, options) {return {test: helper_1.REG_IMAGE,type: 'asset',parser: {dataUrlCondition: {maxSize: options.limit || 2 * 1024 // 2kb}},generator: {emit: options.emitFile || options.emit,outputPath: options.outputPath,publicPath: options.publicPath,filename({ filename }) {if ((0, shared_1.isFunction)(options.name))return options.name(filename);return options.name || filename.replace(sourceRoot + '/', '');}}};
}
postcss-url 部分
plugin
options 是个对象,属性有 url、 limit 、 maxSize 。 url 默认值是 inline 。
当 enable 设为 false , options 为 {} ;设为 true , options 为打印配置中所设的,可能有 url、 limit 、 maxSize 。
styles.walkDecls((decl) =>{}); 遍历 CSS 文件中的每个声明(decl)。对每个声明,调用 declProcessor 函数,并将结果(是个 Promise ) push 到 promises 数组中。返回的 Promise 通过 then 打印的结果,是一个数组,值为'url("OSS || Base64 || assets")'。
const plugin = (options) => {options = options || {};return {postcssPlugin: "postcss-url",Once(styles, { result }) {const promises = [];const opts = result.opts;const from = opts.from ? path.dirname(opts.from) : ".";const to = opts.to ? path.dirname(opts.to) : from;styles.walkDecls((decl) =>promises.push(declProcessor(from, to, options, result, decl)));return Promise.all(promises);},};
};
declProcessor
获取 patten ,值存在 undefined 、/(url\(\s*['"]?)([^"')]+)(["']?\s*\))/g 还有其他。
如果 patten 是 undefined ,直接返回 Promise.resolve() 。这是为了在 pattern 不存在时直接短路,避免不必要的计算。
正常函数内部执行了
Promise.resolve()后,后面的代码是可以继续执行的。
如果 patten 不是 undefined ,新建一个 promises 数组,用来存储所有异步操作的 Promise 。 decl.value.replace(pattern, (matched, before, url, after) => { ... }) 使用 replace 函数遍历和处理 decl.value 中的每个匹配项。 replace 函数的第二个函数是个回调函数,最终的返回值是 matched 。
在回调函数中执行 replaceUrl 函数,返回一个为 Promise 的 newUrlPromise ,然后调用 then 方法获取 newUrlPromise 的值。
如果 newUrlPromise 的值是 undefined ,直接返回 matched 。这种情况会发生在没有转换成 Base64 编码的本地图片路径上。
declProcessor 最终返回的是 Promise.all(promises) (是个 Promise )。
const declProcessor = (from, to, options, result, decl) => {const dir = { from, to, file: getDirDeclFile(decl) };const pattern = getPattern(decl);if (!pattern) return Promise.resolve();const promises = [];decl.value = decl.value.replace(pattern, (matched, before, url, after) => {const newUrlPromise = replaceUrl(url, dir, options, result, decl);promises.push(newUrlPromise.then((newUrl) => {if (!newUrl) return matched;if (WITH_QUOTES.test(newUrl) && WITH_QUOTES.test(after)) {before = before.slice(0, -1);after = after.slice(1);}decl.value = decl.value.replace(matched, `${before}${newUrl}${after}`);}));return matched;});return Promise.all(promises);
};
replaceUrl
asset 是一个对象,里面有 url 、 originUrl 、 pathname 、 absolutePath 、 relativePath 、 search 、 hash 。
当传入的是 OSS 路径或者 data:font/woff;base64 时, matchedOptions 是 undefined ,直接返回 Promise.resolve() 。
当传入的是 asset/images 下的图片时, matchedOptions 是 options 的值。会判断 matchedOptions 是不是个数组。如果是数组,则对数组里面的值一一执行 process 函数;不是数组,直接执行 process 函数。
process 函数里的 getUrlProcessor 函数会根据 url 的值判断走哪种类型的编译方法。
process 函数里的 wrapUrlProcessor 函数实现了对 urlProcessor 的“增强”,使其在处理 URL 的过程中可以记录警告信息和依赖关系。
replaceUrl 最终返回一个 Promise ,在 resultPromise.then(...) 的链式调用中, return newUrl ; 实际上是将 newUrl 封装在一个新的 Promise 中作为最终返回值,并且 Promise 的解析值是 newUrl ,可以是经过编码的 URL 、文件路径或 undefined 。
const replaceUrl = (url, dir, options, result, decl) => {const asset = prepareAsset(url, dir, decl);const matchedOptions = matchOptions(asset, options);if (!matchedOptions) return Promise.resolve();const process = (option) => {const wrappedUrlProcessor = wrapUrlProcessor(getUrlProcessor(option.url),result,decl);return wrappedUrlProcessor(asset, dir, option);};let resultPromise = Promise.resolve();if (Array.isArray(matchedOptions)) {for (let i = 0; i < matchedOptions.length; i++) {resultPromise = resultPromise.then(() => process(matchedOptions[i])).then((newUrl) => {asset.url = newUrl;return newUrl;});}} else {resultPromise = process(matchedOptions);}return resultPromise.then((newUrl) => {asset.url = newUrl;return newUrl;});
};
getUrlProcessor
根据 url 的值判断走哪种 post-url 类型
function getUrlProcessor(optionUrl) {const mode = getUrlProcessorType(optionUrl);if (PROCESS_TYPES.indexOf(mode) === -1) {throw new Error(`Unknown mode for postcss-url: ${mode}`);}return require(`../type/${mode}`);
}
wrapUrlProcessor
wrapUrlProcessor 实现了对 urlProcessor 的“增强”,使其在处理 URL 的过程中可以记录警告信息和依赖关系。
const wrapUrlProcessor = (urlProcessor, result, decl) => {const warn = (message) => decl.warn(result, message);const addDependency = (file) =>result.messages.push({type: "dependency",file,parent: getPathDeclFile(decl),});return (asset, dir, option) =>urlProcessor(asset, dir, option, decl, warn, result, addDependency);
};
inline.js
根据 options 中的 maxSize 获取 maxSize ,所以配置表中传入的maxSize不需要乘 1024 。
如果 maxSize 不是 0 ,获取图片的 size 。如果图片的 size 大于 maxSize ,调用 processFallback ,按回退处理方式(如文件路径链接)返回;如果图片的 size 小于 maxSize ,调用 inlineProcess 编译成 Base64 。
module.exports = function (asset,dir,options,decl,warn,result,addDependency
) {return getFile(asset, options, dir, warn).then((file) => {if (!file) return;if (!file.mimeType) {warn(`Unable to find asset mime-type for ${file.path}`);return;}const maxSize = (options.maxSize || 0) * 1024;if (maxSize) {const size = Buffer.byteLength(file.contents);if (size >= maxSize) {return processFallback.apply(this, arguments);}}return inlineProcess(file, asset, warn, addDependency, options);});
};
processFallback
根据 options.fallback 的值进行调用,当前 options.fallback 是 undefined ,直接返回 Promise.resolve(); 。
function processFallback(originUrl, dir, options) {if (typeof options.fallback === "function") {return options.fallback.apply(null, arguments);}switch (options.fallback) {case "copy":return processCopy.apply(null, arguments);case "rebase":return processRebase.apply(null, arguments);default:return Promise.resolve();}
}
inlineProcess
该方法实现了将文件进行 Base64 转换,如果是 SVG 文件,则使用 encodeURIComponent ,否则使用 base64 编码。
const inlineProcess = (file, asset, warn, addDependency, options) => {const isSvg = file.mimeType === "image/svg+xml";const defaultEncodeType = isSvg ? "encodeURIComponent" : "base64";const encodeType = options.encodeType || defaultEncodeType;// Warn for svg with hashes/fragmentsif (isSvg && asset.hash && !options.ignoreFragmentWarning) {// eslint-disable-next-line max-lenwarn(`Image type is svg and link contains #. Postcss-url cant handle svg fragments. SVG file fully inlined. ${file.path}`);}addDependency(file.path);const optimizeSvgEncode = isSvg && options.optimizeSvgEncode;const encodedStr = encodeFile(file, encodeType, optimizeSvgEncode);const resultValue =options.includeUriFragment && asset.hash? encodedStr + asset.hash: encodedStr;// wrap url by quotes if percent-encoded svgreturn isSvg && encodeType !== "base64" ? `"${resultValue}"` : resultValue;
};