使用 Turborepo 构建 Monorepo
项目目录
bash
├── apps
│ ├── web(业务文件夹)
│ └── docs(业务文件夹)
├── packages
│ ├── ui
│ │ ├── tsconfig.json
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.js
│ │ │ ├── Button
│ │ │ │ ├── index.vue
│ ├── typescript-config
│ │ ├── base.json
│ │ ├── package.json
│ │ ├── vue.json
├── pnpm-workspace.yaml
├── turbo.json
├── .editorconfig
├── .env
├── .gitignore
├── .npmrc
├── .prettierrc.cjs
├── .turbo.json
├── package.json
├── pnpm-lock.yaml根目录 创建几个初始文件
初始化命令
bash
pnpm initpackage.json
- name 换成你自己的项目名称
json
{
"name": "result",
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"check-types": "turbo run check-types"
},
"devDependencies": {
"@antfu/eslint-config": "3.0.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"prettier": "^3.7.4",
"turbo": "^2.7.3",
"typescript": "5.9.2"
},
"packageManager": "pnpm@9.0.0",
"engines": {
"node": ">=18"
}
}turbo.json
json
{
"$schema": "https://turborepo.com/schema.json",
"ui": "tui",
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": ["dist/**", "build/**", "output/**", "public/**", ".nuxt/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"check-types": {
"dependsOn": ["^check-types"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}pnpm-workspace.yaml
yaml
packages:
- "apps/*"
- "packages/*"eslint.config.js
js
import antfu from "@antfu/eslint-config";
const apps = "apps";
const histoirePath = `${apps}/histoire`;
const packages = "packages";
const packagesPath = `${packages}/ui`;
export default await antfu(
{
typescript: true,
vue: true,
},
// Histoire Overrides
{
files: [`${histoirePath}/**/*.{js,ts,vue}`],
rules: {
"import/default": "off",
},
},
{
files: [`${packagesPath}/**/*.{js,ts,vue}`],
rules: {
"import/default": "off",
},
}
);.prettierrc.cjs
js
module.exports = {
/*打印宽度,超过后,会将属性换行*/
printWidth: 120,
/*禁止使用尾随逗号,对象和数组最后一个逗号去掉*/
trailingComma: "none",
/*在对象字面量中的括号之间添加空格*/
bracketSpacing: true,
/*使用单引号而不是双引号来定义字符串*/
singleQuote: true,
/*当箭头函数只有一个参数时,省略参数前后的括号*/
arrowParens: "avoid",
/*script和style标签中间的内容缩进*/
vueIndentScriptAndStyle: true,
// 将>多行 HTML(HTML、JSX、Vue、Angular)元素放在最后一行的末尾,而不是单独放在下一行(不适用于自闭合元素
bracketSameLine: false,
};.npmrc
bash
shamefully-hoist=true
strict-peer-dependencies=false
# 设置淘宝镜像
registry=https://registry.npmmirror.com.gitignore
bash
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
node_modules
.pnp
.pnp.js
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Outputs
.next/
out/
build
dist
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Misc
.DS_Store
*.pem.env
- 空着
.editorconfig
bash
# http://editorconfig.org
root = true
[*] # 表示所有文件
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 4 # 缩进大小
end_of_line = lf # 换行符(lf | cr | crlf)
insert_final_newline = true # 在文件结尾插入新行
trim_trailing_whitespace = true # 去除行尾空白
[*.md] # 表示对 md 文件应用以下规则
insert_final_newline = false # 不在文件结尾插入新行
trim_trailing_whitespace = false # 不去除行尾空白packages/typescript-config
- base.json
json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022"
}
}- package.json
json
{
"name": "@repo/typescript-config",
"version": "0.0.0",
"private": true,
"license": "MIT",
"publishConfig": {
"access": "public"
}
}- vue.json
json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022",
"types": ["vue"],
"baseUrl": ".",
"paths": {
"vue": ["node_modules/vue"]
}
},
"include": [
"apps/**/*",
"packages/**/*",
"src/**/*.ts",
"src/**/*.vue",
"src/**/*.d.ts"
]
}packages/ui
- package.json
json
{
"name": "@repo/vueui",
"type": "module",
"version": "0.0.1",
"private": true,
"main": "./src/index.ts",
"scripts": {
"lint": "eslint . --max-warnings 0"
},
"dependencies": {
"@vue/runtime-dom": "^3.5.26",
"vue": "^3.5.24"
},
"devDependencies": {
"@repo/typescript-config": "workspace:*",
"@types/node": "^22.15.3",
"eslint": "^9.39.1",
"typescript": "5.9.2"
}
}- tsconfig.json
json
{
"extends": "@repo/typescript-config/vue.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}- src/index.ts
注意
- 这个文件就是导出组件汇总方便调用
代码如下
ts
import VButton from "./Button/index.vue";
export { VButton };- src/Button/index.vue
代码如下
vue
<script lang="ts" setup>
import type { PropType } from "vue";
const props = defineProps({
type: {
type: String as PropType<"button" | "submit" | "reset">,
default: "button",
},
text: {
type: String,
required: true,
},
onClick: {
type: Function as PropType<() => void>,
default: () => {},
},
});
function handleClick() {
if (props.onClick) {
props.onClick(); // 调用传入的点击事件
}
}
</script>
<template>
<button :type="type" @click="handleClick">
{{ text }}
</button>
</template>
<style scoped>
button {
padding: 10px 20px;
border: none;
cursor: pointer;
}
</style>然后进入到 packages/ui
运行pnpm install
apps
apps/web
- 先安装 vue
ts
npm create vite@latest- tsconfig.node.json
ts
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
},
"include": ["vite.config.ts"]
}- App.vue 里面引入
html
<script setup lang="ts">
import { VButton } from "@repo/vueui";
import { ref, type Ref } from "vue";
const msg: Ref<string> = ref("点击我");
const typeName: Ref<"reset" | "button" | "submit" | undefined> = ref("reset");
function handleClick() {
window.alert("出来吧");
}
</script>
<template>
<div>
<VButton :text="msg" :type="typeName" :onClick="handleClick"></VButton>
</div>
</template>
<style scoped></style>