Vue3中使用pnpm搭建monorepo开发环境

目录
  • 前言
  • Pnpm 和 Monorepo
  • 搭建开发环境
    • 创建项目
    • 配置 monorepo
    • 安装依赖
    • 初始化Typescript
    • 准备两个模块
      • shared
      • reactivity
    • 编写构建脚本
    • 完成第一次调试
  • 小结

前言

Vue3 源码阅读系列,计划从环境搭建开始,将 Vue3 的响应式模块,运行时模块和编译器模块,以及状态库 Pinia、路由库 Vue-Router的核心原理做一个梳理。这大概是一个漫长的过程。祝自己不要烂尾,祝大家有所收获。

Pnpm 和 Monorepo

Pnpm 是新一代的 nodejs 包管理工具。第一个 “P”意为 Performance,代表着更佳的性能。

它的主要优点有两个,一是采用了 hard-link 机制,避免了包的重复安装,节省了空间,同时能提高项目依赖的安装速度。二是对monorepo 的支持非常友好,只需要一条配置就能实现。

Monorepo 是一种新的仓库管理方式。过去的项目,大多采用一个仓库维护一个项目的方案。对于一个庞大复杂的项目,哪怕只进行一处小小的修改,影响的也是整体。而采用 monorepo 的形式,我们可以在一个仓库中管理多个包。每个包都可以单独发布和使用,就好像是一个仓库中又有若干个小仓库。

Vue3 源码采用 monorepo 方式进行管理,将众多模块拆分到 packages 目录中。

这带来的最直观的好处,就是方便管理和维护。而且,它不像 Vue2 那样将源码整体打包对外暴露。Vue3的这种组织形式,方便的实现了 Tree-shaking,需要哪个功能就引入对应的模块,能大大减少打包后项目的体积。

搭建开发环境

创建项目

首先全局安装 pnpm

npm install -g pnpm

新建一个目录并进行初始化:

mkdir vue3-learn
cd vue3-learn
pnpm init
mkdir packages

配置 monorepo

在项目根目录下新建 pnpm-workspace.yaml 文件:

packages:
  - 'packages/*'

意思是,将 packages 目录下所有的目录都作为单独的包进行管理。

通过这样一个简单的配置,Monorepo 开发环境搭建好了。

如果大家之前接触过 lerna + yarn workspace的方案,就会深有体会,使用 pnpm 的确方便。Vue3Element Plus以前采用的方案就是前者,现在都已经改用后者了。

安装依赖

如果你使用过 Vite,就一定体验过它的快。因为 Vite 内置了 esbuild 作为开发阶段的构建工具。esbuild 的特点就是快。

Vue3 采用了和 vite 一致的选择,开发阶段使用 esbuild 作为构建工具,在生产阶段采用 rollup 进行打包。

我们先安装一些依赖:

# 源码采用 typescript 编写
pnpm add  -D -w typescript
# 构建工具,命令行参数解析工具
pnpm add -D -w esbuild rollup rollup-plugin-typescript2 @rollup/plugin-json @rollup/plugin-node-resolve @rollup/plugin-commonjs minimist execa

说明:

-D:作为开发依赖安装

-wmonorepo 环境默认会认为应该将依赖安装到具体的 package中。使用 -w 参数,告诉 pnpm 将依赖安装到 workspace-root,也就是项目的根目录。

依赖说明:

依赖 描述
typescript 项目使用 typescript 进行开发
esbuild 开发阶段的构建工具
rollup 生产阶段的构建工具
rollup-plugin-typescript2 rollup 编译 ts 的插件
@rollup/plugin-json rollup 默认采用 esm 方式解析模块,该插件将 json 解析为 esm 供 rollup 处理
@rollup/plugin-node-resolve rollup 默认采用 esm 方式解析模块,该插件可以解析安装在 node_modules 下的第三方模块
@rollup/plugin-commonjs 将 commonjs 模块 转化为 esm 模块
minimist 解析命令行参数
execa 生产阶段开启子进程

初始化Typescript

pnpm tsc --init

pnpm 的使用基本和 npm 一致。这里的用法就相当于 npm 中的 npx

npx tsc --init

意思是,去 node_modules 下的 .bin 目录中找到tsc 命令,并执行它。

执行完该命令,会在项目根目录生成一个 tsconfig.json 文件,进行一些配置:

