mfc多文档重载onfilenew_Vue多组件仓库搭建

7a549a6d047c4b913f33e0f34e345ac8.png

关注【搜狐技术产品】公众号,第一时间获取技术干货

导读

组件化是目前前端最为流行的代码复用方式,无论是React还是Vue也都是以组件为单元进行开发的。 在我们的工作过程中,随着业务的增长,积累下来的组件也越来越多。这时候,如何高效的组织我们的组件就成了一个需要解决的问题。

代码存放

在项目创建之前,我们比较了现在主流的两种代码仓库存储方式——单代码仓库(mono-repo)和多代码仓库(multi-repo)。

多代码仓库中,我们以前端打包工具rollup为例,完整的项目应该是包括rollup主仓库,以及rollup-plugin-aliasrollup-plugin-bublerollup-starter-lib等很多相关仓库。 按多代码仓库方式组织代码会有下面一些问题:

  1. issue管理混乱。用户在使用过程中遇到问题,常常会直接在主仓库下提问,而这些问题很多是属于其他模块的,应该放在其他仓库中。
  2. 版本更新麻烦。被依赖的仓库代码变更之后,需要同步到所有依赖方的代码仓库中。
  3. 版本日志维护麻烦。和版本更新后修改依赖一样,版本日志也需要更新到每个代码仓库中。

单代码仓库中,以JavaScript编译工具babel为例,项目中在packages文件夹下有babel-clibabel-corebabel-helper-*babel-plugin-*等模块。 按单代码仓库方式组织代码面临的问题包括:

  1. 代码仓库会很大,可能会带来版本控制的问题。
  2. 对构建工具要求较高,需要构建工具可以构建packages下的所有模块。

比较过后,我们选择了按单代码仓库的方式来存放我们的组件。主要有两方面的原因,一方面是这些组件存在依赖的情况,并且可能变动会比较频繁,另一方面是目前我们统一了开发库为vue,构建工具本身就是统一的。

项目创建

项目的创建主要使用了@vue/cli,它是官方提供的基于Vue.js进行快速开发的完整的脚手架、工具链系统。

创建时,cli的交互式配置省去了对webpack以及各种loader的繁杂编写。 开发过程中,cli基于webpack-dev-server提供了支持模块热重载的开发服务器。 完成后,cli的构建功能提供了“应用、库、Web组件”等多种构建目标选择。 这些可以很好的满足我们组件库的需求。

组件的存放

在使用@vue/cli搭建起项目脚手架后,参照babelElement等单代码仓库,在项目根目录下创建packages文件夹。该文件夹下每个组件对应一个目录,在目录下存放package.jsonREADME.mdCHANGELOG.md等文件,以及srcdistnode_module等目录。

├── packages
│   ├── error-page-vue
│   │   └── ...
│   ├── feed
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── dist
│   │   ├── node_modules
│   │   ├── package-lock.json
│   │   ├── package.json
│   │   └── src
│   ├── feed-edit
│   │   └──  ...
│   └──  ...
└──  ...

因为该项目中存放的都是Vue组件,我们可以把每个组件都需要的Vue相关的依赖以及构建相关的依赖(比如vuebabelsass等依赖)统一放在项目的根目录下,组件目录下只包含了组件业务的依赖。这样,我们的组件依赖就会纯粹很多。

演示/调试组件

组件包括了对功能和样式的复用,所以在组件开发/调试过程中,需要对组件进行展示。 我们在项目根目录下创建了examples文件夹,文件夹下对应每个组件创建vue文件作为示例。 为了更方便的查看对应的示例,我们维护了一个配置文件components.js,该配置文件用于项目中示例路由以及导航列表的创建等。

// routes.js
​
// Navigation.vue是导航组件
import Navigation from "./Navigation";
// components.js是维护组件的数组
import components from "./components";
​
let routes = components.map(component => ({
  path: `/${component.name}`,
  component: () => import(`../examples/${component.name}`)
}));
​
routes.unshift({
  path: "",
  component: Navigation
});
​
export default routes;

