现代前端四支柱:工程化、安全、设计模式与拓展能力实战体系 1. 这不是“学完JavaScript就完事了”的时代工程化、安全、设计模式、拓展四根支柱撑起现代前端真实战场你有没有遇到过这样的场景一个用原生JavaScript写的表单校验逻辑在测试环境跑得好好的上线后突然在iOS Safari里报Cannot read property trim of undefined或者团队新来的同学改了三行代码结果导致整个订单模块的支付流程卡死排查了两天才发现是某个全局变量被意外覆盖又或者CI流水线里跑着跑着突然提示“检测到目标站点存在JavaScript框架库漏洞”但没人知道这个漏洞到底在哪、影响多大、怎么修。这些不是偶然事故而是当JavaScript从“胶水语言”蜕变为支撑亿级用户核心业务的工程级语言后必然要直面的现实。今天聊的这四个词——工程化、安全、设计模式、拓展——不是教科书里的抽象概念而是我在过去八年带过12个中大型前端项目、亲手踩过至少37次线上故障坑之后总结出的四根承重柱。它们彼此咬合没有工程化安全和设计模式就是空中楼阁没有安全兜底再优雅的设计模式也可能成为攻击入口没有设计模式沉淀工程化会沦为一堆重复造轮子的流水线而拓展能力则决定了你的系统能否在不推倒重来的情况下持续应对业务裂变。这不是“进阶技巧”而是你现在写每一行const、每一个function时背后必须存在的思维背景板。如果你还在用script标签直接引入jQuery写业务逻辑那这套体系对你可能还遥远但如果你正参与一个需要支持日活50万、迭代周期压缩到2周、安全审计每季度必过的项目那么接下来的内容就是你每天打开编辑器前该默念的 checklist。2. 工程化从“能跑就行”到“可验证、可追溯、可预测”的系统性重构2.1 工程化的本质不是加工具而是建契约为什么Webpack配置里一个resolve.alias能救回三天联调时间很多人把工程化等同于“配Webpack”“搭Vite”“搞CI/CD”这就像把建筑学理解为“买水泥”。真正的工程化是给开发过程立下三份契约人与人之间的契约协作规范、人与机器之间的契约自动化规则、现在与未来的契约可维护性保障。我带的第一个电商后台项目初期用纯HTMLJS硬编码上线前夜发现商品列表页加载慢排查发现是17个页面各自引入了不同版本的moment.js有的用CDN有的本地打包有的甚至直接复制粘贴了源码片段。最后花48小时统一替换但没人敢动日期格式化逻辑——因为不知道谁在哪个文件里改过moment.locale(zh-cn)。这就是缺乏契约的典型代价。工程化第一步永远不是选工具而是定义“什么算完成”。比如我们团队现在强制要求所有API请求必须走统一的request封装层禁止直接使用fetch或axios实例组件内状态管理必须明确标注来源props / local state / context / store并在注释里写清变更触发路径每次提交必须包含可运行的单元测试用例覆盖率低于85%的PR自动拒绝合并。这些规则看似琐碎但直接对应到工具链上ESLint规则里禁用no-restricted-imports防止乱引第三方库Huskylint-staged在commit前自动检查注释规范Jest配合testing-library/jest-dom确保测试用例真实模拟用户行为。关键参数选择都有依据比如Jest的testTimeout设为5000ms是因为我们压测过95%的业务组件渲染交互测试在3200ms内完成留1800ms缓冲防偶发网络延迟ESLint的max-len设为100字符不是拍脑袋而是基于可读性研究——超过100字符的单行代码开发者眼球横向移动距离增加37%错误率上升22%。提示别迷信“最新版工具”。我们线上项目仍用Webpack 5.89非最新6.x因为升级到6.x后Module Federation在IE11兼容模式下会生成无效chunk hash导致热更新失效。工程化不是追求时髦而是用最稳的版本解决最痛的问题。2.2 构建流程的隐形成本从npm run build到交付产物中间藏着多少“不可见劳动”执行一次npm run build你以为只是几秒等待实际背后是至少7层隐性处理。以我们当前主站构建为例Vue3 TypeScript Vite步骤耗时均值关键作用常见陷阱1. 类型检查18.3stsc --noEmit扫描所有.ts文件捕获any滥用、未定义属性访问开发者常关掉此步图快导致any泛滥后期重构成本指数级上升2. 依赖预构建12.7sVite将node_modules中ESM模块转为浏览器可执行格式避免启动时解析失败若package.json中exports字段配置错误会导致某些库如lodash-es无法正确解析3. 模块解析9.5s解析import路径处理/components等别名定位真实文件位置vite.config.ts中resolve.alias若未同步更新tsconfig.json的pathsTypeScript类型检查会通过但运行时报错4. 代码转换24.1s将TSX/JSX转为ES2015注入HMR代码处理CSS-in-JSbabel.config.js若漏配babel/preset-env的targets产出代码在旧版Android WebView中直接白屏5. 代码分割15.8s根据dynamic import()和splitChunks策略拆分chunksplitChunks.cacheGroups若未排除node_modules中的大型库如echarts会导致vendor chunk过大首屏加载超时6. 静态资源处理8.2s压缩图片、字体生成srcset处理SVG Spritevite-plugin-imagemin若未配置mozjpeg质量参数压缩后图片色阶丢失严重7. 产物校验6.4s检查dist目录是否存在index.html验证manifest.json完整性扫描敏感关键词如console.log残留自定义脚本若未校验sourceMap是否开启会导致生产环境调试困难实测下来关闭类型检查能提速18秒但后续因类型错误导致的联调返工平均耗时4.2小时——这笔账必须算清楚。我们现在的构建流程强制保留所有步骤但通过vite-plugin-inspect可视化分析各阶段耗时针对性优化比如将eslint-plugin-vue的检查移到pre-commit而非build既保质量又不拖慢构建。2.3 CI/CD不是“自动部署”而是构建可信度的信用背书为什么我们坚持让测试在Docker容器里跑很多团队的CI只是“git push后自动builddeploy”这最多算自动化远未达工程化。真正的CI/CD必须回答三个问题这次变更真的没破坏现有功能吗它在真实环境中表现如何如果出问题能否5分钟内回滚我们当前的CI流程GitLab Runner Docker包含6个强制关卡语法与规范关ESLintPrettier全量扫描错误数0则终止类型安全关tsc --noEmit --skipLibCheck严格模式下不允许any单元测试关Jest执行所有*.spec.ts覆盖率阈值85%且branch覆盖率不低于70%E2E冒烟关Cypress在Chrome Headless容器中执行12个核心路径登录→首页→搜索→商品详情→加入购物车→结算任一失败即阻断安全扫描关npm audit --audit-levelmoderatesnyk test扫描node_modules发现已知漏洞立即告警性能基线关Lighthouse CI对比上一版first-contentful-paint和largest-contentful-paint恶化超5%需负责人说明。关键细节在于环境一致性所有测试都在node:18-alpine容器中运行而非开发者本地环境。曾有个PR在本地npm test全绿CI却失败——原因是本地装了全局pnpm而CI用npm导致package-lock.json生成规则不同lodash版本从4.17.21变成4.17.20后者有个未修复的throttle内存泄漏bug。容器化让“在我机器上能跑”这种话彻底失效。注意CI流程里最易被忽视的是“可追溯性”。我们要求每个构建产物生成唯一BUILD_ID时间戳git commit hash前8位并写入dist/version.json。线上报错时运维只需提供BUILD_ID就能精准定位到对应代码分支、构建日志、测试报告避免“哪个版本出的问题”这种低效扯皮。3. 安全JavaScript不是沙盒里的玩具而是暴露在公网的攻防前线3.1 XSS不是“加个escape就完事”而是数据流全程的防御纵深从DOM操作到模板引擎的七层过滤看到“XSS漏洞”就想到innerHTML那说明你还没经历过真实攻防。去年我们一个金融类小程序被渗透攻击者没碰innerHTML而是利用a hrefjavascript:alert(1)绕过所有常规过滤。JavaScript安全的本质是对所有外部输入URL参数、API响应、localStorage、甚至CSS变量建立不可绕过的信任边界。我们现在的防御体系分七层像洋葱一样包裹核心逻辑第1层URL参数净化不用URLSearchParams直接取值而是通过safeParseUrlParam(key, type)函数typestring时自动移除script、javascript:、data:text/html等危险协议typenumber时用parseInt(val, 10)并校验isNaN()typejson时用JSON.parse()捕获异常绝不使用eval。第2层API响应校验后端返回的JSON前端绝不直接JSON.stringify()渲染。我们用Zod定义Schemaconst ProductSchema z.object({ id: z.number().int().positive(), name: z.string().max(100).regex(/^[^\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]*$/), // 过滤控制字符 price: z.number().nonnegative().multipleOf(0.01) });校验失败时显示“数据异常请刷新重试”而非崩溃。第3层模板引擎沙箱Vue/React默认已做XSS防护但自定义指令如v-html仍是高危区。我们禁用所有v-html改用v-textCSScontent属性展示富文本并用DOMPurify.sanitize()二次过滤// 自定义指令 app.directive(safe-html, { mounted(el, binding) { el.innerHTML DOMPurify.sanitize(binding.value, { ALLOWED_TAGS: [b, i, u, br], ALLOWED_ATTR: [class] }); } });第4层事件处理器隔离onclickdoSomething()这种内联事件我们用addEventListener替代并禁用Function构造器// ❌ 危险 element.setAttribute(onclick, alert(${userInput})); // ✅ 安全 element.addEventListener(click, () { // 所有动态内容在此处处理且作用域隔离 const safeValue escapeHtml(userInput); alert(safeValue); });第5层第三方SDK管控接入微信JS-SDK、支付宝SDK时绝不直接script src引入。我们用动态import()按需加载并设置integrity哈希// 加载微信SDK const loadWechatSDK async () { const script document.createElement(script); script.src https://res.wx.qq.com/open/js/jweixin-1.6.0.js; script.integrity sha384-...; // 从官方文档获取 document.head.appendChild(script); };第6层CSP策略落地不只是Content-Security-Policy响应头我们在index.html中也声明meta http-equivContent-Security-Policy contentdefault-src self; script-src self unsafe-inline https://res.wx.qq.com; style-src self unsafe-inline; img-src self data: https:;unsafe-inline仅允许内联样式脚本必须外链且带哈希。第7层运行时防护部署helmet中间件Node.js后端和sentry/browser前端监控实时捕获eval、setTimeout字符串调用等危险行为。实操心得安全不是“加功能”而是“减能力”。我们曾为省事允许iframe嵌入结果被利用postMessage劫持跳转。后来强制所有iframe必须通过SafeIframe组件封装自动添加sandboxallow-scripts allow-same-origin并监听message事件做白名单校验。安全投入的ROI体现在过去两年0次XSS导致的数据泄露而每次安全审计节省的整改工时约200人日。3.2 供应链攻击你的node_modules里可能躺着一颗定时炸弹2023年colors.js事件后“依赖安全”不再是理论风险。我们团队的依赖治理流程包含三个硬性动作动作1锁定依赖树package-lock.json必须提交到Git且禁止npm install时生成新lock文件。CI流程中第一行命令就是# 校验lock文件完整性 npm ci --no-audit --no-fund--no-audit不是忽略漏洞而是将审计交给专用步骤见2.3节避免npm install时因网络波动失败。动作2依赖溯源所有npm install xxx命令必须附带--save-dev或--save标识并在PR描述中说明用途。例如npm install types/lodash --save-dev用途为_.debounce等函数提供TS类型定义避免any泛滥无说明的依赖安装CI自动拒绝合并。动作3定期深度扫描每周日凌晨2点执行# 1. 扫描已知漏洞 npx snyk test --severity-thresholdhigh --json snyk-report.json # 2. 检查恶意包如typosquatting npx detect-package-manager-malware # 3. 分析依赖图谱 npx depcheck --json depcheck-report.json报告自动发送至安全组邮箱高危漏洞CVSS≥7.0必须24小时内修复。真实案例去年发现ansi-regex被200包依赖存在正则回溯漏洞CVSS评分8.1。我们没等上游修复而是用resolutions强制指定安全版本resolutions: { ansi-regex: 6.0.1 }并通过patch-package生成补丁确保所有依赖都生效。3.3 安全不是后端的事前端也要懂的加密与密钥管理常识很多前端认为“加密交给后端”结果在登录页明文传输密码。我们的加密规范强制三点1. 密码绝不裸传登录接口不接受明文密码必须前端RSA加密// 使用jsencrypt已fork修复已知漏洞 const encryptor new JSEncrypt(); encryptor.setPublicKey(PUBLIC_KEY); // 从后端API动态获取非硬编码 const encryptedPassword encryptor.encrypt(password); // 发送{ username, password: encryptedPassword }公钥每次登录时刷新避免被截获重放。2. 敏感数据本地存储隔离localStorage存token那是自杀。我们用crypto.subtle生成密钥派生// 基于用户密码派生密钥 const deriveKey async (password: string) { const encoder new TextEncoder(); const keyMaterial await crypto.subtle.importKey( raw, encoder.encode(password), { name: PBKDF2 }, false, [deriveKey] ); return crypto.subtle.deriveKey( { name: PBKDF2, salt, iterations: 100000, hash: SHA-256 }, keyMaterial, { name: AES-GCM, length: 256 }, false, [encrypt, decrypt] ); };加密后的数据存入indexedDB解密密钥仅存在于内存页面刷新即销毁。3. API通信双向认证不仅后端验证token前端也验证响应签名// 后端在响应头添加X-Signature: SHA256(payloadsecret) const verifyResponse (response: Response, payload: string) { const signature response.headers.get(X-Signature); const expected sha256(payload API_SECRET); // API_SECRET由后端动态下发 return signature expected; };防止中间人篡改响应。警告所有密钥、证书、API_SECRET绝不硬编码我们用dotenv加载环境变量但.env文件不提交Git由CI从Vault中注入。曾有个实习生把API_SECRET写进vue.config.js并提交触发CI安全扫描告警立即回滚并全员培训。4. 设计模式不是炫技的“工厂模式”而是降低认知负荷的通用语言4.1 为什么说“简单工厂模式”在现代前端是反模式从订单创建看模式演进的真实逻辑网上教程总爱用“简单工厂模式创建不同形状”但真实业务中这种模式往往成为技术债源头。以我们电商系统的订单创建为例阶段1简单工厂2019年class OrderFactory { static create(type, data) { switch(type) { case normal: return new NormalOrder(data); case group: return new GroupOrder(data); case seckill: return new SeckillOrder(data); default: throw new Error(Unknown order type); } } }问题很快暴露新增“跨境订单”需修改OrderFactory违反开闭原则NormalOrder和GroupOrder共享大量逻辑但无法复用。阶段2策略模式组合2021年// 策略接口 interface OrderStrategy { validate(data: any): boolean; calculatePrice(data: any): number; generateReceipt(data: any): string; } // 具体策略 class NormalOrderStrategy implements OrderStrategy { /* ... */ } class GroupOrderStrategy implements OrderStrategy { /* ... */ } // 上下文 class OrderContext { private strategy: OrderStrategy; constructor(strategy: OrderStrategy) { this.strategy strategy; } execute(data) { if (!this.strategy.validate(data)) throw new Error(Invalid data); return { price: this.strategy.calculatePrice(data), receipt: this.strategy.generateReceipt(data) }; } }优势新增策略无需改上下文但OrderContext仍需手动实例化策略耦合度高。阶段3依赖注入装饰器2023年// 装饰器注册策略 injectable() class NormalOrderService implements OrderService { inject(TYPES.OrderValidator) private validator: OrderValidator; inject(TYPES.PriceCalculator) private calculator: PriceCalculator; create(data) { this.validator.validate(data); return this.calculator.calculate(data); } } // DI容器自动注入 const container new Container(); container.bindOrderService(TYPES.NormalOrderService).to(NormalOrderService); container.bindOrderService(TYPES.GroupOrderService).to(GroupOrderService); // 使用时 const service container.getOrderService(TYPES.NormalOrderService); service.create(orderData);这才是设计模式的真谛用约定代替硬编码用配置代替条件判断让新人一眼看懂“订单创建”这件事本质上是“验证→计算→生成”的流水线而非一堆if-else。4.2 观察者模式不是EventBus而是状态同步的契约为什么我们弃用mitt改用signal很多团队用mitt实现跨组件通信但很快陷入“谁监听了什么事件”的混乱。观察者模式的核心价值是定义清晰的状态变更契约。我们现在的状态管理基于preact/signals轻量级Signal因为它强制契约// 定义信号契约 export const cartSignal signal{ items: CartItem[], total: number }({ items: [], total: 0 }); // 组件A订阅变更 function CartSummary() { // 自动订阅无需手动on/off const cart useSignal(cartSignal); return div总价¥{cart.value.total}/div; } // 组件B触发变更 function AddToCartButton({ item }) { const handleClick () { // 直接修改信号值所有订阅者自动更新 cartSignal.value { items: [...cartSignal.value.items, item], total: cartSignal.value.total item.price }; }; return button onClick{handleClick}加入购物车/button; }优势在于无订阅管理Signal自动追踪依赖组件卸载时自动清理强类型cartSignal的类型在定义时即确定TS能精准推导可预测所有状态变更都通过signal.value xxx杜绝emit(cart-update, data)这种模糊事件。实操心得设计模式的价值不在“用了什么”而在“消除了什么”。用Signal后我们删掉了3个EventBus文件、12个$emit/$on调用相关Bug下降76%。模式选择标准很简单能否让代码更接近自然语言cartSignal.value newCart比bus.emit(cart-change, newCart)更直白。4.3 组合模式不是“树形结构”而是复杂UI的可组装DNA“组合模式”常被误解为菜单树、组织架构图。其实质是将容器与叶子节点抽象为同一接口使客户端无需区分对待。我们表单系统的重构就是典型案例重构前脆弱// 每种表单项单独实现 NormalInput label姓名 / DatePicker label生日 / RadioGroup label性别 options{[男,女]} / // 新增“身份证号”需写新组件且校验逻辑分散重构后组合// 定义统一接口 interface FormField { render(): JSX.Element; validate(): Promiseboolean; getValue(): any; } // 叶子节点基础输入 class TextInput implements FormField { constructor(private props: { label: string; value: string }) {} render() { return input value{this.props.value} /; } validate() { return Promise.resolve(this.props.value.length 0); } getValue() { return this.props.value; } } // 容器节点表单组 class FormGroup implements FormField { private fields: FormField[] []; add(field: FormField) { this.fields.push(field); } render() { return div{this.fields.map(f f.render())}/div; } async validate() { const results await Promise.all(this.fields.map(f f.validate())); return results.every(r r); } getValue() { return Object.fromEntries( this.fields.map(f [f.constructor.name, f.getValue()]) ); } } // 使用自由组合 const form new FormGroup(); form.add(new TextInput({ label: 姓名, value: })); form.add(new DatePicker({ label: 生日, value: })); form.add(new IdCardInput({ label: 身份证, value: })); // 新增类型不影响现有代码现在新增“银行卡号”组件只需实现FormField接口即可无缝接入任何表单。组合模式让UI开发从“写组件”变为“搭积木”。5. 拓展不是“装插件”而是系统生命力的可持续进化能力5.1 插件系统不是技术炫技而是业务快速试错的基础设施从营销活动看插件架构设计我们每年要上线20营销活动618、双11、春节红包每个活动都有独特交互逻辑。如果每个活动都独立开发人力成本爆炸。我们的解决方案是标准化插件系统核心契约插件必须导出Plugin接口interface Plugin { id: string; // 唯一标识如 spring-festival-redpack init: (context: PluginContext) void; // 初始化钩子 destroy: () void; // 销毁钩子 events: Recordstring, (payload: any) void; // 事件监听 }运行时加载// 根据活动ID动态加载 const loadPlugin async (activityId: string) { try { const pluginModule await import(./plugins/${activityId}/index.ts); const plugin pluginModule.default as Plugin; // 注入运行时上下文 const context: PluginContext { api: createActivityApi(activityId), // 隔离API域名 storage: new ActivityStorage(activityId), // 隔离localStorage key logger: createLogger(activityId) }; plugin.init(context); return plugin; } catch (e) { console.error(插件 ${activityId} 加载失败, e); } };真实收益618活动开发周期从14天缩短至3天复用80%插件双11期间同时运行5个活动插件内存占用仅增加12MB春节红包活动下线后plugin.destroy()自动清理所有定时器、事件监听、DOM节点。注意插件系统最大的陷阱是“过度设计”。我们刻意限制插件能力禁止直接操作document.body禁止eval所有DOM操作必须通过context.dom提供的安全API。安全永远优先于灵活性。5.2 浏览器拓展不是“改UI”而是用户工作流的延伸从VS Code拓展看前端能力外溢前端工程师的价值早已不限于网页。我们团队开发的VS Code拓展CodeReview Assistant就是JavaScript能力的外溢核心能力AST解析用babel/parser解析TS/JS文件识别TODO、FIXME标记AI集成调用本地Ollama模型对// TODO: 优化性能生成具体建议Git集成读取git diff只对本次修改的代码行做审查。技术栈选择逻辑为何不用Webview因为需要直接读取本地文件系统Webview权限受限为何用TypeScript而非JSVS Code拓展API全是TS定义类型安全减少50%调试时间为何选Ollama而非API避免网络请求延迟保证离线可用且模型可定制。这个拓展上线后团队Code Review效率提升40%更重要的是它证明前端技术栈可以无缝迁移到IDE、桌面应用、甚至IoT设备——拓展的本质是让JavaScript成为连接各种环境的通用胶水。5.3 拓展的终极形态微前端不是“拆应用”而是组织能力的分布式部署微前端常被当作技术方案实则是组织架构的映射。我们主站拆分为4个微前端shell导航、登录、权限product商品中心order订单系统marketing营销活动每个团队独立开发、独立部署技术栈可不同product用Vueorder用React。关键不在技术而在契约治理契约1运行时沙箱所有微应用通过qiankun加载window对象被代理避免全局变量污染。契约2通信协议禁止直接调用对方API必须通过initGlobalState发布/订阅// product微应用发布商品变更 const { setGlobalState } initGlobalState({ user: product }); setGlobalState({ type: PRODUCT_UPDATE, payload: { id: 123, stock: 99 } }); // order微应用订阅 onGlobalStateChange((state) { if (state.type PRODUCT_UPDATE) { updateCartStock(state.payload.id, state.payload.stock); } });契约3样式隔离每个微应用CSS加[data-appproduct]前缀通过styled-components的StyleSheetManager注入杜绝样式冲突。微前端让我们实现了marketing团队用新框架SolidJS开发活动页不影响其他系统order团队升级React 18product团队继续用Vue2互不干扰线上故障可精准定位到某微应用快速回滚不影响整体。最后分享个小技巧拓展能力的检验标准不是“能做什么”而是“不能做什么时依然可用”。比如插件系统我们强制要求即使某个插件init()抛出未捕获异常主应用必须正常运行。这通过try/catch包裹插件加载并降级为“该插件不可用”状态实现。真正的拓展是让系统在不确定性中保持韧性。6. 四根支柱如何真正咬合一个真实项目的工程化落地全景图6.1 从需求到上线一个“优惠券领取”功能的全链路实践现在用一个真实需求串起所有支柱用户点击按钮领取优惠券成功后弹窗提示并更新顶部导航栏的优惠券数量。工程化落地创建feature/coupon-claim分支ESLint检查通过禁用console.log强制async/awaitJest编写单元测试模拟API成功/失败验证状态变更Cypress编写E2E从首页→点击领券按钮→验证弹窗→检查导航栏数字。安全加固领取按钮的onclick绑定addEventListener而非内联API响应用Zod校验couponId必须为正整数弹窗内容通过textContent渲染杜绝innerHTML领取成功后localStorage存储用AES加密密钥派生自用户密码。设计模式应用使用Observer PatterncouponCountSignal被导航栏和弹窗组件订阅领取逻辑封装为CouponService遵循单一职责失败重试用Strategy Pattern网络失败用指数退避余额不足用直接提示。拓展准备将领券逻辑抽为CouponPlugin未来可复用于其他活动插件提供onClaimSuccess钩子供营销团队自定义后续动作如跳转、弹广告微前端架构下该功能作为marketing子应用独立部署。上线后验证Lighthouse报告FCP1.2s无安全警告Sentry监控领取成功率99.97%失败原因TOP3为网络超时、库存不足、用户未登录Datadog追踪API平均耗时320msP95800ms。这个功能上线后marketing团队基于相同插件3天内上线了“邀请好友得券”活动复用率85%。工程化、安全、设计模式、拓展不是割裂的模块而是同一枚硬币的四面。6.2 技术选型决策树当你要搭建新项目时如何不踩坑面对一堆工具我们用这张决策树快速收敛开始 │ ├─ 项目规模 → 小型5人3模块 → Vite Vanilla JS ESLint │ 中型5-20人5-10模块 → Vue3/React TypeScript Pinia/Zustand Vitest │ 大型20人10模块 → 微前端qiankun Monorepopnpm workspaces Turborepo │ ├─ 安全等级 → 金融/医疗 → 强制CSP Snyk扫描 密钥派生 审计日志 │ 一般业务 → 基础XSS防护 npm audit HTTPS强制 │ ├─ 团队能力 → 新人多 → 选Vue模板直观 ESLint严格模式 详细文档 │ 老手多 → 选React生态丰富 自定义Hook TypeScript高级类型 │ └─ 交付节奏 → 快速