动态 import

news2024/10/6 2:27:39

文章目录

  • 动态 import
    • 1. 动态导入
    • 2. 语法
    • 3. 描述
    • 4. 模块命名空间对象
    • 5. 使用示例
    • 6. 动态导入的原理
    • 7. 兼容

动态 import

import() 语法,通常称为动态导入,是一种类似函数的表达式,允许将 ECMAScript 模块异步和动态地加载到可能的非模块环境中。

与声明式导入(静态导入)不同,动态导入仅在需要时才进行求值,并允许更高的语法灵活性。

在这里,“进行求值”(evaluate)是指在需要时才解析和执行动态导入的模块代码。动态导入不同于静态声明式导入,它不会在代码编译阶段就解析和执行模块,而是在运行时根据实际需求动态地加载和执行模块。

这种按需求加载和执行的方式可以带来更好的性能优化,例如减少应用程序的初始加载时间和资源消耗。同时,动态导入还提供了更高的语法灵活性,允许在运行时根据条件或用户交互来决定加载哪些模块。
在这里插入图片描述

1. 动态导入

  • 动态导入是什么

    动态导入(Dynamic Imports)是一种 JavaScript 模块加载机制,允许你在运行时按需加载模块。它是通过使用 import() 函数实现的,该函数返回一个 Promise 对象,该对象在模块加载完成后解析为模块的导出内容。动态导入与静态导入(使用 import something from "somewhere"语句)不同,后者在编译时就确定了需要加载的模块。

  • 动态导入的优点和缺点

    优点:

    • 按需加载:动态导入允许你根据应用程序的实际需求加载模块,从而减少初始加载时间和资源消耗。
    • 代码分割:通过动态导入,Webpack 可以将你的代码自动拆分成多个代码块(chunks),从而实现更细粒度的代码分割和优化。
    • 懒加载:动态导入可以实现懒加载,即在某些条件满足时(例如用户交互)再加载特定模块。这有助于提高应用程序的性能和响应速度。
    • 路由级别的代码分割:对于使用前端路由库(如React Router或Vue Router)的单页应用程序,可以为每个路由组件使用动态导入,实现路由级别的代码分割和懒加载。

    缺点:

    • 兼容性:虽然动态导入已经成为 ECMAScript 标准的一部分,但在较旧的浏览器(如 Internet Explorer)中可能不受支持。为了在这些浏览器中使用动态导入,你可能需要使用 Babel 等工具进行转译和 polyfill。
    • 复杂性:与静态导入相比,动态导入需要处理 Promise 对象,可能会增加代码的复杂性。此外,你需要确保在使用动态导入的模块之前正确处理加载状态和错误。

    兼容性目前已经不是什么问题了,

2. 语法

import(moduleName)

import() 调用是一种与函数调用非常相似的语法,但 import 本身是一个关键字,而不是函数。您不能像 const myImport = import 这样为其设置别名,否则会抛出 SyntaxError

  • 参数

    moduleName

    要导入的模块。模块标识符(specifier)的求值方式由宿主(host)指定,但始终遵循与静态导入声明相同的算法。

    在这里,“宿主”(host)是指运行 JavaScript 代码的环境。JavaScript 可以在多种环境中运行,例如浏览器、Node.js 服务器、Web Workers 等。这些不同的运行环境被称为宿主环境。

    在动态导入中,模块标识符的求值方式由宿主环境决定。这意味着不同的宿主环境可能会有不同的实现细节,但它们都遵循与静态导入声明相同的算法。这种设计使得动态导入能够在各种宿主环境中保持一致性和可移植性。

  • 返回值

    返回一个 Promise 对象,该对象在满足条件时解析为模块命名空间对象:一个包含来自 moduleName 的所有导出内容的对象。

import() 的求值过程永远不会同步地抛出错误。moduleName 会被强制转换为字符串,如果转换过程中抛出错误,则 Promise 会被拒绝,并返回抛出的错误。

3. 描述