创建/构建组件

根据上面创建的目录结构,在创建新的组件时,我们需要修改components.js配置文件,在examplespackages目录下分别创建对应的组件。 在完成组件的开发后,需要构建并发布组件,如果同时修改了多个组件(比如有依赖的两个组件),最好是可以同时构建多个组件并发布。

我们可以通过脚本来实现诸如此类便捷化的需求,在项目根目录下创建scripts文件夹用来存放这些脚本。目前我们项目中包括创建组件脚本new.js、构建脚本build.js、删除脚本delete.js等。

创建脚本

根据需求,创建脚本就可以分为三部分。

// scripts/new.js部分
​
...
​
const updateConfig = function(path, components) {
    // 更新 components.js 文件
    ...
}
​
const createPackages = function(componentName) {
    // 在 packages 目录下创建组件项目目录
    // 包括创建package.json、src/index.js、README.md、CHANGELOG.md等文件,及初始化文件内容
    ...
}
​
const createExample = function(componentName) {
    // 在 example 目录下创建组件示例
    // 包括创建index.vue,及初始化文件内容
    ...
}
​
...

d0b10ea21fe46f5bacb085f0ac1055ca.png
执行创建脚本

创建脚本除了创建目录和文件之外,我们还可以做一些文件内容初始化的工作,比如README.md写入组件名、使用说明、开发说明等标题,examples/<component name>/index.vue中写入template、对组件的引入等等。

格式化初始内容

在对文件写入时,我们遇到的一个问题就是文件初始内容格式与项目代码规范不同。

起初我们的解决方案是使用 ES6 的模板字符串,按照格式规范小心翼翼的写入初始化的模板,多余的空格、回车都会导致格式上的问题,不利于后期修改维护。 引入模板文件替代模板字符串,会使模板编写相对容易一些,但是因为模板文件中会有一些非规范的标记,导致难以对其格式化,这依然是治标不治本的方案。

更好的方案是在脚本中引入eslinteslint除了可以在命令行中运行,同样也可以在脚本中执行,eslint官网提供在NodeJS中运行的API文档。 这样,我们就可以通过项目中的配置文件(比如.eslintrc.js)格式化文件的初始内容,即使以后我们改变了项目的代码规范,也不需要再变动创建文件的模板。

// 根据配置初始化cli
const CLIEngine = eslint.CLIEngine;
// 需要注意的是比命令行中配置多添加`fix: true`才能格式化文件
const cli = new CLIEngine({
    ...require("../.eslintrc.js"),
    fix: true
});
​
// 执行eslint的fix
CLIEngine.outputFixes(
    cli.executeOnFiles([filePath])
);

构建脚本

我们期望使用@vue/cli进行组件库中组件的构建,毕竟官方对自身更为了解。在官方文档中,只提到了在 shell 中执行 cli 提供构建功能,这样一次只能构建一个组件。

我们可以通过脚本多次通过child_process.exec执行 shell 命令,来实现同时构建多个组件,这种方式在终端输出中会有文本颜色丢失现象。

8d8abf23e354eabd4c2282e9c07fb50d.png

2884fe8025826129842199b232cd1741.png
颜色丢失

另一种方式就像上面使用eslint那样,我们可以在脚本中执行 shell 命令对应的API,麻烦的是@vue/cli文档中并没有提供 Node API 文档。通过阅读分析源码,我们在@vue/cli下发现@vue/cli-service中的run方法可以执行build

// scripts/build.js
​
...
// 通过cli执行build命令
const vueCliService = require("@vue/cli-service");
​
const buildService = new vueCliService();
​
async function build() {
  for (let i = 0, len = components.length; i < len; i++) {
    const name = components[i].name
    // 传入对应各项参数
    await buildService.run(
      "build",
      {
        _: ["build", `${root}/packages/${name}/src/index.js`],
        target: "lib",
        name: `hy-${name}`,
        dest: `${root}/packages/${name}/dist`,
        // 生成格式: umd格式会同时成功demo.html commonjs,umd,umd-min
        formats: "commonjs,umd-min"
      }
    )
  }
}
​
...

