Vue3.0中的monorepo管理模式的实现


Posted in Javascript onOctober 14, 2019

前言

前段时间9月21日参加了在成都举办的第五届FEDAY, 印象比较深刻的是白鹭引擎首席架构师@王泽分享的《框架开发中的基础设施搭建》 ,提到了在下一代白鹭引擎中使用到monorepo模式,以用来管理多个模块,协调各个模块之间的依赖更新。
正好在国庆期间10月5日尤大公开了vue3.0已完成的源码,也是采用了monorepo管理模式,看来monorepo确实有其独到的优势,再加上以前在项目中遇到过相关的痛点,所以深入地了解了一下这种模式,本文将基于vue3.0讨论如何通过monorepo模式来管理代码的。

什么是 monorepo?

monorepo是一种将多个package放在一个repo中的代码管理模式,摒弃了传统的多个package多个repo的模式。

目前 Babel, React, Angular, Ember, Meteor, Jest等许多开源项目都使用该种模式来管理代码。

解决的问题

  • 多个repo难以管理,编辑器需要打开多个项目;
  • 某个模块升级,依赖改模块的其他模块需要手动升级,容易疏漏;
  • 公用的npm包重复安装,占据大量硬盘容量,比如打包工具webpack会在每个项目中安装一次;
  • 对新人友好,一句命令即可完成所有模块的依赖安装,且整个项目模块不用到各个仓库去找;

带来的问题

  • 所有package代码集中在一个项目,单个项目体积较大;
  • 所有package代码对所有人可见,无法做权限管理;

如何实现 monorepo?

目前业界最佳实践是采用yarn workspace + lerna 来实现,vue3.0也是采用两者结合的方式来实现。

yarn workspace可以实现在一个项目中实现多个模块的依赖新增和共用,而lerna的功能则更完善,不仅可以管理多个模块,还有清除模块node_modules,发布模块到npm,自动更新模块间版本依赖,并支持全量发布和根据改动单独发布等功能。

yarn官方推荐用yarn来处理依赖安装,用lerna来处理依赖更新和发布问题。

下面开始基于monorepo模式来搭建一个仿vue3.0的项目

1. 全局安装 yarn 和 lerna

$ npm i -g yarn lerna

2. 初始化项目

$ mkdir vue-next && cd vue-next 
$ lerna init

// 此时项目结构
vue-next
|-packages
|-lerna.json
|-package.json

3. 添加yarn workspace配置

// vue-next/package.json
"private": true, // 项目根目录下的private必须设置成true,否则workspace不会被启用
"workspaces": [ // 指定需要管理的模块
  "packages/*"
],

4. 添加模块内容

// 此时项目结构
vue-next
|-packages     // packages下的所有包结构类似,下面仅展示了compiler-core包的目录结构
  |-compiler-core   // 与平台无关的编译器
    |-__tests__     // 测试用例
    |-src        // 源文件
    |-index.js     // 根文件
    |-package.json   // 包配置
  |-compiler-dom   // 与浏览器相关的编译器
  |-reactivity    // 数据响应式系统
  |-runtime-core   // 与平台无关的runtime
  |-runtime-dom    // 与浏览器相关的runtime
  |-runtime-test   // 为了测试的轻量级runtime
  |-server-renderer  // 服务端渲染
  |-shared      // 内部帮助方法
  |-template-explorer // 预览模版转化成render函数的工具
  |-vue        // 用于构建完整的vue版本

|-lerna.json
|-package.json

5. 安装相关依赖

项目中一般只会用到以下3种安装形式

5.1 给根项目安装依赖(一般是公用的开发工具)

// yarn 使用 workspace 模式安装 npm 包时必须加 -W 参数
$ yarn add -W -D rollup typescript jest prettier ...

5.2 给package安装外部npm包

// @vue/compiler-core 是取 vue-next/packages/compiler-core/package.json 的 name 字段
$ yarn workspace @vue/compiler-core add acorn estree-walker source-map 
$ yarn workspace @vue/template-explorer add monaco-editor

5.3 给package安装内部npm包

// @vue/compiler-core 是取 vue-next/packages/compiler-core/package.json 的 name 字段
$ yarn workspace @vue/compiler-dom add @vue/compiler-core@3.0.0-alpha.1 // 一定要指定正确的版本号,不然会到npm查找包
$ yarn workspace @vue/runtime-core add @vue/reactivity@3.0.0-alpha.1 
$ yarn workspace @vue/runtime-dom add @vue/runtime-core@3.0.0-alpha.1 
$ yarn workspace @vue/runtime-test add @vue/runtime-core@3.0.0-alpha.1 
$ yarn workspace vue add @vue/compiler-dom@3.0.0-alpha.1 @vue/runtime-dom@3.0.0-alpha.1 // 可同时安装多个

至此已经完成了项目的基础搭建(打包等工程化内容略过,本文主要介绍monorepo相关),似乎主要是yarn在工作,lerna没有没有派上用场,下面来介绍lerna在哪些地方可以赋能vue3.0。

lerna介绍

A tool for managing JavaScript projects with multiple packages.

一个在js项目中用来管理多个package的工具。

主要是便于开发者更加高效简便地管理package本身,依赖,发布。

1. 初始化项目

// 生成基本项目结构和 lerna 配置
$ lerna init

2. 安装依赖

如果需要重新安装依赖,切记在删除项目根路径的node_modules前进行git提交保存,因为monorepo模式是以软链接的形式来实现内部 package 引用的,直接删除整个node_modules的同时会把所有package的文件删掉,难以恢复!

建议永不进行删除整个node_modules的操作,可以进入node_modules全选后再取消勾选内部package软链接再删除。