静态导入声明语法(import something from "somewhere")是静态的,导入的模块会在加载完成后就立即进行求值。动态导入允许绕过静态导入的语法严格性,并根据条件或按需加载模块。以下是一些可能需要使用动态导入的原因:

  1. 当静态导入显著减慢代码加载速度,且使用导入的代码的可能性较低,或者并不是立即需要而是延后才需要它时。
  2. 当静态导入显著增加程序的内存使用,且使用导入的代码的可能性较低时。
  3. 当在加载时导入的模块不存在时。
  4. 当需要动态构建导入标识符字符串时。(静态导入仅支持静态标识符。)
  5. 当导入的模块具有副作用,且仅当某个条件为真时才希望产生这些副作用。(建议模块不要有任何副作用,但有时在模块依赖中无法控制这一点。)
  6. 当处于非模块环境(例如,eval 或脚本文件)。

仅在必要时使用动态导入。静态形式更适用于加载初始依赖项,并且可以更容易地从静态分析工具和树摇(Tree shaking)中受益。

如果文件未作为模块运行(如果在 HTML 文件中引用,脚本标签必须具有 type="module" ),则无法使用静态导入声明,但始终可以使用异步动态导入语法,允许将模块导入到非模块环境中。

并非所有执行上下文都允许动态模块导入。例如,import() 可以在主线程、共享工作线程或专用工作线程中使用,但在 service worker 或 worklet 中调用时将会抛出错误。

4. 模块命名空间对象

模块命名空间对象是一个描述模块中所有导出内容的对象。它是在模块求值时创建的静态对象。有两种方法可以访问模块的模块命名空间对象:通过命名空间导入(import * as name from moduleName)或通过动态导入的完成值。

模块命名空间对象是一个封闭的、无原型(prototype 为 null)的对象。这意味着对象的所有字符串键都对应于模块的导出内容,且不会有额外的键。所有键按字典顺序(即 Array.prototype.sort() 的默认行为)可枚举,其中默认导出作为名为 default 的键可用。此外,模块命名空间对象具有一个名为@@toStringTag 的属性,其值为 "Module",用于 Object.prototype.toString()

如下示例是动态导入的 utils 模块,它对外暴露了 adddefalut 键,同时还有一个 Symbol.iterator 键。

utils

当使用 Object.getOwnPropertyDescriptors() 获取其描述符时,可以看到,对象的属性是可写但不可配置的。

utils

Object.getOwnPropertyDescriptors() 方法用于返回一个对象的所有自身属性(非继承属性)的描述符。描述符对象包含以下属性:

  1. configurable:表示属性是否可以被删除或者重新配置。如果为 true,则该属性可以被删除或者重新配置(例如,可以通过Object.defineProperty()方法修改属性描述符)。如果为 false,则该属性不可被删除或重新配置。

  2. enumerable:表示属性是否可以被枚举。如果为 true,则该属性会出现在对象的枚举属性中(例如,可以通过for...in循环或Object.keys()方法访问到该属性)。如果为 false,则该属性不可枚举。

  3. value:表示属性的值。可以是任意类型的值,如数字、字符串、对象等。

  4. writable:表示属性值是否可以被修改。如果为 true,则该属性的值可以被修改。如果为 false,则该属性的值不可被修改。

然而,它们实际上是只读的,即使它们的 writable 描述符为 true,原因在于 ES6 模块的规范要求导出的值是只读的。这是为了确保模块的引用和导出的值始终保持一致。这与静态导入是一致的,称为“实时绑定”或“动态绑定”——值可以由导出它们的模块重新分配,但不能由导入它们的模块重新分配。也就是说,当一个模块导出的值发生变化时,其他引用该模块的地方也会自动得到更新的值。为了实现这个特性,模块导出的值被设计为只读的。

每个模块标识符对应一个唯一的模块命名空间对象,因此以下通常是正确的:

import * as mod from "/my-module.js";

import("/my-module.js").then((mod2) => {
  console.log(mod === mod2); // true
});

除了一个特殊情况:由于 Promise 永远不会满足一个 thenable,如果 my-module.js 模块导出了一个名为 then() 的函数,那么当动态导入的 Promise 被实现时,该函数将作为 Promise 解析过程的一部分被自动调用。

// my-module.js
export function then(resolve) {
  console.log("then() called");
  resolve(1);
}
// main.js
import * as mod from "/my-module.js";

