微前端容器标准化:接入指南

摘要

本文基于 Webpack Module Federation 技术,精简梳理微前端架构的接入流程、核心能力与最佳实践,聚焦开发者实际落地需求,助力快速上手并完成企业级部署。

一、接入前准备:选对方案少走弯路

接入前优先根据项目场景选择合适方案,新项目直接使用官方模板,旧项目按需改造,避免无效配置。

1.1 优先使用官方模板(强烈推荐)

全新主应用/子应用,直接用官方模板开箱即用,无需复杂配置:

bash 复制代码
npm i -g @spc-fe-common/cli
spc-cli create projectName

优势:集成最佳实践、持续更新、含完整示例,大幅提升开发效率。

1.2 本文适用场景

仅针对改造/集成场景,非以下场景建议优先用模板:

  • 主子应用版本差异:新主应用加载旧子应用、旧主应用集成新子应用
  • 能力升级:从 Qiankun 迁移、集成国际化/请求/监控等企业级能力
  • 特定能力接入:通用国际化翻译、通用 CDN、通用请求、通用打印、通用监控、通用跨框架互通等

二、核心概览

基于 Webpack Module Federation,核心目标是实现新旧主应用快速加载任意 MF 子应用,具备技术栈无关、向后兼容、渐进式接入三大核心优势。

2.1 核心兼容性

特性 说明
技术栈无关 支持任意技术栈 MF 子应用,主子应用技术栈差异不影响使用
向后兼容 完全兼容 Qiankun 子应用,主应用无需改造
渐进式接入 从最小化配置到企业级功能,可平滑升级

2.2 核心架构与快速接入流程

基于 spc-fe-common MonoRepo 构建,采用"核心必须包+可选增强包"架构,快速接入流程如下:
主应用
安装核心包
loader - 加载引擎
context - 上下文管理
i18n - 国际化
配置 Loader
加载子应用
完成接入
可选增强包集成

核心必须包(最小化接入必备)

包名 功能
@spc-fe-common/loader 子应用加载核心引擎
@spc-fe-common/context 全局上下文与实例共享
@spc-fe-common/i18n 多语言翻译支持

可选增强包(按需集成)

