前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【干货】打造自己的web前端工作流(一)--- 交互的命令行工具模板篇

【干货】打造自己的web前端工作流(一)--- 交互的命令行工具模板篇

作者头像
腾讯NEXT学位
发布2018-12-04 13:54:26
2.6K0
发布2018-12-04 13:54:26
举报
文章被收录于专栏:腾讯NEXT学位腾讯NEXT学位

前言

web前端领域技术日新月异,技术栈也不断丰富,在日常工作中涉及到的内容也不断增加,一个前端项目从开发到发布涉及的步骤也很多,很多重复工作内容,因此我们需要开发一些工作来减少这些工作量---工作流。工作流现在也存在很多解决方案,大都是采用GUI方式+自定义脚本方式,相比GUI的方式很多人更爱命令行的的方式,轻量化,可以方便自定义开发,更好适应现有业务的情况。

本文章目的,基于一个命令行模板工具,循序渐进的告诉读者,开发一个命令行工具,会用到哪些现有的轮子,如何让你的工具变得丰满起来。同时我也会简要介绍这些轮子是用来做什么的,以及在实际操作中具体的基本用法。

关于web前端工作流 我计划分为 采用三个篇文章来介绍其他两个主题:

  1. 构建篇
  2. 发布篇

为何要做一个工作流工具?

我们在做这件事的时候 主要是基于以下三个痛点:

项目初始化

通常来说一个团队涉及到的项目都会很多,以我们 企鹅辅导 来说,前端项目非常之多, 并且我们团队也有自己的一些开发规范和要求,对于新开发一个项目,同样需要遵循规范和约束,以前我们的做法大都是复制现有项目,然后删减其中我们觉得不需要的内容,然后基于此继续开发。

那么问题来了,复制的项目通常有太多新项目用不上的内容,并且我们很难区分哪些是需要删除的、保留的。 这时候就出现了我们的模板工具,通过开发一个模板工具,通过交互是的命令行初始化项目。 但是如果仅仅做一个命令行工具,就为了初始化项目(低频操作),是很难用起来的。

构建工具

现在的前端项目几乎都有构建工具,涉及到构建,又需要做构建优化,这包括:构建本身速度优化,构建的静态内容优化等等,其次构建本身的升级也需要跟上时代步伐,通常的做法都是每个项目独立维护一份构建配置,这就造成优化无法快速应用到项目中,需要在每个项目重复这些工作,如何让这些能力通用,这也可以纳入工作流工具中。

项目发布

以我们 企鹅辅导 产品来说,发布一个需求大致需要经历如下过程:

  1. 构建系统构建
  2. 测试环境部署
  3. 创建merge request
  4. 申请发布单
  5. 发布群发布知会
  6. 预发布环境部署与验证
  7. 正式发布与验证(发布静态资源、nodejs服务、APP离线包)
  8. 代码合并主干、关闭发布单
  9. 完成发布

显而易见,发布一个需求涉及到的步骤非常多,并且每个不同的步骤都可能是在不同系统中完成,对我们开发者也增加了很多不必要的工作。从实际情况来看,任何一个修改,就算是小到一个wording的修改,都需要经历这些步骤,这就造成任何一个发布没有数小时,基本没法完成。

基于这些原因,才有了团队工具流开发的必要性,将一个项目从初始化到发布上线的所有过程都集中到工具中,简化工作内容,保证发布流程。对于不同的团队情况不同,部分内容也不一定适用,具体是否有必要依赖具体业务情况。

命令行工具

接下来我将演示,如何看法自己的模板项目命令行工具

创建命令行项目,基本目录如下

代码语言:javascript
复制
imt/
├── bin/
│   └── cli.js
├── LICENSE
├── package.json
├── README.md
├── src/
│   ├── commands/
│   │   ├── create.js
│   ├── const.js
│   ├── index.js
│   ├── plugins/
│   │   └── index.js
│   ├── Service.js
│   ├── utils/
│   │   ├── index.js
│   │   ├── logger.js
│   │   ├── spinner.js

处理命令行参数,解析参数并执行不同操作, 下面这个脚本就是一个nodejs脚本,其中第一行是为了告诉bash 使用 node 执行脚本

./bin/cli.js

