溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境

發(fā)布時間:2022-08-25 11:21:14 來源:億速云 閱讀:525 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境文章都會有所收獲,下面我們一起來看看吧。

    Pnpm 和 Monorepo

    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 目錄中。

    Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境

    這帶來的最直觀的好處,就是方便管理和維護(hù)。而且,它不像 Vue2 那樣將源碼整體打包對外暴露。Vue3的這種組織形式,方便的實(shí)現(xiàn)了 Tree-shaking,需要哪個功能就引入對應(yīng)的模塊,能大大減少打包后項(xiàng)目的體積。

    搭建開發(fā)環(huán)境

    創(chuà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ā)依賴安裝

    -wmonorepo 環(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-typescript2rollup 編譯 ts 的插件
    @rollup/plugin-jsonrollup 默認(rèn)采用 esm 方式解析模塊,該插件將 json 解析為 esm 供 rollup 處理
    @rollup/plugin-node-resolverollup 默認(rèn)采用 esm 方式解析模塊,該插件可以解析安裝在 node_modules 下的第三方模塊
    @rollup/plugin-commonjs將 commonjs 模塊 轉(zhuǎn)化為 esm 模塊
    minimist解析命令行參數(shù)
    execa生產(chǎn)階段開啟子進(jìn)程

    初始化Typescript

    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中找
        },
      }
    }

    準(zhǔn)備兩個模塊

    我們先在 packages 目錄下新建兩個模塊,分別是 reactivity 響應(yīng)式模塊 和 shared 工具庫模塊。然后編寫構(gòu)建腳本進(jìn)行第一次的開發(fā)調(diào)試。

    shared

    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
    reactivity

    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"
    }

    編寫構(gòu)建腳本

    在根目錄下新建 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 ...')
    })

    使用該腳本,會使用 esbuildpackages 下的包進(jìn)行構(gòu)建,打包的結(jié)果放到各個包的 dist 目錄下。

    在開發(fā)階段,我們默認(rèn)打包成 IIFE 格式,方便在瀏覽器中使用 html 文件進(jìn)行測試。在生產(chǎn)階段,會分別打包成 CommonJS,ES ModuleIIFE 的格式。

    完成第一次調(diào)試

    給項(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>

    打開瀏覽器控制臺:

    Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境

    關(guān)于“Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Vue3中怎么使用pnpm搭建monorepo開發(fā)環(huán)境”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(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)容。

    AI