esbuild的介绍、使用及配置

esbuild的介绍、使用及配置

「esbuild」是一个「JavaScript」打包和压缩工具,核心目标是开创构建工具性能的新时代, 同时创建一个易于使用的现代构建工具。

主要特性:

  • 极快的速度,无需缓存
  • 支持 ES6 和 CommonJS 模块
  • 支持对 ES6 模块进行 tree shaking
  • API 可同时用于 JavaScript 和 Go
  • 兼容 TypeScript 和 JSX 语法
  • 支持 Source maps
  • 支持 Minification
  • 支持 plugins

js 脚本构建

可以通过如下两种方式调用 API:

  • 在命令行中调用

    echo 'let x: number = 1' | npx esbuild --loader=ts
    

    如果是使用命令行调用API,参数设置有三种方式:

    • –foo (主要用于设置 Boolean 类型的值)
    • –foo=bar (设置单个 String 类型的值)
    • –foo:bar (设置多个值,可以被重复设置)
  • 在 JavaScript 中调用

    require('esbuild').transformSync('let x: number = 1', {loader: 'ts',
    });// 输出
    {
    warnings: [],code: 'let x = 1;\n',map: '',mangleCache: undefined,legalComments: undefined
    }
    

在 esbuild 的 API 中有两种主要的 API 调用:transform 与 build。

transform

transform API 操作单个字符串,而不访问文件系统。这使其能够比较理想地在没有文件系统的环境中使用(比如浏览器)或者作为另一个工具链的一部分(比如 vite 或者 snowPack)。

require('esbuild').transformSync('let x: number = 1', {loader: 'ts',
});// 输出
{warnings: [],code: 'let x = 1;\n',map: '',mangleCache: undefined,legalComments: undefined
}

build

调用 build API 操作文件系统中的一个或多个文件。 它允许文件互相引用并且打包在一起。

require('fs').writeFileSync('in.ts', 'let x: number = 1');require('esbuild').buildSync({entryPoints: ['in.ts'],bundle: true,outfile: 'out.js',
});
require('fs').readFileSync('out.js', 'utf8');// 输出
('let x = 1;\n');

如果我们使用命令行去打包,请注意 esbuild 不会 默认打包,必须传递 --bundle 标志启用打包。

配置项

platform

  • browser(默认):使用立即执行函数包裹,避免变量泄露到全局作用域中。
  • node: ES6-风格的导出使用的 export 语句将会被转换为 CommonJS exports 对象中的 getters。
  • neutral:也是浏览器端运行,使用 ECMAScript 2015 (即 ES6) 中引入的 export 语法。

默认情况下,esbuild 的打包器为浏览器生成代码。 如果打包好的代码想要在 node 环境中运行,应该设置 platform 为 node。

bundle

打包一个文件意味着将任何导入的依赖项内联到文件中。默认情况下,esbuild 不会打包输入的文件。

require('esbuild').build({entryPoints: ['in.js'],bundle: true,outfile: 'out.js',
});

打包与文件连接不同。在启用打包时向 esbuild 传递多个输入文件将创建两个单独的 bundle 而不是将输入文件连接在一起。 为了使用 esbuild 将一系列文件打包在一起, 在一个入口起点文件中引入所有文件, 然后就像打包一个文件那样将它们打包

动态import

动态 import 不会打包到结果当中,而是当作 external 进行配置。

import 'pkg';
import('pkg');
require('pkg');// 非静态import,不会打包处理,而是直接生成到结果文件中
import(`pkg/${foo}`);
require(`pkg/${foo}`);
['pkg'].map(require);

webpack 会把所有可能访问到的文件都进行打包,然后在运行时中模拟一个文件系统。

而 esbuild 并不会模拟一个文件系统。

Define

该特性提供了一种用常量表达式替换全局标识符的方法。 它可以在不改变代码本身的情况下改变某些构建之间代码的行为。

let js = 'name = dynamicValue';
const result = require('esbuild').transformSync(js, {define: { dynamicValue: 'leopord' },
});
console.log(result);// 输出
{warnings: [],code: 'name = leopord;\n',map: '',mangleCache: undefined,legalComments: undefined
}

