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

news2024/12/28 15:55:25

设置方法

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;
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[实战-11] FlinkSql 设置时区对TIMESTAMP和TIMESTAMP_LTZ的影响

table.local-time-zone table.local-time-zoneDataStream-to-Table Conversion&#xff08;拓展知识&#xff09;代码测试flinksql代码执行结果截图1. Asia/Shanghai 结果如下2. UTC结果如下 table.local-time-zone table.local-time-zone可用于设置flinksql的时区。 flink的内…

Bypassuac之白名单结合注册表方式

参考 Bypass UAC 原来这么简单 本章记录一下系统白名单文件结合注册表bypassuac&#xff0c;uac这个东西并不是Windows设置的防御机制而是相当于保护机制&#xff0c;只是用来控制用户行为的&#xff0c;弹个窗来提醒一下用户的行为&#xff0c;和直接的杀软是不一样的性质&am…

【力扣打卡系列】单调栈

坚持按题型打卡&刷&梳理力扣算法题系列&#xff0c;语言为go&#xff0c;Day20 单调栈 题目描述 解题思路 单调栈 后进先出 记录的数据加在最上面丢掉数据也先从最上面开始 单调性 记录t[i]之前会先把所有小于等于t[i]的数据丢掉&#xff0c;不可能出现上面大下面小的…

如何通过CDN加速提升电商网站双十一购物节用户体验

随着双十一购物节的到来&#xff0c;电商平台迎来了一年中流量的高峰。各大电商平台如天猫、京东和抖音等纷纷推出了全新的促销活动和玩法。在这场购物狂欢中&#xff0c;用户体验成为了电商网站能否脱颖而出的关键。而CDN&#xff08;内容分发网络&#xff09;加速服务&#x…

Linux信号_信号的产生

信号概念 信号是进程之间事件异步通知的一种方式&#xff0c;属于软中断。 异步&#xff1a;在异步操作中&#xff0c;任务可以独立执行。一个任务的开始或完成不依赖于其他任务的状态。 同步&#xff1a;在同步操作中&#xff0c;任务之间的执行是相互依赖的。一个任务必须等待…

Docker学习—Docker核心概念总结

核心概念总结 容器&#xff1a;容器就是将应用运行所需的所有内容比如代码、运行时环境&#xff0c;进行打包和隔离。 容器和虚拟机的对比 虚拟机是在同一个硬件上虚拟化出多个操作系统&#xff08;OS&#xff09;实例。 容器是在操作系统上进行虚拟化&#xff0c;用于隔离…

51单片机教程(六)- LED流水灯

1 项目分析 基于点亮LED灯、LED灯闪烁&#xff0c;扩展到构成最简单、花样流水灯。 2 技术准备 1 流水灯硬件及原理图 流水灯是由多个LED灯组成的 2 C语言知识点 数组 数组声明&#xff1a;长度不可变 数据类型 数组名称[长度n] // 整数型默认为0&#xff0c;小数型默认…

供热的一些基础技术数据

1、应该了解的几个实用数据:(1)室内采暖达标温度182℃(2)建筑面积采暖热负荷 4060kcal/h㎡(4570W/㎡)(3)建筑面积采暖所需合理流量 2.53.5kg/h㎡(节能建筑12 kg/h㎡)(4)一次网严寒期外网总供、回水温度5570℃(5)热网的补水量应小于热网循环量的1%(6)1蒸吨的热量可供11.5 万平方…

【1个月速成Java】基于Android平台开发个人记账app学习日记——第7天,申请阿里云SMS短信服务SDK

系列专栏链接如下&#xff0c;方便跟进&#xff1a; https://blog.csdn.net/weixin_62588253/category_12821860.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12821860&sharereferPC&sharesourceweixin_62588253&sharefromfrom_link 同时篇幅…

A02、JVM性能监测调优

1、JVM内存模型 1.1、介绍 JVM 自动内存分配管理机制的好处很多&#xff0c;但实则是把双刃剑。这个机制在提升 Java 开发效率的同时&#xff0c;也容易使 Java 开发人员过度依赖于自动化&#xff0c;弱化对内存的管理能力&#xff0c;这样系统就很容易发生 JVM 的堆内存异常&…

