Chrome插件开发技术选型

本文由 AI 生成,内容基于 DeepResearch 的调研和个人想法。

前言

在过去的几周里,我和我的团队深度参与了一个 Chrome 插件开发项目,从最初的技术选型到最终的 POC 验证,踩了不少坑,也积累了一些经验。今天我想把这些经验分享给正在考虑 Chrome 插件开发的你。

这篇文章会带你走过一次完整的技术选型过程,我们会聊到为什么选择某个框架、放弃另一个框架,以及在真实项目中遇到的各种问题和解决方案。无论你是技术管理者还是开发者,希望这篇文章能为你的技术决策提供一些有价值的参考。

背景与需求

业务场景的思考

在我们日常的工作中,经常遇到这样的场景:阅读技术文档时需要快速理解复杂概念、分析财经报告时希望数据能自动图表化、研究学术论文时想要提取关键信息并结构化展示。传统的做法是复制文本到其他工具中进行处理,这个过程既繁琐又打断了阅读的连贯性。

我们想要解决的核心问题是:如何让用户在不离开当前网页的情况下,获得文本内容的智能分析和可视化增强?

需求分析

经过深入调研,我们明确了几个关键需求:

  • 无感知集成:用户无需安装额外软件或跳转页面,在任何网页上都能使用
  • 智能化处理:能够理解文本语义并自动选择合适的分析方法
  • 多样化呈现:支持图表、思维导图、时间线等多种可视化形式
  • 实时反馈:用户能够看到 AI 的思考过程和处理进度
  • 交互增强:可视化结果支持进一步的交互和关联分析

功能说明

我开发的 Chrome 插件是”AI 可视化增强分析助手”,功能是:自动识别网页内容,对其进行分段,提取段落信息,然后调用 LLM,分析每个段落适合用什么形式进行增强(比如’card’ | ‘chart’ | ‘infographic’ | ‘summary’等等),然后调用 LLM 生成对应工具的内容,追加到原网页中的对应位置,进行增强说明。

项目特性

这个工具有如下特性:

  • 需要修改用户访问的原网页,要给其插入内容,并且保证样式的美观度
  • 不需要后端,是直接调用 LLM 的 API 接口处理逻辑
  • 需要快速出 POC,验证技术和业务的可行性

团队情况

我们是一个 3 人小团队,大家对 Chrome 插件开发的熟悉程度很一般,之前没怎么开发过。POC 时间线只有 5 天,需要在短时间内快速迭代。

我关注的内容

在做技术选型时,我特别关注这几个方面:

  1. 热更新很重要:特别是 Chrome 插件开发,似乎搞了热更新的不多;不然每次都要重新 build、更新插件,巨麻烦
  2. 避免过度复杂:chrome-extension-boilerplate-react-vite 感觉有点重,对于快速 POC 项目来说,其目录结构似乎太多了,开发人员要了解的东西也多,学习和维护成本较大;并且这个因为 monorepo 的缘故,build 很慢,这个不喜欢;是不是大型的项目更适合这个?
  3. 开发者体验很重要:干得爽不爽很重要
  4. 团队协作:要考虑多人合作的问题、分工的问题等等
  5. AI Coding 契合度:重点考虑和 AI 编码工具的契合度,现在我们都是用 AI 工具编码(比如 Cursor、Claude Code、Github Copilot 等等),自己基本不写代码
  6. 调试便利性:调试的便利性很重要,如何查看 Chrome 插件的日志、请求的 network 信息、方便的测试功能等等,可能实际上 20%时间是开发功能,80%时间都是在调试修改

技术方案

基于我们的需求,我调研了几个主流的 Chrome 插件开发方案:

方案一:chrome-extension-boilerplate-react-vite

这是一个相当成熟的样板项目,基于 React + TypeScript + Vite 构建,采用 Monorepo 架构。

优点:

  • 成熟的 React 生态系统
  • TypeScript 类型安全
  • Vite 构建速度快(相对于 webpack)
  • 完整的 Chrome 扩展 API 封装

缺点:

  • 目录结构复杂:monorepo 架构对 POC 项目过重
  • 学习成本高:需要理解多个 package 的职责
  • 构建时间较长:monorepo 构建相对较慢
  • 配置复杂:需要理解多个配置文件
  • 过度工程化:对 5 天 POC 项目来说过于复杂