{
  "compilerOptions": {
    "outDir": "dist", // 输出的目录
    "sourceMap": true, // 开启 sourcemap
    "target": "es2016", // 转译的目标语法
    "module": "esnext", // 模块格式
    "moduleResolution": "node", // 模块解析方式
    "strict": false, // 关闭严格模式,就能使用 any 了
    "resolveJsonModule": true, // 解析 json 模块
    "esModuleInterop": true, // 允许通过 es6 语法引入 commonjs 模块
    "jsx": "preserve", // jsx 不转义
    "lib": ["esnext", "dom"], // 支持的类库 esnext及dom
    "baseUrl": ".",  // 当前目录,即项目根目录作为基础目录
    "paths": { // 路径别名配置
      "@my-vue/*": ["packages/*/src"]  // 当引入 @my-vue/时,去 packages/*/src中找
    },
  }
}

准备两个模块

我们先在 packages 目录下新建两个模块,分别是 reactivity 响应式模块 和 shared 工具库模块。然后编写构建脚本进行第一次的开发调试。

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 作用域,它相当于 npm 包的命名空间,可以使项目结构更加清晰,也能减少包的重名。

编写该模块的入口文件:

// src/index.ts
/**
 * 判断对象
 */
export const isObject = (value) =>{
    return typeof value === 'object' && value !== null
}
/**
 * 判断函数
 */
export const isFunction= (value) =>{
    return typeof value === 'function'
}
/**
 * 判断字符串
 */
export const isString = (value) => {
    return typeof value === 'string'
}
/**
 * 判断数字
 */
export const isNumber =(value)=>{
    return typeof value === 'number'
}
/**
 * 判断数组
 */
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 格式使用响应式模块时,需要给模块指定一个全局变量名字,通过 buildOptions.name 进行指定,将来打包时会作为配置使用。

main 指定的文件支持 commonjs 规范进行导入,也就是说在nodejs 环境中,通过 require 方法导入该模块时,会导入 main 指定的文件。

同理,module 指定的是使用 ES Module 规范导入模块时的入口文件。

编写该模块的入口文件:

// 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 内的 @my-vue/shared 包,安装到 @my-vue/reactivity包中去。

此时,查看 reactivity 包的依赖信息:

"dependencies": {
   "@my-vue/shared": "workspace:^1.0.0"
}

编写构建脚本

在根目录下新建 scripts 目录,存放项目构建的脚本。

新建 dev.js,作为开发阶段的构建脚本。

// scripts/dev.js
// 使用 minimist 解析命令行参数
const args = require('minimist')(process.argv.slice(2))
const path = require('path')
// 使用 esbuild 作为构建工具
const { build } = require('esbuild')
// 需要打包的模块。默认打包 reactivity 模块
const target = args._[0] || 'reactivity'
// 打包的格式。默认为 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 目录下,并以各自的模块规范为后缀名作为区分
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({
  // 打包入口文件,是一个数组或者对象
  entryPoints: [entry],
  // 输入文件路径
  outfile,
  // 将依赖的文件递归的打包到一个文件中,默认不会进行打包
  bundle: true,
  // 开启 sourceMap
  sourcemap: true,
  // 打包文件的输出格式,值有三种:iife、cjs 和 esm
  format: outputFormat,
  // 如果输出格式为 IIFE,需要为其指定一个全局变量名字
  globalName: pgkGlobalName,
  // 默认情况下,esbuild 构建会生成用于浏览器的代码。如果打包的文件是在 node 环境运行,需要将平台设置为node
  platform: format === 'cjs' ? 'node' : 'browser',
  // 监听文件变化,进行重新构建
  watch: {
   onRebuild (error, result) {
       if (error) {
           console.error('build 失败:', error)
       } else {
           console.log('build 成功:', result)
       }
    }
  }
}).then(() => {
  console.log('watching ...')
})

使用该脚本,会使用 esbuildpackages 下的包进行构建,打包的结果放到各个包的 dist 目录下。

在开发阶段,我们默认打包成 IIFE 格式,方便在浏览器中使用 html 文件进行测试。在生产阶段,会分别打包成 CommonJSES ModuleIIFE 的格式。

完成第一次调试

给项目增加一条 scripts 命令:

// package.json
"scripts": {
    "dev": "node scripts/dev.js reactivity -f global"
}

意思是,以 IIFE 的格式,打包 reactivity 模块,打包后的文件可以运行在浏览器中。

