【万字详解】如何在微信小程序的 Taro 框架中设置静态图片 assets/image 的 Base64 转换上限值

设置方法

mini 中提供了 imageUrlLoaderOptionpostcss.url

其中:
config.limitimageUrlLoaderOption.limit 服务于 Taro 的 MiniWebpackModule.js , 值的写法要 ()KB * 1024
config.maxSize 服务于 postcss-url 的 inline.js , 值的写法要 ()KB

关于为什么 limitmaxSize 的写法不同?
因为在源码层:
limit 在使用时是判断 limit2 * 1024 的值: maxSize: options.limit || 2 * 1024
maxSize 在使用时是判断 maxSize0 ,再乘以 1024const 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.limitimageUrlLoaderOption.limit 的替换关系如图所示

limit 的对决

源码解析

Taro 部分

class MiniWebpackModule 是 Taro 框架中用于处理和封装 Webpack 构建模块的类。它负责配置、加载和编译与各类文件格式相关的模块,通常在 Taro 生成项目构建配置时发挥作用。它的目标是将开发者的源代码转换成适用于小程序、 H5 、 React Native 等平台的最终代码。

getModules

getModules 方法的核心作用是配置和返回不同类型模块的处理规则,包括对 Sass 、 Scss 、 Less 、 Stylus 等样式文件的处理,并将其与 CSS 、 PostCSS 等工具结合起来,最终生成适合各种小程序平台的构建规则。

parsePostCSSOptions() 解析 PostCSS 的配置选项,返回 postcssOptionpostcssUrlOptioncssModuleOption 等。

通过 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 时,postcssOptionsimageUrlLoaderOption 合并生成新的 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 是个对象,属性有 urllimitmaxSizeurl 默认值是 inline

enable 设为 falseoptions{} ;设为 trueoptions 为打印配置中所设的,可能有 urllimitmaxSize

