vue-cli2.x.x源码解析(部分)

2023-12-16 20:04

本文主要是介绍vue-cli2.x.x源码解析(部分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.bin

先从bin文件夹开始:

vue.js

#!/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)

vue-build

#!/usr/bin/env node
// 高亮工具
const chalk = require('chalk')console.log(chalk.yellow('\n' +'  We are slimming down vue-cli to optimize the initial installation by ' +'removing the `vue build` command.\n' +'  Check out Poi (https://github.com/egoist/poi) which offers the same functionality!' +'\n'
))

vue-init

#!/usr/bin/env node
// 加载依赖配置
// 下载远程仓库内容
const download = require('download-git-repo')
// 命令行处理工具
const program = require('commander')
// node下的文件操作了,existsSync - 检测路径是否存在
const exists = require('fs').existsSync
// node自带path模块,拼接路径
const path = require('path')
// 命令行加载效果工具
const ora = require('ora')
// 获取用户的根目录
const home = require('user-home')
// 绝对路径替换成带波浪号的路径
const tildify = require('tildify')
// 高亮
const chalk = require('chalk')
// 用户与脚本的命令行交互工具
const inquirer = require('inquirer')
// rm -rf js版本
const rm = require('rimraf').sync// 自建工具
const logger = require('../lib/logger')
const generate = require('../lib/generate')
const checkVersion = require('../lib/check-version')
const warnings = require('../lib/warnings')
const localPath = require('../lib/local-path')// 获取本地路径
const isLocalPath = localPath.isLocalPath
// 获取本地模版路径
const getTemplatePath = localPath.getTemplatePath/*** Usage.*/
// 面试:如何使用第三方模版?
program.usage('<template-name> [project-name]').option('-c, --clone', 'use git clone').option('--offline', 'use cached template')/*** Help. 帮助手册*/program.on('--help', () => {console.log('  Examples:')console.log()console.log(chalk.gray('    # create a new project with an official template'))console.log('    $ vue init webpack my-project')console.log()console.log(chalk.gray('    # create a new project straight from a github template'))console.log('    $ vue init username/repo my-project')console.log()
})/*** Help.*/function help () {program.parse(process.argv)if (program.args.length < 1) return program.help()
}
help()/*** Settings. 主要设置*/
// 如何取到模版 => 如何获取命令行参数
// 模版名称
let template = program.args[0]
// 是否包含斜杠 => 模版名称是否包含路径层级
const hasSlash = template.indexOf('/') > -1
// 项目名称
const rawName = program.args[1]
// 输入空的项目名称 => 是否在当前目录新建
const inPlace = !rawName || rawName === '.'
// 当前目录名为项目构建目录名 or 当前目录新建子目录
const name = inPlace ? path.relative('../', process.cwd()) : rawName
const to = path.resolve(rawName || '.')
const clone = program.clone || false// 拼接目录的地址 => 本地缓存路径
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
}/*** Padding.*/console.log()
process.on('exit', () => {console.log()
})// 标准的交互询问(确认类型)
if (inPlace || exists(to)) {inquirer.prompt([{type: 'confirm',message: inPlace? 'Generate project in current directory?': 'Target directory exists. Continue?',name: 'ok'}]).then(answers => {if (answers.ok) {run()}}).catch(logger.fatal)
} else {run()
}/*** Check, download and generate the project.*/
// 主功能函数
function run () {// check if template is local// 是否为本地路径if (isLocalPath(template)) {// ~/.vue-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(() => {// 官方 or 第三方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)}})}
}/*** Download a generate from a template repo.** @param {String} 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)})})
}

vue-list

#!/usr/bin/env nodeconst logger = require('../lib/logger')
const request = require('request')
const chalk = require('chalk')/*** Padding.*/console.log()
process.on('exit', () => {console.log()
})/*** List repos.*/
// 面试:vue list展示的是所有模版吗?只有官方的吗?会展示本地缓存模版吗?->只要官方,不会展示本地缓存模板
request({url: 'https://api.github.com/users/vuejs-templates/repos',headers: {'User-Agent': 'vue-cli'}
}, (err, res, body) => {if (err) logger.fatal(err)const requestBody = JSON.parse(body)if (Array.isArray(requestBody)) {console.log('  Available official templates:')console.log()requestBody.forEach(repo => {console.log('  ' + chalk.yellow('★') +'  ' + chalk.blue(repo.name) +' - ' + repo.description)})} else {console.error(requestBody.message)}
})

二.lib

check-version.js

const request = require('request')
const semver = require('semver')
const chalk = require('chalk')
const packageConfig = require('../package.json')// 版本检测
module.exports = done => {// Ensure minimum supported node version is used// 面试:如何检测功能模块与node版本是否搭配if (!semver.satisfies(process.version, packageConfig.engines.node)) {return console.log(chalk.red('  You must upgrade node to >=' + packageConfig.engines.node + '.x to use vue-cli'))}// 面试:如何检测当前模块是否已经最新并提示用户升级request({url: 'https://registry.npmjs.org/vue-cli',timeout: 1000}, (err, res, body) => {if (!err && res.statusCode === 200) {const latestVersion = JSON.parse(body)['dist-tags'].latestconst localVersion = packageConfig.versionif (semver.lt(localVersion, latestVersion)) {console.log(chalk.yellow('  A newer version of vue-cli is available.'))console.log()console.log('  latest:    ' + chalk.green(latestVersion))console.log('  installed: ' + chalk.red(localVersion))console.log()}}done()})
}

generate.js