通用国际化翻译通用 CDN通用请求、[通用打印通用监控通用跨框架互通 等。

三、快速接入(最小化配置)

前提:子应用已配置 Module Federation 并符合暴露规范(新子应用推荐用模板)。

3.1 安装核心包

bash 复制代码
pnpm i @spc-fe-common/{loader,context,i18n}@latest

3.2 初始化全局上下文

在主应用入口(src/index.ts)配置,注入国际化实例:

typescript 复制代码
import { setContext } from '@spc-fe-common/context';
import I18n from '@spc-fe-common/i18n';
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

// 初始化全局上下文,注入国际化实例
const initGlobalContext = async () => {
  const i18nInstance = I18n.getInstance({
    resourceIds: [xxx], // 替换为项目实际翻译资源 ID
    projects: ['your-project'], // 替换为当前项目名称
  });
  await i18nInstance.initialization();
  setContext('i18n', i18nInstance, true); // 全局共享
};

// 启动应用,先初始化上下文再渲染
export const runApp = async () => {
  await initGlobalContext();
  const appContainer = document.getElementById('app');
  if (appContainer) {
    createRoot(appContainer).render(<App />);
  }
};

runApp();

3.3 配置 Loader

创建 src/loader.ts,统一管理子应用加载规则:

typescript 复制代码
import { useLoader as originalUseLoader } from '@spc-fe-common/loader';

// 封装子应用加载器,返回路由组件供主应用挂载
export const useSubAppLoader = () => {
  const { ReactRoutes } = originalUseLoader({
    routerMode: 'history', // 可选 hash 模式
    modules: [
      {
        name: 'YourSubApp', // 与子应用 MF 配置 name 一致
        entryUrl: '/subapp/remoteEntry.js', // 子应用远程入口
      }
    ],
  });
  return { ReactRoutes };
};

3.4 渲染子应用路由

在主应用根组件(src/App.tsx)挂载子应用路由:

tsx 复制代码
import React from 'react';
import { BrowserRouter as Router, Switch } from 'react-router-dom';
import { useSubAppLoader } from './loader';

function App() {
  const { ReactRoutes } = useSubAppLoader();
  return (
    <Router>
      <Switch>
        {ReactRoutes} {/* 挂载子应用路由 */}
        {/* 可添加主应用自身路由 */}
      </Switch>
   </Router>
  );
}

export default App;

至此,主应用已具备加载 MF 子应用的核心能力。

四、主应用企业级配置

需集成跨框架互通、通用请求等企业级能力时,参考以下配置,兼顾可维护性与扩展性。

4.1 安装完整依赖

bash 复制代码
# 核心包(必装)
pnpm i @spc-fe-common/{loader,context,i18n}@latest

# 推荐增强包
pnpm i @spc-fe-common/{bridge,request,types}@latest
pnpm i -D @spc-fe-common/cdn@latest

4.2 完整上下文初始化与启动配置

4.2.1 启动文件拆分(推荐)

分层拆分启动文件,避免逻辑冗余,保障共享依赖加载正常:

typescript 复制代码
// src/main.ts(启动入口,动态导入避免阻塞)
import('./bootstrap');
typescript 复制代码
// src/bootstrap.ts(启动核心,统一初始化与渲染)
import './index';
import { runEnterpriseApp } from './index';
runEnterpriseApp();

4.2.2 全局上下文初始化(核心)

注入请求、国际化实例,供主/子应用共享,统一全局能力:

typescript 复制代码
import { ReactRender } from '@spc-fe-common/bridge';
import { setContext } from '@spc-fe-common/context';
import I18n from '@spc-fe-common/i18n';
import { RequestFactory } from '@spc-fe-common/request';
import React from 'react';
import App from './App';

// 初始化全局上下文,注入核心实例
const initEnterpriseContext = async () => {
  // 1. 全局请求实例(统一接口配置)
  const globalRequest = RequestFactory.getInstance({
    baseURL: process.env.REACT_APP_API_BASE_URL,
    timeout: 10000,
    requestInterceptors: [(config) => {
      const token = localStorage.getItem('token');
      if (token) config.headers.Authorization = `Bearer ${token}`;
      return config;
    }]
  });
  setContext('request', globalRequest, true);

  // 2. 国际化实例(同步项目多语言配置)
  const i18nInstance = I18n.getInstance({ 
    resourceIds: [4726], // 替换为项目实际 ID
    projects: ['your-project'] // 替换为当前项目名称
  });
  await i18nInstance.initialization();
  setContext('i18n', i18nInstance, true);
};

// 企业级应用启动方法,适配多框架集成
export const runEnterpriseApp = async () => {
  try {
    await initEnterpriseContext();
    ReactRender({ el: '#app', Component: <App /> }); // 桥接渲染
  } catch (error) {
    console.error('企业级应用启动失败:', error);
    throw error;
  }
};

4.3 Webpack 高级配置

优先使用统一构建包简化配置,需高度自定义时采用传统配置,保障热更新等关键能力。

4.3.1 统一构建包配置(推荐)

内置热更新、CDN 管理等能力,零配置开箱即用:

javascript 复制代码
// sc.react.config.js(统一构建包核心配置)
module.exports = {
  moduleName: 'HostApp', // 主应用唯一模块名
  uniqueName: 'hostapp', // 主应用唯一标识
  webpackConfig: {
    plugins: [/* 按需添加自定义插件 */],
    resolve: {
      extensions: ['.ts', '.tsx', '.js', '.jsx', '.less', '.css']
    }
  },
};

核心优势:自动集成热更新插件、CDN 控制、全局变量注入,与 spc-fe-common 生态无缝兼容

4.3.2 传统 Webpack 配置(自定义场景)

需高度自定义时使用,核心配置微前端必备能力:

javascript 复制代码
// webpack.config.js(传统自定义配置)
const webpack = require('webpack');
const { LoaderReactRefreshPlugin } = require('@spc-fe-common/loader/webpack/plugins');

module.exports = {
  mode: process.env.NODE_ENV || 'development',
  plugins: [
    new LoaderReactRefreshPlugin(), // 微前端热更新核心(必配)
    new webpack.DefinePlugin({
      'process.env.REACT_APP_API_BASE_URL': JSON.stringify(process.env.REACT_APP_API_BASE_URL)
    })
  ],
  devServer: {
    hot: true, // 开启热更新,与插件配合
    port: 8080,
    historyApiFallback: true, // 解决 history 路由刷新 404
    proxy: { /* 按需配置接口代理 */ }
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    alias: { '@': path.resolve(__dirname, 'src') }
  }
};

关键注意:LoaderReactRefreshPlugin 与 devServer.hot 需同时配置,否则热更新失效;传统配置需手动管理 CDN、全局变量,推荐优先使用统一构建包。

4.4 Loader 高级配置

Loader 是子应用加载核心,支持 3 种配置方式,根据业务需求选择。

4.4.1 useLoader Hook 配置(推荐)

基于 Hook 封装,简洁易用,支持 MF 子应用与 Qiankun 旧应用共存:

typescript 复制代码
import { i18n } from '@spc-fe-common/context';
import { Lang } from '@spc-fe-common/i18n';
import { useLoader as originalUseLoader } from '@spc-fe-common/loader';

// 企业级子应用加载器,同步全局配置
export const useEnterpriseSubAppLoader = () => {
  const { ReactRoutes, appRoutes } = originalUseLoader({
    routerMode: 'history', // 与主应用路由模式一致
    lang: i18n.lang as Lang, // 同步全局语言
    globalContext: {
      userInfo: i18n.context.get('userInfo'), // 透传全局用户信息
      appEnv: process.env.NODE_ENV
    },
    modules: [
      // 标准 MF 子应用(新开发首选)
      { name: 'YourSubApp', entryUrl: '/subapp/remoteEntry.js', type: 'react' },
      // Qiankun 旧应用兼容(无需改造旧应用)
      {
        name: 'LegacyReactApp',
        entryUrl: '/legacy-react/index.html',
        loadType: 'sandbox', // 沙箱隔离,避免冲突
        type: 'react',
        sandbox: { activeRule: '/legacy-react', container: 'legacy-react-container' }
      },
    ],
  });
  return { ReactRoutes, appRoutes };
};

4.4.2 直接 new Loader 配置(自定义场景)

适用于需高度定制加载逻辑的场景(如加载状态监控):

typescript 复制代码
import { Loader } from '@spc-fe-common/loader';

// 自定义 Loader 初始化,支持回调与错误处理
export const initCustomLoader = async () => {
  const loader = new Loader({
    isDev: process.env.NODE_ENV === 'development',
    routerMode: 'history',
    lang: 'en',
    modules: [{ name: 'YourSubApp', entryUrl: '/subapp/remoteEntry.js', type: 'react' }],
    onModuleLoaded: (data) => console.log('模块加载完成:', data), // 加载回调
    onError: (error) => console.error('子应用加载失败:', error), // 错误处理
  });

  try {
    loader.run();
    await loader.cacheHandler.getAllModulesDonePromise();
    const appRoutes = loader.getModulesRoutes('federation');
    const ReactRoutes = loader.getModulesRoutesDOM('federation', 'react');
    return { loader, appRoutes, ReactRoutes };
  } catch (error) {
    console.error('Loader 初始化失败:', error);
    throw error;
  }
};

4.4.3 应用中心模式

实现子应用分级管理与动态加载:

typescript 复制代码
import { Loader } from '@spc-fe-common/loader';

// 应用中心模式 Loader 初始化
export const initLoaderWithPortal = async () => {
  const loader = new Loader({
    isDev: process.env.NODE_ENV === 'development',
    mode: 'react',
    routerMode: 'history',
    lang: 'en',
    portalId: 'your-portal-id', // 门户唯一标识
    name: 'your-app-name', // 应用中心名称
    region: 'your-region', // 多区域部署用
  });

  try {
    await loader.runAsync({ portalId: 'your-portal-id', name: 'your-app-name', region: 'your-region' });
    await loader.cacheHandler.getAllModulesDonePromise();
    const appCenterConfigs = loader.getAppCenterPortalData();
    const appRoutes = loader.getModulesRoutes('federation');
    const ReactRoutes = loader.getModulesRoutesDOM('federation', 'react');
    return { loader, appCenterConfigs, appRoutes, ReactRoutes };
  } catch (error) {
    console.error('应用中心模式 Loader 初始化失败:', error);
    throw error;
  }
};

4.5 主应用挂载子应用路由(企业级优化)

结合 spc-ui-react 组件库,优化加载状态与国际化适配:

typescript 复制代码
import '@/common/styles/index.less';
import { i18n } from '@spc-fe-common/context';
import { PlainObject } from '@spc-fe-common/types';
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Switch } from 'react-router-dom';
import { ConfigProvider, Spin } from 'spc-ui-react';
import { useEnterpriseSubAppLoader } from './loader';

