0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于vite3的monorepo前端工程搭建步骤

OSC开源社区 来源:OSCHINA 社区 2023-06-09 10:21 次阅读

一、技术栈选择

1. 代码库管理方式 - Monorepo:将多个项目存放在同一个代码库中

a44a3d48-05f0-11ee-962d-dac502259ad0.png

选择理由

1:多个应用(可以按业务线产品粒度划分)在同一个 repo 管理,便于统一管理代码规范、共享工作流 选择理由

2:解决跨项目 / 应用之间物理层面的代码复用,不用通过发布 / 安装 npm 包解决共享问题

2. 依赖管理 - PNPM:消除依赖提升、规范拓扑结构

选择理由 1:通过软 / 硬链接方式,最大程度节省磁盘空间 选择理由 2:解决幽灵依赖问题,管理更清晰

3. 构建工具 - Vite:基于 ESM 和 Rollup 的构建工具

选择理由:省去本地开发时的编译过程,提升本地开发效率

4. 前端框架 - Vue3:Composition API

选择理由:除了组件复用之外,还可以复用一些共同的逻辑状态,比如请求接口 loading 与结果的逻辑

5. 模拟接口返回数据 - Mockjs

选择理由:前后端统一了数据结构后,即可分离开发,降低前端开发依赖,缩短开发周期

二、目录结构设计:重点关注 src 部分

1. 常规 / 简单模式:根据文件功能类型集中管理

```
mesh-fe
├── .husky                  #git提交代码触发
│   ├── commit-msg            
│   └── pre-commit                  
├── mesh-server             #依赖的node服务
│   ├── mock   
│   │   └── data-service   #mock接口返回结果 
│   └── package.json
├── README.md
├── package.json
├── pnpm-workspace.yaml     #PNPM工作空间
├── .eslintignore           #排除eslint检查
├── .eslintrc.js            #eslint配置
├── .gitignore
├── .stylelintignore        #排除stylelint检查
├── stylelint.config.js     #style样式规范
├── commitlint.config.js    #git提交信息规范
├── prettier.config.js      #格式化配置
├── index.html              #入口页面
└── mesh-client #不同的web应用package
    ├── vite-vue3 
        ├── src
            ├── api                 #api调用接口层
            ├── assets              #静态资源相关
            ├── components          #公共组件
            ├── config              #公共配置,如字典/枚举等
            ├── hooks               #逻辑复用
            ├── layout              #router中使用的父布局组件
            ├── router              #路由配置
            ├── stores              #pinia全局状态管理
            ├── types               #ts类型声明
            ├── utils
            │   ├── index.ts        
            │   └── request.js     #Axios接口请求封装
            ├── views               #主要页面
            ├── main.ts             #js入口
            └── App.vue
```

2. 基于 domain 领域模式:根据业务模块集中管理

```
mesh-fe
├── .husky                  #git提交代码触发
│   ├── commit-msg            
│   └── pre-commit                  
├── mesh-server             #依赖的node服务
│   ├── mock   
│   │   └── data-service   #mock接口返回结果 
│   └── package.json
├── README.md
├── package.json
├── pnpm-workspace.yaml     #PNPM工作空间
├── .eslintignore           #排除eslint检查
├── .eslintrc.js            #eslint配置
├── .gitignore
├── .stylelintignore        #排除stylelint检查
├── stylelint.config.js     #style样式规范
├── commitlint.config.js    #git提交信息规范
├── prettier.config.js      #格式化配置
├── index.html              #入口页面
└── mesh-client             #不同的web应用package
    ├── vite-vue3 
        ├── src                    #按业务领域划分
            ├── assets              #静态资源相关
            ├── components          #公共组件
            ├── domain              #领域
            │   ├── config.ts
            │   ├── service.ts 
            │   ├── store.ts        
            │   ├── type.ts                       
            ├── hooks               #逻辑复用
            ├── layout              #router中使用的父布局组件
            ├── router              #路由配置
            ├── utils
            │   ├── index.ts        
            │   └── request.js     #Axios接口请求封装
            ├── views               #主要页面
            ├── main.ts             #js入口
            └── App.vue
```
可以根据具体业务场景,选择以上 2 种方式其中之一。