在终端中执行:

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 文件进行测试:

// packages/reactivity/test/index.html
<body>
    <div id="app"></div>
    <script src="../dist/reactivity.global.js"></script>
</body>

打开浏览器控制台:

小结

到此,一个基本的 monorepo 开发环境就搭建完毕了。

代码已上传至 Github点击访问

更多关于Vue3 pnpm搭建monorepo的资料请关注我们其它相关文章!

(0)

相关推荐

  • pnpm的安装和使用指南(推荐!)

    目录 什么是pnpm pnpm优势 与 npm 的差别 pnpm使用 全局安装 设置源 使用 移除 更新 设置存储路径 个人使用 在系统上禁止使用脚本解决方法 总结 什么是pnpm pnpm是 Node.js 的替代包管理器.它是 npm 的直接替代品,但速度更快.效率更高. 为什么效率更高?当您安装软件包时,我们会将其保存在您机器上的全局存储中,然后我们会从中创建一个硬链接,而不是进行复制.对于模块的每个版本,磁盘上只保留一个副本.例如,当使用 npm 或 yarn 时,如果您有 100 个使

  • JS前端架构pnpm构建Monorepo方式管理demo

    目录 写在前面 什么是Monorepo?什么是pnpm? 搞一个Monorepo的demo玩玩

  • 使用pnpm包管理器替代npm及yarn的命令示例

    目录 前言 为什么会有 pnpm 不止于此 什么是扁平化的 node_modules pnpm 的 node_modules 如何使用 pnpm 安装 直接安装 通过 npm 安装 通过 HomeBrew 安装( 一种 MacOS 包管理器) 升级 使用 配置 命令 和其他包管理器比较 Monorepo 及 工作空间(Workspace) 什么是 Monorepo Workspace workspace: 协议 结语 前言 今天给大家介绍一种新的包管理器:pnpm,pnpm 由 zkochan

  • 一文带你了解前端包管理工具npm、yarn和pnpm

    目录 为什么需要包管理工具? 版本管理规范 前端主流包管理工具 yarn vs npm vs pnpm 包管理工具安装和版本切换 安装项目依赖 npm .yarn 和 pnpm 常用命令 安全性 lock 文件 性能对比 pnpm 的优势 总结 为什么需要包管理工具? 每种主流编程语言都有包管理工具,比如 java 的 Maven.Gradle,Python 的 pip,nodejs 的 npm.yarn.pnpm 等. 包管理工具的主要作用是管理第三方依赖,也可以看成一个"轮子"工厂

  • pnpm对npm及yarn降维打击详解

    目录 正文 npm2 yarn pnpm 总结 正文 大家最近是不是经常听到 pnpm,我也一样.今天研究了一下它的机制,确实厉害,对 yarn 和 npm 可以说是降维打击. 那具体好在哪里呢? 我们一起来看一下. 我们按照包管理工具的发展历史,从 npm2 开始讲起: npm2 用 node 版本管理工具把 node 版本降到 4,那 npm 版本就是 2.x 了. 然后找个目录,执行下 npm init -y,快速创建个 package.json. 然后执行 npm install exp

  • Vue3中使用pnpm搭建monorepo开发环境

    目录 前言 Pnpm 和 Monorepo 搭建开发环境 创建项目 配置 monorepo 安装依赖 初始化Typescript 准备两个模块 shared reactivity 编写构建脚本 完成第一次调试 小结 前言 Vue3 源码阅读系列,计划从环境搭建开始,将 Vue3 的响应式模块,运行时模块和编译器模块,以及状态库 Pinia.路由库 Vue-Router的核心原理做一个梳理.这大概是一个漫长的过程.祝自己不要烂尾,祝大家有所收获. Pnpm 和 Monorepo Pnpm 是新一代

  • 在VsCode中搭建Go开发环境的配置教程

    现在Go1.14都已经发布好些日子了,之前发的Go环境搭建教程早已过时,只是因为时间问题一直没来得及更新 这次怀着愧疚的心情,在凌晨四点时,将这教程进行一个更新 注意:本教程最大的好处是不需要梯子. 直接在墙内可进行一切操作,文章写给纯小白的,部分Linux常识解释的过多,熟悉的人请略过 Go的安装 安装基本还是之前的老样子,不过现在的安装早已省事不少,不再需要配置环境变量.直接去官网,下载了安装包后直接安装即可 在Go中文网进行Go最新版安装包的下载(或者复制网址浏览器打开https://st

  • 在VSCode中搭建Python开发环境并进行调试

    Get Started Tutorial for Python in Visual Studio Code 一.安装Python Python简介与Python安装 二.VSCode中安装和调试Python 在 VSCode 中搜索扩展 Python,如下图: 安装完成后需要重新加载 VSCode 使插件生效. 1.配置 Python 环境 在 VSCode 中点击状态栏左下角的 Python 图标: 然后选择 Python 解释器,这里博主选择我们刚才安装好的 3.8版本 2.代码检测和格式化

  • 详解ubuntu搭建Java开发环境

    没有用Java写过程序,做为一个Java新手,在写第一个Hello,world程序之前,先在Ubuntu中搭建Java开发环境. 本文结构: 一.JDK安装 二.MyEclipse安装 三.Hello World测试 一.JDK安装 好吧,我选择JDK1.6,是不是有点out了? 1.下载JDK1.6,你可以到官网去下载,下载时请看清自己的系统版本,记得一定要下载相应的版本. 2.将下载的文件放置到/usr/lib/java目录下(需要手动创建java目录),并修改文件的可执行权限,如chmod

  • Ubuntu搭建Java开发环境笔记

    没有用Java写过程序,做为一个Java新手,在写第一个Hello,world程序之前,先在Ubuntu中搭建Java开发环境. 一.JDK安装 好吧,我选择JDK1.6,是不是有点out了? 1.下载JDK1.6,你可以到官网去下载,下载时请看清自己的系统版本,记得一定要下载相应的版本. http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-419409.ht

  • 利用docker-compose搭建AspNetCore开发环境

    使用docker-compose搭建AspNetCore开发环境 1 使用docker-compose搭建开发环境 我们的目标很简单:使用docker-compose把若干个docker容器组合起来就成了. 首先使用Nginx代理所有的Web程序,这样只需要在主机上监听一个端口就可以了,不污染主机.再组合各Web程序.Redis/Memcached.SqlServerOnLinux. 新建一个目录sites,所有和集群相关的都放在这里,目录结构如下所示 sites     nginx      

  • 在Mac OS下搭建LNMP开发环境的步骤详解

    一.概述 大家应该都知道LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构.Linux是一类Unix计算机操作系统的统称,是目前最流行的免费操作系统.代表版本有:debian.centos.ubuntu.fedora.gentoo等.Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器.Mysql是一个小型关系型数据库管理系统.PHP是一种在服务器端执行的嵌入HTML文档的脚本语言.这四种软件均为免费开源软件,组合到一

  • 用Eclipse搭建Android开发环境并创建第一个Android项目(eclipse+android sdk)

    一.搭建Android开发环境 准备工作:下载Eclipse.JDK.Android SDK.ADT插件 1.安装和配置JAVA开发环境:  ①把准备好的Eclipse和JDK安装到本机上(最好安装在全英文路径下),并给JDK配置环境变量,其中JDK的变量值为JDK安装路径的根目录,如我的为:D:\Program Files\Java\jdk1.7.0_02: ②打开命令提示符(cmd),输入java -version命令,显示如下图则说明JAVA环境变量已经配置好了. 2.安装ADT插件: ①

  • 怎样搭建PHP开发环境

    搭建PHP开发环境首先第一步要 下载开发环境 wampserver 下载sublime text 2 sublime使用技巧 1:安装漂亮的编程字体 http://pan.baidu.com/s/1xMex9 下载"程序编写字体 – Yahei Consolas Hybrid", 双击安装 2:解压sublime到你的程序目录,如D:/programe files/ 3:ctrl+b打开浏览器,如果你已经安装apache,nginx等,并假设你的www目录为D:\\www\\ 编辑su

  • windows下apache搭建php开发环境

    本文详细介绍了在Windows2003下使用Apache2.2.21/PHP5.3.5/Mysql5.5.19/phpMyAdmin3.4.9搭建php开发环境. 第一步:下载安装的文件 1. Apache 版本 httpd-2.2.21-win32-x86-no_ssl.msi 2. MySQL 版本 mysql-5.5.19-win32.msi 3. PHP 版本 php-5.3.5-Win32-VC6-x86.zip 4. phpMyadmin 版本 phpMyAdmin-3.4.9-al

随机推荐