您好,登錄后才能下訂單哦!
這篇文章主要介紹了Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境文章都會有所收獲,下面我們一起來看看吧。
Pnpm
是新一代的 nodejs
包管理工具。第一個 “P”
意為 Performance
,代表著更佳的性能。
它的主要優(yōu)點(diǎn)有兩個,一是采用了 hard-link
機(jī)制,避免了包的重復(fù)安裝,節(jié)省了空間,同時能提高項(xiàng)目依賴的安裝速度。二是對monorepo
的支持非常友好,只需要一條配置就能實(shí)現(xiàn)。
Monorepo
是一種新的倉庫管理方式。過去的項(xiàng)目,大多采用一個倉庫維護(hù)一個項(xiàng)目的方案。對于一個龐大復(fù)雜的項(xiàng)目,哪怕只進(jìn)行一處小小的修改,影響的也是整體。而采用 monorepo
的形式,我們可以在一個倉庫中管理多個包。每個包都可以單獨(dú)發(fā)布和使用,就好像是一個倉庫中又有若干個小倉庫。
Vue3 源碼采用 monorepo 方式進(jìn)行管理,將眾多模塊拆分到 packages 目錄中。
這帶來的最直觀的好處,就是方便管理和維護(hù)。而且,它不像 Vue2
那樣將源碼整體打包對外暴露。Vue3
的這種組織形式,方便的實(shí)現(xiàn)了 Tree-shaking
,需要哪個功能就引入對應(yīng)的模塊,能大大減少打包后項(xiàng)目的體積。
首先全局安裝 pnpm
:
npm install -g pnpm
新建一個目錄并進(jìn)行初始化:
mkdir vue3-learn cd vue3-learn pnpm init mkdir packages
monorepo
在項(xiàng)目根目錄下新建 pnpm-workspace.yaml
文件:
packages: - 'packages/*'
意思是,將 packages
目錄下所有的目錄都作為單獨(dú)的包進(jìn)行管理。
通過這樣一個簡單的配置,Monorepo
開發(fā)環(huán)境搭建好了。
如果大家之前接觸過 lerna + yarn workspace
的方案,就會深有體會,使用 pnpm
的確方便。Vue3
,Element Plus
以前采用的方案就是前者,現(xiàn)在都已經(jīng)改用后者了。
如果你使用過 Vite
,就一定體驗(yàn)過它的快。因?yàn)?Vite
內(nèi)置了 esbuild
作為開發(fā)階段的構(gòu)建工具。esbuild
的特點(diǎn)就是快。
Vue3
采用了和 vite
一致的選擇,開發(fā)階段使用 esbuild 作為構(gòu)建工具,在生產(chǎn)階段采用 rollup 進(jìn)行打包。
我們先安裝一些依賴:
# 源碼采用 typescript 編寫 pnpm add -D -w typescript # 構(gòu)建工具,命令行參數(shù)解析工具 pnpm add -D -w esbuild rollup rollup-plugin-typescript2 @rollup/plugin-json @rollup/plugin-node-resolve @rollup/plugin-commonjs minimist execa
說明:
-D
:作為開發(fā)依賴安裝
-w
:monorepo
環(huán)境默認(rèn)會認(rèn)為應(yīng)該將依賴安裝到具體的 package
中。使用 -w 參數(shù),告訴 pnpm 將依賴安裝到 workspace-root,也就是項(xiàng)目的根目錄。
依賴說明:
依賴 | 描述 |
---|---|
typescript | 項(xiàng)目使用 typescript 進(jìn)行開發(fā) |
esbuild | 開發(fā)階段的構(gòu)建工具 |
rollup | 生產(chǎn)階段的構(gòu)建工具 |
rollup-plugin-typescript2 | rollup 編譯 ts 的插件 |
@rollup/plugin-json | rollup 默認(rèn)采用 esm 方式解析模塊,該插件將 json 解析為 esm 供 rollup 處理 |
@rollup/plugin-node-resolve | rollup 默認(rèn)采用 esm 方式解析模塊,該插件可以解析安裝在 node_modules 下的第三方模塊 |
@rollup/plugin-commonjs | 將 commonjs 模塊 轉(zhuǎn)化為 esm 模塊 |
minimist | 解析命令行參數(shù) |
execa | 生產(chǎn)階段開啟子進(jìn)程 |
pnpm tsc --init
pnpm
的使用基本和 npm
一致。這里的用法就相當(dāng)于 npm
中的 npx
:
npx tsc --init
意思是,去 node_modules
下的 .bin
目錄中找到tsc
命令,并執(zhí)行它。
執(zhí)行完該命令,會在項(xiàng)目根目錄生成一個 tsconfig.json
文件,進(jìn)行一些配置:
{ "compilerOptions": { "outDir": "dist", // 輸出的目錄 "sourceMap": true, // 開啟 sourcemap "target": "es2016", // 轉(zhuǎn)譯的目標(biāo)語法 "module": "esnext", // 模塊格式 "moduleResolution": "node", // 模塊解析方式 "strict": false, // 關(guān)閉嚴(yán)格模式,就能使用 any 了 "resolveJsonModule": true, // 解析 json 模塊 "esModuleInterop": true, // 允許通過 es6 語法引入 commonjs 模塊 "jsx": "preserve", // jsx 不轉(zhuǎn)義 "lib": ["esnext", "dom"], // 支持的類庫 esnext及dom "baseUrl": ".", // 當(dāng)前目錄,即項(xiàng)目根目錄作為基礎(chǔ)目錄 "paths": { // 路徑別名配置 "@my-vue/*": ["packages/*/src"] // 當(dāng)引入 @my-vue/時,去 packages/*/src中找 }, } }
我們先在 packages
目錄下新建兩個模塊,分別是 reactivity 響應(yīng)式模塊 和 shared 工具庫模塊。然后編寫構(gòu)建腳本進(jìn)行第一次的開發(fā)調(diào)試。
在 packages
下新建 shared
目錄,并初始化:
pnpm init
然后修改 package.json
:
{ "name": "@my-vue/shared", "version": "1.0.0", "description": "@my-vue/shared", "main": "dist/shared.cjs.js", "module": "dist/shared.esm-bundler.js" }
注意 name
字段的值,我們使用了一個 @scope
作用域,它相當(dāng)于 npm
包的命名空間,可以使項(xiàng)目結(jié)構(gòu)更加清晰,也能減少包的重名。
編寫該模塊的入口文件:
// src/index.ts /** * 判斷對象 */ export const isObject = (value) =>{ return typeof value === 'object' && value !== null } /** * 判斷函數(shù) */ export const isFunction= (value) =>{ return typeof value === 'function' } /** * 判斷字符串 */ export const isString = (value) => { return typeof value === 'string' } /** * 判斷數(shù)字 */ export const isNumber =(value)=>{ return typeof value === 'number' } /** * 判斷數(shù)組 */ export const isArray = Array.isArray
在packages
下新建 reactivity
目錄,并初始化:
pnpm init
然后修改 package.json
:
{ "name": "@my-vue/reactivity", "version": "1.0.0", "description": "@my-vue/reactivity", "main": "dist/reactivity.cjs.js", "module": "dist/reactivity.esm-bundler.js", "buildOptions": { "name": "VueReactivity" } }
在瀏覽器中以 IIFE
格式使用響應(yīng)式模塊時,需要給模塊指定一個全局變量名字,通過 buildOptions.name
進(jìn)行指定,將來打包時會作為配置使用。
main
指定的文件支持 commonjs
規(guī)范進(jìn)行導(dǎo)入,也就是說在nodejs
環(huán)境中,通過 require
方法導(dǎo)入該模塊時,會導(dǎo)入 main
指定的文件。
同理,module
指定的是使用 ES Module
規(guī)范導(dǎo)入模塊時的入口文件。
編寫該模塊的入口文件:
// src/index.ts import { isObject } from '@my-vue/shared' const obj = {name: 'Vue3'} console.log(isObject(obj))
在 reactivity
包中用到了另一個包 shared
,需要安裝才能使用:
pnpm add @my-vue/shared@workspace --filter @my-vue/reactivity
意思是,將本地 workspace
內(nèi)的 @my-vue/shared
包,安裝到 @my-vue/reactivity
包中去。
此時,查看 reactivity
包的依賴信息:
"dependencies": { "@my-vue/shared": "workspace:^1.0.0" }
在根目錄下新建 scripts
目錄,存放項(xiàng)目構(gòu)建的腳本。
新建 dev.js
,作為開發(fā)階段的構(gòu)建腳本。
// scripts/dev.js // 使用 minimist 解析命令行參數(shù) const args = require('minimist')(process.argv.slice(2)) const path = require('path') // 使用 esbuild 作為構(gòu)建工具 const { build } = require('esbuild') // 需要打包的模塊。默認(rèn)打包 reactivity 模塊 const target = args._[0] || 'reactivity' // 打包的格式。默認(rèn)為 global,即打包成 IIFE 格式,在瀏覽器中使用 const format = args.f || 'global' // 打包的入口文件。每個模塊的 src/index.ts 作為該模塊的入口文件 const entry = path.resolve(__dirname, `../packages/${target}/src/index.ts`) // 打包文件的輸出格式 const outputFormat = format.startsWith('global') ? 'iife' : format === 'cjs' ? 'cjs' : 'esm' // 文件輸出路徑。輸出到模塊目錄下的 dist 目錄下,并以各自的模塊規(guī)范為后綴名作為區(qū)分 const outfile = path.resolve(__dirname, `../packages/${target}/dist/${target}.${format}.js`) // 讀取模塊的 package.json,它包含了一些打包時需要用到的配置信息 const pkg = require(path.resolve(__dirname, `../packages/${target}/package.json`)) // buildOptions.name 是模塊打包為 IIFE 格式時的全局變量名字 const pgkGlobalName = pkg?.buildOptions?.name console.log('模塊信息:\n', entry, '\n', format, '\n', outputFormat, '\n', outfile) // 使用 esbuild 打包 build({ // 打包入口文件,是一個數(shù)組或者對象 entryPoints: [entry], // 輸入文件路徑 outfile, // 將依賴的文件遞歸的打包到一個文件中,默認(rèn)不會進(jìn)行打包 bundle: true, // 開啟 sourceMap sourcemap: true, // 打包文件的輸出格式,值有三種:iife、cjs 和 esm format: outputFormat, // 如果輸出格式為 IIFE,需要為其指定一個全局變量名字 globalName: pgkGlobalName, // 默認(rèn)情況下,esbuild 構(gòu)建會生成用于瀏覽器的代碼。如果打包的文件是在 node 環(huán)境運(yùn)行,需要將平臺設(shè)置為node platform: format === 'cjs' ? 'node' : 'browser', // 監(jiān)聽文件變化,進(jìn)行重新構(gòu)建 watch: { onRebuild (error, result) { if (error) { console.error('build 失?。?#39;, error) } else { console.log('build 成功:', result) } } } }).then(() => { console.log('watching ...') })
使用該腳本,會使用 esbuild
對 packages
下的包進(jìn)行構(gòu)建,打包的結(jié)果放到各個包的 dist
目錄下。
在開發(fā)階段,我們默認(rèn)打包成 IIFE
格式,方便在瀏覽器中使用 html
文件進(jìn)行測試。在生產(chǎn)階段,會分別打包成 CommonJS
,ES Module
和 IIFE
的格式。
給項(xiàng)目增加一條 scripts
命令:
// package.json "scripts": { "dev": "node scripts/dev.js reactivity -f global" }
意思是,以 IIFE
的格式,打包 reactivity
模塊,打包后的文件可以運(yùn)行在瀏覽器中。
在終端中執(zhí)行:
pnpm dev
輸出:
PS D:\vue3-learn> pnpm dev
> vue3-learn@1.0.0 dev D:\vue3-learn
> node scripts/dev.js reactivity -f global
模塊信息:
D:\vue3-learn\packages\reactivity\src\index.ts
global
iife
D:\demo3\vue3-learn\packages\reactivity\dist\reactivity.global.js
watching ...
編寫一個 html
文件進(jìn)行測試:
// packages/reactivity/test/index.html <body> <div id="app"></div> <script src="../dist/reactivity.global.js"></script> </body>
打開瀏覽器控制臺:
關(guān)于“Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。