使用 Node API 这种方式打包带来的另一个好处是我们可以实现更精细的控制。 在我们的组件中,有一些是需要不同的打包配置的。在源码中,我们发现@vue/cli-service在实例化的时候,可以通过传入不同的路径作为参数,实现使用不同的配置执行build。在对每个组件执行build时,会先检测模块下是否存在配置文件,如果存在则用这个配置文件实例化@vue/cli-service,不存在就用根目录下配置文件。

...
​
const packagePath = `${root}/packages/${pkgName}`;
const packageVueConfigJSPath = `${packagePath}/vue.config.js`;
​
// cli工作目录,支持package内自定义配置,默认使用根目录下vue.config.js
let cliWorkDict = fs.existsSync(packageVueConfigJSPath)
  ? packagePath
  : root;
console.log(">> vue.config.js", packageVueConfigJSPath);
​
let pkgCliService = new vueCliService(cliWorkDict);
​
...

版本管理

在完成创建、编写、构建过程之后,最后的步骤就是发布组件。

组件的发布版本号在自身目录下的package.json中,组件越来越多,组件之间可能存在相互依赖,这些导致管理维护版本号会是很痛苦的事情,而lerna可以很好的解决这个问题。

Lerna

lerna是一个单代码仓库的管理工具,提供了维护单代码仓库中多个package的创建、管理、发布等功能。 在创建和开发过程中,lerna可以帮助我们解决依赖安装问题,省去我们进入每个组件目录下执行npm install的麻烦。 发布时,lerna主要帮助我们维护版本以及执行发布。lerna通过检测哪些组件中代码有变化,让我们选择更新主版本号、次版本号、还是修订版本号,帮助我们更新该组件版本号。之后,会帮我们提交到远程仓库以及npm仓库中。

0a4332af7c21320aeee280394126b6c8.png
版本选择

Changelog规范

更新日志可以帮助用户和开发人员简单明确的知道项目中不同版本之间的变化。 在我们的项目中,选择了人工以时间倒序的方式来维护CHANGELOG.md文件。内容主要包括版本号和变动列表,其中变动列表包括新增、变更、移除、修复等。新增用Added、变更用Changed、移除用Removed、修复用Fixed。

<!-- 下面是一次版本变动的片段 -->
...
​
## v0.5.9
#### Added
- 增加70-92号表情
#### Fixed
- 没有对应表情时,可以显示对应文本
​
...

总结

至此,我们的Vue多组件仓库基本上搭建完成,最后做个总结。

  • 通过@vue/cli初始化了项目脚手架
  • 创建了用于调试和演示的examples文件夹、用于存放各组件源码的packages文件夹
  • 创建了存放创建/构建的脚本,以及存放脚本的scripts文件夹
  • 使用lerna进行版本管理

另外,附下我们目前的开发流程:

  1. 执行npm run pkg-new <package name>创建组件;
  2. packages/<package name>下编写组件;
  3. examples/<packages name>/index.vue中添加测试数据,运行调试;
  4. 执行npm run pkg-build [package name]构建组件;
  5. 执行npm run pkg-publish,选择版本号,发布组件。

最后,狐友前端组在类库/组件维护和NPM私服搭建上做了很多有趣的探索,如果大家有兴趣可以留言或与我们联系(spcfe@sohu-inc.com)。


分布式追踪系统概述及主流开源系统对比​mp.weixin.qq.com
e9fc59f79d80a666c173d1eecbdc7906.png
不了解GIF的加载原理?看我就够了!​mp.weixin.qq.com
21613be247222802a5389877f5f22be3.png
安卓系统权限,你真的了解吗?​mp.weixin.qq.com
c44da0210e6073bee3e6f644b96acf96.png

加入搜狐技术作者天团,千元稿费等你来!

获取更多资讯请关注微信公众号【搜狐技术产品】,微信后台联系搜狐技术产品小助手。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值