替换表达式必须是一个 JSON 对象(null、boolean、number、string、array 或者 object) 或者一个标识符。除了数组和对象之外,替换表达式是内联替换的,这意味着他们可以参与常数折叠。 数组与对象替换表达式会被存储在一个变量中,然后被标识符引用而不是内联替换, 这避免了替换重复复制一个值,但也意味着该值不能参与常数折叠。

如果我们想用字符串字面值替换某些东西,记住传递给 esbuild 的替换值本身必须包含引号。 省略引号意味着替换的值是一个标识符。

require('esbuild').transformSync('id, name', {define: { id: 'text', name: '"text"' },
})
{code: 'text, "text";\n',map: '',warnings: []
}

entryPoints

入口文件,数组|对象 形式

require('esbuild').buildSync({entryPoints: ['index.js', 'home.js'],bundle: true,outdir: 'out',
})// 生成文件结构
|- out
|--home.js
|--index.js

默认是根据入口文件名来生成打包后的文件名,但是可以通过传递对象来实现自定义文件名。

require('esbuild').buildSync({entryPoints: {out1: 'index.js',out2: 'home.js',},bundle: true,outdir: 'out',
})// 生成文件
-out
--out1.js
--out2.js

external

可以标记一个文件或者包为外部(external),从而将其从我们的打包结果中移除。 导入将被保留(对于 iife 以及 cjs 格式使用 require,对于 esm 格式使用 import),而不是被打包,并将在运行时进行计算引入。

用处:

  1. 去除在打包文件中生成永远不会执行的代码(比如浏览器端不需要node端的包),直接通过 external 移除
require('fs').writeFileSync('index.js', 'require("fs")')
require('esbuild').buildSync({entryPoints: ['index.js'],outfile: 'out.js',bundle: true,platform: 'node',external: ['fs'],
})
  1. 我们也可以在外部(external)路径中使用 * 通配符标记所有符合该模式的为外部(external)。 例如,我们可以使用 .png 移除所有的 .png 文件或者使用 /images/ 移除所有路径以 /images/ 开头的路径。

format

为生成的 JavaScript 文件设置输出格式。有三个可选的值:iife、cjs 与 esm。

inject

可以自动替换从另一个文件引入的全局变量。当我们为无法控制的代码适配新环境时是非常有用的。

// process-shim.js
export let process = {cwd: () => ''
}// index.js
console.log(process.cwd())

可以使用 inject 特性将一个 import 置于文件中以替换所有的全局标识符 process。这样可以替换 node 的 process.cwd() 函数的使用,解决在浏览器中运行时因缺失变量而导致崩溃的问题。

require('esbuild').buildSync({entryPoints: ['index.js'],bundle: true,inject: ['./process-shim.js'],outfile: 'out.js',
})// 打包结果
// out.js
let process = {cwd: () => ""};
console.log(process.cwd());
结合 define 一起使用
require('esbuild').buildSync({entryPoints: ['entry.js'],bundle: true,define: { 'process.cwd': 'dummy_process_cwd' },inject: ['./process-shim.js'],outfile: 'out.js',
})// 打包结果
// out.js
function dummy_process_cwd() {return "";
}
console.log(dummy_process_cwd());

loader

改变了输入文件解析的方式。例如, js loader 将文件解析为 JavaScript, css loader 将文件解析为 CSS。

transform API 调用仅使用一个 loader,因为它不涉及与文件系统的交互,因此不需要处理文件拓展名。build API 可以使用多个 loader,因为引入的文件可能有不同类型。

require('esbuild').buildSync({entryPoints: ['app.js'],bundle: true,loader: {'.png': 'dataurl','.svg': 'text',},outfile: 'out.js',
})

minify

启用该配置时,生成的代码会被压缩而不是格式化输出。压缩后的代码与未压缩代码是相等的,但是会更小。这意味着下载更快但是更难调试。 一般情况下在生产环境而不是开发环境压缩代码。

var js = 'fn = obj => { return obj.x }'
require('esbuild').transformSync(js, {minify: true,
}){code: 'fn=n=>n.x;\n',map: '',warnings: []
}

该配置项结合起来做三件独立的事情:移除空格、重写语法使其更体积更小、重命名变量为更短的名称。 一般情况下这三件事情我们都想做,但是如果有必要的话,这三个配置项可以单独启用:

var js = 'fn = obj => { return obj.x }'
require('esbuild').transformSync(js, {minifyWhitespace: true,
})
// 输出
{code: 'fn=obj=>{return obj.x};\n',map: '',warnings: []
}require('esbuild').transformSync(js, {minifyIdentifiers: true,
})
// 输出
{code: 'fn = (n) => {\n  return n.x;\n};\n',map: '',warnings: []
}require('esbuild').transformSync(js, {minifySyntax: true,
})
// 输出
{code: 'fn = (obj) => obj.x;\n',map: '',warnings: []
}

outdir

该配置项为 build 操作设置输出文件夹名称。

require('esbuild').buildSync({entryPoints: ['index.js'],bundle: true,outdir: 'out',
})

输出文件夹如果不存在的话将会创建该文件夹,但是当其包含一些文件时不会被清除。 生成的文件遇到同名文件会进行静默覆盖。如果我们想要输出文件夹只包含当前 esbuild 运行生成的文件, 我们应该在运行 esbuild 之前清除输出文件夹。

outfile

该配置项为 build 操作设置输出文件名。这仅在单个入口点时适用。 如果有多个入口点,我们必须使用 outdir 配置项来制定输出文件夹。

require('esbuild').buildSync({entryPoints: ['index.js'],bundle: true,outfile: 'out.js',
})

sourcemap

Source map 可以使调试代码更容易。 它们编码从生成的输出文件中的行/列偏移量转换回 对应的原始输入文件中的行/列偏移量所需的信息。 如果生成的代码与原始代码有很大的不同, 这是很有用的(例如 我们的源代码为 Typescript 或者我们启用了 压缩)。 如果我们更希望在我们的浏览器开发者工具中寻找单独的文件, 而不是一个大的打包好的文件, 这也很有帮助。

有4种方式:

  1. linked:单独一个 .map 文件,在.js 文件末尾存在 //# sourceMappingURL= 注释
require('esbuild').buildSync({entryPoints: ['index.js'],sourcemap: true,outfile: 'out.js',
})
  1. external:单独一个 .map 文件,在 .js 文件末尾不带注释
require('esbuild').buildSync({entryPoints: ['index.js'],sourcemap: 'external',outfile: 'out.js',
})
  1. inline:插入整个 source map 到 .js 文件中,source map 通常是比较大的,因为他们包含所有的源代码,因此我们通常不会想让代码中包含 inline source maps。为了移除 source map 中的源代码(只保存文件名以及行/列映射关系), 请使用 sources content 配置项。
require('esbuild').buildSync({entryPoints: ['index.js'],sourcemap: 'inline',outfile: 'out.js',
})
  1. both:inline 和 external 的合并版,既插入整个 source map 到 .js 文件中,又生成单独一个 .map 文件
require('esbuild').buildSync({entryPoints: ['index.js'],sourcemap: 'both',outfile: 'out.js',
})

build API 支持上面4中,而 transform API 不支持 linked 模式,因为传入 transform 中的没有文件名字,只有内容。

浏览器仅在堆栈跟踪打印在控制台后才会使用 source maps

target

设置生成 JavaScript 代码的目标环境。默认的 target 为 esnext,这意味着默认情况下,esbuild 将假设所有最新的 JavaScript 特性都是受支持的。

require('esbuild').buildSync({entryPoints: ['app.js'],target: ['es2020','chrome58','firefox57','safari11','edge16','node12',],outfile: 'out.js',
})

write

build API 可以写入文件系统中,也可以返回本应作为内存缓冲区写入的文件。默认情况下 CLI 与 JavaScript API 写入到文件系统,GO API 不是。使用内存缓冲区:

let result = require('esbuild').buildSync({entryPoints: ['app.js'],sourcemap: 'external',write: false,outdir: 'out',
})for (let out of result.outputFiles) {console.log(out.path, out.contents)
}

allowOverwrite

可以让输出流覆盖输入流,也就是生成的打包文件直接替换掉入口文件。

require('esbuild').buildSync({entryPoints: ['index.js'],outdir: '.',allowOverwrite: true,
})

metafile

该配置告诉 esbuild 以 JSON 格式生成一些构建相关的元数据。 下面的例子就是将元数据置于名为 meta.json 的文件中