方案二:WXT 框架

WXT 是一个现代化的 Chrome 扩展开发框架,支持多种前端框架。

优点:

  • 开发体验极佳:专门为 Chrome 扩展开发优化
  • 热更新支持:真正的 HMR,无需手动刷新插件
  • 目录结构简洁:单仓库,易于理解
  • 构建速度快:优化过的构建流程
  • AI 编码友好:简单直观的项目结构
  • 调试友好:内置开发工具和日志系统
  • 文档完善:丰富的 API 文档和示例

缺点:

  • 相对较新,社区生态相对较小
  • 不适用于非 Chrome 扩展项目

方案三:Plasmo 框架

Plasmo 是一个”电池包式的浏览器扩展 SDK”,号称”Like Next.js for browser extensions!”。

优点:

  • 开发者体验极佳:”Like Next.js for browser extensions!”
  • 热更新支持:Live-reloading + React HMR
  • 零配置开箱即用:pnpm create plasmo快速启动
  • 声明式开发:简化 Chrome 扩展 API 调用
  • 强大的生态集成:内置 Firebase、Redux、Supabase 示例
  • Content Script UI:专门为网页内容注入优化
  • 远程打包:支持云端构建和部署
  • 企业级支持:被 Liveblocks、ArConnect 等公司使用

缺点:

  • 相对较新,社区仍在成长中
  • 高级功能可能需要理解其特定的工作模式
  • 对非 React 项目支持有限

方案四:Create React App + Chrome Extension Template

这是最传统的方案,使用 CRA 然后手动配置 Chrome 扩展相关功能。

优点:

  • 团队熟悉 CRA 开发模式
  • 相对简单的项目结构
  • 丰富的 React 生态

缺点:

  • 需要手动配置 Chrome 扩展相关功能
  • 热更新支持有限
  • 构建优化需要手动处理

深入实现

最终选择:WXT 框架

经过深入分析,我最终选择了WXT 框架作为我们的技术方案。这个决策基于以下几个关键因素:

1. 项目健康状况调查

在做技术选型时,我发现了一个重要问题:Plasmo 项目实际上已经基本停滞。社区的反馈极其负面,多位开发者直言”Plasmo is dead”,并且其 GitHub 仓库至今仍挂着”alpha 软件”的免责声明。

更关键的是,拥有 60 万用户的知名插件 ChatGPT Writer 已经从 Plasmo 迁移到了 WXT,主要原因就是 Plasmo 的性能问题和糟糕的开发体验。

2. 技术架构对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// WXT配置示例 - 简洁明了
import { defineConfig } from 'wxt';

export default defineConfig({
manifest: {
name: 'AI可视化增强分析助手',
description: '智能分析网页内容并生成可视化增强',
version: '0.1.0',
},
modules: ['@wxt-dev/module-react'],
dev: {
port: 3000,
},
});

相比之下,Plasmo 使用 Parcel 作为构建工具,而 WXT 使用更现代的 Vite。这意味着更好的 HMR 支持、更快的构建速度和更丰富的插件生态。

3. 核心功能实现

我们的核心功能是在网页中注入 React 组件,这需要解决样式隔离问题。WXT 提供了createShadowRootUi助手,完美解决了这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// contents/content.tsx
import { createShadowRootUi } from 'wxt/client';
import React from 'react';
import { createRoot } from 'react-dom/client';
import VisualCard from '../components/VisualCard';

export default defineContentScript({
matches: ['<all_urls>'],
main() {
// 创建Shadow DOM UI
const ui = createShadowRootUi({
name: 'ai-enhancement-ui',
position: 'inline',
onMount: (container) => {
const root = createRoot(container);
root.render(<VisualCard />);
return root;
},
onRemove: (root) => {
root.unmount();
},
});

// 激活UI
ui.mount();
},
});

4. 样式处理

我们选择 Tailwind CSS + CSS Modules 的方案:

1
2
3
4
5
6
7
8
9
10
/* styles/global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* 确保样式在Shadow DOM中正常工作 */
:host {
all: initial;
font-family: system-ui, -apple-system, sans-serif;
}

5. LLM API 调用架构