import("/my-module.js").then((mod2) => {
  // Logs "then() called"
  console.log(mod === mod2); // false
});

警告:不要从模块中导出名为 then() 的函数。这将导致模块在动态导入时与静态导入时的行为不同。

这里的"满足一个 thenable"是指 Promise 在解析过程中不会将其解析为一个具有 then() 方法的对象。“thenable” 是一个术语,通常指代具有 then() 方法的对象。这个方法允许对象参与异步操作,例如 Promise 链式调用。

在这里,是描述了一个特殊情况,即当模块导出一个名为 then() 的函数时,动态导入的 Promise 会自动调用这个函数。这是因为 Promise 规范要求在解析过程中检查解析值是否具有 then() 方法。如果具有该方法,Promise 会将其视为 thenable 对象,并尝试调用该方法来获取最终值。

然而,Promise 本身永远不会将其解析为 thenable 对象。这意味着,即使模块导出了一个名为 then() 的函数,Promise 也不会将其视为 thenable 对象。这是一个特殊情况,因为通常情况下,Promise 会尝试解析 thenable 对象。

5. 使用示例

  • 仅为了其副作用而导入模块

    (async () => {
      if (somethingIsTrue) {
        // import module for side effects
        await import("/modules/my-module.js");
      }
    })();
    

    如果你的项目使用了导出 ESM(ECMAScript 模块)的包,你也可以仅为了副作用而导入它们。这将仅运行包入口文件(以及它导入的任何文件)中的代码。

    当我们谈论 “仅为了其副作用导入模块” 时,意味着我们导入一个模块,但不是为了使用它导出的值或函数,而是为了执行模块内的代码。这些代码可能会产生一些副作用,例如修改全局变量、设置环境配置或执行某些初始化操作。

    在给出的示例中:

    (async () => {
      if (somethingIsTrue) {
        // 仅为了副作用导入模块
        await import("/modules/my-module.js");
      }
    })();
    

    使用动态导入语法来导入一个模块。这里,我们没有使用模块导出的任何值或函数。相反,我们仅仅是执行模块内的代码,这些代码可能会产生一些副作用。这就是为什么称之为 “仅为了其副作用导入模块”。

  • 导入默认值

    您需要从返回的对象中解构并重命名 default 键。

    (async () => {
      if (somethingIsTrue) {
        const {
          default: myDefault,
          foo,
          bar,
        } = await import("/modules/my-module.js");
      }
    })();
    
  • 按需导入以响应用户操作
    下面示例演示了如何根据用户操作(在本例中是按钮点击)将功能加载到页面上,并调用该模块内的函数。当然这不是实现此功能的唯一方法。import() 函数还支持 await

    const main = document.querySelector("main");
    for (const link of document.querySelectorAll("nav > a")) {
      link.addEventListener("click", (e) => {
        e.preventDefault();
    
        import("/modules/my-module.js")
          .then((module) => {
            module.loadPageInto(main);
          })
          .catch((err) => {
            main.textContent = err.message;
          });
      });
    }
    

    在这个示例中,我们为页面上的所有导航链接(nav > a)添加了点击事件监听器。当用户点击链接时,我们阻止了链接的默认行为(e.preventDefault()),然后使用动态导入(import())语法来按需导入模块(/modules/my-module.js)。

    导入成功后,我们调用模块中的 loadPageInto() 函数,并将页面的主要内容区域(main)作为参数传递。这样,我们可以在用户点击链接时动态加载对应的页面内容。

    如果导入过程中发生错误,我们会捕获错误(catch),并将错误消息显示在页面的主要内容区域(main.textContent = err.message)。

  • 根据环境导入不同的模块
    在诸如服务器端渲染等过程中,您可能需要在服务器或浏览器中加载不同的逻辑,因为它们与不同的全局变量或模块进行交互(例如,浏览器代码可以访问诸如 documentnavigatorWeb API,而服务器代码可以访问服务器文件系统)。您可以通过条件动态导入来实现这一点。

    let myModule;
    
    if (typeof window === "undefined") {
      myModule = await import("module-used-on-server");
    } else {
      myModule = await import("module-used-in-browser");
    }
    

    在这个示例中,我们使用了条件动态导入来根据当前的运行环境(服务器端或浏览器端)导入不同的模块。我们检查 window 对象是否存在(typeof window === "undefined")。如果不存在,说明我们正在服务器端环境中运行,因此我们导入服务器端使用的模块(module-used-on-server)。如果存在,说明我们正在浏览器环境中运行,因此我们导入浏览器端使用的模块(module-used-in-browser)。

    通过这种方式,我们可以根据不同的运行环境选择性地导入适当的模块,从而确保正确的功能和逻辑在相应的环境中得到执行。

  • 使用非字面量标识符导入模块

    动态导入允许使用任何表达式作为模块标识符,不一定是字符串字面量。

    在下面的示例中,我们同时加载10个模块,即 /modules/module-0.js、/modules/module-1.js 等,并调用每个模块导出的 load 函数。

    Promise.all(
      Array.from({ length: 10 }).map((_, index) =>
        import(`/modules/module-${index}.js`),
      ),
    ).then((modules) => modules.forEach((module) => module.load()));
    

    这段代码使用 Promise.allArray.from 方法,创建了一个包含 10 个 Promise 的数组。每个 Promise 使用动态导入来加载对应的模块。当所有的模块都加载完成时,通过 then 方法获取到模块数组,并使用 forEach 遍历每个模块,并调用其 load 函数。

