vue-cli@2.x源码阅读

前言

vue-cli是vue项目的脚手架工具,使用vue-cli可以快速创建项目。vue-cli的2.x版本和3.x版本在使用上有很大的不同,本篇文章旨在通过vue-cli源码学习了解脚手架的构成实现,所以选择相对简单的vue-cli的2.x版本(具体版本是2.9.6)。
在2.x版本最常用的命令是vue和vue init,下面的流程梳理主要就是针对这两个命令来梳理的。

vue命令的逻辑

在vue-cli项目的package.json文件中定义了命令对应的文件位置,具体如下:

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

vue命令对应的bin/vue文件的源码比较简单,这里就直接贴出来具体如下:

#!/usr/bin/env node

const program = require('commander')

program
  .version(require('../package').version)
  .usage('<command> [options]')
  .command('init', 'generate a new project from a template')
  .command('list', 'list available official templates')
  .command('build', 'prototype a new project')
  .command('create', '(for v3 warning only)')

program.parse(process.argv)

commander是Node.js命令行的解决方案(即Node.js下实现自定义命令),点此查看commander中文文档

这里简单介绍下常用相关方法:

  • version:定义版本
  • usage:帮助信息输出
  • command:使用独立的可执行文件作为子命令
  • option:定义选项
  • parse:解析参数用于匹配对应选项或命令

当在控制台中输入vue命令,会输出相关的命令和帮助信息,如下:
在这里插入图片描述
这里定义了子命令init等, Commander 将会尝试在vue脚本的目录中搜索program-command形式的可执行文件例如init会查找vue-init等。

vue init命令的逻辑

vue-cli脚手架工具中可以使用vue init或vue-init命令来初始化项目,命令最终都对应bin/vue-init文件。
vue-init文件的逻辑流程主要如下:
在这里插入图片描述
vue-init文件逻辑中有几个重要的依赖包:

  • commander:命令行处理
  • download-git-repo:Node.js下git仓库下载处理
  • inquirer:Node下用户与命令行交互处理
  • ora:终端加载优化处理

vue init语法是:

vue init <template-name> <project-name>

vue官方提供了不同的模板(对应着不同的构建工具等),这些模块仓库都位于vuejs-templates中,简单概括对应了两类:webpack模板和browserify模板。
当然个人也可以自己的模块仓库,通过自有模块来初始化项目,语法如下:

vue init username/repo <project-name>

当webpack模板来初始化项目,其执行逻辑简要概括如下:

  • 判断当前目录是否存在相同名称的目录,存在需要与用户交互处理
  • 使用download-git-repo来下载对应的模块仓库,会在用户目录下创建.vue-templates目录,该目录下存在webpack模板仓库
  • 编译webpack模板仓库生成最终的文件
run函数

该函数的逻辑是检查、下载和编译模板的入口,实际上其逻辑主要就分成3部分:

检查部分的逻辑

实际上检查部分的逻辑主要是:

  • 创建是否是本地模板

    vue脚手架支持依据本地模板来初始化项目,源码中判断本地模块是通过路径来进行的,即脚手架对于以./、/或window环境下[a-zA-Z]:的开头的路径都看成是使用本地模板

  • 对于非本地模板检查相关信息
    • 首先会检查对应node版本和vue-cli是否存在新版本
    • 其次会检查模板路径是否存在 / 来判断是否使用官方模板还是非官方模板
下载部分的逻辑

该部分就是调用download-git-repo库提供的方法下载对应的git模板仓库,其源码具体如下:

// 模板下载优化
const spinner = ora('downloading template')
spinner.start()
// 本地模板存在就删除掉重新下载
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下的generate方法,该模块负责模板编译的具体逻辑。generate模块依赖几个重要的第三方NPM包,具体如下:

  • handlebars:轻量级语义化模板(点此查看handlebars中文文档
  • consolidate:模板引擎整合库,支持目前比较流行的模板库
  • metalsmith:静态站点生成器

首先明确下为什么需要handlebars包?

因为官方vuejs-templates仓库提供的模板是使用handlebars来编写的

接下来具体看看generate模块的具体逻辑,其逻辑主要有如下几点:

获取options

实际上获取options的逻辑比较简单,直接贴源码:

module.exports = function options (name, dir) {
  // 模板位置的实际路径
  const opts = getMetadata(dir)
  // 默认项目名
  setDefault(opts, 'name', name)
  setValidateName(opts)
  // 默认用户名
  const author = getGitUser()
  if (author) {
    setDefault(opts, 'author', author)
  }

  return opts
}

该函数主要的逻辑是getMetadata,实际上这是脚手架模板仓库的必要设置,即模板仓库必须存在meta.json或meta.js的。

function getMetadata (dir) {
  const json = path.join(dir, 'meta.json')
  const js = path.join(dir, 'meta.js')
  // 获取对应文件内容
  return opts
}

这里以官方模板vuejs-templates/webpack为例,这里看看其meta.js的设置,该文件主要包含下面几项:

  • metalsmith对象
  • helpers对象
  • prompts对象
  • filters对象
  • complete函数

这里meta.js的配置信息是用在后续的逻辑中,这里暂时先不展开,后续逻辑在针对性的说明。

helpers处理

Handlebars是一种简单的模板语言,它使用模板和输入对象来生成 HTML 或其他文本格式。
在generate模块中关于Handlebars的逻辑都是调用Handlebars.registerHelper函数。

Handlebars.registerHelper是提供用户自定义助手的功能

generate模板中的定义的助手代码有:

  • if_eq
  • unless_eq
  • if_or
  • template_version

为模板逻辑服务,具体可以看Handlebars的使用和官方模板的具体使用,这里具体就不展开的

metalsmith相关逻辑

使用第三方库metalsmith读取模板仓库中template目录,并对模板的meta数据做相关处理。
Metalsmith是静态站点生成器,其工作原理简单概括只是三个简单步骤:

  1. 读取源目录中的所有文件
  2. 调用一系列处理文件的插件
  3. 将结果写入目标目录

Metalsmith核心逻辑只有读取源目录文件以及输出处理后的文件,所有对于文件的处理都是通过创建来完成的。
从Metalsmith官网对于其工作原理的说明,可以看出与构建工具Glup等相似。
vue脚手架使用Metalsmith原因:

Metalsmith可以自定义插件来实现一些流程性工作

实际上脚手架中对于Metalsmith的使用逻辑就是自定义流程,具体流程如下:

// metalsmith.use就是使用插件
metalsmith
	.use(askQuestions(opts.prompts))
    .use(filterFiles(opts.filters))
    .use(renderTemplateFiles(
    	opts.skipInterpolation
    ))
自定义插件askQuestions的处理

vue init命令执行后首先会下载模板之后会有一系列与用户交互的处理,askQuestions插件就是使用inquirer库处理与用户交互的,处理信息定义在meta.js中,即获取options中的prompts对象。

具体的交互信息内容简单如下:

  • Project name:项目名
  • Project description:项目描述
  • Author:作者
  • Vue build
  • Install vue-router
  • ESLint相关
  • 测试相关
  • 自动安装依赖方式选择
自定义插件filterFiles的处理

过滤相关文件,主要是eslint相关、测试相关,这需要根据之前与用户交互的结果来看是否删除对应文件或目录(官方模板默认是包含所有的文件的)。

自定义插件renderTemplateFiles的处理

该自定义插件逻辑就是处理所有模块文件,该插件使用consolidate库来调用对应的模板引擎来处理每一个文件,vue官方模板是采用Handlebars模板语言的,所以也是采用对应Handlebars的模板引擎来处理的。

输出处理后的模板文件相关逻辑

具体源码如下:

 metalsmith
 	.clean(false)
 	.source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
    .destination(dest)
    .build((err, files) => {
      done(err)
      if (typeof opts.complete === 'function') {
        const helpers = { chalk, logger, files }
        opts.complete(data, helpers)
      } else {
        logMessage(opts.completeMessage, data)
      }
    })

这部分的逻辑就是定义输出目录的路径和具体的编译逻辑,这里涉及到metalsmith的使用,这里就不展开。
需要注意complete函数的逻辑,在模板文件编译输出后会执行complete函数的逻辑,实际上该函数逻辑主要就是自动按照依赖,complete函数主要逻辑如下:

  complete: function(data, { chalk }) {
  	// 安装依赖
    if (data.autoInstall) {
      installDependencies(cwd, data.autoInstall, green);
    } else {
      printMessage(data, chalk)
    }
  },

vue init命令的执行逻辑至此完成。

总结

vue init webpack vue-demo

基于上面命令,这里总结下vue脚手架初始化项目的整体流程:

  • 基于第三方库commander建立脚手架提供的自定义命令
  • 判断模板类型是本地模板、官方模板还是自定义模板,做相关处理,这里使用官方模板
  • 使用第三方库download-git-repo下载官方模板vuejs-templates的webpack模块仓库
  • 开启对webpack模板仓库的编译,输出编译后的模板到指定目录
    • 首先读取webpack模板仓库的meta.js或meta.json文件,获取到对应的options对象
    • 然后使用metalsmith整合流程:使用ininquirer处理与用户交互、过滤文件、使用consolidate调用模板引擎编译所有模板文件
    • metalsmith输出处理后的文件到指定目录下

通过对vue-cli脚手架的分析,知道了目前在Node.js环境下如何实现脚手架,同时也对vue官方模板等有了较为细致的了解,这对个人定制vue项目模板是非常有帮助的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值