前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue3源码学习:搭建monorepo开发环境(一)

Vue3源码学习:搭建monorepo开发环境(一)

作者头像
微芒不朽
发布2022-12-27 21:01:53
1.2K0
发布2022-12-27 21:01:53
举报

初始化

pnpm init
mkdir packages

配置 monorepo

在根目录新建 pnpm-workspace.yaml 文件; 意思是,将 packages 目录下所有的目录都作为单独的包进行管理。

packages:
  - 'packages/*'

安装依赖

# 源码采用 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:作为开发依赖安装 -w:monorepo 环境默认会认为应该将依赖安装到具体的 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中找
    },
  }
}

准备两个模块

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
pnpm init

在packages 下新建 reactivity 目录,并初始化:

{
  "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 ...')
})

使用该脚本,会使用 esbuild 对 packages 下的包进行构建,打包的结果放到各个包的 dist 目录下。在开发阶段,我们默认打包成 IIFE 格式,方便在浏览器中使用 html 文件进行测试。在生产阶段,会分别打包成 CommonJS,ES Module 和 IIFE 的格式。

完成第一次调试

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

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

在终端中执行:

pnpm dev

编写一个 html 文件进行测试:

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 叫我詹躲躲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 初始化
    • 配置 monorepo
      • 安装依赖
        • 初始化Typescript
          • 准备两个模块
            • shared 在 packages 下新建 shared 目录,并初始化:
            • reactivity
        • 编写这个包的入口文件
          • 编写构建脚本
            • 完成第一次调试
            • 在终端中执行:
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档