【源码】Vue-cli2 源码阅读与改造

前言

最近组里让我写个脚手架,用脚手架来生成模板项目。比如每个公司都会有很多后台管理系统,用于给产品、客服等内部人员使用。对于一个新的管理后台,如果每次都从头开始写,必然浪费太多时间。或者copy以前的项目进行改造又不太优雅。如果写一个像vue-cli这样的脚手架进行命令行创建那再好不过了。

一开始我感觉会有点难,像vue-cli这样的项目,经历了很多次的提交,代码量不少。我之前的想象是,vue-cli中进行许多的目录、文件的读写与创建,用户的交互,各种npm包的下载,脚本的运行,最终才生成了项目。想想就有点乱。

但看过后发现变简单多了。总的来说,vue-cli分两部分,一部分为vue-cli,vue-cli只是一个拉取仓库的作用,一个下载的作用。另一部分就是真正的模板了。这个模板也是github上的项目,按照一定的规范进行编写。

正文

这次看的是 vue-cli2,vue-cli3进行了很多封装,既然只需要其拉取仓库的作用,那么vue-cli2就够了。其目录结构如图:

image.png

网上也有许多vue-cli2的源码解析,这里只是简单说明。
首先从package.json开始,查看一些命令。也许用了vue-cli3,很多人已经忘记vue-cli2的命令了。在package.json中有:

  "bin": {
    "vue": "bin/vue",
    "vue-init": "bin/vue-init",
    "vue-list": "bin/vue-list"
  }

"bin" 字段的作用是能让我们在命令窗口全局输入命令执行。

下面将以 vue init webpack my-project 为例。说明输入命令后发生了什么。

输入命令后,此时将运行bin文件夹下的vue-init脚本:


image.png

vue-init主要看这几个部分:

第一部分

首先第一句是必须的,这样才能在全局下运行命令。

#!/usr/bin/env node
第二部分

首先要知道的是,模板可以从各种地方下载,而不仅仅是官方提供的那几个(虽然许多时候用不到,但其的确提供了)。这一部分主要是获取命令中的参数,从而得出模板、项目名等信息。进而确定如何去获取模板。

let template = program.args[0]  // 获取模板名称: webpack
const hasSlash = template.indexOf('/') > -1   // 模板名称是否有斜杠 (这里为false)
const rawName = program.args[1]  // 项目名称: my-project
const inPlace = !rawName || rawName === '.'
const name = inPlace ? path.relative('../', process.cwd()) : rawName
const to = path.resolve(rawName || '.')
const clone = program.clone || false // 是否采用 clone 模式,默认 http 方式下载模版

const tmp = path.join(home, '.vue-templates', template.replace(/[\/:]/g, '-')) // 将模板下载到该路径,并非直接下载到创建项目的路径(缓存)
if (program.offline) { // 是否使用离线模版(使用上一行代码中缓存下的)
  console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
  template = tmp
}
第三部分
function run () {
  // check if template is local
  if (isLocalPath(template)) { // 是否使用本地模版 - 判断模版路径是否是本地
    const templatePath = getTemplatePath(template)
    if (exists(templatePath)) {
      generate(name, templatePath, to, err => {
        if (err) logger.fatal(err)
        console.log()
        logger.success('Generated "%s".', name)
      })
    } else {
      logger.fatal('Local template "%s" not found.', template)
    }
  } else {
    checkVersion(() => {
      if (!hasSlash) {
        // use official templates
        const officialTemplate = 'vuejs-templates/' + template
        if (template.indexOf('#') !== -1) {
          downloadAndGenerate(officialTemplate)
        } else {
          if (template.indexOf('-2.0') !== -1) {
            warnings.v2SuffixTemplatesDeprecated(template, inPlace ? '' : name)
            return
          }

          // warnings.v2BranchIsNowDefault(template, inPlace ? '' : name)
          downloadAndGenerate(officialTemplate)
        }
      } else {
        downloadAndGenerate(template)
      }
    })
  }
}

