Appearance
前端工程化面试题
1. 工程化概述
问题:什么是前端工程化?为什么要进行前端工程化?
答案:
前端工程化定义: 前端工程化是指使用软件工程的技术和方法来进行前端项目的开发、维护和管理,以提高开发效率、代码质量和项目可维护性。
工程化的核心目标:
javascript
// 1. 规范化
// - 代码规范:ESLint、Prettier
// - 提交规范:Commitizen、Husky
// - 目录规范:统一的目录结构
// 2. 自动化
// - 自动化构建:Webpack、Vite、Rollup
// - 自动化测试:Jest、Cypress
// - 自动化部署:CI/CD
// 3. 模块化
// - 代码模块化:ES Modules、CommonJS
// - 组件模块化:Vue、React 组件
// - 样式模块化:CSS Modules、Styled Components
// 4. 组件化
// - UI 组件库:Element UI、Ant Design
// - 业务组件:可复用的业务逻辑
// - 工具组件:通用的工具函数
// 5. 性能优化
// - 代码压缩、Tree Shaking
// - 懒加载、预加载
// - 缓存策略工程化的好处:
- 提高开发效率
- 保证代码质量
- 降低维护成本
- 提升团队协作
- 优化用户体验
2. 构建工具
问题:常用的前端构建工具有哪些?它们有什么区别?
答案:
主流构建工具对比:
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Webpack | 功能强大、生态丰富、配置复杂 | 大型项目、复杂应用 |
| Vite | 快速冷启动、HMR、配置简单 | 现代项目、快速开发 |
| Rollup | 输出优化、Tree Shaking 好 | 库开发、工具库 |
| Parcel | 零配置、开箱即用 | 小型项目、原型开发 |
| esbuild | 极速编译、Go 编写 | 大型项目、性能敏感 |
| SWC | Rust 编写、极速编译 | 替代 Babel、大型项目 |
Webpack 配置:
javascript
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 入口
entry: './src/index.js',
// 输出
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash:8].js',
clean: true
},
// 模块处理
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}
]
},
// 插件
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css'
})
],
// 开发服务器
devServer: {
static: './dist',
hot: true,
port: 3000
},
// 优化
optimization: {
splitChunks: {
chunks: 'all'
}
}
};Vite 配置:
javascript
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
export default defineConfig({
plugins: [vue()],
// 路径别名
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
// 开发服务器
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
// 构建
build: {
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router']
}
}
}
},
// CSS
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "./src/styles/vars.scss";`
}
}
}
});构建工具选择建议:
javascript
// 1. 新项目推荐 Vite
// - 开发体验好
// - 构建速度快
// - 配置简单
// 2. 大型项目考虑 Webpack
// - 生态成熟
// - 配置灵活
// - 社区支持好
// 3. 库开发推荐 Rollup
// - 输出优化
// - Tree Shaking 好
// - 配置简洁
// 4. 性能敏感项目考虑 esbuild/SWC
// - 编译速度极快
// - 适合大型项目3. 模块化
问题:前端模块化有哪些规范?如何使用?
答案:
模块化规范:
javascript
// 1. CommonJS (Node.js)
// math.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = { add, subtract };
// main.js
const { add, subtract } = require('./math');
console.log(add(1, 2));
// 2. ES Modules (ES6+)
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export default function multiply(a, b) {
return a * b;
}
// main.js
import multiply, { add, subtract } from './math.js';
import * as math from './math.js';
// 3. AMD (RequireJS)
define(['dependency'], function(dependency) {
return {
method: function() {}
};
});
// 4. UMD (Universal Module Definition)
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['b'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('b'));
} else {
root.returnExports = factory(root.b);
}
}(typeof self !== 'undefined' ? self : this, function (b) {
return {};
}));模块加载原理:
javascript
// ES Modules 加载过程
// 1. 构建阶段:解析模块依赖,创建模块映射
// 2. 实例化阶段:为模块分配内存,建立导入导出连接
// 3. 执行阶段:执行模块代码,填充内存
// 循环依赖处理
// a.js
import { bar } from './b.js';
export function foo() {
console.log('foo');
bar();
}
// b.js
import { foo } from './a.js';
export function bar() {
console.log('bar');
// foo(); // 可能导致无限循环
}
// 解决方案:使用函数延迟执行
// a.js
import { bar } from './b.js';
export function foo() {
console.log('foo');
}
// b.js
import { foo } from './a.js';
export function bar() {
console.log('bar');
foo(); // 安全调用
}Tree Shaking:
javascript
// Tree Shaking 原理
// 1. 基于 ES Modules 的静态分析
// 2. 只打包使用到的代码
// 3. 需要 sideEffects 配置
// utils.js
export function used() {
return 'used';
}
export function unused() {
return 'unused';
}
// main.js
import { used } from './utils.js';
console.log(used());
// package.json
{
"sideEffects": false,
"sideEffects": [
"*.css",
"*.scss"
]
}4. 代码规范
问题:如何保证代码规范?常用的工具有哪些?
答案:
ESLint 配置:
javascript
// .eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
],
parserOptions: {
ecmaVersion: 2021,
sourceType: 'module',
parser: '@typescript-eslint/parser'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off'
},
overrides: [
{
files: ['*.vue'],
rules: {
'vue/no-multiple-template-root': 'off'
}
}
]
};
// .eslintignore
node_modules
dist
build
*.min.jsPrettier 配置:
javascript
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf"
}
// .prettierignore
node_modules
dist
build
*.min.jsHusky + lint-staged:
javascript
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,less}": [
"stylelint --fix",
"prettier --write"
]
}
}
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'chore', 'revert'
]],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never']
}
};
// 提交格式
// feat: 添加新功能
// fix: 修复 bug
// docs: 更新文档
// style: 代码格式调整
// refactor: 重构代码
// perf: 性能优化
// test: 添加测试
// chore: 构建过程或辅助工具的变动Stylelint 配置:
javascript
// .stylelintrc.js
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-config-recommended-vue'
],
rules: {
'selector-class-pattern': null,
'scss/at-import-partial-extension': null
}
};5. 包管理工具
问题:npm、yarn、pnpm 有什么区别?
答案:
包管理工具对比:
| 特性 | npm | yarn | pnpm |
|---|---|---|---|
| 安装速度 | 一般 | 快 | 最快 |
| 磁盘占用 | 大 | 大 | 小 |
| 依赖管理 | 嵌套/扁平 | 扁平 | 内容可寻址 |
| 锁文件 | package-lock.json | yarn.lock | pnpm-lock.yaml |
| 工作区 | 支持 | 支持 | 支持 |
| 离线模式 | 支持 | 支持 | 支持 |
pnpm 优势:
bash
# 1. 安装速度快
pnpm install
# 2. 磁盘占用小(硬链接)
# 所有项目共享同一份依赖
# 3. 严格依赖管理
# 不会访问未声明的依赖
# 4. 支持 monorepo
pnpm-workspace.yamlpackage.json 配置:
json
{
"name": "my-project",
"version": "1.0.0",
"description": "项目描述",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .vue,.js,.jsx,.ts,.tsx --fix",
"format": "prettier --write ."
},
"dependencies": {
"vue": "^3.3.0"
},
"devDependencies": {
"vite": "^4.4.0",
"eslint": "^8.0.0"
},
"engines": {
"node": ">=16.0.0",
"pnpm": ">=8.0.0"
},
"packageManager": "pnpm@8.6.0"
}版本管理:
javascript
// 版本号规则:主版本.次版本.修订号
// ^1.2.3 - 兼容 1.x.x,不低于 1.2.3
// ~1.2.3 - 兼容 1.2.x,不低于 1.2.3
// 1.2.3 - 精确版本
// 锁定版本
// package-lock.json / yarn.lock / pnpm-lock.yaml
// 更新依赖
npm update // 更新到最新兼容版本
npm outdated // 查看可更新依赖
npm audit // 安全检查
npm audit fix // 自动修复安全问题6. CI/CD
问题:什么是 CI/CD?如何配置前端项目的 CI/CD?
答案:
CI/CD 概念:
- CI (Continuous Integration):持续集成,频繁地将代码集成到主干
- CD (Continuous Delivery/Deployment):持续交付/部署,自动将代码部署到生产环境
GitHub Actions 配置:
yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Run linter
run: pnpm lint
- name: Run tests
run: pnpm test
- name: Build project
run: pnpm build
deploy:
needs: lint-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
echo "Deploying to production..."
# 部署脚本GitLab CI 配置:
yaml
# .gitlab-ci.yml
stages:
- install
- lint
- test
- build
- deploy
variables:
NODE_VERSION: "18"
PNPM_VERSION: "8"
install:
stage: install
image: node:18
script:
- npm install -g pnpm@${PNPM_VERSION}
- pnpm install
cache:
paths:
- node_modules/
- .pnpm-store/
lint:
stage: lint
image: node:18
script:
- pnpm lint
dependencies:
- install
test:
stage: test
image: node:18
script:
- pnpm test
dependencies:
- install
build:
stage: build
image: node:18
script:
- pnpm build
artifacts:
paths:
- dist/
dependencies:
- install
deploy:
stage: deploy
image: node:18
script:
- echo "Deploying..."
only:
- mainDocker 部署:
dockerfile
# Dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 安装 pnpm
RUN npm install -g pnpm
# 复制依赖文件
COPY package.json pnpm-lock.yaml ./
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY . .
# 构建
RUN pnpm build
# 生产阶段
FROM nginx:alpine
# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]nginx
# nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 缓存静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 前端路由支持
location / {
try_files $uri $uri/ /index.html;
}
}7. Monorepo
问题:什么是 Monorepo?如何管理 Monorepo 项目?
答案:
Monorepo vs Polyrepo:
| 特性 | Monorepo | Polyrepo |
|---|---|---|
| 代码共享 | 容易 | 困难 |
| 版本管理 | 统一 | 分散 |
| 构建优化 | 可以优化 | 难以优化 |
| 团队协作 | 紧密 | 松散 |
| 权限管理 | 复杂 | 简单 |
Monorepo 工具:
javascript
// 1. pnpm workspace
// pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
// 2. Turborepo
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}
// 3. Nx
// nx.json
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test"]
}
}
}
}
// 4. Lerna
// lerna.json
{
"version": "independent",
"npmClient": "pnpm",
"command": {
"publish": {
"conventionalCommits": true
}
}
}Monorepo 目录结构:
my-monorepo/
├── apps/
│ ├── web/ # Web 应用
│ ├── admin/ # 管理后台
│ └── mobile/ # 移动端应用
├── packages/
│ ├── ui/ # UI 组件库
│ ├── utils/ # 工具函数
│ ├── hooks/ # 通用 Hooks
│ └── config/ # 共享配置
├── package.json
├── pnpm-workspace.yaml
└── turbo.json包间依赖管理:
json
// packages/ui/package.json
{
"name": "@myorg/ui",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
},
"dependencies": {
"@myorg/utils": "workspace:*"
}
}
// apps/web/package.json
{
"name": "@myorg/web",
"dependencies": {
"@myorg/ui": "workspace:*",
"@myorg/utils": "workspace:*"
}
}8. 测试
问题:前端测试有哪些类型?如何配置测试环境?
答案:
测试类型:
javascript
// 1. 单元测试
// 测试单个函数或组件
import { add } from './math';
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
// 2. 集成测试
// 测试多个模块的协作
import { createApp } from './app';
test('app initializes correctly', async () => {
const app = await createApp();
expect(app.isReady()).toBe(true);
});
// 3. E2E 测试
// 测试完整用户流程
describe('User Login', () => {
it('should login successfully', () => {
cy.visit('/login');
cy.get('[data-testid="username"]').type('user');
cy.get('[data-testid="password"]').type('password');
cy.get('[data-testid="submit"]').click();
cy.url().should('include', '/dashboard');
});
});
// 4. 快照测试
// 测试 UI 是否发生变化
test('Button renders correctly', () => {
const { container } = render(<Button>Click me</Button>);
expect(container).toMatchSnapshot();
});
// 5. 视觉回归测试
// 测试 UI 视觉效果Jest 配置:
javascript
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest'
},
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1'
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
// jest.setup.js
import '@testing-library/jest-dom';
// 测试示例
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('Button click handler is called', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});Cypress E2E 测试:
javascript
// cypress/e2e/login.cy.js
describe('Login', () => {
beforeEach(() => {
cy.visit('/login');
});
it('displays login form', () => {
cy.get('[data-testid="login-form"]').should('be.visible');
cy.get('[data-testid="username"]').should('exist');
cy.get('[data-testid="password"]').should('exist');
});
it('logs in successfully', () => {
cy.get('[data-testid="username"]').type('testuser');
cy.get('[data-testid="password"]').type('password123');
cy.get('[data-testid="submit"]').click();
cy.url().should('include', '/dashboard');
cy.get('[data-testid="welcome"]').should('contain', 'Welcome');
});
it('shows error for invalid credentials', () => {
cy.get('[data-testid="username"]').type('wronguser');
cy.get('[data-testid="password"]').type('wrongpassword');
cy.get('[data-testid="submit"]').click();
cy.get('[data-testid="error"]').should('be.visible');
});
});
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: false,
screenshotOnRunFailure: true
}
});9. 性能监控
问题:如何监控前端性能?
答案:
性能指标监控:
javascript
// 1. Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
// 使用 sendBeacon 发送数据
if (navigator.sendBeacon) {
navigator.sendBeacon('/analytics', body);
} else {
fetch('/analytics', {
body,
method: 'POST',
keepalive: true
});
}
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
// 2. 自定义性能监控
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.init();
}
init() {
// 页面加载时间
window.addEventListener('load', () => {
setTimeout(() => {
this.collectNavigationTiming();
this.collectResourceTiming();
}, 0);
});
// 长任务监控
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.reportLongTask(entry);
}
});
observer.observe({ entryTypes: ['longtask'] });
}
// 错误监控
window.addEventListener('error', (event) => {
this.reportError(event.error);
});
window.addEventListener('unhandledrejection', (event) => {
this.reportError(event.reason);
});
}
collectNavigationTiming() {
const navigation = performance.getEntriesByType('navigation')[0];
this.metrics.navigation = {
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
ttfb: navigation.responseStart - navigation.requestStart,
download: navigation.responseEnd - navigation.responseStart,
dom: navigation.domComplete - navigation.domInteractive,
load: navigation.loadEventEnd - navigation.fetchStart
};
}
collectResourceTiming() {
const resources = performance.getEntriesByType('resource');
this.metrics.resources = resources.map(r => ({
name: r.name,
duration: r.duration,
size: r.transferSize
}));
}
reportLongTask(entry) {
console.warn('Long task detected:', entry.duration);
// 上报长任务数据
}
reportError(error) {
console.error('Error:', error);
// 上报错误数据
}
}
// 使用
const monitor = new PerformanceMonitor();错误监控:
javascript
// Sentry 集成
import * as Sentry from '@sentry/vue';
Sentry.init({
app,
dsn: 'your-dsn-url',
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router)
}),
new Sentry.Replay()
],
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0
});
// 自定义错误上报
function captureError(error, context) {
Sentry.captureException(error, {
extra: context
});
}10. 微前端
问题:什么是微前端?如何实现微前端架构?
答案:
微前端概念: 微前端是一种架构风格,将前端应用拆分为多个独立的、可单独部署的子应用,每个子应用可以由不同的团队使用不同的技术栈开发。
微前端方案:
javascript
// 1. qiankun(阿里)
// 主应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'app1',
entry: '//localhost:8081',
container: '#container',
activeRule: '/app1'
},
{
name: 'app2',
entry: '//localhost:8082',
container: '#container',
activeRule: '/app2'
}
]);
start();
// 子应用
export async function bootstrap() {
console.log('app bootstraped');
}
export async function mount(props) {
ReactDOM.render(<App />, props.container);
}
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(props.container);
}
// 2. Module Federation(Webpack 5)
// 主应用
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
app2: 'app1@http://localhost:3002/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
]
};
// 使用远程组件
import('app1/Button').then((Button) => {
// 使用 Button
});
// 3. single-spa
import { registerApplication, start } from 'single-spa';
registerApplication({
name: 'app1',
app: () => import('./app1/app1.js'),
activeWhen: '/app1'
});
start();微前端通信:
javascript
// 1. 通过 props 传递
// 主应用
<MicroApp
name="app1"
props={{ user, theme, onEvent: handleEvent }}
/>
// 2. 全局事件总线
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
const eventBus = new EventBus();
window.eventBus = eventBus;
// 3. 共享状态(Redux、Vuex)
// 创建全局 store
const globalStore = createStore(reducer);
window.globalStore = globalStore;微前端优缺点:
javascript
// 优点:
// 1. 技术栈无关
// 2. 独立开发、部署
// 3. 团队自治
// 4. 渐进式升级
// 缺点:
// 1. 复杂度增加
// 2. 性能开销
// 3. 样式隔离问题
// 4. 公共依赖重复
// 解决方案:
// 1. 样式隔离:CSS Modules、Shadow DOM
// 2. JS 隔离:Proxy、快照
// 3. 依赖共享:Module Federation、externals