// 同步 spc-ui-react 国际化配置
const useSpcUILocale = () => {
  const [locale, setLocale] = useState<PlainObject>({});
  useEffect(() => {
    const loadLocale = async () => {
      try {
        const { locales } = __DEV__ ? require('spc-ui-react/dist/spc-ui-react-with-locales') : await import('spc-ui-react');
        const spcUILocales = {
          en: locales.en_US, id: locales.id_ID, 'ms-my': locales.ms_MY,
          'pt-br': locales.pt_BR, th: locales.th_TH, ph: locales.tl_PH,
          vi: locales.vi_VN, 'zh-tw': locales.zh_TW, 'zh-cn': locales.zh_CN
        };
        setLocale(spcUILocales[i18n?.locale || i18n?.lang] || spcUILocales.en);
      } catch (e) {
        console.error('spc-ui-react 国际化资源加载失败:', e);
        setLocale({});
      }
    };
    loadLocale();
  }, [i18n?.lang, i18n?.locale]);
  return locale;
};

// 主应用根组件(企业级优化)
const App = () => {
  const { ReactRoutes } = useEnterpriseSubAppLoader();
  const [loading, setLoading] = useState(false);
  const spcUILocale = useSpcUILocale();

  return (
    <React.StrictMode>
      <ConfigProvider
        prefixCls={__SPCUI_REACT_PREFIX__} // 统一 UI 前缀,避免样式冲突
        rcPrefixCls={__SPCUI_REACT_RC_PREFIX__}
        locale={spcUILocale}
      >
<Spin size="large" mask tips="Loading" spinning={loading}>
          <Router>
            <Switch>
              {ReactRoutes} {/* 挂载子应用路由 */}
              {/* 主应用自身路由可在此添加 */}
            </Switch>
          </Router>
        </Spin>
      </ConfigProvider>
    </React.StrictMode>
  );
};