6. 动态导入的原理

当使用动态导入进行按需加载时,浏览器会在需要的时候(例如点击事件触发)发起请求去加载对应的 JavaScript 模块。这个请求通常是一个 HTTP 请求,用于从服务器获取模块文件。当请求成功返回后,浏览器会执行加载的模块代码,并将模块中导出的内容提供给你的应用程序使用。

例如,假设你有一个名为 moduleA.js 的模块,当用户点击一个按钮时,你想要动态加载这个模块。你可以这样实现:

<button id="loadModule">加载模块</button>
<script>
  document.getElementById('loadModule').addEventListener('click', async () => {
    const moduleA = await import('./moduleA.js');
    // 使用moduleA中的导出内容
  });
</script>

浏览器是怎么执行这段 js 的呢?因为执行 js 要么用 script 标签,要么用 eval,要么 new Function

浏览器在执行动态加载的 JavaScript 模块时,实际上内部也是使用了类似于 <script>标签、eval() new Function() 的方式来执行代码。不过,这个过程是由浏览器自动完成的,开发者不需要直接操作这些底层细节。

当浏览器收到动态加载的 JavaScript 模块文件后,它会将文件内容解析为 JavaScript 代码。然后,浏览器会创建一个新的执行上下文(Execution Context),用于执行这段代码。在这个执行上下文中,浏览器会处理模块的导入、导出和其他逻辑。最后,浏览器会将模块中导出的内容提供给你的应用程序使用。

这个过程中,浏览器确保了模块的隔离性和安全性。与直接使用 <script> 标签、eval()new Function()相比,动态 import() 提供了更高级别的抽象,使得开发者可以更简单地实现按需加载、代码分割等功能,而无需关心底层的执行细节。

7. 兼容

动态导入需要浏览器的支持。动态导入是 ECMAScript(JavaScript)规范中的一个特性,现代浏览器大多数已经支持了这个特性。然而,一些较旧的浏览器或者不支持最新特性的浏览器可能无法使用 import()

为了确保在不支持动态导入的浏览器上也能正常工作,可以使用一些工具和技术进行兼容处理:

  1. Babel:Babe l是一个 JavaScript 编译器,可以将新的 JavaScript 特性转换为旧版本的浏览器可以理解的代码。通过配置 Babel 插件,可以将 import()转换为类似于 require.ensure() 的语法,以实现类似的按需加载功能。

  2. Webpack:Webpack 是一个前端构建工具,可以自动处理代码分割和动态加载。Webpack 支持将 import() 转换为兼容旧浏览器的代码,这样你就可以在不支持 import() 的浏览器上实现按需加载。

  3. 使用polyfill:polyfill 是一种用于填补浏览器功能缺失的技术。有一些 polyfill 库,例如 dynamic-import-polyfill,可以在不支持动态 import() 的浏览器上提供类似的功能。引入这些 polyfill 库后,你可以在旧浏览器上使用 import(),而不需要修改你的代码。