确定好命令中各种参数后,然后就根据参数进行下载了。首先isLocalPath(template) 判断参数是否是一个路径,如 ../../webpack,而我们输入是参数为 webpack。则走到 else 逻辑。else 中首先进行版本检查: checkVersion,这就是如果有新版本的vue-cli了,将在命令行中询问我们是否下载。

由于没有斜杠(hasSlash),且不带版本号,最终走到了 downloadAndGenerate(officialTemplate) 去下载官方模板,其中经过拼接后officialTemplate的值为: vuejs-templates/webpack。至于 downloadAndGenerate(template)是自定义模板,后面再讲。

第四部分
function downloadAndGenerate (template) {
  const spinner = ora('downloading template')
  spinner.start()
  // Remove if local template exists
  if (exists(tmp)) rm(tmp)
  download(template, tmp, { clone }, err => {
    spinner.stop()
    if (err) logger.fatal('Failed to download repo ' + template + ': ' + err.message.trim())
    generate(name, tmp, to, err => {
      if (err) logger.fatal(err)
      console.log()
      logger.success('Generated "%s".', name)
    })
  })
}

downloadAndGenerate 方法没什么内容,主要看其中的 download 方法。download 是通过一个npm包 download-git-repo
引入的 :const download = require('download-git-repo')。download 方法也没有什么内容,主要是根据url去下载(http)或者 clone项目。

那么如何根据传入的 officialTemplate: vuejs-templates/webpack 就能得出URL呢,主要在于 normalize:

image.png

最终得出的URL为:https://github.com/vuejs-templates/webpack。就这样,我们终于找到官方模板的位置了,总的来说vue-cli只起了一个下载的作用,而真正的模板隐藏于此!这也就是命令 vue init webpack my-projectwebpack 参数的作用。在vuejs-templates 这个用户下,我们还可以看到其他几个模板,如当运行 vue init pwa my-project 时将下载pwa模板。

image.png

还记得第三部分中说到的 downloadAndGenerate(template)逻辑吗?那是有斜杆/ 时将去下载自定义模板。
也就是说,如果运行命令 vue init my-templates/webpack my-project,它将到 my-templates 这个用户下寻找自定义的模板进行下载!

这个自定义模板编写是具有一定规范,具体可以看 https://github.com/vuejs/vue-cli/tree/v2#custom-templates

第五部分

在执行完 download 后,这是将模板下载完成而已。这个模板不是直接就用的,还记得我们创建项目时在命令窗口进行的询问与选择吗:

项目的作者?项目的描述?是否选择eslint?eslint的版本?等等。那么如何根据模板来生成自己最终的项目呢?

可以看到后面再调用 generate 进行项目的生成:

generate(name, tmp, to, err => {
      if (err) logger.fatal(err)
      console.log()
      logger.success('Generated "%s".', name)
    })

generate中主要使用了 Metalsmith 这个静态网站生成器的插件,这个插件配合https://github.com/vuejs/vue-cli/tree/v2#custom-templates 规范进行生成项目。具体怎么使用看其文档即可了。

限于篇幅,vue-cli的源码阅读就到这了,网上也有很多分析。下面说明如何进行简单的改造就可以使vue-cli为自己所用。也就是如何从公司gitlab进行拉取模板。

vue-cli已经提供了不同平台下载模板的方式:github、gitlab、bitbucket。默认是从github下载模板,如从gitlab下载则运行:

vue init gitlab:username/repo my-project

而我们是要从公司的gitlab上拉取,公司gitlab的域名是这样组成的gitlab.xxxx.com。需要我们进行改造。在第三部分的run方法中,将 officialTemplate 的拼接改为:

const officialTemplate = `gitlab:gitlab.xxxx.com:username/${template}`

那么,当你运行vue init webpack my-project时,将默认从你公司的gitlab上拉取模板。那接下来就是如何编写模板的事了,按照https://github.com/vuejs/vue-cli/tree/v2#custom-templates 去做吧。

结束语

9月30日:文章是之前写的,简书之前不给发。祝大家国庆快乐吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,306评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,657评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,928评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,688评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,105评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,024评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,159评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,937评论 0 212
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,689评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,851评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,325评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,651评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,364评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,192评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,985评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,154评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,955评论 2 279

推荐阅读更多精彩内容