版本说明

$ vue --version
@vue/cli 4.5.9
$ node --version
v14.0.0
$ npm --version
7.6.1

源码位置-mac

/usr/local/lib/node_modules/@vue/cli

流程说明

  1. 根据package.json中的bin可以知道入口文件在bin/vue.js
  2. vue.js中找到create <app-name>的命令设定,在action钩子中调用了lib/create

create.js

  1. 获取项目绝对路径
  2. 判断项目文件夹是否存在
  3. 创建文件夹
  4. 调用lib/Creator.js

Creator.js

Creator.create()

  1. 根据创建项目时的选择项配置preset插件组

  2. 深度拷贝preset

  3. 配置preset中的默认插件的配置项:@vue/cli-service、@vue/cli-plugin-router、@vue/cli-plugin-typescript、@vue/cli-plugin-vuex

  4. 获取包管理器:yarn/npm/pnpm

  5. 开始创建项目,获取vue-cli版本

  6. 根据preset中的插件配置生成package.json文件

  7. 为pnpm生成.npmrc文件,为yarn生成.yarnrc

  8. 为项目添加git配置:git init

  9. 安装CLI插件:生成指定格式的文件目录

    await generator.generate({
    extractConfigFiles: preset.useConfigFiles,
    });
  10. 唤醒插件

  11. 安装配置额外的依赖

  12. running completion hooks

  13. 生成README.md

  14. 配置git的状态,为测试配置Git

  15. 完成

自定义修改脚手架程序

公司内项目所使用的插件等配置基本相似,在项目开发过程中也有很多提高开发效率的做法,这些可以在脚手架的程序流程中自定义

  1. 摆脱复制-粘贴的重复工作,同时避免复制-粘贴丢失导致的可以不用debug的bug
  2. 提高项目开发效率,统一项目开发风格

生成vue.config.js-仿生成README.md

  1. generateVueConfig.js
// 生成README.md:lib/util/generateReademe.js
// 生成vue.config.js:lib/util/generateVueConfig.js,我另有其他配置项,需要根据配置生成vue.config.js
module.exports = function generateVueConfig(plugins) {
const iconSvg = plugins['svg-sprite-loader'] ? `chainWebpack: config => {
// svg rule loader
const svgRule = config.module.rule('svg') // 找到svg-loader
svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
svgRule // 添加svg新的loader处理
.test(/\.svg$/)
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]',
}) // 修改images loader 添加svg处理
const imagesRule = config.module.rule('images')
imagesRule.exclude.add(resolve('src/icon/svg'))
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
}` : '' return `const path = require('path') function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
publicPath: './',
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
},
productionSourceMap: false,
${iconSvg}
}`
}
  1. 修改create流程-Creator.js

    // 自定义生成vue.config.js,写入多次使用的配置,比如跨域配置,可以直接写,也可以将内容写在另一个js文件中并引入
    if (!generator.files['vue.config.js']) {
    log()
    log('⚙\u{fe0f} Generating vue.config.js...')
    await writeFileTree(context, {
    'vue.config.js': generateVueConfig(preset.otherPlugins),
    })
    }

配置并引入自定义npm包-仿配置@vue/cli-xxx包

在开发中,有一些包总会被引入,比如axios,加密的包,UI框架包等,可以在vue create前期选择时将其加入,来生成代码。

引入@vue/cli-xxx包流程:

  1. create.js中,根据new Creator来进入整体流程,初始化Creator时,传入了初始包列表,以下仅摘要了重要代码
// create.js
const { getPromptModules } = require('./util/createTools')
const creator = new Creator(name, targetDir, getPromptModules())
// getPromptModules()
exports.getPromptModules = () => {
return [
'vueVersion',
'babel',
'typescript',
'pwa',
'router',
'vuex',
'cssPreprocessors',
'linter',
'unit',
'e2e'
].map(file => require(`../promptModules/${file}`))
}
  1. 初始化Creator时,调用了PromptModuleAPI引入了相关包,调用了相关包的配置命令lib/promptModules/xxx