通过这些方法,可以确保动态导入在不同浏览器上都能正常工作,从而实现更好的兼容性和用户体验。

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

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

相关文章

【python自动化应用】借助ChatGPT与Python轻松实现办公自动化 —— AIC松鼠活动第九期

背景&#xff1a;当今的工作环境中&#xff0c;高效和快速地完成日常任务对于个人和机构都至关重要。许多人正在利用Python自动化来提高他们的工作效率。Python自动化可以帮助您自动完成繁琐的、重复的、容易出错的任务&#xff0c;从而节省时间和精力。 Python自动化有很多应用…

《徐亦达机器学习:Kalman Filter 卡尔曼滤波笔记 (一)》

P ( x t P(x_t P(xt​| x t − 1 ) x_{t-1}) xt−1​) P ( y t P(y_t P(yt​| x t ) x_t) xt​) P ( x 1 ) P(x_1) P(x1​)Discrete State DM A X t − 1 , X t A_{X_{t-1},X_t} AXt−1​,Xt​​Any π \pi πLinear Gassian Kalman DM N ( A X t − 1 B , Q ) N(AX_{t-1}B,Q)…

Golang gin Cookie的使用

Cookie介绍 HTTP是无状态协议&#xff0c;服务器不能记录浏览器的访问状态&#xff0c;也就是说服务器不能区分两次请求是否由同一个客户端发出Cookie就是解决HTTP协议无状态的方案之一&#xff0c;中文是小甜饼的意思Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有…

AI绘画:StableDiffusion实操教程-斗破苍穹-云韵-婚服(附高清图下载)

大家好&#xff0c;我是小梦&#xff0c;最近一直研究AI绘画。 不久前&#xff0c;我与大家分享了StableDiffusion的全面教程&#xff1a;“AI绘画&#xff1a;Stable Diffusion 终极宝典&#xff1a;从入门到精通 ” 然而&#xff0c;仍有些读者提出&#xff0c;虽然他们已经…

nVisual光纤资源管理软件,亮相第24届光博会

第24届中国国际光电博览会&#xff08;CIOE&#xff09;于9月6日至8日在深圳国际会展中心盛大开幕。作为行业领先的网络基础设施管理软件供应商&#xff0c;耐威迪携nVisual光纤资源管理软件亮相9号馆C001、C002展位&#xff0c;全方位展示室外网络基础设施管理、光纤资源管理、…

【数据结构】堆的创建

文章目录 一、堆的概念及结构1、什么是堆2、堆的性质3、堆的结构及分类 二、堆的创建1、堆向下调整算法2、堆向上调整算法3、堆的创建 一、堆的概念及结构 1、什么是堆 堆就是以二叉树的顺序存储方式来存储元素&#xff0c;同时又要满足父亲结点存储数据都要大于儿子结点存储数…

Win10系统电脑没有键盘怎么启用软键盘

如果使用电脑的过程中出现键盘故障的话&#xff0c;我们是可以通过系统自带的软键盘&#xff0c;也就是屏幕键盘来应急的。那么如何打开软键盘来使用呢&#xff1f;下面就和大家讲讲电脑Win10如何打开软键盘吧&#xff0c;操作也是很简单的。 1、找到桌面上的控制面板&#xf…

距离度量方法

距离度量方法 一、欧式距离(Euclidean Distance)二、余弦相似度(Cosine Similarity)三、汉明距离(Hamming Distance)四、曼哈顿距离(Manhattan Distance)五、切比雪夫距离(Chebyshev Distance)六、闵可夫斯基距离(Minkowski Distance)七、Jaccard Index八、Haversine Distance九…

【C++--类和对象】开篇

内联 在频繁调用一个函数时&#xff0c;会建立栈帧&#xff0c;如何减少内存的消耗 int add(int x,int y) { return (xy)*10; } c语言中&#xff0c;用宏函数 #define add(x,y) (((x)(y))*10) 若不加括号会出现一些问题 宏优势 不需要建立栈帧&#xff0c;提高调用效率&a…

