WXT Entrypoints 完整指南:目录约定、类型详解与 manifest 自动化生成
2026/9/24 23:46:54 网站建设 项目流程
  • 前端
  • 开发工具
  • 构建工具
  • 插件系统

【免费下载链接】wxt

⚡ Next-gen Web Extension Framework

项目地址:https://gitcode.com/gh_mirrors/wx/wxt
点击查看免费下载

Entrypoints(入口点)是 WXT 扩展开发的核心概念——entrypoints/目录下的每一个文件或目录,都是 WXT 打包扩展的输入,也是 manifest.json 自动生成的依据。本篇指南基于 WXT 官方文档(docs/guide/essentials/entrypoints.md)与源码实现(find-entrypoints.ts),完整梳理入口点的目录规则、13 类入口点的文件名模式与可配置选项,并结合 wxt-demo 的真实示例,帮助你彻底掌握"声明式定义扩展"的 WXT 开发方式。

什么是 Entrypoints

在 WXT 中,entrypoints/目录是扩展的"装配车间":目录内的文件作为打包时的输入(inputs),参与构建产物生成。它们可以是 HTML、JS、CSS,也可以是 Vite 支持的任意变体——TypeScript(.ts)、JSX(.tsx/.jsx)、SCSS、Sass、Less、Stylus 等,均无需额外配置即可直接使用。

入口点不仅决定"构建什么",还决定"manifest 里写什么"。WXT 会读取每个入口点内声明的选项(manifest options),在构建时自动生成对应的 manifest.json,省去了手动维护backgroundcontent_scriptsaction等清单字段的繁琐工作。

目录结构约定

entrypoints/目录内,一个入口点既可以是单个文件,也可以是一个包含index文件的目录,两种写法等价:

📂 entrypoints/ 📄 {name}.{ext}
📂 entrypoints/ 📂 {name}/ 📄 index.{ext}

入口点的name直接决定它的类型。例如,要添加一个 Background 入口点,以下两种文件布局任选其一:

📂 entrypoints/ 📄 background.ts
📂 entrypoints/ 📂 background/ 📄 index.ts

从源码看,WXT 通过PATH_GLOB_TO_TYPE_MAP这张文件名 glob 模式映射表来识别入口点类型,见 find-entrypoints.ts。getEntrypointName则取相对路径中第一个./之前的片段作为入口点名称,见 entrypoints.ts。因此youtube.content.ts的名称是youtube、类型是 content-script,而example-tsx.content.tsx(见 wxt-demo)也能被正确识别。

在入口点目录中放置相关文件

使用目录形式entrypoints/{name}/index.{ext}时,可以在index文件旁边放置与该入口点配套的其它文件,它们会作为模块被正确打包,而不会被误认为新入口点:

📂 entrypoints/ 📂 popup/ 📄 index.html ← 这是入口点 📄 main.ts 📄 style.css 📂 background/ 📄 index.ts ← 这是入口点 📄 alarms.ts 📄 messaging.ts 📂 youtube.content/ 📄 index.ts ← 这是入口点 📄 style.css

禁止把相关文件直接放进 entrypoints/ 根目录

千万不要把某个入口点的附属文件直接放在entrypoints/目录下——WXT 会把它们当成独立入口点尝试构建,通常会直接报错。正确做法是把这些文件放进对应入口点的目录中:

📂 entrypoints/ 📄 popup.html ✗ 错误:应使用目录 📄 popup.ts ✗ 错误 📄 popup.css ✗ 错误 📂 popup/ ✓ 正确 📄 index.html 📄 main.ts 📄 style.css

不支持深层嵌套

entrypoints/目录虽然在观感上类似 Nuxt 或 Next.js 的pages/目录,但WXT 不支持同样的深层嵌套。入口点只能位于entrypoints/下零层或一层(即entrypoints/{name}.{ext}entrypoints/{name}/index.{ext}),嵌套更深将无法被发现和构建:

📂 entrypoints/ 📂 youtube/ ✗ 错误:嵌套过深 📂 content/ 📄 index.ts 📄 ... 📂 injected/ 📄 index.ts 📄 ... 📂 youtube.content/ ✓ 正确:用命名后缀区分 📄 index.ts 📄 ... 📂 youtube-injected/ ✓ 正确:用命名后缀区分 📄 index.ts 📄 ...