// 给所有 package 安装依赖,在对于的目录生成 node_modules,并在 node_modules 中为内部package生成软链接
$ lerna bootstrap
// 默认会使用 npm 安装,但是本项目使用 yarn,可以指定使用 yarn
$ lerna bootstrap --npm-client=yarn   // 或者在 lerna.json 配置 {"npmClient": "yarn"}
// 上述安装方式如果不同package之间安装了同一个npm包,会造成重复安装,增加安装时间和项目体积
// 可以利用 node_modules 向上查找的特性,将所有依赖安装到项目根路径的 node_modules 中,lerna 加上 --hosit即可
$ lerna bootstrap --hosit
// 使用lerna bootstrap --npm-client=yarn --hoist
// 会有冲突,报错--hoist is not supported with --npm-client=yarn, use yarn workspaces instead
------------------------------------------------
$ yarn // 推荐!! 用 yarn workspace 特性替代 lerna bootstrap

3. 清除pacakge中多余的node_modules

// 在 6.2 安装依赖时,部分npm包会给当前的package目录安装 node_modules
// 同时根路径中的 node_modules 也会安装项目
$ lerna clean

4. 查看所有pacakge

// package.json 中设置了 "private": true 的 package 不会展示
$ lerna ls

5. 查看有改动的pacakge

// package.json 中设置了 "private": true 的 package 不会展示
// 在 Independent mode 下只有改动过的 package 才会被发布
// vue3.0 采用的是 Fixed/Locked mode (default),每次都会发布所有 package,package 版本跟随项目的版本走
$ lerna changed

6. 推送到git和发布到npm(重要)

// 根据当前的 lerna 模式(Fixed/Locked mode (default) 或者 Independent mode)来进行整包发布或者有改动的 package 单独发布
// 每次发布会自动更新相关package的版本号,并且会更新引用该package的其他package依赖
$ lerna publish

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript面向对象设计二 构造函数模式
Dec 20 Javascript
JavaScript获得表单target属性的方法
Apr 02 Javascript
jQuery获取上传文件的名称的正则表达式
May 21 Javascript
js实现的简单radio背景颜色选择器代码
Aug 18 Javascript
javascript显示系统当前时间代码
Dec 29 Javascript
AngularJS学习第一篇 AngularJS基础知识
Feb 13 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
Jun 09 Javascript
SelectPage v2.4 发布新增纯下拉列表和关闭分页功能
Sep 07 Javascript
浅谈高大上的微信小程序中渲染html内容—技术分享
Oct 25 Javascript
vue 基于element-ui 分页组件封装的实例代码
Dec 10 Javascript
VUE前后端学习tab写法实例
Aug 06 Javascript
vue实现简单计算商品价格
Sep 14 Javascript
Vue3 源码导读(推荐)
Oct 14 #Javascript
基于JS实现父组件的请求服务过程解析
Oct 14 #Javascript
JavaScript this在函数中的指向及实例详解
Oct 14 #Javascript
vue循环数组改变点击文字的颜色
Oct 14 #Javascript
基于纯JS实现多张图片的懒加载Lazy过程解析
Oct 14 #Javascript
VUE+node(express)实现前后端分离
Oct 13 #Javascript
javascript sort()对数组中的元素进行排序详解
Oct 13 #Javascript
You might like
php使用cookie实现记住登录状态
2015/04/27 PHP
分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件
2015/08/23 PHP
PHP设计模式之观察者模式实例
2016/02/22 PHP
浅谈PHP中静态方法和非静态方法的相互调用
2016/10/04 PHP
解决PHP 7编译安装错误:cannot stat ‘phar.phar’: No such file or directory
2017/02/25 PHP
PHP fclose函数用法总结
2019/02/15 PHP
php生成HTML文件的类方法
2019/10/11 PHP
刷新页面实现方式总结(HTML,ASP,JS)
2008/11/13 Javascript
JavaScript根据CSS的Media Queries来判断浏览设备的方法
2016/05/10 Javascript
基于jQuery实现仿百度首页选项卡切换效果
2016/05/29 Javascript
【经验总结】编写JavaScript代码时应遵循的14条规律
2016/06/20 Javascript
深入理解JavaScript中的尾调用(Tail Call)
2017/02/07 Javascript
vue移动端项目缓存问题实践记录
2018/10/29 Javascript
详解vue中使用vue-quill-editor富文本小结(图片上传)
2019/04/24 Javascript
微信小程序以ssm做后台开发的实现示例
2020/04/08 Javascript
Vue移动端项目实现使用手机预览调试操作
2020/07/18 Javascript
Python字符串匹配算法KMP实例
2015/07/18 Python
python自带的http模块详解
2016/11/06 Python
Python实现判断字符串中包含某个字符的判断函数示例
2018/01/08 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
2018/12/18 Python
Python unittest单元测试框架及断言方法
2020/04/15 Python
css3 media 响应式布局的简单实例
2016/08/03 HTML / CSS
h5调用摄像头的实现方法
2016/06/01 HTML / CSS
英国电子产品购物网站:Tech in the basket
2019/11/08 全球购物
教师岗位职责
2013/11/17 职场文书
写自荐信要注意什么
2013/12/26 职场文书
美德少年事迹材料
2014/01/23 职场文书
中学教师请假制度
2014/02/03 职场文书
幼儿园毕业教师感言
2014/02/21 职场文书
学校四群教育实施方案
2014/06/12 职场文书
中小学生学籍证明
2014/10/25 职场文书
2015年挂职锻炼工作总结
2014/12/12 职场文书
投标邀请书范本
2015/02/02 职场文书
刑事附带民事诉讼答辩状
2015/05/22 职场文书
优质服务心得体会(共4篇)
2016/01/22 职场文书
基于Redis6.2.6版本部署Redis Cluster集群的问题
2022/04/01 Redis