我们采用 Background Script + Content Script 协作的架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// background/background.ts
export default defineBackground({
type: 'module',
main() {
// 监听来自content script的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'ANALYZE_CONTENT') {
handleContentAnalysis(request.content)
.then((result) => sendResponse({ success: true, data: result }))
.catch((error) =>
sendResponse({ success: false, error: error.message })
);
return true; // 保持消息通道开放
}
});
},
});

async function handleContentAnalysis(content: string) {
// 调用LLM API
const response = await fetch(
'https://api.llm-provider.com/v1/chat/completions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LLM_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{
role: 'system',
content:
'你是一个内容分析助手,需要分析文本内容并推荐合适的可视化形式。',
},
{
role: 'user',
content: `请分析以下内容,并返回适合的可视化形式:${content}`,
},
],
}),
}
);

return response.json();
}

6. 内容脚本实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// contents/content.tsx
export default defineContentScript({
matches: ['<all_urls>'],
main() {
// 分析页面内容
const paragraphs = document.querySelectorAll('p');

paragraphs.forEach((paragraph, index) => {
const content = paragraph.textContent;
if (content && content.length > 100) {
// 只分析足够长的段落
// 发送到background script进行分析
chrome.runtime.sendMessage(
{ type: 'ANALYZE_CONTENT', content },
(response) => {
if (response.success) {
// 创建增强UI
createEnhancementUI(paragraph, response.data);
}
}
);
}
});
},
});

function createEnhancementUI(targetElement: HTMLElement, analysisData: any) {
const enhancementContainer = document.createElement('div');
enhancementContainer.className = 'ai-enhancement-container';

// 根据分析结果创建不同的组件
switch (analysisData.recommendedFormat) {
case 'card':
renderCard(enhancementContainer, analysisData);
break;
case 'chart':
renderChart(enhancementContainer, analysisData);
break;
case 'summary':
renderSummary(enhancementContainer, analysisData);
break;
default:
renderDefault(enhancementContainer, analysisData);
}

// 插入到原元素后面
targetElement.parentNode?.insertBefore(
enhancementContainer,
targetElement.nextSibling
);
}

项目结构

最终的项目结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
my-extension/
├── src/
│ ├── components/ # React组件
│ │ ├── VisualCard.tsx
│ │ ├── ChartContainer.tsx
│ │ └── SummaryBox.tsx
│ ├── contents/ # Content Scripts
│ │ └── content.tsx # 主要内容脚本
│ ├── background/ # Background Scripts
│ │ └── background.ts # 后台脚本
│ ├── hooks/ # 自定义hooks
│ │ ├── useContentAnalysis.ts
│ │ └── useLLMApi.ts
│ ├── utils/ # 工具函数
│ │ ├── contentParser.ts
│ │ ├── styleInjector.ts
│ │ └── logger.ts
│ └── styles/ # 样式文件
│ └── global.css
├── assets/
│ ├── icon-16.png
│ ├── icon-48.png
│ └── icon-128.png
├── wxt.config.ts # WXT配置
└── package.json

踩坑经验

1. 热更新的坑

问题:Chrome 插件的热更新一直是个老大难问题。传统的开发流程是:修改代码 → 构建插件 → 在 Chrome 中手动更新插件 → 刷新页面。这个过程非常繁琐。

解决方案:WXT 提供了真正的 HMR 支持。在开发模式下,只需要:

1
pnpm dev

WXT 会自动监听文件变化,重新构建插件,并在 Chrome 中自动更新,整个过程无需手动干预。

2. 样式隔离的坑

问题:在网页中注入内容时,很容易与原网页的样式发生冲突。我们遇到过注入的卡片被原网页的 CSS 样式影响,导致显示异常。

解决方案:使用 Shadow DOM 来隔离样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const ui = createShadowRootUi({
name: 'ai-enhancement-ui',
position: 'inline',
onMount: (container) => {
// 创建Shadow DOM,样式完全隔离
const shadowRoot = container.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = `
:host {
all: initial;
}
.card {
/* 样式定义 */
}
`;
shadowRoot.appendChild(style);

const root = createRoot(shadowRoot);
root.render(<VisualCard />);
return root;
},
});

3. 调试困难的坑

问题:Chrome 插件的调试比普通网页复杂得多。content script、background script、popup 各有各的调试环境,查看日志和网络请求都很麻烦。

解决方案

  1. 统一的日志系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// utils/logger.ts