代码语言:javascript
复制
#!/usr/bin/env nodeconst program = require('commander'); // 一个命令行参数解析工具const pkg = require('../package.json'); // 这里是为了自动设置代理,有的仓库在外网,需要设置代理才能够下载program  .version(pkg.version)
  .option('-P, --proxy <proxy>', '设置代理')
  .option('-x, --defaultProxy', '使用内网代理', false);// 定义一个action,匹配后会执行指定的函数program  .command('create [template] [dir]')
  .description('初始化项目')
  .action((template, dir, options) => {
    require('../src/commands/create')(template, dir, options);
  });
  program.parse(process.argv); // 解析命令行参数if (!process.argv.slice(2).length) { // 没有参数直接输出帮助信息
  program.outputHelp();} else if (program.proxy) { // 将proxy设置到环境变量
  process.env.proxy = program.proxy;}

配置可执行命令 让我们工具连接到全局并且可直接执行, 配置package.json

代码语言:javascript
复制
 "bin": {
    "imt": "./bin/cli.js"
  },

执行 npm link ,将命令软连接到全局命令搜索目录下, 执行完毕后,直接在terminal中输入imt 然后回车键,会看到如下信息帮助信息:

代码语言:javascript
复制
Usage: imt [options] [command]

Options:
  -V, --version             output the version number
  -P, --proxy <proxy>       设置代理
  -x, --defaultProxy        使用内网代理
  -h, --help                output usage information

Commands:
  create [template] [dir]   初始化项目

模板代码下载和初始化

现在你的的命令行工具已经安装成功,接下来就是根据命令行输入的参数,执行函数。我们看如何实现模板功能,模板通常我们不止一种,因此具体模板我们不会放在这个工具中,是通过imt create gitName/projectName 中的参数gitName/projectName获取具体模板所在仓库位置。

共需要做三件事情:

  1. 提示用户选择默认模板
  2. 下载模板仓库
  3. 初始化模板仓库并执行命令。

当用户没有输入任何模板地址时,你需要提供给默认的选择,交互式选择项目模板

代码语言:javascript
复制
const inquirer = require('inquirer');// 内置模板const defaultTemplates = [
    { 
     name: 'React应用', 
     value: 'hxfdarling/imt-react-template' 
    },
    { 
     name: '微信小程序', 
     value: 'hxfdarling/imt-mp-template' 
    }];// ... 省略其他代码
  async getTemplate() {
    let { template } = this;
    if (!template) {
      ({ template } = await inquirer.prompt([
        {
          name: 'template',
          message: '选择内置模板',
          type: 'list',
          choices: defaultTemplates,
        },
      ]));
    }
    if (/\.zip$/.test(template)) {
      template = `direct:${template}`;
    }
    this.template = template;
  }
 // ... 省略其他代码 

我们通过inquirer模块 快速的生成交互式的选择界面,该工具支持多种不通过的交互式输入方式。这里使用到了他的list列表选择能力,具体效果如下:

得到目标模板地址后,需要下载模板的仓库代码:

代码语言:javascript
复制
const download = require('download-git-repo');const chalk = require('chalk');const ora = require('ora');//... 省去部分代码
  downloadTemplate() {
    const { template } = this; 
    const spinner = ora('模板下载中').start();
    return new Promise(resolve => {
      download(template, this.templateDir, {}, error => {
        if (error) {
          console.log(chalk.red(`下载模板失败:${template},请确认网络是否正常`));
          console.error(error);
          process.exit(1);
        } else {
          spinner.stop();
          console.log(chalk.green('模板下载成功'));
          resolve();
        }
      });
    });
  }

这里下载仓库代码,我使用了download-git-repo快速实现地址解析和下载,下载过程我们需要美化一下,通过ora工具支持添加一个loading图标,表示正在处理。具体效果如下:

接下来就是初始化我们的模板项目并执行模板项目中的代码,以初始化项目,具体代码如下:

代码语言:javascript
复制
//... 省略其他代码
  await this.confirmDir(); // 提示用户当前目录不为空(如果不为空)
  await this.getTemplate();// 获取仓库地址
  await this.downloadTemplate(); // 下载模板
  console.log(chalk.yellow('初始化模板'));
  shell.cd(templateDir); // 切换命令行执行目录到模板目录
 // 由于内网,检测用户是否安装了tnpm,安装了就使用tnpm命令
  let npmCmd = 'npm';
  if (!shell.exec('tnpm -v', { silent: true }).stderr) {
    npmCmd = 'tnpm';
  }
  // 执行模板项目的node_modules初始化
  shell.exec(`${npmCmd} i`, { silent: true });
  const main = require(`${templateDir}/package.json`).main || 'index.js';
  // 运行模板项目的代码
  spawnSync('node', [`.template/${main}`], {
    cwd: this.projectDir,
    // 由于是在child_process中执行,可能有交互命令,需要继承父进程的标准输入,否则子进程无法获取到键盘输入
    stdio: 'inherit',
  });
  // 清理模板项目目录
  fs.emptyDirSync(templateDir);
  fs.removeSync(templateDir);//... 省略其他代码