const result = require('esbuild').buildSync({entryPoints: ['index.js'],bundle: true,metafile: true,outfile: 'out.js',
})
require('fs').writeFileSync('meta.json',JSON.stringify(result.metafile))

analyzeMetafile

生成易读的打包结果

(async () => {let esbuild = require('esbuild')let result = await esbuild.build({entryPoints: ['example.jsx'],outfile: 'out.js',minify: true,metafile: true,})let text = await esbuild.analyzeMetafile(result.metafile, {verbose: true,})console.log(text)
})()// 打印out.js ────── 36b ── 100.0%└ index.js ─ 35b ─── 97.2%

资源名称

当 loader 设置为 file 时,该配置项控制额外生成的文件名称。它使用带有占位符的模板来配置输出路径,当生成输出路径时,占位符将被特定 于文件的值替换。例如,例如,指定 assets/[name]-[hash] 的资源名称模板,将所有资源放入输出目录内名为 assets 的子目录中,并在文件名中包含资产的内容哈希。

require('esbuild').buildSync({entryPoints: ['app.js'],assetNames: 'assets/[name]-[hash]',loader: { '.png': 'file' },bundle: true,outdir: 'out',
})

在资源路径模板中有4个可用占位符:

  • [dir]:相对路径
  • [name]:这是不带拓展名的原始资源文件名称。例如,如果一个资源原来名为 image.png,然后模板中的 [name] 就会被 image 替换。没有必要使用该占位符;它的存在只是为了提供对人类友好的资源 名称,使调试更容易。
  • [hash]:这是资源的内容哈希,可以避免命名冲突。
  • [ext]:通过文件后缀来生成一个目录,并把所有相同后缀的文件放在一起(–asset-names=assets/[ext]/[name]-[hash] 会把 image.png 重名为 assets/png/image-CQFGD2NG.png)

banner

使用它可以在生成的 JavaScript 和 CSS 文件的开头插入任意字符串。

require('esbuild').buildSync({entryPoints: ['index.js'],banner: {js: '// banner comment',css: '/* banner comment */',},outfile: 'out.js',
})

footer

使用它可以在生成的 JavaScript 和 CSS 文件的末尾插入任意字符串。这通常用于插入注释:

require('esbuild').buildSync({entryPoints: ['index.js'],footer: {js: '// footer comment',css: '/* footer comment*/',},outfile: 'out.js',
})

splitting

代码分割。

分割仍然处于开发状态。

当启用代码分割时,我们必须使用 outdir 配置输出文件夹。

require('esbuild').buildSync({entryPoints: ['home.js', 'index.js'],bundle: true,splitting: true,outdir: 'out',format: 'esm',
})

chunkNames

此选项控制在启用 代码分割 时自动生成的共享代码块的文件名。 它使用带有占位符的模板来配置输出路径,当生成输出路径时,占位符将被特定于 chunk 的值替换。 例如,指定 chunks/[name]-[hash] 的 chunk 名称模板, 将所有生成的块放入输出目录内的名为 chunks 的子目录中,并在文件名中包含 chunk 的内容哈希。

require('esbuild').buildSync({entryPoints: ['index.js'],chunkNames: 'chunks/[name]-[hash]',bundle: true,outdir: 'out',splitting: true,format: 'esm',
})

有3个可用占位符:

  • [name]:目前这将始终是 chunk,尽管这个占位符在将来的版本中可能会有额外的值
  • [hash]:这是 chunk 的内容哈希。在生成多个共享代码块的情况下,内容哈希是区分不同 chunk 的必要条件
  • [ext]:后缀

resolveExtensions

node 使用的解析算法 支持隐式的文件扩展名。我们可以 require(‘./file’),然后他将会按照顺序检查 ./file、./file.js、./file.json 与 ./file.node。包括 esbuild 在内的现代打包器都将此概念拓展到了其他文件类型。在 esbuild 中可以使用解析插件设置对隐式文件拓展名进行自定义配置, 默认为 .tsx,.ts,.jsx,.js,.css,.json

require('esbuild').buildSync({entryPoints: ['index.js'],bundle: true,resolveExtensions: ['.ts', '.js'],outfile: 'out.js',
})