export default App;

五、子应用标准配置

子应用需基于 Module Federation 构建,支持独立运行和集成运行两种模式。

5.1 安装依赖

bash 复制代码
# 核心包
pnpm i @spc-fe-common/{loader,context,i18n}@latest
# 构建工具
pnpm i -D @spc-fe-common/build@latest

# 推荐增强包
pnpm i @spc-fe-common/{bridge,request,types}@latest

# React 依赖(固定版本,避免冲突)
pnpm i react@17.0.0 react-dom@17.0.0 react-router-dom@5.3.0
pnpm i spc-ui-react@latest
pnpm i -D @types/react@17.0.0 @types/react-dom@17.0.0 @types/react-router-dom@5.3.0

5.2 Module Federation 配置

javascript 复制代码
// sc.react.config.js(子应用 Webpack 配置)
const webpack = require('webpack');
const { ModuleFederationPlugin } = webpack.container;

module.exports = {
  moduleName: 'SubAppReact', // 子应用模块名称
  uniqueName: 'subapp-react', // 子应用唯一标识
  webpackConfig: {
    plugins: [
      new ModuleFederationPlugin({
        name: 'SubAppReact', // 与主应用加载时的名称一致
        filename: 'remoteEntry.react.js', // 远程入口文件名
        exposes: { './module': './src/moduleEntries/react-project' } // 暴露核心模块
      })
    ]
  }
};

5.3 暴露对象与路由配置

暴露对象(src/moduleEntries/react-project.tsx)

typescript 复制代码
import { depends } from '@spc-fe-common/loader';
import { reactRoutes } from '@/common/router';
import packageInfo from '../../package.json';

// 子应用暴露对象,供主应用加载时获取
export default {
  routerMode: 'history', // 与主应用路由模式一致
  reactRoutes, // 子应用路由配置
  packageInfo, // 版本信息,用于版本管理
  depends // 依赖配置,告知主应用需共享的依赖
};

路由配置(src/common/router/react.tsx)

typescript 复制代码
import { ReactRouterFactory } from '@spc-fe-common/bridge';
import routeModules from './modules';

// 生成标准化子应用路由
export const { routes: reactRoutes, Routes: ReactRoutes } = new ReactRouterFactory({
  uniquename: process.env.REACT_APP_UNIQUENAME || 'SubApp',
  basename: process.env.REACT_APP_ROUTE_NAME_SPACE || '', // 可选路由前缀
  routeModules, // 子应用页面路由集合
  hash: false // 与主应用一致,使用 history 模式
});

5.4 独立运行配置

App.tsx 根组件