三、搭建部分细节

1.Monorepo+PNPM 集中管理多个应用(workspace)

根目录创建 pnpm-workspace.yaml,mesh-client 文件夹下每个应用都是一个 package,之间可以相互添加本地依赖:pnpm install

packages:
  # all packages in direct subdirs of packages/
  - 'mesh-client/*'
  # exclude packages that are inside test directories
  - '!**/test/**'
pnpm install #安装所有package中的依赖 pnpm install -w axios #将axios库安装到根目录 pnpm --filter | -F #执行某个package下的命令 与 NPM 安装的一些区别: 所有依赖都会安装到根目录 node_modules/.pnpm 下; package 中 packages.json 中下不会显示幽灵依赖(比如 tslib@types/webpack-dev),需要显式安装,否则报错 安装的包首先会从当前 workspace 中查找,如果有存在则 node_modules 创建软连接指向本地 workspace "mock": "workspace:^1.0.0"

2.Vue3 请求接口相关封装

request.ts 封装:主要是对接口请求和返回做拦截处理,重写 get/post 方法支持泛型

import axios, { AxiosError } from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'

// 创建 axios 实例
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_URL,
  timeout: 1000 * 60 * 5, // 请求超时时间
  headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})

const toLogin = (sso: string) => {
  const cur = window.location.href
  const url = `${sso}${encodeURIComponent(cur)}`
  window.location.href = url
}

// 服务器状态码错误处理
const handleError = (error: AxiosError) => {
  if (error.response) {
    switch (error.response.status) {
      case 401:
        // todo
        toLogin(import.meta.env.VITE_APP_SSO)
        break
      // case 404:
      //   router.push('/404')
      //   break
      // case 500:
      //   router.push('/500')
      //   break
      default:
        break
    }
  }
  return Promise.reject(error)
}

// request interceptor
service.interceptors.request.use((config) => {
  const token = ''
  if (token) {
    config.headers!['Access-Token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
  }
  return config
}, handleError)

// response interceptor
service.interceptors.response.use((response: AxiosResponse) => {
  const { code } = response.data
  if (code === '10000') {
    toLogin(import.meta.env.VITE_APP_SSO)
  } else if (code !== '00000') {
    // 抛出错误信息,页面处理
    return Promise.reject(response.data)
  }
  // 返回正确数据
  return Promise.resolve(response)
  // return response
}, handleError)

// 后端返回数据结构泛型,根据实际项目调整
interface ResponseData {
  code: string
  message: string
  result: T
}

export const httpGet = async (url: string, config?: AxiosRequestConfig) => {
  return service.get>(url, config).then((res) => res.data)
}

export const httpPost = async (
  url: string,
  data?: D,
  config?: AxiosRequestConfig,
) => {
  return service.post>(url, data, config).then((res) => res.data)
}

export { service as axios }

export type { ResponseData }
useRequest.ts 封装:基于 vue3 Composition API,将请求参数、状态以及结果等逻辑封装复用
import { ref } from 'vue'
import type { Ref } from 'vue'
import { ElMessage } from 'element-plus'
import type { ResponseData } from '@/utils/request'
export const useRequest = (
  api: (...args: P[]) => Promise>,
  defaultParams?: P,
) => {
  const params = ref

() as Ref

if (defaultParams) { params.value = { ...defaultParams, } } const loading = ref(false) const result = ref() const fetchResource = async (...args: P[]) => { loading.value = true return api(...args) .then((res) => { if (!res?.result) return result.value = res.result }) .catch((err) => { result.value = undefined ElMessage({ message: typeof err === 'string' ? err : err?.message || 'error', type: 'error', offset: 80, }) }) .finally(() => { loading.value = false }) } return { params, loading, result, fetchResource, } }

API 接口层
import { httpGet } from '@/utils/request'

const API = {
  getLoginUserInfo: '/userInfo/getLoginUserInfo',
}
type UserInfo = {
  userName: string
  realName: string
}
export const getLoginUserInfoAPI = () => httpGet(API.getLoginUserInfo)
页面使用:接口返回结果 userInfo,可以自动推断出 UserInfo 类型,
// 方式一:推荐
const {
  loading,
  result: userInfo,
  fetchResource: getLoginUserInfo,
} = useRequest(getLoginUserInfoAPI)

// 方式二:不推荐,每次使用接口时都需要重复定义type
type UserInfo = {
  userName: string
  realName: string
}
const {
  loading,
  result: userInfo,
  fetchResource: getLoginUserInfo,
} = useRequest(getLoginUserInfoAPI)

onMounted(async () => {
  await getLoginUserInfo()
  if (!userInfo.value) return
  const user = useUserStore()
  user.$patch({
    userName: userInfo.value.userName,
    realName: userInfo.value.realName,
  })
})
3.Mockjs 模拟后端接口返回数据
import Mock from 'mockjs'
const BASE_URL = '/api'
Mock.mock(`${BASE_URL}/user/list`, {
  code: '00000',
  message: '成功',
  'result|10-20': [
    {
      uuid: '@guid',
      name: '@name',
      tag: '@title',
      age: '@integer(18, 35)',
      modifiedTime: '@datetime',
      status: '@cword("01")',
    },
  ],
})
四、统一规范

1.ESLint

注意:不同框架下,所需要的 preset 或 plugin 不同,建议将公共部分提取并配置在根目录中,package 中的 eslint 配置设置 extends。

/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
  root: true,
  extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier',
  ],
  overrides: [
    {
      files: ['cypress/e2e/**.{cy,spec}.{js,ts,jsx,tsx}'],
      extends: ['plugin:cypress/recommended'],
    },
  ],
  parserOptions: {
    ecmaVersion: 'latest',
  },
  rules: {
    'vue/no-deprecated-slot-attribute': 'off',
  },
}