// Creator.js
constructor(name, context, promptModules) {
const promptAPI = new PromptModuleAPI(this);
promptModules.forEach((m) => m(promptAPI));
// 以上命令执行后,在shell界面中会显示每个相关包的配置命令,此时使用者进行选择或输入
}
  1. 在Creator.create()方法中,根据preset为每一个包配置了配置项,存储在preset.plugins中
// Creator.js-create()方法,以下仅举例
preset.plugins["@vue/cli-service"] = Object.assign(
{
projectName: name,
},
preset
); if (cliOptions.bare) {
preset.plugins["@vue/cli-service"].bare = true;
} // legacy support for router
if (preset.router) {
preset.plugins["@vue/cli-plugin-router"] = {}; if (preset.routerHistoryMode) {
preset.plugins["@vue/cli-plugin-router"].historyMode = true;
}
}

仿制以上流程即可导入自己想在初始化过程中导入的包,比如axios等,为避免冲突,全部为另开发的代码,以下以axios以及可选择式的UI框架来举例

  1. createOtherTools.js -仿制getPromptModules函数,配置自定义包列表,并添加到初始化Creator中
// lib/util/createOtherTools.js  otherModules文件夹存储自定义包的相关命令行
exports.getOtherPromptModules = () => {
return [
'axios',
'uiStruct'
].map(file => require(`../otherModules/${file}`))
}
// create.js
const { getPromptModules } = require('./util/createTools')
const { getOtherPromptModules } = require('./util/createOtherTools')
const creator = new Creator(name, targetDir, getPromptModules(), getOtherPromptModules())
  1. 导入相关包的命令行配置,将自定义包的配置存储在options.otherPlugins中
// Creator.js
constructor(name, context, promptModules, otherPromptModules) {
const promptAPI = new PromptModuleAPI(this);
promptModules.forEach((m) => m(promptAPI));
otherPromptModules.forEach((m) => m(promptAPI));
// 以上命令执行后,在shell界面中会显示每个相关包的配置命令,此时使用者进行选择或输入
}
// 新建 otherModules 文件夹
// otherModules/axios.js
module.exports = cli => {
cli.injectFeature({
name: 'Axios', // 显示在vue create命令后的选择项里的名字
value: 'axios', // 相对应的value值
description: 'Promise based HTTP client for the browser and node.js', // 介绍
link: 'https://github.com/mzabriskie/axios', // 相关链接
checked: true // 显示在vue create命令后的选择项里,是否默认选中
}) cli.onPromptComplete((answers, options) => {
if (answers.features.includes('axios')) { // includes(value)是否包含上面定义的value值
options.otherPlugins['axios'] = {} // 在此指定相对应的报名
}
})
}
// otherModules/uiStruct.js
module.exports = cli => {
cli.injectFeature({
name: 'Choose UI Struct', // 显示在vue create命令后的选择项里的名字
value: 'ui-struct', // 相对应的value值
description: 'Choose a struct of UI that you want to use with the project' // 介绍
}) cli.injectPrompt({
name: 'uiStruct',
when: answers => answers.features.includes('ui-struct'), // 判断是否选择
message: 'Choose a struct of UI that you want to use with the project', // 描述
type: 'list', // 选择类shell命令
choices: [
{
name: 'ant-design-vue', // 选项名
value: 'ant-design-vue' // 选项相对应的包名
},
{
name: 'element-ui',
value: 'element-ui'
}
],
default: 'element-ui' // 默认选项
}) cli.onPromptComplete((answers, options) => {
if (answers.uiStruct) {
options.otherPlugins[answers.uiStruct] = {} // answers.uiStruct存储了包名
}
})
}
  1. 将自定义包的引入加入到流程中,在生成package.json之前引入
// Creator.js - create()函数
if (preset.otherPlugins) {
Object.keys(preset.otherPlugins).forEach((dep) => {
let { version } = preset.otherPlugins[dep];
pkg.dependencies[dep] = version ? version : "latest";
});
}

