1、全局安装pnpm
npm install pnpm -g
2、创建项目文件夹monorepo-manage及其packages文件夹
md monorepo-manage
cd monorepo-manage
3、初始化项目
//根目录下
pnpm init
4、创建pnpm-workspace.yaml文件
在根目录下,创建pnpm-workspace.yaml文件,内容:
type nul >pnpm-workspace.yaml
packages:
- 'packages/*' # 代表所有项目都放在packages文件夹之下
注释:代表所有项目都放在packages文件夹之下
5、创建.npmrc文件
根目录下创建.npmrc文件,内容:
type nul >.npmrc
shamefully-hoist = true
注释:三方依赖一也有依赖,要是项目中使用了第三方的依赖,要是哪天第三方卸载不在该包了,那就找不到了,称之为“幽灵依赖” ,所以需要“羞耻提升”,暴露到外层中,即在根目录下的node_modules内,而非在.pnpm文件夹中。
6、创建packages文件夹
md packages
cd packages
7、创建shared跟web等子项目
在packages下创建shared文件夹,后续再创建web1、web2...项目。
md shared
注释:shared项目用来服务其他多个web项目,提供公共方法、组件、样式等等。
8、全局安装Vue
pnpm install vue -w
注释:-w的意思是,workspace-root把依赖包安装到工作目录的根路径下,则根目录下会生成node_modules文件夹。可以共用,后续每个项目需要用到vue的,都直接从根目录node_modules里取。
可以看出根目录下的node_modules里,vue安装到了与.pnpm同层级位置当中了,这就是第3步骤shamefully-hoist = true的效果,把vue从.pnpm内提到node_modules中,并且vue的相关依赖,也拍平到了该层级文件夹中。
注释:若是此时看到vue的依赖没有拍平到node_modules下,还是在.pnpm当中,不用慌,执行pnpm uninstall vue -w,接着删掉了.npmrc文件重新创建.npmrc文件,然后删除node_modules文件,这会终端就会提示:“ERR_PNPM_PUBLIC_HOIST_PATTERN_DIFF This modules directory was created using a different public-hoist-pattern value. Run "pnpm install" to recreate the modules directory.”,按照指示,执行“pnpm install”即可,
9、全局安装typescript
pnpm install typescript -w -D
10、初始化shared项目
pnpm init初始化shared项目的package.json
shared/package.json:
{
"name": "@manage/shared",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
注释: 注意name的名字,后续需要配置关联项目之间,要对应。
11、shared下创建分享共用的内容
md components
md utils
md fetch
md style
......
utils/index.ts 随意添加点导出函数,等会给web1项目用。
12、初始化web1项目,pnpm安装vue项目
pnpm create vite web1 -- --template vue
注意:安装时候可能会出现如下图告警信息,导致安装失败 安装指令需要改成:
pnpm create vite web1 --- --template vue
前2杠变成3杠即可,详见官方issue
注释:打开paclage.json,注意name的名字,改成“@manage/web1”,后续需要关联项目之间,要对应。
可以看出,安装成功之后vue、@vitejs/plugin-vue、vite,这三者也会被安装在当前的web1之下node_modules
"dependencies": {
"vue": "^3.2.25"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.3.3",
"vite": "^2.9.9"
}
web1下执行pnpm install,验证一下
果真,web1/node_modules下有依赖包,那么问题来了,就是说,将来我们这个项目会有web2、web3...出来,不能每个子项目都在自身安装vue、@vitejs/plugin-vue、vite,这样不好管理,而且依赖包臃肿,应当把这三者安装到全局当中,以备共用。 卸载vue、@vitejs/plugin-vue、vite
pnpm uninstall vue
pnpm uninstall @vitejs/plugin-vue -D
pnpm uninstall vite -D
全局安装vue、@vitejs/plugin-vue、vite vue在第8步骤已经安装过,此时不用安装了
pnpm install @vitejs/plugin-vue vite -D -w
可以看出web1/node_modules下已经没有vue跟vite的文件夹。 接着跑项目试试
pnpm run dev
没问题,说明web1所依赖的资源,自身没有的话,就往上层去取。
13、建立关联
13.1 指定版本号
pnpm install @manage/shared@workspace --filter @manage/web1
会添加版本号,代表只能使用@manage/shared的version:1.0.0版本 13.2 不指定版本号,取最新版本
pnpm install @manage/shared@* --filter @manage/web1
14、web1开始引用的shared的数据
此时我们发现,我们是可以用相对路径引用shared,好比:
//尝试在App.vue中引用
import { isObject } from '../../shared/utils';
但我们不能这么做,low,而且路径引用层级不好管理。 所以需要加入tsconfig.json来配置路径,定义按照规则去查找shared 安装typescript到全局
pnpm install typescript -D -w
根路径下执行:
pnpm tsc --init
tsconfig.json配置如下:
{
"compilerOptions": {
"outDir":"dist", // 输出的目录
"sourceMap": true, //采用sourcemap
"target": "es2016", // 目标语法
"module": "esnext", // 模块格式
"moduleResolution": "node", // 模块解析
"strict": false, // 严格模式
"resolveJsonModule": true, // 解析json模块
"esModuleInterop": true,// 允许通过es6语法引入commonjs模块
"jsx":"preserve",// jsx不转义
"lib":["esnext","dom"],// 支持的类库esnext及dom
"baseUrl": ".",// 当前是以该路径进行查找
"paths":{
// "@manage/*":[
// "packages/*/src",
// ], // 即以@manage开头的都去该路径下查找,是个数组
"@manage/shared/components":["packages/shared/components"],
"@manage/shared/utils":["packages/shared/utils"],
"@manage/shared/fetch":["packages/shared/fetch"],
"@manage/shared/styles":["packages/shared/styles"],
// 或者用*号处理匹配
"@manage/shared/*":["packages/shared/*"]
}
}
}
15. web项目正确引用shared的utils
import { isObject } from '@manage/shared/utils';
16. web项目正确引用shared的styles
// 全局安装sass,执行指令
pnpm install sass -D -w
/* 本子项目variables.scss */
@import 'variables.scss';
/* 本子项目mixin.scss */
@import 'mixin.scss';
/* 引用项目最顶层基础初始化样式 , web1/src/styles/index.scss */
@import "@manage/shared/styles/index.scss";
// 此处不能省略结尾的.scss后缀
17. 封装shared共用组件
//packages/components/src下,创建
type nul >Button.vue
//packages/components/src/index.ts
type nul >index.ts
18. 配置指令,可以在项目内的任何路径下跑起web项目
因为开发项目比较多,有时候总是cd与cd ../切换或者手动重新切换终端路径,比较麻烦,费时间。 在全局的package.json中配置scripts
{
"scripts": {
"dev:web1": "cd packages/web1 & pnpm dev"
},
// 或者
"scripts": {
"dev:web1": "pnpm -C packages/web1 & pnpm dev"
},
}
跑项目时候,执行
pnpm run -w dev:web1
// 就能跑起web1项目,同理web2、web3也一样配置。
后续:(shared项目的components、fetch、utils中的index.ts文件,都移到了src文件内,也修改了ts.config.json的paths配置)
"paths":{
// "@manage/*":[
// "packages/*/src",
// ], // 即以@manage开头的都去该路径下查找,是个数组
"@manage/shared/*":["packages/shared/*"]
}
19. 封装自己的插件库xyplayer
packages下执行
md plugins
cd plugins
md xyplayer
cd xyplayer
md src
type nul >index.ts
20. xyplayer项目关联shared项目
pnpm-workspace.yaml添加
- 'packages/plugins/*'
xyplayer下执行:
pnpm install @manage/shared@* --filter @manage/xyplayer
21. 处理插件库的打包
根目录下创建scripts文件夹
md scripts
cd scripts
type nul >dev-plugins.js
安装minimist esbuild
pnpm install typescript minimist esbuild -w -D
build-plugins.js:
// minimist 可以解析命令行参数,非常好用,功能简单
import minimist from 'minimist'
// 打包模块
import { build } from 'esbuild'
// node 中的内置模块
import path from 'path'
import fs from 'fs'
const __dirname = path.resolve();
const args = minimist(process.argv.slice(2));
const target = args._[0];
const format = args.f || "global";
const entry = path.resolve(__dirname, `../packages/plugins/${target}/src/index.ts`);
/* iife 立即执行函数(function(){})()
cjs node中的模块 module.exports
esm 浏览器中的esModule模块 import */
const outputFormat = format.startsWith("global") ?
"iife" :
format === "cjs" ?
"cjs" :
"esm";
const outfile = path.resolve(__dirname, `../packages/plugins/${target}/dist/${target}.${format}.js`);
const pkaPath = `../packages/plugins/${target}/package.json`;
const pkaOps = JSON.parse(fs.readFileSync(pkaPath, 'utf8'));
const packageName = pkaOps.buildOptions?.name;
build({
entryPoints: [entry],
outfile,
bundle: true,
sourcemap: true,
format: outputFormat,
globalName: packageName,
platform: format === "cjs" ? "node" : "browser",
watch: {
onRebuild(error) {
if (!error) {
console.log(`rebuild~~~`);
}
},
},
}).then(() => {
console.log("watching~~~");
});
22. 开始撸插件库xyplayer代码
plugins/xyplayer/src/index.ts:
import { isObject } from "@manage/shared/utils";
// 测试
export const testFunc = ()=>{
return isObject({})
}
23. 运行打包插件库
全局配置打包指令,package.json中scripts
"scripts": {
"dev:web1": "cd packages/web1 & pnpm dev",
"dev:xyplayer": "node scripts/dev-plugins.js xyplayer -f global"
},
执行:
pnpm -w run dev:xyplayer
24. 试验插件库的调用
引入打包好的dist下的文件,使用试试,没问题
25. vite-cli方式初始化vue3项目
目标:pnpm + vue3.0 + vite + pinia + vueuse + ts
25.1 vite-cli脚手架指令
//在packages文件夹目录下 终端执行:
pnpm create vite
25.2 输入项目名
? Project name: » web2
25.3 选中vue3
√ Project name: ... web2
? Select a framework: » - Use arrow-keys. Return to submit.
vanilla
> vue
react
preact
lit
svelte
25.4 选中vue-ts
√ Project name: ... web2
√ Select a framework: » vue
? Select a variant: » - Use arrow-keys. Return to submit.
vue
> vue-ts
25.5 完成配置,进入web2,执行
Done. Now run:
cd web2
pnpm install
pnpm run dev
25.6 移除web2中的vue、@vitejs/plugin-vue、vite、typescript,因为顶层已经安装共用的了。
pnpm uninstall vue
pnpm uninstall @vitejs/plugin-vue -D
pnpm uninstall vite -D
pnpm uninstall typescript -D
// 全局安装vue-tsc
pnpm install vue-tsc -w -D
绑定关联
25.7 绑定web2与shared的关联
pnpm install @manage/shared@* --filter @manage/web2
25.8 重新运行web2
pnpm run dev
// 跑服务出现了问题跑不下去,然后我重新安装了顶层跟web2的依赖包,就可以了
25.9 为保证 node 的使用
pnpm i @types/node --save-dev
25.10 安装element-plus、unplugin-auto-import、unplugin-vue-components,并且修改main.ts,vite.config.ts
- 安转element-plus: element-plus.gitee.io/zh-CN/guide…
- 按需导入介绍: element-plus.gitee.io/zh-CN/guide…
mian.ts:
import { createApp } from 'vue'
import App from './App.vue'
import router from '@/router'
// 引入全局样式
import '@/styles/index.scss'
// 引入Element
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(router)
// 注册element所有icon
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
app.mount('#app')
vite.config.ts:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// import myPlugin from './zip'
// https://vitejs.dev/config/
export default defineConfig({
base: "./",
plugins: [
vue(),
// myPlugin('dist', require('path').resolve(__dirname, './dist')),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),],
resolve: {
// 启用别名
alias: {
"@": "/src/",
assets: "/src/assets/",
api: "/src/api/",
views: "/src/views/",
components: "/src/components/",
},
},
css: {
preprocessorOptions: {
// 配置scss,自动引入指定的scss文件
scss: {
additionalData: `
@import "@/styles/mixin.scss";
@import "@/styles/variables.scss";
`,
},
},
},
server: {
host: "0.0.0.0", // 将监听所有地址,包括局域网和公网地址。Network显示
},
})
25.11 安装eslint格式化统一代码(VsCode编辑器安装Volar、ESlint插件)
pnpm install eslint eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D -w
25.12 创建.eslintignore,将不需要进行eslint的文件过滤
node_modules/
dist/
25.12 创建.eslintrc.js
module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
// 'no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/no-use-before-define': 'off',
// '@typescript-eslint/no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'space-before-function-paren': 'off',
quotes: ['error', 'single'],
'comma-dangle': ['error', 'never']
}
};
25.13 配置eslint的pnpm scripts指令
{
"scripts":{
"eslint:command": "终端上可以找出不符合规则的代码以及位置",
"eslint": "eslint --ext .js,.ts,vue packages",
"lint:fix:command": "找出不符合规则的代码并尝试安装项目配置的.eslintrc.js规则进行修正",
"lint:fix": "eslint --fix --ext .js,.ts,vue packages"
}
}
25.14 安装prettier
pnpm install prettier eslint-config-prettier eslint-plugin-prettier -D -w
(此处有个问题,安转之后,.vue文件也能按照.eslintrc.js的配置进行格式化,而原本我是打算根目录创建.prettierrc.js,结果貌似没用,我就去掉了.prettierrc.js)
25.15 .vscode配置
根目录下创建.vscode文件夹,里面再新建一个settings.json,用来设置vscode的该项目的全局配置。(需要重启VsCode)
// settings.json:
{
"editor.fontSize": 20, // 编辑器字体大小
"terminal.integrated.fontSize": 18, // terminal 框的字体大小
"editor.tabSize": 2, // Tab 的大小 2个空格
"editor.formatOnSave": true, // 保存是格式化
"prettier.singleQuote": true, // 单引号,没安转prettier,无效
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.format.enable": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
"vue",
"typescript",
"typescriptreact"
],
}
26. 安装或删除全局依赖包跟所有子项目依赖包,配置pnpm scripts指令
应开发有需要一键安装(或删除)全部子项目以及全局的node_modules,特此配置pnpm scripts指令
npm install rimraf -g
// 指令需要用rimraf来快速删除node_modules包,所以需要安装
{
"scripts":{
"installAllNm:command": "安装全局依赖以及的所有子项目的依赖",
"installAllNm": "pnpm install",
"clearAllNm:command": "删除全局依赖以及的所有子项目的依赖",
"clearAllNm": "rimraf node_modules && rimraf */**/node_modules"
}
}