上面这段代码,也用到了一个高度封装的轮子shelljs。

至此我们一个完整的命令行工具搞定

具体如何初始化模板代码,我们继续看模板项目中的实现:

模板项目

通常来说项目的大多数技术栈都统一,但是具体到不同业务中,实际用到的框架、库可能也不尽相同。因此模板项目并非一个简单的复制仓库代码搞定,我们需要让模板在初始化时,可以选择功能。

这里以React应用模板为例,具体代码地址imt-react-template,这个模板代码支持初始化多页面应用和单页面引用,是否使用rem,是否初始化index.html内容等可选项。

项目目录

代码语言:javascript
复制
imt-react-template/
├── bin/
│   └── cli.js
├── index.js // 模板执行脚本
├── package.json
├── README.md
└── templates/
    ├── common/ 两种不同模板共享文件
    ├── multi/ 多页面模板
    └── single/ 单页面模板

我们看一下具体模板如何初始化, 这里诺列具体代码了,说一下具体步骤:

  1. 弹出选择框选择初始化类型:单页面应用,多页面应用
  2. 是否初始化REM代码,用于H5开发
  3. 单页面应用是否初始化webapp
  4. 等等,其他选项
  5. 接下来就是复制文件

复制文件需要用到一个工具yeoman-generator,该工具,提供模板变量和逻辑的能力,在复制文件时,通过传入对象,中的所有属性可以直接在模板代码的全局内访问,具体使用如下:

复制模板,第三个参数是模板执行的上下文对象

代码语言:javascript
复制
this.fs.copyTpl(
    this.templatePath('./common/.imtrc.js'),
    this.destinationPath('.imtrc.js'), 
    {
        app:"single",
        webapp:true
    });

.imtrc.js文件模板代码如下:

代码语言:javascript
复制
module.exports = {
  // 应用类型
  mode:"<%= app %>",
  <% if (webapp) { %>
  // 支持webapp插件
  webappConfig:{
  },
  <% } %>
};

这里通过<%= app %> 实现访问this.props中的属性,直接打印到当前位置,通过<% if (webapp) { %> xxx <% } %>实现条件判断,是否需要输出到最终文件,其他更多的语法参见 yeoman文档

最终效果我们看一张动图看完整效果

当然这只是一个初级的模板初始化工具,我们还可以再丰富它,例如:是否使用某些库、支持添加空页面模板等。

工具用到的模块介绍

介绍一些用于开发命令行工具会用到的工具,下面这些工具都可直接在github中搜索,都是开源项目。

工具名称

介绍

lint-staged

可以用于实现提交前代码格式化,eslint等处理

husky

git钩子,例如提交前的一些脚本处理,提交消息检测等

commitlint

用于git仓库提交的message规范检测,统一团队项目提交规范,这里有一个简单的库,能够快速接入这个能力到项目中commitlint-config-imt

chalk

命令行颜色工具,命令行工具输入日志时,带有颜色

commander

命令行工具,必备工具,简化参数解析和帮助信息输出

inquirer

交互式命令行工具,让你可以再命令行中实现可交互输入

semver

版本工具,可以用于提示用户你的命令行支持版本的nodejs

yeoman-generator

快速项目初始化的模板工具,功能相当强大,具体能力参考官方文档

debug

很好用的日志工具,可以给不同日志设置标题,能够快速调整日志打印策略

shelljs

shell执行工具,非常方便的在js代码中执行shell命令,甚至直接在js代码中使用shell命令!

tapable

webpack插件基础库,提供了多种插件执行的模式,并行、异步、同步等等,如果需要让你的工具支持插件机制,使用这个库将这个事儿变得非常方便

ora

简单来说,提供命令行中loading图标,和步骤标示能力,具体效果看git仓库

当然,这些工具只是冰山一角。

--------------------------------------------------------------------------

原文作者:腾讯高级工程师刘华

  来源:腾讯内部KM论坛

你也想成为腾讯工程师?

也想年终奖人手一部 Iphone X?

那就快加入NEXT学院吧!

NEXT学院课程「Web前端工程师NEXT学位完整课程」火热招生中!

  感兴趣的同学赶紧点击原文了解详情吧~

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

本文分享自 腾讯NEXT学院 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 为何要做一个工作流工具?
  • 命令行工具
    • 模板项目
    • 工具用到的模块介绍
    相关产品与服务
    命令行工具
    腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档