钉钉调试微应用整理2

第一步 新建应用 钉钉开放平台](https://open-dev.dingtalk.com/) 去新增应用 第二步 配置应用信息 把本地代码运行起来&#xff0c;并设置本地地址 第三步 在本地代码添加调试命令 这里有2中添加方式 哪一种都可以 方式一&#xff1a; index.html页面中 <!DOCTYPE h…

《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列

《TCP/IP网络编程》学习笔记 | Chapter 3&#xff1a;地址族与数据序列 《TCP/IP网络编程》学习笔记 | Chapter 3&#xff1a;地址族与数据序列分配给套接字的IP地址和端口号网络地址网络地址分类和主机地址边界用于区分套接字的端口号数据传输过程示例 地址信息的表示表示IPv4…

飞牛fnOs内网穿透-使用Lucky实现ipv6动态解析+HTTPS访问NAS服务

&#x1f9ed;Lucky官方介绍 Lucky最初是作为一个小工具&#xff0c;由开发者为自己的个人使用而开发&#xff0c;用于替代socat&#xff0c;在小米路由AX6000官方系统上实现公网IPv6转内网IPv4的功能。Lucky的设计始终致力于让更多的Linux嵌入式设备运行&#xff0c;以实现或…

《安富莱嵌入式周报》第345期:开源蓝牙游戏手柄,USB3.0 HUB带电压电流测量,LCR电桥前端模拟,开源微型赛车,RF信号扫描仪,开源无线电收发器

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 本周更新一期视频教程 第5期&#xff1a;RTX5/FreeRTOS全家桶源码工程综合实战模板集成CANopen组件&#xff08;2024-1…

微服务中常用分布式锁原理及执行流程

1.什么是分布式锁 分布式锁是一种在分布式系统环境下实现的锁机制&#xff0c;它主要用于解决&#xff0c;多个分布式节点之间对共享资源的互斥访问问题&#xff0c;确保在分布式系统中&#xff0c;即使存在有多个不同节点上的进程或线程&#xff0c;同一时刻也只有一个节点可…

三:LoadBalancer负载均衡服务调用

LoadBalancer负载均衡服务调用 1.LB负载均衡(Load Balance)是什么2.loadbalancer本地负载均衡客户端 与 Nginx服务端负载均衡区别3.实现loadbalancer负载均衡实例3-1.首先应模拟启动多个服务提供者应用实例&#xff1a;3-2.在服务消费项目引入LoadBalancer3-3&#xff1a;测试用…

简单入门Git

Git作用 Git简介 作用&#xff1a;版本控制多人协作 集中式 典型代表&#xff1a;SVN 特点&#xff1a;所有的版本库都存在中央服务器&#xff0c;本地备份动作必须依赖中央服务器&#xff0c;如果一旦服务器挂掉&#xff0c;或者网络状况不好&#xff0c;没法提交版本。…

解决echarts桑基图为0时tooltip不显示的问题

关键代码 formatter: function (params) {console.log("params",params)if (params.value 0) {// 如果值为0&#xff0c;返回空字符串&#xff0c;不显示任何内容return params.name : params.value;// return ;} else {// 否则返回标准的格式化信息return par…

DevOps业务价值流:版本规划的最佳实践

初入公司&#xff0c;面对瀑布研发模式下的冗长周期与频繁返工&#xff0c;我率先尝试局部敏捷迭代&#xff0c;但成效有限。随后&#xff0c;推动全面敏捷化&#xff0c;从需求阶段即开始规划&#xff0c;虽方向正确&#xff0c;却遭遇版本规划难题。项目经理与产品经理对敏捷…

NewStar CTF 2024 misc WP

decompress 压缩包套娃&#xff0c;一直解到最后一层&#xff0c;将文件提取出来 提示给出了一个正则&#xff0c;按照正则爆破密码&#xff0c;一共五位&#xff0c;第四位是数字 ^([a-z]){3}\d[a-z]$ 一共就五位数&#xff0c;直接ARCHPR爆破&#xff0c;得到密码 xtr4m&…