【文末送书】2023年以就业为目的学习Java还有必要吗?

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

软件测试7大误区

随着软件测试对提高软件质量重要性的不断提高&#xff0c;软件测试也不断受到重视。但是&#xff0c;国内软件测试过程的不规范&#xff0c;重视开发和轻视测试的现象依旧存在。因此&#xff0c;对于软件测试的重要性、测试方法和测试过程等方面都存在很多不恰当的认识&#xf…

外卖配送系统开发指南:打造高效、智能的外卖服务

随着外卖市场的蓬勃发展&#xff0c;餐厅和外卖平台需要不断提高外卖服务的效率和智能性&#xff0c;以满足不断增长的需求。在本文中&#xff0c;我们将探讨如何开发一个高效、智能的外卖配送系统&#xff0c;同时提供一些关键代码示例来帮助您入门。 环境准备 在开始外卖配…

驱动开发 day3

总结&#xff1a;自动创建设备节点udev的流程 1.如何创建节点 手动创建&#xff1a;mknod 地址 设备文件类型 主设备号 次设备号(0 - 255) 自动创建&#xff1a;devfs (创建节点的逻辑在内核 ---> 2.4版本以前使用) udev (创建节点的逻辑在应用层) mdev (轻量级的udev) 2.…

图论第一天|深度优先搜索理论基础、广度优先搜索理论基础、797.所有可能的路径

深度优先搜索理论基础 文档讲解 &#xff1a; 代码随想录 - 深度优先搜索理论基础Hello 算法 9.3 图的遍历 状态&#xff1a;开始学习。 dfs&#xff08;深度优先搜索&#xff09;与bfs&#xff08;广度优先搜索&#xff09;区别 dfs是可一个方向去搜&#xff0c;不到黄河不回…

视频监控/安防监控/AI视频分析/边缘计算EasyCVR平台如何调取登录接口获取token?

安防视频监控管理平台/视频汇聚/视频云存储平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅、全网分发、云存储、AI智能分析等&#xff0c;视频监控智能分析平台EasyCVR融合性强、…

(JavaEE)线程的状态

前言&#xff1a;进程的状态&#xff0c;最核心的就是 就绪状态 和 阻塞状态&#xff0c;这对于线程同样适用&#xff0c;在Java中&#xff0c;又赋予了线程一些其他的状态。 线程的所有状态 NEW : Thread对象已经有了&#xff0c;但是start 方法还没调用。TERMINATED : Thre…

uniapp H5生成画布,插入网络图片。下载画布

因为网络图片不能直接使用ctx.drawImage(&#xff09;插入。得使用uni.getImageInfo()方法下载后插入。 但是当画布中存在多张网络图片时&#xff0c;必须等待uni.getImageInfo()下载完成后才行。这样得下载套下载。太过于繁琐。所以定义了一个递归下载方法。同时避免下载图片异…

【数据结构】长篇详解堆,堆的向上/向下调整算法,堆排序及TopK问题

文章目录 堆的概念性质图解 向上调整算法算法分析代码整体实现 向下调整算法算法分析整体代码实现 堆的接口实现初始化堆销毁堆插入元素删除元素打印元素判断是否为空取首元素实现堆 堆排序创建堆调整堆整合步骤 TopK问题 堆的概念 堆就是将一组数据所有元素按完全二叉树的顺序…

四川玖璨电子商务有限公司:新媒体视频运营

新媒体视频运营是随着互联网技术的发展而兴起的一种新型媒体运营方式。它通过制作、发布和推广优质视频内容&#xff0c;以吸引受众眼球&#xff0c;提升品牌知名度和影响力。四川玖璨电商小编在此次新媒体视频运营实验中&#xff0c;我们对新媒体视频运营的关键要素进行了探索…

大数据精准营销适合什么行业,面临哪些问题?

大数据营销适合的行业还是非常多的&#xff0c;毕竟现在大部分的企业都是通过网络方式来获得客户的&#xff0c;只要同领域有网站&#xff0c;而且他们存在竞价或者是优化&#xff0c;或者是使用了相关的软件&#xff0c;这种情况下都是完全可以运用的。比较常见的行业有金融行…