正如PATH_GLOB_TO_TYPE_MAP所示,WXT 只匹配entrypoints/下一层的 glob 模式(如*.content.[jt]s?(x)*/index.html),更深层的路径不在匹配范围内,这就是嵌套不被支持的根源。

Listed 与 Unlisted:两类入口点

Web 扩展中存在两种入口点:

  1. Listed(已列出):被引用在manifest.json中的入口点,例如 Popup、Options、Background、Content Script 等。WXT 文档中通常直接用名字称呼它们。
  2. Unlisted(未列出):不出现在 manifest 中,但扩展运行时会用到的入口点,例如:
    • 扩展安装后在新标签页展示的欢迎页;
    • 由内容脚本注入到主世界(main world)的 JS 文件。

Unlisted 入口点的具体用法见下文 Unlisted Pages、Unlisted Scripts 与 Unlisted CSS。

在入口点内部定义 manifest 选项

大多数 listed 入口点需要在manifest.json中声明对应选项。与"另起一个文件维护 manifest"的传统方式不同,WXT 把选项直接定义在入口点文件内部

  • 对 JS 类入口点,选项作为defineXxx工厂函数的参数传入。例如给内容脚本声明matches
export default defineContentScript({ matches: ['*://*.wxt.dev/*'], main() { // ... }, });
  • 对 HTML 类入口点,选项通过<meta>标签配置。例如为 MV2 popup 使用page_action
<!doctype html> <html lang="en"> <head> <meta name="manifest.type" content="page_action" /> </head> </html>

构建时,WXT 会收集入口点中声明的选项,据此生成 manifest.json。

meta 标签的解析机制(源码级)

从源码看,HTML 入口点的选项解析逻辑位于 find-entrypoints.ts 的importHtmlEntrypoint函数:

  • 只处理namemanifest.wxt.前缀开头的<meta>标签,其余标签直接忽略;
  • manifest.前缀后的内容会通过camelCase转为驼峰键名(例如manifest.default_icondefaultIcon);
  • <meta>content属性会优先尝试用 JSON5 解析成结构化数据(这就是 Popup 的default_icon能写成对象字面量、theme_icons能写成数组的原因),解析失败才退化为原始字符串;
  • <title>标签的内容也会被读取,作为title选项(例如 Popup 的default_title)。

按浏览器差异化配置选项

WXT 的入口点选项还支持per-browser(按浏览器)差异化:任何选项都可以写成{ chrome: ..., firefox: ... }这种按目标浏览器取值的形式。源码中的resolvePerBrowserOption/resolvePerBrowserOptions(见 entrypoints.ts)会在解析时根据当前构建目标浏览器替换成对应值,其中defaultIcon是唯一的特例——它是 Record 结构,被显式排除在解析之外,以避免与 per-browser 语法冲突。这意味着你可以在同一个入口点里为 Chrome 与 Firefox 声明不同的runAtpersistent等行为。

Entrypoint 类型详解

下面按类型逐一说明文件名模式、可配置选项与注意事项。文件名模式中的[jt]sx?表示.js.ts.jsx.tsx均可用;[jt]s表示.js.ts

Background

文件名模式:

  • background.[jt]s→ 输出为background.js
  • background/index.[jt]s→ 输出为background.js

最小写法:

export default defineBackground(() => { // 后台脚本加载时执行 });

带 manifest 选项的写法:

export default defineBackground({ // 设置 manifest 选项 persistent: undefined | true | false, type: undefined | 'module', // 设置在部分浏览器构建中是否移除该入口点 include: undefined | string[], exclude: undefined | string[], main() { // 后台脚本加载时执行,注意:不能是 async }, });

关键行为

  • 在 MV2 中,background 作为脚本挂到后台页面(background page);在 MV3 中,background 成为 Service Worker。
  • 构建过程中,WXT 会在 Node.js 环境中导入该文件以读取配置,因此任何运行时逻辑都不能写在main函数之外。下面这种写法是错误的:
browser.action.onClicked.addListener(() => { // ✗ 错误:模块顶层代码会在 Node 构建环境执行 // ... }); export default defineBackground(() => { browser.action.onClicked.addListener(() => { // ✓ 正确:放进 main 内 // ... }); });

源码层面,defineBackground是一个纯类型包装函数(见 define-background.ts):传入函数时包装成{ main: fn },传入对象时原样返回。类型定义BackgroundDefinition明确要求main(): void(见 types.ts),即 background 的 main 不允许是异步的。WXT 的加载机制详见 Entrypoint Loaders。

Bookmarks

文件名模式:

  • bookmarks.html→ 输出为bookmarks.html
  • bookmarks/index.html→ 输出为bookmarks.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Title</title> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

定义 Bookmarks 入口点后,WXT 会自动修改 manifest,用你的 HTML 页面覆盖浏览器的书签管理页。

Content Scripts

文件名模式:

  • content.[jt]sx?→ 输出为content-scripts/content.js
  • content/index.[jt]sx?→ 输出为content-scripts/content.js
  • {name}.content.[jt]sx?→ 输出为content-scripts/{name}.js
  • {name}.content/index.[jt]sx?→ 输出为content-scripts/{name}.js

注意:命名内容脚本({name}.content.ts)是内容脚本推荐的组织方式,多个内容脚本不会互相覆盖。

export default defineContentScript({ // 设置 manifest 选项 matches: string[], excludeMatches: undefined | [], includeGlobs: undefined | [], excludeGlobs: undefined | [], allFrames: undefined | true | false, runAt: undefined | 'document_start' | 'document_end' | 'document_idle', matchAboutBlank: undefined | true | false, matchOriginAsFallback: undefined | true | false, world: undefined | 'ISOLATED' | 'MAIN', // 设置在部分浏览器构建中是否移除该入口点 include: undefined | string[], exclude: undefined | string[], // 配置 CSS 注入页面的方式 cssInjectionMode: undefined | "manifest" | "manual" | "ui", // 配置内容脚本的注册方式 registration: undefined | "manifest" | "runtime", main(ctx: ContentScriptContext) { // 内容脚本加载时执行,可以是 async }, });

关键行为

  • 与 background 相同,构建时该文件也会在 Node.js 环境被导入,所以运行时代码必须放进main
const container = document.createElement('div'); // ✗ 错误 document.body.append(container); // ✗ 错误 export default defineContentScript({ main: function () { const container = document.createElement('div'); // ✓ 正确 document.body.append(container); // ✓ 正确 }, });
  • defineContentScript同样是类型包装函数(见 define-content-script.ts),运行时不做任何处理。
  • 在 wxt-demo 中可以看到content.tsiframe.content.tslocation-change.content.tsmain-world.content.tsexample-tsx.content.tsxautomount.content/ui.content/injected.content/等多个内容脚本实例(见 entrypoints 目录)。
  • 内容脚本 UI 的创建方式与 CSS 注入配置,详见 Content Scripts 指南。

Devtools

文件名模式:

  • devtools.html→ 输出为devtools.html
  • devtools/index.html→ 输出为devtools.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

Devtools 入口点用于注册浏览器的开发者工具面板;如需添加不同的面板(panels)与窗格(panes),可以参考 WXT 官方的 devtools-extension 示例项目。

History

文件名模式:

  • history.html→ 输出为history.html
  • history/index.html→ 输出为history.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Title</title> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

定义 History 入口点后,WXT 会自动修改 manifest,用你的 HTML 页面覆盖浏览器的历史记录页。

Newtab

文件名模式:

  • newtab.html→ 输出为newtab.html
  • newtab/index.html→ 输出为newtab.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Title</title> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

定义 Newtab 入口点后,WXT 会自动修改 manifest,用你的 HTML 页面覆盖浏览器的新标签页。

Options

文件名模式:

  • options.html→ 输出为options.html
  • options/index.html→ 输出为options.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Options Title</title> <!-- 自定义 manifest 选项 --> <meta name="manifest.open_in_tab" content="true|false" /> <meta name="manifest.chrome_style" content="true|false" /> <meta name="manifest.browser_style" content="true|false" /> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

open_in_tab决定选项页是在新标签页打开还是嵌入在扩展管理界面中;chrome_style/browser_style则分别对应 Chrome 与 Firefox 的浏览器内置样式。wxt-demo 中的 options 入口点 就是该模式的完整示例(配套main.tsstyle.css)。

Popup

文件名模式:

  • popup.html→ 输出为popup.html
  • popup/index.html→ 输出为popup.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- 设置 manifest 中的 `action.default_title` --> <title>Default Popup Title</title> <!-- 自定义 manifest 选项 --> <meta name="manifest.default_icon" content="{ '16': '/icon-16.png', '24': '/icon-24.png', ... }" /> <meta name="manifest.type" content="page_action|browser_action" /> <meta name="manifest.browser_style" content="true|false" /> <!-- 仅 Firefox:设置动作按钮的放置位置 --> <meta name="manifest.default_area" content="navbar|menupanel|tabstrip|personaltoolbar" /> <!-- 仅 Firefox:亮色/暗色主题下的图标 --> <meta name="manifest.theme_icons" content="[ { light: '/icon-light-16.png', dark: '/icon-dark-16.png', size: 16 }, { light: '/icon-light-32.png', dark: '/icon-dark-32.png', size: 32 } ]" /> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

Popup 是扩展工具栏弹出窗,上述选项最终都会映射到 manifest 的action(MV3)或browser_action/page_action(MV2)字段。源码中getPopupEntrypoint(见 find-entrypoints.ts)会做额外的规整:title被重命名为defaultTitletype被映射为actionType(非page_action一律视为browser_action),并通过 getter/setter 保持与旧字段mv2Key的同步兼容。wxt-demo 的 popup.html 是真实示例。

Sandbox

警告:仅 Chromium 支持沙箱页面(sandboxed pages),Firefox 不支持。

文件名模式:

  • sandbox.html→ 输出为sandbox.html
  • sandbox/index.html→ 输出为sandbox.html
  • {name}.sandbox.html→ 输出为{name}.html
  • {name}.sandbox/index.html→ 输出为{name}.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Title</title> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

wxt-demo 中提供了 sandbox.html 与目录形式的 example.sandbox 两个示例。命名沙箱({name}.sandbox)常用于为扩展提供不受扩展 CSP 限制的 iframe 运行环境。

Side Panel

文件名模式:

  • sidepanel.html→ 输出为sidepanel.html
  • sidepanel/index.html→ 输出为sidepanel.html
  • {name}.sidepanel.html→ 输出为{name}.html
  • {name}.sidepanel/index.html→ 输出为{name}.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Default Side Panel Title</title> <!-- 自定义 manifest 选项 --> <meta name="manifest.default_icon" content="{ '16': '/icon-16.png', '24': '/icon-24.png', ... }" /> <meta name="manifest.open_at_install" content="true|false" /> <meta name="manifest.browser_style" content="true|false" /> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

在 Chrome 中,侧边栏使用side_panelAPI;在 Firefox 中则使用sidebar_actionAPI。WXT 会根据目标浏览器自动生成对应的 manifest 字段。wxt-demo 中的 sidepanel.html 可作参考。

Unlisted CSS

文件名模式:

  • {name}.(css|scss|sass|less|styl|stylus)→ 输出为{name}.css
  • {name}/index.(css|scss|sass|less|styl|stylus)→ 输出为{name}.css
  • content.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/content.css
  • content/index.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/content.css
  • {name}.content.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/{name}.css
  • {name}.content/index.(css|scss|sass|less|styl|stylus)→ 输出为content-scripts/{name}.css
body { /* ... */ }

CSS 入口点始终是 unlisted(不会进入 manifest 的content_scripts声明)。如需为内容脚本注入 CSS,请参考 Content Scripts 文档中的 CSS 章节。使用 SCSS、Less 等预处理器时,按 Vite 官方指南配置对应预处理器依赖即可,WXT 无需额外配置。wxt-demo 中 injected.content/index.css、example-2.scss 都是 CSS 入口点的实例。

Unlisted Pages

文件名模式:

  • {name}.html→ 输出为{name}.html
  • {name}/index.html→ 输出为{name}.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Title</title> <!-- 设置在部分浏览器构建中是否移除该页面 --> <meta name="manifest.include" content="['chrome', ...]" /> <meta name="manifest.exclude" content="['chrome', ...]" /> </head> <body> <!-- ... --> </body> </html>

运行时,unlisted pages 通过/{name}.html路径访问:

const url = browser.runtime.getURL('/{name}.html'); console.log(url); // "chrome-extension://{id}/{name}.html" window.open(url); // 在新标签页打开该页面

这非常适合"安装后欢迎页"之类的场景。注意{name}需要替换成你实际的文件名,例如welcome.html

Unlisted Scripts

文件名模式:

  • {name}.[jt]sx?→ 输出为{name}.js
  • {name}/index.[jt]sx?→ 输出为{name}.js

最小写法:

export default defineUnlistedScript(() => { // 脚本加载时执行 });

带选项的写法:

export default defineUnlistedScript({ // 设置在部分浏览器构建中是否移除该脚本 include: undefined | string[], exclude: undefined | string[], main() { // 脚本加载时执行 }, });

运行时,unlisted scripts 通过/{name}.js路径访问:

const url = browser.runtime.getURL('/{name}.js'); console.log(url); // "chrome-extension://{id}/{name}.js"

你需要在需要的地方自行加载/运行这些脚本。如果脚本要注入到网页中(如内容脚本向主世界注入),别忘了把脚本及其相关资源加入 manifest 的web_accessible_resources,否则网页环境无法访问它们。

与其他 JS 入口点一致,构建时该文件会在 Node.js 环境被导入,因此运行时代码必须放在main内:

document.querySelectorAll('a').forEach((anchor) => { // ✗ 错误:顶层代码 // ... }); export default defineUnlistedScript(() => { document.querySelectorAll('a').forEach((anchor) => { // ✓ 正确 // ... }); });

defineUnlistedScript的实现与defineBackground完全对称(见 define-unlisted-script.ts):函数参数会被包装成{ main: fn }。wxt-demo 中的 unlisted.ts 是实际示例。

构建时的校验与跳过机制(源码级)

从 find-entrypoints.ts 可以还原 WXT 构建入口点的完整流程:

  1. 发现:用tinyglobbyPATH_GLOB_TO_TYPE_MAP的键在entrypoints/下做 glob 匹配,得到候选文件列表;
  2. 去重:如果{name}/index.{ext}{name}/index.html同时存在,非 HTML 的 index 文件会被过滤掉;
  3. 校验preventNoEntrypoints在目录为空时报错No entrypoints found in ...preventDuplicateEntrypointNames在出现同名入口点时抛出 "Multiple entrypoints with the same name detected"(同名可能来自popup.htmlpopup/index.html这类冲突,见 find-entrypoints.ts);
  4. 读取选项:HTML 入口点走importHtmlEntrypoint解析<meta><title>;JS 入口点通过wxt.builder.importEntrypoints在构建器(Vite)中导入并读取默认导出;CSS 入口点没有选项;
  5. 组装:按类型分别调用getPopupEntrypointgetBackgroundEntrypointgetContentScriptEntrypoint等工厂函数,生成带输出目录与规范化选项的入口点对象;内容脚本的产物统一输出到content-scripts/子目录;
  6. dev 兜底:在wxt serve(开发模式)下如果没有 background 入口点,会自动注入一个 noop(空操作)background,保证扩展能正常加载(见 find-entrypoints.ts);
  7. 跳过判定isEntrypointSkipped依据include/exclude选项判断当前目标浏览器是否应跳过该入口点——注意includeexclude不能同时使用,同时声明会打印警告并将该入口点标记为跳过(见 find-entrypoints.ts)。这也是文档中所有类型都提供include/excludemeta 选项的原因:同一份代码可以按['chrome']['firefox']等目标浏览器裁剪构建产物。

总结

WXT 的 entrypoints 机制把"文件系统即配置"的思想贯彻到了扩展开发中:

  • 目录约定决定了入口点的类型与产物路径:零层或一层的文件布局、{name}.{type}的命名后缀(.content.sandbox.sidepanel等)是关键;
  • 选项内联让 manifest 与入口点代码同处一地:JS 入口点用defineBackground/defineContentScript/defineUnlistedScript声明,HTML 入口点用manifest.*前缀的<meta>标签声明,并天然支持按浏览器差异化;
  • 自动生成 manifest让多浏览器构建(Chrome、Firefox、Safari 等)无需手工维护清单字段,配合include/exclude还能精准裁剪每个目标平台的入口点集合。

掌握了这份指南,你就能从"写文件"开始,完整地定义扩展的每个功能面——后台、弹窗、选项页、内容脚本、覆盖页与各类辅助脚本——剩下的打包与清单生成全部交给 WXT 完成。继续深入可以阅读 project-structure 了解目录全貌,或通过 entrypoint-loaders 理解入口点的加载时序。

  • 前端
  • 开发工具
  • 构建工具
  • 插件系统

【免费下载链接】wxt

⚡ Next-gen Web Extension Framework

项目地址:https://gitcode.com/gh_mirrors/wx/wxt
点击查看免费下载
上一篇:TypeScript Any 类型完全指南:通用超类型、安全边界与替代方案(The Concise TypeScript Book 深度解读)
下一篇:如何快速掌握小红书批量下载神器:XHS-Downloader完整使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询