vue create 初步解析以及定制化修改的更多相关文章

  1. 定制化Azure站点Java运行环境(4)

    定制化使用您自己的Tomcat版本和JDK环境 在上面章节中,介绍了如何通过web.config,定制默认的Azure website的Java运行环境,默认情况下,Azure站点的Tomcat是7. ...

  2. 定制化Azure站点Java运行环境(3)

    定制化Azure Website提供的默认的Tomcat和JDK环境 在我们之前的测试中,如果你访问你的WEB站点URL时不加任何上下文,实际上你看到的web界面是系统自带的测试页面index.jsp ...

  3. jquery-ui-datepicker定制化,汉化,因手机布局美观化源码修改

    感谢浏览,欢迎交流=.= 公司微信网页需要使用日历控件,想到jquery-mobile,但是css影响页面布局,放弃后使用jquery-ui-datepicker. 话不多说,进入正题: 1.jque ...

  4. 【vuejs深入二】vue源码解析之一,基础源码结构和htmlParse解析器

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. vuejs是一个优秀的前端mvvm框架,它的易用性和渐进式的理念可以使每一个前端开发人员感到舒服,感到easy.它内 ...

  5. Oceanus:美团HTTP流量定制化路由的实践

    背景简述 Oceanus是美团基础架构部研发的统一HTTP服务治理框架,基于Nginx和ngx_lua扩展,主要提供服务注册与发现.动态负载均衡.可视化管理.定制化路由.安全反扒.session ID ...

  6. AI应用开发实战 - 定制化视觉服务的使用

    AI应用开发实战 - 定制化视觉服务的使用 本篇教程的目标是学会使用定制化视觉服务,并能在UWP应用中集成定制化视觉服务模型. 前一篇:AI应用开发实战 - 手写识别应用入门 建议和反馈,请发送到 h ...

  7. EpiiServer 更快捷更方便的php+nginx环境定制化方案

    EpiiServer是什么 更快捷更方便的php+nginx多应用部署环境. github仓库首页 https://github.com/epaii/epii-server gitee仓库 https ...

  8. unittest加载测试用例名称必须以test开头,是否可以定制化

    ​ 前几天,在一个群里,一个人问了,这样一个问题.说他面试遇到一个面试官,问他,为啥unittest的测试用例要用test 开头,能不能定制化.他不知道为啥. 看到这个题目,我回答当然可以了,可以用l ...

  9. ASP.NET Core应用的错误处理[3]:ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”

    DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求的详细信息以辅助开发人员更好地进行纠错诊断工作,而ExceptionHandlerMi ...

随机推荐

  1. 每日自动健康打卡(Python+腾讯云服务器)

    每日自动健康打卡(Python+腾讯云服务器) 1.配置需要 python3.7,Chrome或者Edeg浏览器,Chrome驱动或者Edge驱动 #需要配置selenium库,baidu-aip库, ...

  2. TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN

    前言 上一章为大家介绍过深度学习的基础和多层感知机 MLP 的应用,本章开始将深入讲解卷积神经网络的实用场景.卷积神经网络 CNN(Convolutional Neural Networks,Conv ...

  3. 巩固javaweb的第三十天

    显示用户输入信息 1 .代码 要想输出用户在上一个页面提交的信息,可以使用下面的代码: ${param.userid} ${param.username} ${param.userpass} ${pa ...

  4. 日常Java 2021/10/27

    java HashMap HashMap是一个散列表,它存储的内客是键值对(key-value)映射.HashMap实现了Map.接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许 ...

  5. Scala(七)【异常处理】

    目录 一.try-catch-finally 二.Try(表达式).getOrElse(异常出现返回的默认值) 三. 直接抛出异常 一.try-catch-finally 使用场景:在获取外部链接的时 ...

  6. c学习 - 第八章:函数

    8.7 数组作函数的参数 1.数组元素作函数的参数--值传递,单向传递 2.数组名做函数的参数--地址传送 (1)实参:数组名做实参,传递的是数组首元素的地址 (2)形参:使用同类型的数组名或指针变量 ...

  7. 转 Android应用开发必备的20条技能

    https://blog.csdn.net/u012269126/article/details/52433237 有些andorid开发人员感觉很迷茫,接下来该去看系统源码还是继续做应用,但是感觉每 ...

  8. [学习总结]7、Android AsyncTask完全解析,带你从源码的角度彻底理解

    我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴 ...

  9. Maven错误收集

    Eclipse 创建项目时报错 Could not resolve archetype org.apache.maven.archetypes:maven-archetype-quickstart:1 ...

  10. springboot中如何向redis缓存中存入数据

    package com.hope;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jack ...