stdin

通常,build API 调用接受一个或多个文件名作为输入。但是,stdin 这个配置项可以用于在文件系统上根本不存在模块的情况下运行构建,因为它对应于在命令行上用管道将文件连接到 stdin。

let result = require('esbuild').buildSync({stdin: {contents: `export * from "./another-file"`,// 可选配置resolveDir: require('path').join(__dirname, 'src'),sourcefile: 'imaginary-file.js',loader: 'ts',},format: 'cjs',write: false,
})

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

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

相关文章

桌面便签怎么设置提醒,哪个备忘录便签好?

2024年终于开工了,第一天上班比较迷茫,不知道做什么比较好,这个时候如果有一款简单好用且可提醒的桌面便签软件该多好。那么,桌面便签怎么设置提醒,哪个备忘录便签好? 桌面便签怎么设置提醒,哪个…

SpringBoot项目嵌入RabbitMQ

在Spring Boot中嵌入RabbitMQ可以通过添加相应的依赖来完成。首先需要在pom.xml文件中引入spring-boot-starter-amqp依赖&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</a…

216961-98-7,BODIPY 493/503 SE,具有相对较长的激发状态寿命

文章关键词&#xff1a;216961-98-7&#xff0c;BODIPY 493/503 NHS 活化酯&#xff0c;BODIPY 493/503 NHS ester&#xff0c;BODIPY 493/503 SE 一、基本信息 产品简介&#xff1a;BODIPY染料是一种独特的荧光染料&#xff0c;由于其具有疏水性&#xff0c;特别适合用于染色…

骑砍战团MOD开发(45)-卡拉迪亚无敌舰队称霸海上

一.玩家船只航行 (0, 0, 0, [(eq, "$g_player_warship_drive_mode", 1)],[(init_position, pos1),(init_position, pos2),(get_player_agent_no, ":player_agent"),(call_script, "script_get_warship_instance_no_by_index", 0),(assign, "…

基于芯驰 X9HP PTG4.1 在 yocto 中添加 Linux 应用

1.参考例程并添加应用 1.1 参考例程 &#xff08;1&#xff09;查看自带的串口测试例程 uart_test &#xff0c;查看 bb 文件怎么写的。 1.2 添加 printf-test 应用 &#xff08;1&#xff09;在 yocto/meta-semidrive/recipes-bsp/ 目录中 copy 自带例程 uart-test 改名为 …

java中容易被忽视的toString()方法

之前一直认为toString就是将数据转换成字符类型&#xff0c;直到最近写出了一个bug才对toString有了新的认识 不同数据类型&#xff0c;toString() 有不同的操作 定义一个student类&#xff0c;包含姓名 String类型、性别 String类型、年龄 int 类型、分数列表 String类型的li…

2024年机器人技术的五大发展趋势,你有看好的吗?

文 BFT机器人 前言&#xff1a; 近些年来机器人技术作为推动社会发展的重要动力之一&#xff0c;它的发展成为重点关注对象&#xff0c;这一领域经历了漫长的发展。随着计算机、传感器和人工智能等技术的进步&#xff0c;现代机器人已经从简单的工具变成了各行各业的生产力。从…

9、使用 ChatGPT 的 GPT 制作自己的 GPT!

使用 ChatGPT 的 GPT 制作自己的 GPT! 想用自己的 GPT 超越 GPT ChatGPT 吗?那么让我们 GPT GPT 吧! 山姆 奥特曼利用这个机会在推特上宣传 GPTs 的同时还猛烈抨击了埃隆的格罗克。 GPTs概览 他们来了! 在上周刚刚宣布之后,OpenAI 现在推出了其雄心勃勃的新 ChatGPT…

信奥一本通:1075:药房管理

这个题可能有点误解&#xff0c;看这个实例&#xff0c;不是用30依次去减10 5 20 6 7 8&#xff0c;如果按照这个减法&#xff0c;30先减10再减5就剩15了&#xff0c;那完全不够后面20减的&#xff0c;所以次数还剩4次。但是&#xff0c;这道题是谁能减就减谁&#xff0c;意思就…

使用云渲染要注意什么?渲染100邀请码1a12

云渲染是利用云服务器进行渲染的方法&#xff0c;它能节省用户成本&#xff0c;提高效率&#xff0c;如果要用云渲染需要注意以下几点&#xff1a; 1、选择专业平台 一个专业的平台对渲染非常重要&#xff0c;比如渲染100&#xff0c;它是国内知名的云渲染提供商&#xff0c;拥…

【力扣 - 二叉树的中序遍历】

题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 提示&#xff1a; 树中节点数目在范围 [0, 100] 内 -100 < Node.val < 100方法一&#xff1a;递归 思路与算法 首先我们需要了解什么是二叉树的中序遍历&#xff1a;按照访问左子树——…

Linux小程序--进度条

目录 1.知识补充 1.1回车和换行 1.2缓冲区 2.实现倒计时 3.实现进度条 1.知识补充 1.在制作小程序进度条之前&#xff0c;我们先了解一下&#xff0c;回车换行和行缓冲区的概念。 2.动态效果&#xff0c;在同一个位置刷新不同的图像&#xff0c;实现一个倒计时的效果。…

2024最新软件测试面试题(带答案)

1. 请自我介绍一下(需简单清楚的表述自已的基本情况&#xff0c;在这过程中要展现出自信&#xff0c;对工作有激情&#xff0c;上进&#xff0c;好学) 面试官您好&#xff0c;我叫###&#xff0c;今年26岁&#xff0c;来自江西九江&#xff0c;就读专业是电子商务&#xff0c;毕…

2024年华为OD机试真题- 求字符串中所有整数的最小和-Java-OD统一考试(C卷)

题目描述: 输入字符串s,输出s中包含所有整数的最小和 说明 1. 字符串s,只包含 a-z A-Z +- ; 2. 合法的整数包括 1) 正整数 一个或者多个0-9组成,如 0 2 3 002 102 2)负整数 负号 - 开头,数字部分由一个或者多个0-9组成,如 -0 -012 -23 -00023 输入描述: 包含…

Linux 磁盘分区、挂载

Linux 磁盘分区、挂载 Linux 分区 介绍 Linux 来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;一个独立且唯一的文件结构 , Linux 中每个分区都是用来组成整个文件系统的一部分。 Linux 采用了一种叫“载入”的处理方法&…

(十四)devops持续集成开发——jenkins流水线使用pipeline方式发布项目

前言 本节内容我们使用另外一种方式pipeline实现项目的流水线部署发布&#xff0c;Jenkins Pipeline是一种允许以代码方式定义持续集成和持续交付流水线的工具。通过Jenkins Pipeline&#xff0c;可以将整个项目的构建、测试和部署过程以脚本的形式写入Jenkinsfile中&#xff…

爬虫02-python爬虫使用的库及详解

文章目录 1 Urllib库的基本使用① 基本url请求② 查看http请求的响应信息③ 另一个请求方法&#xff1a;Request④ Handler 处理请求和响应 & Cookie 存储用户信息⑤ 异常处理⑥ url解析 2 Requests库的基本使用① 模块的简单使用② requests模块的各种请求方式③ 请求④ 响…

打通全渠道,聚道云助力时尚巨头提升运营效能

客户介绍&#xff1a; 北京某时尚有限公司是一家集设计、生产、销售于一体的时尚产业领军企业。自成立以来&#xff0c;该公司一直秉承着对时尚的独特理解和不懈追求&#xff0c;以打造高品质、高品位的时尚产品为己任&#xff0c;深受国内外消费者的喜爱。 客户痛点&#xff…

springcloud:1.Eureka详细讲解

Eureka 是 Netflix 开源的一个服务注册和发现工具,被广泛应用于微服务架构中。作为微服务架构中的核心组件之一,Eureka 提供了服务注册、发现和失效剔除等功能,帮助构建弹性、高可用的分布式系统。在现代软件开发领域,使用 Eureka 可以有效地管理和监控服务实例,实现服务之…

网络安全综合实验

1.实验拓扑 在这里注意因为第四个要求配置双击热备&#xff0c;我们可以第一时间配置&#xff0c;避免二次重复配置消耗时间 4、FW1和FW3组成主备模式的双机热备 具体配置位置在系统-->高可靠性-->双机热备-->配置 这里上行链路有两组&#xff0c;分别为电信和移动&…