Vue 中多项目的组件共用方案

目录

一、cdn/服务器

[二、npm 包形式封装共享组件](#二、npm 包形式封装共享组件)

[1、 组件文件 src/components/MyButton.vue](#1、 组件文件 src/components/MyButton.vue)

[2、入口文件 src/index.js](#2、入口文件 src/index.js)

[3、Vite 打包配置 vite.config.js](#3、Vite 打包配置 vite.config.js)

[4. package.json 配置](#4. package.json 配置)

5、发布/使用组件

关键注意事项

[三、Monorepo 架构管理多个子项目](#三、Monorepo 架构管理多个子项目)

[四、软链接 / symlink 方式本地测试复用模块](#四、软链接 / symlink 方式本地测试复用模块)

项目结构

[1. 共享模块配置(shared-module)](#1. 共享模块配置(shared-module))

[2. 注册共享模块](#2. 注册共享模块)

[3. 消费项目引用(my-app)](#3. 消费项目引用(my-app))

4、实时开发流程

方案优势与注意点

适用场景

[五、Git Submodule 管理公共资源库](#五、Git Submodule 管理公共资源库)

完整实现步骤

[1. 创建公共资源库(子模块)](#1. 创建公共资源库(子模块))

[2. 主项目添加子模块](#2. 主项目添加子模块)

[3. 提交主项目变更](#3. 提交主项目变更)

使用示例

[1. 克隆含子模块的项目](#1. 克隆含子模块的项目)

[2. 业务代码中引用](#2. 业务代码中引用)

[3. 更新子模块代码](#3. 更新子模块代码)

关键操作命令

典型工作流程

注意事项

六、使用git地址依赖

实现原理

完整实现步骤

[1. 创建共享组件库(独立 Git 仓库)](#1. 创建共享组件库(独立 Git 仓库))

[2. 创建组件入口文件](#2. 创建组件入口文件)

[3. 配置自动生成脚本](#3. 配置自动生成脚本)

[4. 主项目引用组件库](#4. 主项目引用组件库)

[5. 安装依赖(主项目)](#5. 安装依赖(主项目))

组件使用

更新流程

方案优势对比


随着前端项目越来越大,功能越来越多,那么项目开的效率,代码的一致性,维护的成本,可复用性等问题都会不断冒出来,为解决这些问题项目可以使用多项目间的组件共用方案,

如:cdn/服务器 ,‌npm 包形式封装共享组件 ,‌Monorepo 架构管理多个子项目 ,‌软链接 / symlink 方式本地测试复用模块,Git Submodule 管理公共资源库使用git地址依赖

一、cdn/服务器

使用CDN引入组件方案在优化加载速度方面确实优势显著,但也存在更新维护的挑战。

问题场景 传统方案缺陷 优化方案 效果提升
‌组件频繁更新‌ 需手动刷新CDN/替换服务器文件 文件名哈希版本化 lib-v1.2.3.min.js 用户自动获取新版本
‌缓存更新延迟‌ 用户可能访问旧版本组件 动态加载 + Cache-Control: max-age=300 5分钟强制更新
‌多版本共存冲突‌ 全局替换导致兼容性问题 灰度发布 + A/B测试 故障率降低 ‌90%‌
复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue CDN组件共用示例</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <!--CDN 引入共享组件库 -->
  <script src="https://cdn.jsdelivr.net/npm/element-plus@2.4.4/dist/index.full.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus@2.4.4/dist/index.css">
  <style>
    body {
      font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', Arial, sans-serif;
      margin: 0;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
    }
    .container {
      max-width: 1200px;
      margin: 0 auto;
      background: rgba(255, 255, 255, 0.95);
      border-radius: 15px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
      padding: 30px;
    }
    .header {
      text-align: center;
      margin-bottom: 30px;
    }
    .header h1 {
      color: #333;
      margin-bottom: 10px;
    }
    .component-demo {
      margin: 20px 0;
      padding: 20px;
      border-radius: 10px;
      background: #f8f9fa;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }
    .demo-title {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 15px;
      color: #444;
    }
    .button-group {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
    }
    .notification-btn {
      margin-right: 10px;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <div class="header">
        <h1>Vue多项目CDN组件共用方案</h1>
        <p>通过CDN引入Element Plus组件库实现在多个项目中的组件复用</p>
      </div>

      <!-- 按钮组件演示 -->
      <div class="component-demo">
        <div class="demo-title">按钮组件演示</div>
        <div class="button-group">
          <el-button type="primary" @click="handleClick('Primary Button')">主要按钮</el-button>
          <el-button type="success" @click="handleClick('Success Button')">成功按钮</el-button>
          <el-button type="warning" @click="handleClick('Warning Button')">警告按钮</el-button>
          <el-button type="danger" @click="handleClick('Danger Button')">危险按钮</el-button>
          <el-button type="info" @click="handleClick('Info Button')">信息按钮</el-button>
        </div>
      </div>

      <!-- 输入框组件演示 -->
      <div class="component-demo">
        <div class="demo-title">表单组件演示</div>
        <el-form :model="form" label-width="120px">
          <el-form-item label="用户名">
            <el-input v-model="form.username" placeholder="请输入用户名"></el-input>
          </el-form-item>
          <el-form-item label="密码">
            <el-input v-model="form.password" type="password" placeholder="请输入密码"></el-input>
          </el-form-item>
          <el-form-item label="邮箱">
            <el-input v-model="form.email" placeholder="请输入邮箱地址"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitForm">提交</el-button>
            <el-button @click="resetForm">重置</el-button>
          </el-form-item>
        </el-form>
      </div>

      <!-- 数据表格演示 -->
      <div class="component-demo">
        <div class="demo-title">数据表格演示</div>
        <el-table :data="tableData" style="width: 100%" stripe>
          <el-table-column prop="id" label="ID" width="80"></el-table-column>
          <el-table-column prop="name" label="姓名" width="180"></el-table-column>
          <el-table-column prop="email" label="邮箱"></el-table-column>
          <el-table-column prop="role" label="角色" width="120"></el-table-column>
          <el-table-column label="操作" width="150">
            <template #default="scope">
              <el-button size="small" @click="editUser(scope.row)">编辑</el-button>
              <el-button size="small" type="danger" @click="deleteUser(scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <!-- 通知提醒演示 -->
      <div class="component-demo">
        <div class="demo-title">通知提醒演示</div>
        <div>
          <el-button class="notification-btn" @click="showSuccess">成功消息</el-button>
          <el-button class="notification-btn" type="warning" @click="showWarning">警告消息</el-button>
          <el-button class="notification-btn" type="danger" @click="showError">错误消息</el-button>
          <el-button class="notification-btn" type="info" @click="showInfo">信息消息</el-button>
        </div>
      </div>

      <!-- 进度条演示 -->
      <div class="component-demo">
        <div class="demo-title">进度条演示</div>
        <el-progress :percentage="progressPercentage" :stroke-width="15"></el-progress>
        <div style="margin-top: 10px;">
          <el-button @click="increaseProgress">增加进度</el-button>
          <el-button @click="decreaseProgress">减少进度</el-button>
          <el-button @click="resetProgress">重置进度</el-button>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp } = Vue;
    const { ElMessage, ElNotification } = ElementPlus;

    createApp({
      data() {
        return {
          form: {
            username: '',
            password: '',
            email: ''
          },
          tableData: [
            { id: 1, name: '张三', email: 'zhangsan@example.com', role: '管理员' },
            { id: 2, name: '李四', email: 'lisi@example.com', role: '用户' },
            { id: 3, name: '王五', email: 'wangwu@example.com', role: '用户' },
            { id: 4, name: '赵六', email: 'zhaoliu@example.com', role: '访客' }
          ],
          progressPercentage: 30
        };
      },
      methods: {
        handleClick(buttonType) {
          ElMessage.success(`点击了${buttonType}`);
        },
        submitForm() {
          if (!this.form.username || !this.form.password) {
            ElMessage.error('请填写必填项');
            return;
          }
          ElMessage.success('表单提交成功!');
          console.log('提交的数据:', this.form);
        },
        resetForm() {
          this.form = { username: '', password: '', email: '' };
          ElMessage.info('表单已重置');
        },
        editUser(user) {
          ElMessage.warning(`编辑用户: ${user.name}`);
        },
        deleteUser(user) {
          ElMessage.error(`删除用户: ${user.name}`);
        },
        showSuccess() {
          ElNotification({
            title: '成功',
            message: '这是一条成功的消息',
            type: 'success'
          });
        },
        showWarning() {
          ElNotification({
            title: '警告',
            message: '这是一条警告的消息',
            type: 'warning'
          });
        },
        showError() {
          ElNotification({
            title: '错误',
            message: '这是一条错误的消息',
            type: 'error'
          });
        },
        showInfo() {
          ElNotification({
            title: '信息',
            message: '这是一条信息的消息',
            type: 'info'
          });
        },
        increaseProgress() {
          if (this.progressPercentage < 100) {
            this.progressPercentage += 10;
          }
        },
        decreaseProgress() {
          if (this.progressPercentage > 0) {
            this.progressPercentage -= 10;
          }
        },
        resetProgress() {
          this.progressPercentage = 0;
        }
      },
      mounted() {
        // 页面加载完成后显示欢迎消息
        ElNotification({
          title: '欢迎',
          message: 'Vue CDN组件共用方案演示页面已加载完成',
          type: 'success'
        });
      }
    }).use(ElementPlus).mount('#app');
  </script>
</body>
</html>

二、npm 包形式封装共享组件

将通用组件打包发布为私有或公共 npm 包,在各项目中安装并按需导入使用。适用于大型团队协作或多产品线场景。

实现:创建Vue项目并开发组件 --> 编写插件的install方法(用于全局注册组件)-->配置打包命令(使用Vue CLI或Vite进行库模式打包)-->配置package.json(设置入口文件、版本、依赖等)-->发布到npm

1、 组件文件 src/components/MyButton.vue
复制代码
<!-- 组件文件 src/components/MyButton.vue -->
<template>
  <button class="my-button" @click="$emit('click')">
    <slot></slot>
  </button>
</template>

<style scoped>
.my-button {
  padding: 8px 16px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 4px;
}
</style>
2、入口文件 src/index.js
复制代码
import MyButton from './components/MyButton.vue'

// 全局注册组件
const install = (app) => {
  app.component('MyButton', MyButton)
}
// 支持按需引入
export { MyButton}
export default install
3、Vite 打包配置 vite.config.js
复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: 'src/index.js',    // 入口文件
      name: 'MyComponentLib',   // 全局变量名
      fileName: 'my-component-lib' // 输出文件名
    },
    rollupOptions: {
      external: ['vue'],        // 排除 Vue 依赖
      output: {
        globals: {
          vue: 'Vue'            // 全局 Vue 变量名
        }
      }
    }
  }
})
4. package.json 配置
复制代码
{
  "name": "my-component-lib",
  "version": "1.0.0",
  "main": "dist/my-component-lib.umd.cjs",   // CommonJS 入口
  "module": "dist/my-component-lib.js",      // ES Module 入口
  "files": ["dist"],                         // 发布目录
  "peerDependencies": {                      // 宿主环境依赖 
    "vue": ">=3.0.0"
  },
  "scripts": {
    "build": "vite build"                    // 打包命令
  }
}
5、发布/使用组件
复制代码
## 1.‌构建组件库
npm run build  # 生成 dist 目录

## 2.‌登录 npm 账户
npm login     # 输入账号/密码/邮箱

## 3.发布到 npm
npm publish   # 自动上传 dist 内容

# 使用组件库

##  安装依赖
npm install my-component-lib

## 全局注册(main.js)
import { createApp } from 'vue'
import MyComponentLib from 'my-component-lib'

createApp(App)
  .use(MyComponentLib)  // 注册所有组件
  .mount('#app')

## 按需引入(单文件组件)

<script setup>
import { MyButton } from 'my-component-lib'
</script>

<template>
  <MyButton>点击我</MyButton>
</template>

## 项目结构:
my-component-lib/
├── src/
│   ├── components/
│   │   ├── MyButton.vue    # 组件代码
│   └── index.js            # 组件导出入口
├── package.json            # 包配置
└── vite.config.js          # 打包配置
关键注意事项
  1. 版本管理

    • 每次更新需修改 package.json 中的 version 字段
    • 遵循语义化版本规范(major.minor.patch
  2. 依赖声明

    • 生产依赖 → dependencies
    • 开发依赖 → devDependencies
    • 宿主环境依赖 → ‌**peerDependencies**‌(避免重复安装)
  3. CDN 引入支持

    通过 unpkg 自动提供 CDN 访问:

    复制代码
    <script src="https://unpkg.com/my-component-lib@1.0.0/dist/my-component-lib.umd.cjs"></script>

三、Monorepo 架构管理多个子项目

Monorepo 是一种管理项目代码的方式,它指的是在一个大的仓库(repo)中管理多个模块或包(package)。这样可以方便地统一管理依赖、版本、配置等,也可以提高代码的复用性和协作效率。

Monorepo 架构管理多个子项目实现 demo

通过 npm link 命令创建符号链接,使本地模块无需发布即可被其他项目引用:

  1. 模块注册 ‌:在共享模块目录执行 npm link,创建全局软链接
  2. 项目引用 ‌:在消费项目执行 npm link <package-name>,链接到全局模块
  3. 实时同步‌:修改共享模块代码 → 所有链接项目立即生效

项目结构
复制代码
projects/
├── shared-module/    # 共享模块
│   ├── src/
│   │   └── Button.vue
│   ├── package.json
│   └── vite.config.js
└── my-app/           # 消费项目
    ├── src/
    │   └── App.vue
    └── package.json
1. 共享模块配置(shared-module)
复制代码
// package.json
{
  "name": "shared-module",
  "version": "1.0.0",
  "main": "dist/shared-module.umd.js",
  "peerDependencies": {
    "vue": ".3.0"
  }
}

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: 'src/Button.vue',
      name: 'SharedModule'
    }
  }
})

<!-- src/Button.vue -->
<template>
  <button class="shared-btn">
    <slot />
  </button>
</template>

<style scoped>
.shared-btn {
  padding: 12px 24px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 8px;
}
</style>
2. 注册共享模块
复制代码
cd shared-module
npm link  # 创建全局软链接
3. 消费项目引用(my-app)
复制代码
cd my-app
npm link shared-module  # 链接到全局模块

<!-- src/App.vue -->
<script setup>
import SharedButton from 'shared-module'
</script>

<template>
  <SharedButton>点击我</SharedButton>
</template>
4、实时开发流程
  1. 启动共享模块监听‌(shared-module 目录)

    vite build --watch # 开启构建监听

  2. 启动消费项目‌(my-app 目录)

    npm run dev

修改 Button.vue 代码 → 自动触发重建 → my-app 实时更新组件


方案优势与注意点
复制代码
# 优势
# 1. ‌**零发布延迟**‌:修改代码立即生效,无需 `npm publish`
# 2. ‌**跨项目复用**‌:同一模块可被多个项目同时链接使用
# 3. ‌**磁盘空间优化**‌:软链接不复制文件,节省存储空间

# 关键注意
# 路径一致性‌:

# 检查软链接路径
ls -l node_modules/shared-module
# 输出:shared-module -> ../../.nvm/versions/node/v20.12.0/lib/node_modules/shared-module

# 版本管理
# 消费项目 package.json
"dependencies": {
  "shared-module": "file:../shared-module"  // 防止意外安装
}
#清除链接‌:

# 解除项目链接
npm unlink shared-module

# 注销全局模块
npm unlink -g shared-module
适用场景
  1. 跨项目组件调试‌:多个前端项目共用同一组件库
  2. 微前端基座开发‌:主应用实时调试子应用模块
  3. 本地SDK测试‌:未发布的工具库在业务项目中验证

此方案通过操作系统级符号链接实现高效模块复用,相比 Monorepo 更轻量灵活,但需注意路径解析和版本管理问题

五、Git Submodule 管理公共资源库

Git Submodule允许将一个Git仓库作为另一个Git仓库的子目录嵌入,同时保持独立的版本控制,适合管理公共库或共享资源。

核心实现原理

复制代码
graph LR
A[主项目] --> B[.gitmodules]
B --> C[子模块路径]
C --> D[公共资源库]

通过 .gitmodules 文件建立主项目与子模块的映射关系,实现:

  1. 独立版本控制‌:子模块保持独立仓库的提交历史
  2. 引用特定提交‌:主项目锁定子模块的特定版本
  3. 跨项目复用‌:多个项目可引用同一公共库
完整实现步骤
1. 创建公共资源库(子模块)
复制代码
# 创建公共库
mkdir shared-lib && cd shared-lib
git init
echo "# 公共工具库" > README.md
git add . && git commit -m "初始化公共库"
2. 主项目添加子模块
复制代码
# 创建主项目
mkdir main-project && cd main-project
git init

# 添加子模块 (关键步骤)
git submodule add /path/to/shared-lib src/shared

此时生成 .gitmodules 文件:

复制代码
[submodule "src/shared"]
    path = src/shared
    url = /path/to/shared-lib
3. 提交主项目变更
复制代码
git add .gitmodules src/shared
git commit -m "添加shared-lib子模块"
使用示例
1. 克隆含子模块的项目
复制代码
git clone /path/to/main-project
cd main-project
git submodule update --init  # 初始化子模块
2. 业务代码中引用
复制代码
// main-project/src/app.js
import { utils } from './shared/utils.js';

console.log(utils.formatDate(new Date()));
3. 更新子模块代码
复制代码
# 进入子模块目录修改
cd src/shared
echo "export const formatDate = (date) => date.toISOString()" > utils.js
git add . && git commit -m "新增日期格式化工具"

# 返回主项目更新引用
cd ..
git add src/shared
git commit -m "更新共享库到最新版本"

关键操作命令
场景 命令 作用
添加子模块 git submodule add <repo> [path] 添加公共库到指定路径
初始化子模块 git submodule update --init [--recursive] 首次克隆后初始化子模块
更新子模块到最新版本 git submodule update --remote 拉取子模块最新提交
查看子模块状态 git submodule status 显示子模块提交ID和路径
批量操作子模块 git submodule foreach 'git pull' 所有子模块执行git pull

典型工作流程
复制代码
sequenceDiagram
    participant 主项目
    participant 子模块
    主项目->>子模块: 引用特定提交(SHA1)
    子模块->>主项目: 提供代码副本
    loop 开发过程
        子模块->>子模块: 独立开发提交
        主项目->>主项目: 更新子模块引用
    end

注意事项
  1. 版本锁定机制

    主项目记录子模块的 ‌具体提交ID‌(非分支名),确保版本一致性

    复制代码
    # 查看锁定的提交ID
    git ls-tree HEAD src/shared
    # 输出:160000 commit a1b2c3d...  src/shared
  2. 分支管理技巧

    在子模块中切换分支:

    复制代码
    cd src/shared
    git checkout dev-branch
    cd ..
    git add src/shared
    git commit -m "切换子模块到dev分支"
  3. 删除子模块

    复制代码
    git submodule deinit src/shared  # 解除注册
    git rm src/shared                # 删除目录
    rm -rf .git/modules/src/shared   # 清除缓存

完整示例参考:Git Submodule Demo

六、使用git地址依赖

将共享组件库作为一个独立的git仓库,然后在主项目的package.json中直接引用该git地址作为依赖

此方案类似于Git Submodule,但它是通过npm管理git依赖,而不是直接使用git submodule命令。

创建共享组件库(假设已经存在一个git仓库,这里以GitHub为例)

  • 共享组件库的package.json中需要设置正确的入口文件(比如"main":"dist/shared.umd.min.js")
  • 共享组件库需要打包成可被其他项目引用的库(例如使用vue-cli的build --target lib)

在主项目的package.json中添加依赖,指向共享组件库的git地址

格式:"依赖名": "git+https://github.com/用户名/仓库名.git" 或者指定分支git+https://.../仓库名.git#分支名

实现原理
复制代码
graph LR
A[主项目] --> B[package.json]
B --> C{{Git 依赖声明}}
C --> D[共享组件库]
D --> E[独立仓库]
完整实现步骤
1. 创建共享组件库(独立 Git 仓库)
复制代码
# 创建组件库项目
vue create shared-components
cd shared-components

# 配置打包脚本(package.json)
{
  "name": "shared-components",
  "version": "1.0.0",
  "scripts": {
    "lib": "vue-cli-service build --target lib --name compoment-demo src/index.js",
    "push": "node script.js && npm run lib && git add . && git commit -m 'update' && git push"
  },
  "main": "dist/compoment-demo.umd.min.js"
}
2. 创建组件入口文件
复制代码
// src/index.js
import Button from './components/Button.vue'
import Input from './components/Input.vue'

export { Button, Input }
3. 配置自动生成脚本
复制代码
// script.js
const fs = require('fs')

// 自动扫描components目录生成入口文件
const components = fs.readdirSync('./src/components')
  .filter(file => file.endsWith('.vue'))
  .map(file => `export { default as ${file.split('.')[0]} } from './components/${file}'`)

fs.writeFileSync('./src/index.js', components.join('\n'))
4. 主项目引用组件库
复制代码
// 主项目 package.json
{
  "dependencies": {
    "shared-components": "git+https://github.com/yourname/shared-components.git"
  }
}
5. 安装依赖(主项目)
复制代码
# 标准安装
npm install git+https://github.com/yourname/shared-components.git

# 若安装失败,使用替代方案
git config --global url."https://hub.fastgit.xyz/".insteadOf "ssh://git@github.com/"
npm install
组件使用
复制代码
<template>
  <div>
    <SharedButton>主要按钮</SharedButton>
    <SharedInput v-model="text" placeholder="输入内容" />
  </div>
</template>

<script>
import { Button as SharedButton, Input as SharedInput } from 'shared-components'

export default {
  components: { SharedButton, SharedInput },
  data() {
    return { text: '' }
  }
}
</script>
更新流程
复制代码
# 修改组件后自动打包推送
npm run push

# 更新组件库版本
npm update shared-components

# 或指定特定版本
npm install shared-components@1.2.0
方案优势对比
‌特性‌ ‌Git 依赖方案‌ ‌Git Submodule‌
依赖管理 npm 自动管理版本 需手动更新子模块引用
构建流程 组件库预构建,主项目直接使用 需主项目参与构建
跨项目一致性 版本锁定保证一致性 依赖特定提交ID
安装复杂度 npm install 一键完成 需额外 git submodule init

vue-shared-components-demo

相关推荐
笨笨狗吞噬者2 小时前
【uniapp】微信小程序实现自定义 tabBar
前端·微信小程序·uni-app
angerdream2 小时前
最新版vue3+TypeScript开发入门到实战教程之路由详解二
javascript·vue.js
恋猫de小郭2 小时前
React Native 鸿蒙 2026 路线发布,为什么它的适配成本那么高?
android·前端·react native
一颗烂土豆2 小时前
拒绝 rem 计算!Vue3 大屏适配,我用 vfit 一行代码搞定
vue.js·响应式设计·数据可视化
徐小夕2 小时前
一个普通Word文档,为什么99%的开源编辑器都"认怂"了?我们选择正面硬刚
vue.js·后端·github
呆头鸭L2 小时前
Electron进程通信
前端·javascript·electron·前端框架·vue
splage2 小时前
spring-boot-starter和spring-boot-starter-web的关联
前端
张元清2 小时前
使用 Hooks 构建无障碍 React 组件
前端·javascript·面试
Mahut3 小时前
从零构建神经影像可视化库:neuroviz 的架构设计与实现
前端·javascript·github