styles.walkDecls((decl) =>{}); 遍历 CSS 文件中的每个声明(decl)。对每个声明,调用 declProcessor 函数,并将结果(是个 Promisepushpromises 数组中。返回的 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 还有其他。

如果 pattenundefined ,直接返回 Promise.resolve() 。这是为了在 pattern 不存在时直接短路,避免不必要的计算。

正常函数内部执行了 Promise.resolve() 后,后面的代码是可以继续执行的。

如果 patten 不是 undefined ,新建一个 promises 数组,用来存储所有异步操作的 Promisedecl.value.replace(pattern, (matched, before, url, after) => { ... }) 使用 replace 函数遍历和处理 decl.value 中的每个匹配项。 replace 函数的第二个函数是个回调函数,最终的返回值是 matched

在回调函数中执行 replaceUrl 函数,返回一个为 PromisenewUrlPromise ,然后调用 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 是一个对象,里面有 urloriginUrlpathnameabsolutePathrelativePathsearchhash

当传入的是 OSS 路径或者 data:font/woff;base64 时, matchedOptionsundefined ,直接返回 Promise.resolve()
当传入的是 asset/images 下的图片时, matchedOptionsoptions 的值。会判断 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.fallbackundefined ,直接返回 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;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/913214.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于STM32通过TM1637驱动4位数码管详细解析(可直接移植使用)

目录 1. 单位数码管概述 2. 对应编码 2.1 共阳数码管 2.2 共阴数码管 3. TM1637驱动数码管 3.1 工作原理 3.1.1 读键扫数据 3.1.2 显示器寄存器地址和显示模式 3.2 时序 3.2.1 指令数据传输过程&#xff08;读案件数据时序&#xff09; 3.2.2 写SRAM数据…

数字信号处理Python示例(11)生成非平稳正弦信号

文章目录 前言一、生成非平稳正弦信号的实验设计二、生成非平稳正弦信号的Python代码三、仿真结果及分析写在后面的话 前言 本文继续给出非平稳信号的Python示例&#xff0c;所给出的示例是非平稳正弦信号&#xff0c;在介绍了实验设计之后给出Python代码&#xff0c;最后给出…

Linux 系统结构

Linux系统一般有4个主要部分&#xff1a;内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本的操作系统结构&#xff0c;它们使得用户可以运行程序、管理文件并使用系统。 1. linux内核 内核是操作系统的核心&#xff0c;具有很多最基本功能&#xff0c;它…

网络安全之SQL初步注入

一.字符型 平台使用pikachu $name$_GET[name]; ​ $query"select id,email from member where username$name"; 用户输入的数据会被替换到SQL语句中的$name位置 查询1的时候&#xff0c;会展示username1的用户数据&#xff0c;可以测试是否有注入点&#xff08;闭…

【IEEE/EI会议】第八届先进电子材料、计算机与软件工程国际学术会议(AEMCSE 2025)

会议通知 会议时间&#xff1a;2025年4月25-27日 会议地点&#xff1a;中国南京 会议官网&#xff1a;www.aemcse.org 会议简介 第八届先进电子材料、计算机与软件工程国际学术会议&#xff08;AEMCSE 2025&#xff09;由南京信息工程大学主办&#xff0c;将于2025年4月25日…

华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目——共8套(每套四十题)

华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目分享——共九套&#xff08;每套四十题&#xff09; 岗位——芯片与器件设计工程师 岗位意向——模拟芯片 真题题目分享&#xff0c;完整题目&#xff0c;无答案&#xff08;共8套&#xff09; 实习岗位…

Python——数列1/2,2/3,3/4,···,n/(n+1)···的一般项为Xn=n/(n+1),当n—>∞时,判断数列{Xn}是否收敛

没注释的源代码 from sympy import * n symbols(n) s n/(n1) print(数列的极限为&#xff1a;,limit(s,n,oo))

104、Python并发编程:基于事件Event实现多线程间的同步

引言 继续介绍关于多线程同步的实现方式&#xff0c;本文将介绍基于Event的线程同步方式。 本文的主要内容有&#xff1a; 1、什么是Event 2、Event的使用场景 3、Event的代码实例 4、Event与Condition的比较 什么是Event 在Python的多线程编程中&#xff0c;Event是一个…

首次超越扩散模型和非自回归Transformer模型!字节开源RAR:自回归生成最新SOTA!

文章链接&#xff1a;https://arxiv.org/pdf/2411.00776 项目链接&#xff1a;https://yucornetto.github.io/projects/rar.html 代码&模型链接&#xff1a;https://github.com/bytedance/1d-tokenizer 亮点直击 RAR&#xff08;随机排列自回归训练策略&#xff09;&#x…

IDEA在编译时: java: 找不到符号符号: 变量 log

一、问题 IDEA在编译的时候报Error:(30, 17) java: 找不到符号符号: 变量 log Error:(30, 17) java: 找不到符号 符号: 变量 log 位置: 类 com.mokerson.rabbitmq.config.RabbitMqConfig 二、解决方案 背景&#xff1a;下载其他同事代码时&#xff0c;第一次运行&#xff0c…

【Hadoop实训】Hive 数据操作②

延续上一篇文章&#xff0c;不懂的宝子们请看以下链接&#xff1a; 【Hadoop实训】Hive 数据操作①-CSDN博客 目录 一、Group by 语句 (1)、计算emp表每个部门的平均工资 (2)、计算emp表每个部门中每个岗位的最高工资 二、Having 语句 (1)、求每个部门的平均工资 (2)、求每个…

nginx的相关命令

nginx的启用和停止有多种方式1、nginx服务的信号控制&#xff1b;2、nginx的命令行控制。 1、信号控制 ps -ef | grep nginx 可以查询跟nginx有关的所有线程。 有一个master进程和worker进程 我们作为管理员&#xff0c;只需要通过master进程发送信号来控制nginx&#xff0c…

【SpringMVC】——Cookie和Session机制

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;实践 1&#xff1a;获取URL中的参数 &#xff08;1&#xff09;PathVariable 2&…

31.校园志愿者管理系统(基于springboot和vue的Java项目)

目录 1.系统的受众说明 2.开发技术与环境配置 2.1 SpringBoot框架 2.2Java语言简介 2.3 MySQL环境配置 2.4 MyEclipse环境配置 2.5 mysql数据库介绍 2.6 B/S架构 3.系统分析与设计 3.1 可行性分析 3.1.1 技术可行性 3.1.2 操作可行性 3.1.3经济可行性 3.4.1 …

Android下的系统调用 (syscall),内联汇编syscall

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 什么是系统调用 (syscall) 系统调用是操作系统提供给应用程序的一组接口&#xff0c;允许用户空间程序与内核进行交互。 在 Android&#xff08;基于 Linux …

linux-vlan

# VLAN # 1.topo # 2.创建命名空间 ip netns add ns1 ip netns add ns2 ip netns add ns3 # 3.创建veth设备 ip link add ns1-veth0 type veth peer name ns21-veth0 ip link add ns3-veth0 type veth peer name ns23-veth0 # 4.veth设备放入命名空间,启动接口 ip link set n…

浙江大学高等数学研究所已变样

跟我199X年春季到访相比&#xff0c;现改名为“研究院”&#xff0c;说是2017年建立的&#xff0c;刘克峰&#xff08;1965-&#xff0c;研究黎曼几何&#xff0c;加州洛杉矶大学教授&#xff09;已退位&#xff0c;励建书&#xff08;1959-&#xff0c;香港科技大学教授&#…

使用 AMD GPU 上的 Whisper 进行语音转文字

Speech-to-Text on an AMD GPU with Whisper — ROCm Blogs 2024年4月16日&#xff0c;作者&#xff1a;Clint Greene. 介绍 Whisper是由 OpenAI 开发的高级自动语音识别&#xff08;ASR&#xff09;系统。它采用了一个简单的编码器-解码器 Transformer 架构&#xff0c;其中…

统信UOS开发环境支持rust

集成了Rust编译器和包管理工具,支持系统级编程、网络应用等多场景,为开发者提供丰富的库支持。 文章目录 一、环境部署1. rust开发环境安装2. rust开发环境配置二、代码示例三、常见问题1. 借用和所有权问题2. 编译器错误和警告一、环境部署 1. rust开发环境安装 rust是一门…

上海沪尚茗居干货分享:码住这4步,投影仪不再吃灰

在追求高品质家庭娱乐的今天&#xff0c;投影仪已成为年轻人打造家庭影院的新宠。然而&#xff0c;面对市场上琳琅满目的投影仪品牌和型号&#xff0c;如何做出明智的选择呢&#xff1f;上海沪尚茗居为您精心整理了一份投影选择4步曲&#xff0c;助您轻松选购心仪的家庭投影仪。…