tsx 复制代码
import { ReactRouterFactory } from '@spc-fe-common/bridge';
import { PlainObject } from '@spc-fe-common/types';
import React, { useEffect, useState } from 'react';
import { ConfigProvider, Spin } from 'spc-ui-react';
import routeModules from '@/common/router/modules';
import '@/common/styles/index.less';

export function App() {
  const [loading, setLoading] = useState(false);
  const [locale, setLocale] = useState<PlainObject>({});

  // 初始化路由工厂
  const { Routes: ReactRoutes } = new ReactRouterFactory({
    uniquename: process.env.REACT_APP_UNIQUENAME || 'SubApp',
    basename: process.env.REACT_APP_ROUTE_NAME_SPACE || '',
    routeModules,
    hash: false,
  });

  // 加载 UI 组件国际化资源
  useEffect(() => {
    const loadLocale = async () => {
      try {
        const { locales } = __DEV__ ? require('spc-ui-react') : await import('spc-ui-react');
        setLocale(locales.en_US);
      } catch (e) {
        console.error('Failed to load locales:', e);
        setLocale({});
      }
    };
    loadLocale();
  }, []);

  return (
    <React.StrictMode>
      <ConfigProvider prefixCls="spc-react" rcPrefixCls="spc-react-rc" locale={locale}>
        <Spin size="large" mask tips="Loading" spinning={loading}>
          <ReactRoutes />
        </Spin>
      </ConfigProvider>
    </React.StrictMode>
  );
}

export default App;

index.ts 启动文件

typescript 复制代码
import { ReactRender } from '@spc-fe-common/bridge';
import React from 'react';
import App from './App';

// 子应用启动方法,适配独立/集成两种模式
export const runSubApp = async () => {
  ReactRender({ el: '#app', Component: <App /> });
};

// 独立运行时直接启动(非 Qiankun、非 MF 集成环境)
if (!window.__POWERED_BY_QIANKUN__ && !window.__WEBPACK_SHARE_SCOPES__) {
  runSubApp();
}

六、环境变量与工程化配置

6.1 内置全局变量

使用 @spc-fe-common/build 时,自动注入以下核心全局变量:

全局变量 说明
UNIQUENAME 应用唯一标识
DEV 开发环境标识
IS_ALONE 独立运行模式标识

6.2 CDN 优化配置

在 .env 文件中配置,减少本地打包体积:

bash 复制代码
# .env 配置(CDN 优化)
USE_REACT_CDN=true # 生产环境推荐开启,启用 React CDN 加载
NODE_ENV=production # 环境标识:production/development
ALONE=true # 独立运行模式:true(独立开发)/false(集成运行)

6.3 故障排查

问题 解决方案
全局变量未定义 检查 sc.react.config.js 或 .env 配置
CDN 加载失败 关闭 USE_REACT_CDN,降级到本地资源
样式冲突 统一使用 SPCUI_REACT_PREFIX 前缀

七、高级特性

7.1 全局上下文通信

全应用共享数据/方法,支持监听数据变化,避免内存泄漏:

typescript 复制代码
import { request, i18n, userInfo, $gt, onContextChange } from '@spc-fe-common/context';
import { useEffect } from 'react';

// 监听用户信息变更
useEffect(() => {
  const unsubscribe = onContextChange('userInfo', (event) => {
    console.log('用户信息变更:', event.value);
  });
  return () => unsubscribe(); // 组件卸载取消监听
}, []);

// 核心实例说明:
// request:全局请求实例(直接调用 get/post)
// i18n:全局国际化实例(可调用 changeLanguage 切换语言)
// $gt:全局翻译函数(如 $gt('common.confirm'))
// userInfo:全局用户信息

7.2 自定义请求配置

创建自定义请求实例,适配特殊接口需求:

typescript 复制代码
import { setContext } from '@spc-fe-common/context';
import { RequestFactory } from '@spc-fe-common/request';

// 创建自定义请求实例
const customRequest = RequestFactory.getInstance({
  baseURL: '/api/custom', // 自定义基础地址
  timeout: 10000,
  requestInterceptors: [(config) => {
    const token = localStorage.getItem('token');
    if (token) config.headers.Authorization = `Bearer ${token}`;
    return config;
  }]
});

// 全局共享自定义请求实例
setContext('customRequest', customRequest, true);

7.3 多请求实例管理

区分业务请求与文件上传请求,避免冲突,提升可维护性:

typescript 复制代码
import { setContext } from '@spc-fe-common/context';
import { RequestFactory } from '@spc-fe-common/request';

// 业务请求实例
const businessRequest = RequestFactory.getInstance({
  baseURL: '/api/business',
  timeout: 5000
});

// 文件上传请求实例(适配大文件,延长超时时间)
const uploadRequest = RequestFactory.getInstance({
  baseURL: '/api/upload',
  timeout: 30000,
  headers: { 'Content-Type': 'multipart/form-data' }
});

// 注入全局上下文,供全应用调用
setContext('businessRequest', businessRequest);
setContext('uploadRequest', uploadRequest);

7.4 热更新配置

LoaderReactRefreshPlugin 保障微前端热更新兼容性,配置如下:

主应用配置

javascript 复制代码
const { LoaderReactRefreshPlugin } = require('@spc-fe-common/loader/webpack/plugins');

module.exports = {
  plugins: [new LoaderReactRefreshPlugin()],
  devServer: { hot: true }
};

子应用配置

javascript 复制代码
const { LoaderModuleFedearionShared, LoaderReactRefreshPlugin } = require('@spc-fe-common/loader/webpack/plugins');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({ shared: LoaderModuleFedearionShared }),
    new LoaderReactRefreshPlugin()
  ]
};

7.5 沙箱模式配置

适配旧应用隔离需求,避免环境冲突:

typescript 复制代码
{
  name: 'LegacyReactApp',
  entryUrl: '/legacy-react/remoteEntry.js',
  loadType: 'sandbox',
  type: 'react',
  sandbox: {
    activeRule: '/legacy-react', // 访问该路径激活
    container: 'legacy-react-container', // 挂载容器 ID
    experimentalStyleIsolation: true, // 样式隔离
    strictStyleIsolation: false,
    loose: false
  }
}

八、Qiankun 迁移

8.1 迁移三阶段

  1. 主应用改造:沙箱模式加载现有 Qiankun 子应用
  2. 子应用升级:逐步将非核心子应用改造为 MF 模式
  3. 完全迁移:所有子应用完成 MF 改造,移除沙箱配置

8.2 兼容配置

typescript 复制代码
{
  name: 'LegacyQiankunApp', // 旧应用唯一名称
  entryUrl: '/legacy-app/index.html', // 旧应用入口 HTML
  loadType: 'sandbox', // 沙箱隔离
  type: 'react', // 旧应用技术栈
  sandbox: { activeRule: '/legacy-app', container: 'legacy-app-container' }
}

九、常见问题排查

  • 子应用加载失败:检查 entryUrl 正确性、子应用 MF 配置与主应用名称一致
  • Hook 调用错误:确保主子应用 React 版本一致,检查共享依赖配置
  • 热更新不生效:确认 LoaderReactRefreshPlugin 配置,devServer.hot 设为 true
  • 样式冲突:启用 CSS 隔离,统一 UI 组件前缀

十、总结

10.1 接入策略选择

场景 推荐方案
快速试验/急需加载子应用 最小化接入(loader、context、i18n 核心包)
新项目开发 标准接入(核心包+bridge+request+types)
企业级生产项目 完整接入全生态能力

10.2 核心优势

快速接入、技术栈无关、向后兼容 Qiankun、渐进式增强、完整生态覆盖开发全链路,助力企业级微前端高效落地。

相关推荐
LXXgalaxy3 小时前
Uni-app 小程序页面跳转带参实战笔记(含对象传参与防坑)
开发语言·前端·javascript
2301_768350233 小时前
Vue指令修饰符
前端·javascript·vue.js
oi..3 小时前
Flag和JavaScript document有关
开发语言·前端·javascript·经验分享·笔记·安全·网络安全
Sgf2273 小时前
2026Web前端进阶学习路线
前端·学习
峰向AI3 小时前
从"别急着写代码"到"让AI能稳定干活":AI编程工具进化史
架构
每天吃饭的羊3 小时前
computed 同时写 get() 和 set()
前端·javascript·vue.js
Highcharts.js3 小时前
Highcharts + TypeScript 集成高级技巧|类型与框架集成实战
前端·javascript·vue.js·react.js·typescript·highcharts·图表生成
小凡同志3 小时前
别再把 MCP 和 Skill 混着用了:一个负责接系统,一个负责把事做稳
人工智能·架构·claude
芒果8013 小时前
做文档配图太烦?一个小时写了个工具解决
前端