2.StyleLint

module.exports = {
  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
  plugins: ['stylelint-order'],
  customSyntax: 'postcss-html',
  rules: {
    indentation: 2, //4空格
    'selector-class-pattern':
      '^(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:[.+])?$',
    // at-rule-no-unknown: 屏蔽一些scss等语法检查
    'at-rule-no-unknown': [true, { ignoreAtRules: ['mixin', 'extend', 'content', 'export'] }],
    // css-next :global
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global', 'deep'],
      },
    ],
    'order/order': ['custom-properties', 'declarations'],
    'order/properties-alphabetical-order': true,
  },
}

3.Prettier

module.exports = {
  printWidth: 100,
  singleQuote: true,
  trailingComma: 'all',
  bracketSpacing: true,
  jsxBracketSameLine: false,
  tabWidth: 2,
  semi: false,
}

4.CommitLint

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['build', 'feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert'],
    ],
    'subject-full-stop': [0, 'never'],
    'subject-case': [0, 'never'],
  },
}
五、附录:技术栈图谱

a487d162-05f0-11ee-962d-dac502259ad0.png





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • ESM
    ESM
    +关注

    关注

    0

    文章

    9

    浏览量

    9045
  • SRC
    SRC
    +关注

    关注

    0

    文章

    60

    浏览量

    17962

