设置方法
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/fragments
if (isSvg && asset.hash && !options.ignoreFragmentWarning) {
// eslint-disable-next-line max-len
warn(
`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 svg
return isSvg && encodeType !== "base64" ? `"${resultValue}"` : resultValue;
};
![[实战-11] FlinkSql 设置时区对TIMESTAMP和TIMESTAMP_LTZ的影响](https://i-blog.csdnimg.cn/direct/105e2a9d69c34db183a298fb8ddc2850.png)


