export class Logger {
private static getPrefix() {
return `[AI Assistant ${new Date().toISOString()}]`;
}

static info(message: string, data?: any) {
console.log(`${this.getPrefix()} INFO: ${message}`, data || '');
}

static error(message: string, error?: any) {
console.error(`${this.getPrefix()} ERROR: ${message}`, error || '');
}

static debug(message: string, data?: any) {
if (process.env.NODE_ENV === 'development') {
console.debug(`${this.getPrefix()} DEBUG: ${message}`, data || '');
}
}
}
  1. Background Script 调试
1
2
// 在background script中,所有console.log会出现在Chrome扩展的background page控制台
// chrome://extensions/ → 点击插件的服务工作器
  1. Content Script 调试
1
2
// content script的日志会出现在网页的开发者工具中
// 右键检查元素 → Console标签

4. LLM API 调用的坑

问题:LLM API 调用很慢,而且经常失败。在开发阶段如果没有 mock,调试前端效率极低。

解决方案

  1. 实现 Mock 机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// utils/mockLLM.ts
export const mockLLMResponse = {
card: {
recommendedFormat: 'card',
title: '关键概念解释',
content: '这是一个简化的概念解释...',
},
chart: {
recommendedFormat: 'chart',
chartType: 'bar',
data: [10, 20, 30, 40],
},
};

export function getMockResponse(content: string) {
// 根据内容关键词返回不同的mock响应
if (content.includes('增长') || content.includes('数据')) {
return mockLLMResponse.chart;
}
return mockLLMResponse.card;
}
  1. 错误重试机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// utils/api.ts
export async function callLLMApi(content: string, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(
'https://api.llm-provider.com/v1/chat/completions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LLM_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{
role: 'system',
content: '你是一个内容分析助手...',
},
{
role: 'user',
content: `请分析以下内容:${content}`,
},
],
}),
}
);

if (!response.ok) {
throw new Error(`API调用失败: ${response.status}`);
}

return await response.json();
} catch (error) {
if (i === retries - 1) {
// 最后一次重试失败,返回mock数据
Logger.warn('LLM API调用失败,使用mock数据', error);
return getMockResponse(content);
}
// 等待后重试
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
}

5. 性能优化的坑

问题:在内容较多的网页上,我们的插件会分析每个段落,导致大量的 API 调用和 DOM 操作,严重影响页面性能。

解决方案

  1. 节流和防抖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// utils/performance.ts
export function throttle<T extends (...args: any[]) => any>(
func: T,
delay: number
): T {
let timeoutId: NodeJS.Timeout;
return ((...args: any[]) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => func.apply(null, args), delay);
}) as T;
}

export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): T {
let timeoutId: NodeJS.Timeout;
return ((...args: any[]) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
}) as T;
}
  1. 懒加载和虚拟滚动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// components/VirtualList.tsx
import React, { useState, useEffect, useRef } from 'react';

interface VirtualListProps {
items: any[];
renderItem: (item: any) => React.ReactNode;
itemHeight: number;
containerHeight: number;
}

export const VirtualList: React.FC<VirtualListProps> = ({
items,
renderItem,
itemHeight,
containerHeight,
}) => {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);

const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight),
items.length - 1
);

const visibleItems = items.slice(startIndex, endIndex + 1);

useEffect(() => {
const container = containerRef.current;
if (container) {
const handleScroll = throttle(() => {
setScrollTop(container.scrollTop);
}, 16); // 60fps

container.addEventListener('scroll', handleScroll);
return () => container.removeEventListener('scroll', handleScroll);
}
}, []);

return (
<div
ref={containerRef}
style={{ height: containerHeight, overflow: 'auto' }}
>
<div style={{ height: items.length * itemHeight }}>
<div
style={{
position: 'absolute',
top: startIndex * itemHeight,
width: '100%',
}}
>
{visibleItems.map((item, index) => (
<div key={startIndex + index} style={{ height: itemHeight }}>
{renderItem(item)}
</div>
))}
</div>
</div>
</div>
);
};

6. 团队协作的坑

问题:3 个人同时开发一个 Chrome 插件,经常出现代码冲突、环境不一致、功能重复开发的问题。

解决方案

  1. 清晰的代码分工
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在项目根目录创建TEAM.md
# 团队分工