原文标题:基于vite3的monorepo前端工程搭建

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    前端工程师月薪15000元福利好[1-3年经验]

    岗位职责:1、负责公司软件产品的web前端开发工作;2、将设计效果图进行切图和div+css重构;3、负责软件产品前端架构的设计和搭建,编写javascript界面交互程序;4、参与
    发表于 09-30 16:40

    设计模拟前端需要遵循哪些步骤

    设计模拟前端需要遵循哪些步骤?如何设计一个增益模块和ADC组合?
    发表于 04-07 06:04

    关于虚拟仪器测试环境VITE的分析

    VITE标准体系结构分析VITE实现体系结构VITE核心信息模型结构
    发表于 05-12 06:31

    Android 开发环境搭建步骤详细图解

    Android 开发环境搭建步骤详细图解
    发表于 10-24 08:49 11次下载
    Android 开发环境<b class='flag-5'>搭建</b><b class='flag-5'>步骤</b>详细图解

    Monkey测试环境搭建步骤详解

    本文全面介绍了Monkey测试环境搭建步骤
    的头像 发表于 01-31 18:47 8693次阅读

    如何搭建寄存器的工程环境详细方法步骤说明

    本文档的主要内容详细介绍的是如何搭建寄存器的工程环境详细方法步骤说明。
    发表于 09-19 08:00 0次下载
    如何<b class='flag-5'>搭建</b>寄存器的<b class='flag-5'>工程</b>环境详细方法<b class='flag-5'>步骤</b>说明

    物联网LoRa系列-3:LoRa终端搭建的总体思路、步骤与架构

    搭建的LoRa终端的总体思路与步骤:1. 搭建的LoRa终端的系统需求和目标2. 设计LoRa终端的目标系统3. 设计LoRa终端的主机开发环境4. 设计LoRa终端的软件架构5. 构
    发表于 12-07 14:06 26次下载
    物联网LoRa系列-<b class='flag-5'>3</b>:LoRa终端<b class='flag-5'>搭建</b>的总体思路、<b class='flag-5'>步骤</b>与架构

    Go Vite通用的去中心化应用平台

    go-vite.zip
    发表于 04-22 10:59 1次下载
    Go <b class='flag-5'>Vite</b>通用的去中心化应用平台

    搭建基于Vue3+Vite2+Arco+Typescript+Pinia后台管理系统模板

    今天我们就来快速搭建一个基于Vue3+Vite2+Arco+Typescript+Pinia后台管理系统模板。这样可以帮大家快速制作自己的后台模板
    的头像 发表于 03-01 10:09 741次阅读
    <b class='flag-5'>搭建</b>基于Vue<b class='flag-5'>3+Vite</b>2+Arco+Typescript+Pinia后台管理系统模板

    Vite 4.3正式发布,前端构建工具

    最新发布的 Vite 4.3 显著提升了性能。发布公告写道,Vite 团队在这个版本中将工作重心放在提升开发服务器的性能上,其中包括简化解析逻辑、改进热路径、实现更智能的缓存以查找 package.json,TS 配置文件和解析的 URL。
    的头像 发表于 04-26 14:23 968次阅读
    <b class='flag-5'>Vite</b> 4.3正式发布,<b class='flag-5'>前端</b>构建工具

    ViteConf 2023:Vite即将Rust化挑战

    ViteConf 干货满满,尤大的分享中最值得关注的就要数 Rolldown 了,它为 Vite 现有痛点提供了新的解决方案。
    的头像 发表于 10-07 10:28 1907次阅读
    ViteConf 2023:<b class='flag-5'>Vite</b>即将Rust化挑战

    Vite 5正式发布,性能大幅提升

    公告指出,Vite 5 的重点是清理 API(删除已弃用的功能),并精简了几个功能以解决长期存在的问题。例如,将 define 转换为使用正确的 AST 替换,而不是使用 regexes。项目团队表示,他们将继续推进实现面向未来的 Vite
    的头像 发表于 11-20 16:20 1057次阅读

    芯片设计分为哪些步骤?为什么要分前端后端?前端后端是什么意思

    芯片设计分为哪些步骤?为什么要分为前端后端?前端后端分别是什么意思? 芯片设计分为前端和后端两个主要步骤
    的头像 发表于 12-07 14:31 3598次阅读

     海外云服务器搭建pi节点详细步骤

     海外云服务器搭建pi节点简单吗?海外云服务器搭建pi节点步骤有哪些?小编为您整理发布海外云服务器搭建pi节点相关内容。
    的头像 发表于 02-21 10:16 1069次阅读

    如何搭建3d数字孪生平台

    以及部署与维护等关键步骤。在技术方面,您需要考虑使用三维建模软件、数据库管理系统、数据分析工具、虚拟现实技术、物联网技术以及前端开发技术等来构建功能全面的3D数字孪生平台。 搭建
    的头像 发表于 07-04 15:23 356次阅读