// 依赖加载
const chalk = require('chalk')
// 静态内容生成
const Metalsmith = require('metalsmith')
// 模版引擎
const Handlebars = require('handlebars')
const async = require('async')
const render = require('consolidate').handlebars.render
const path = require('path')
// 多个条件匹配
const multimatch = require('multimatch')
const getOptions = require('./options')
const ask = require('./ask')
const filter = require('./filter')
const logger = require('./logger')// register handlebars helper
Handlebars.registerHelper('if_eq', function (a, b, opts) {return a === b? opts.fn(this): opts.inverse(this)
})Handlebars.registerHelper('unless_eq', function (a, b, opts) {return a === b? opts.inverse(this): opts.fn(this)
})/*** Generate a template given a `src` and `dest`.** @param {String} name* @param {String} src* @param {String} dest* @param {Function} done*/// 1. 获取一个完全体的配置
module.exports = function generate (name, src, dest, done) {// 读取配置项const opts = getOptions(name, src)// ms初始化数据const metalsmith = Metalsmith(path.join(src, 'template'))// 配置完全体合并const data = Object.assign(metalsmith.metadata(), {destDirName: name,inPlace: dest === process.cwd(),noEscape: true})// 注册配置对象 - 动态组件处理opts.helpers && Object.keys(opts.helpers).map(key => {Handlebars.registerHelper(key, opts.helpers[key])})const helpers = { chalk, logger }// 设置调用before钩子if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {opts.metalsmith.before(metalsmith, opts, helpers)}// 问询 + 处理 + 生成metalsmith.use(askQuestions(opts.prompts)).use(filterFiles(opts.filters)).use(renderTemplateFiles(opts.skipInterpolation))// 执行态// after函数执行参数if (typeof opts.metalsmith === 'function') {opts.metalsmith(metalsmith, opts, helpers)} else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {opts.metalsmith.after(metalsmith, opts, helpers)}// 结尾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)// complete钩子if (typeof opts.complete === 'function') {const helpers = { chalk, logger, files }opts.complete(data, helpers)} else {logMessage(opts.completeMessage, data)}})return data
}/*** Create a middleware for asking questions.** @param {Object} prompts* @return {Function}*/function askQuestions (prompts) {return (files, metalsmith, done) => {ask(prompts, metalsmith.metadata(), done)}
}/*** Create a middleware for filtering files.** @param {Object} filters* @return {Function}*/function filterFiles (filters) {return (files, metalsmith, done) => {filter(files, filters, metalsmith.metadata(), done)}
}/*** Template in place plugin.** @param {Object} files* @param {Metalsmith} metalsmith* @param {Function} done*/
// 1. 文件索引处理 2. 跳过要跳过的,去除内容字符串 3. 内容结合元数据进行渲染
function renderTemplateFiles (skipInterpolation) {// 确保是数组skipInterpolation = typeof skipInterpolation === 'string'? [skipInterpolation]: skipInterpolationreturn (files, metalsmith, done) => {const keys = Object.keys(files)const metalsmithMetadata = metalsmith.metadata()async.each(keys, (file, next) => {// 进入异步处理每个文件// skipping files with skipInterpolation optionif (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {return next()}// 内容字符串const str = files[file].contents.toString()// do not attempt to render files that do not have mustachesif (!/{{([^{}]+)}}/g.test(str)) {return next()}render(str, metalsmithMetadata, (err, res) => {if (err) {err.message = `[${file}] ${err.message}`return next(err)}files[file].contents = new Buffer(res)next()})}, done)}
}/*** Display template complete message.** @param {String} message* @param {Object} data*/function logMessage (message, data) {if (!message) returnrender(message, data, (err, res) => {if (err) {console.error('\n   Error when rendering template complete message: ' + err.message.trim())} else {console.log('\n' + res.split(/\r?\n/g).map(line => '   ' + line).join('\n'))}})
}

logger.js

const chalk = require('chalk')
const format = require('util').format/*** Prefix.*/
// 统一打印管理const prefix = '   vue-cli'
const sep = chalk.gray('·')/*** Log a `message` to the console.** @param {String} message*/exports.log = function (...args) {const msg = format.apply(format, args)console.log(chalk.white(prefix), sep, msg)
}/*** Log an error `message` to the console and exit.** @param {String} message*/exports.fatal = function (...args) {if (args[0] instanceof Error) args[0] = args[0].message.trim()const msg = format.apply(format, args)console.error(chalk.red(prefix), sep, msg)process.exit(1)
}/*** Log a success `message` to the console and exit.** @param {String} message*/exports.success = function (...args) {const msg = format.apply(format, args)console.log(chalk.white(prefix), sep, msg)
}

这篇关于vue-cli2.x.x源码解析(部分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/501713

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

css渐变色背景|<gradient示例详解

《css渐变色背景|<gradient示例详解》CSS渐变是一种从一种颜色平滑过渡到另一种颜色的效果,可以作为元素的背景,它包括线性渐变、径向渐变和锥形渐变,本文介绍css渐变色背景|<gradien... 使用渐变色作为背景可以直接将渐China编程变色用作元素的背景,可以看做是一种特殊的背景图片。(是作为背

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

使用Java实现一个解析CURL脚本小工具

《使用Java实现一个解析CURL脚本小工具》文章介绍了如何使用Java实现一个解析CURL脚本的工具,该工具可以将CURL脚本中的Header解析为KVMap结构,获取URL路径、请求类型,解析UR... 目录使用示例实现原理具体实现CurlParserUtilCurlEntityICurlHandler

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...