## 成员A - 负责核心功能
- Content Script开发和调试
- LLM API集成
- 内容分析逻辑

## 成员B - 负责UI组件
- React组件开发
- 样式设计和优化
- 用户交互体验

## 成员C - 负责工具和基建
- 项目配置和构建优化
- 单元测试和E2E测试
- 文档和部署
  1. 统一的开发环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# scripts/setup.sh
#!/bin/bash
echo "🚀 设置AI助手开发环境..."

# 检查Node.js版本
if ! command -v node &> /dev/null; then
echo "❌ 请先安装Node.js"
exit 1
fi

# 安装pnpm
if ! command -v pnpm &> /dev/null; then
echo "📦 安装pnpm..."
npm install -g pnpm
fi

# 安装依赖
echo "📥 安装依赖..."
pnpm install

# 复制环境变量模板
if [ ! -f ".env.local" ]; then
echo "📝 创建环境变量文件..."
cp .env.example .env.local
echo "⚠️ 请编辑 .env.local 文件,添加你的API密钥"
fi

echo "✅ 开发环境设置完成!"
echo "🔥 运行 'pnpm dev' 开始开发"
  1. 代码规范和自动化检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// package.json
{
"scripts": {
"dev": "wxt dev",
"build": "wxt build",
"preview": "wxt build && wxt zip",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix",
"type-check": "tsc --noEmit",
"test": "vitest",
"test:ui": "vitest --ui",
"prepare": "husky install"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"]
}
}

方案对比总结

经过实际开发验证,这里是我们的最终评分:

特性 WXT (首选) Plasmo (高风险) chrome-extension-boilerplate
项目健康度/风险 ⭐⭐⭐⭐⭐ (活跃) ⭐ (停滞/Alpha) ⭐⭐⭐⭐ (良好)
开发体验 (DX) ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐
热更新支持 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
学习成本 (POC) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
内容注入支持 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
AI 编码友好 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
社区生态 (活跃度) ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐

经验总结

1. 技术选型的关键教训

不要被表面数据迷惑:Plasmo 有很高的 GitHub 星标,但实际上项目已经停滞。在做技术选型时,一定要深入查看项目的实际维护状态,而不仅仅是看星标数。

POC 项目越简单越好:我们之前吃过复杂架构的亏,这次坚持”技术架构越简单越好”的原则,避免了不必要的复杂性。

开发者体验直接影响效率:好的热更新、调试工具、文档支持能显著提升开发效率。在 5 天的 POC 中,这些因素往往比功能本身更重要。

2. Chrome 插件开发的特殊考虑

样式隔离是必须的:在网页中注入内容时,一定要使用 Shadow DOM 或其他样式隔离技术,否则会被原网页的样式影响。

调试比想象中复杂:要提前规划好调试策略,包括日志系统、错误处理、网络监控等。

性能优化不能忽视:Chrome 插件运行在用户的浏览器中,性能问题会直接影响用户体验。

3. 团队协作的建议

明确分工很重要:小团队更需要明确的分工,避免功能重复开发。

统一的开发环境:确保所有团队成员使用相同的开发环境,减少”在我电脑上是好的”这类问题。

代码质量和自动化:在 POC 阶段就要建立代码质量意识,使用 lint、pre-commit hooks 等工具。

4. AI 编码工具的使用心得

项目结构要清晰:AI 编码工具在结构清晰、标准化的项目中表现更好。

代码注释要详细:用中文写清楚的注释,帮助 AI 理解代码逻辑。

合理的代码拆分:将复杂功能拆分成小的、职责单一的函数,便于 AI 理解和生成。

参考资料

官方文档

技术文章

工具和资源

结语

Chrome 插件开发看似简单,但实际上有很多坑需要踩。通过这次项目,我们深刻体会到技术选型的重要性,以及开发者体验对项目效率的影响。

WXT 框架虽然相对较新,但其现代化的架构、优秀的开发者体验和活跃的社区维护,使其成为我们这个项目的最佳选择。如果你也在考虑 Chrome 插件开发,希望我们的经验能为你提供一些有价值的参考。

记住,技术选型不仅仅是选择最流行的工具,而是选择最适合你团队、项目需求和时间的方案。在快速 POC 的阶段,简单、高效、可靠往往比功能强大更重要。