什么是对话
官方介绍如下:
对话是在创建一个新的项目或者在已有项目中添加新的插件时处理用户选项时需要的。所有的对话逻辑都存储在 prompts.js 文件中。对话内部是通过 inquirer 实现。
当用户通过调用 vue invoke 初始化插件时,如果插件根目录包含 prompts.js,他将在调用时被使用。这个文件应该导出一个问题数组 – 将被 Inquirer.js 处理。
其实对话就是我们在使用vue create my-project
这个命令的时候,在控制台输出中,可以让我们选择什么东西用的,例如最开始是让我们选择默认配置还是自定配置,选择自定义配置之后,又可以让我们选择是否使用路由、store等东西的。
创建对话
创建对话其实很简单,接着我上一篇的介绍的项目 → 踏上创建Vue-cli-plugin之路(一)
在创建插件的根目录下面创建一个名为prompts.js
的文件,内容如下:
module.exports = [
{
name: "router",
type: 'confirm',
message: '是否需要使用路由?',
default: false
}
];
这里的对话其实也可以是导出一个函数,但是必须要最后返回一个对话数组,如果是函数的话,参数将会是package.json
。
这里面的数组每个属性都有对应的意思,如下:
-
name: 自定义,想写啥就写啥,因为最后是给开发者自己消费的。
-
type: 提示字符的类型,可选值有
list
,rawlist
,expand
,checkbox
,confirm
,input
,number
,password
,editor
-
message: 展示给用户的提示信息。
-
default: 默认值
这里的name
,type
,message
三个属性是必须的,其他的都是非必须,下面是全部属性的介绍:
- type String类型,提示字符的类型,可选值:
list
,rawlist
,expand
,checkbox
,confirm
,input
,number
,password
,editor
。- name (String)最终用户选择的值将存在的结果集的属性名,如果该值包含点".",那么他将会在结果集中创建一个新的子属性,并将值存入该属性中。
- message (String)问题的描述信息。
- default 默认值,根据
type
来确定该值的类型,详情请移步prompt-types- choices (Array | Function) 列表选项,在某些type下可用,如果是数组类型,那么数组的值可以是
Number
|String
|Object
(对象类型需要包含一个name
属性,显示在控制台展示给用户的列表中,还需要一个value
属性,就是用户最后选择的值),如果是一个函数,那么将会把当前的用户已选择答案结果集作为第一个参数,choices数组也可以包含分隔符。- validate (Function)对用户输入的参数进行验证,第一个参数为用户输入的数据。
- filter (Function)对用户输入的参数进行处理,返回最后程序需要结果,并存到答案列表中。
- transformer Function类型,对用户输入的参数进行处理并返回,返回的内容将会展示给用户,该处理并不会影响到用户的输入参数。
- when (Function | Boolean)如果是函数,将会将用户已选择的答案结果集作为第一个参数,可根据该结果集判断该问题是否需要回答,返回
true
|false
,他的值也可以直接是一个true
|false
,但是我想应该很少有人会这么做。- pageSize (Number) 在type为
list
,rawList
,expand
,checkbox
时生效,修改他们最后在控制台渲染的行数。- prefix (String)修改控制台输出的message的默认前缀。
- suffix (String)修改控制台输出的message的默认后缀。
- askAnswered (Boolean)如果该问题已经有答案了,则强提示该问题(亲测不知道有啥用)。
- loop (Boolean)是否启用列表循环,默认为
true
(也不知道有啥用)
手动翻译,可能表达能力不太行,参考一下就行,关键的时候还是官方文档靠谱。
我上面的代码片段的意思就是在使用我的插件的时候,用户可以自己选择是否需要使用路由,那么我们具体该怎么去使用它呢?
使用对话
对话最后用户输入的答案信息将会传入generator.js
的第二个参数中,在第二个参数直接点创建对话的name
属性值即可,例如我的name
属性值为router
,第二个参数名称为opts
,就直接opts.router
就能取到用户最终选择的值。
现在我们修改generator
目录下的index.js
,修改后的内容如下
module.exports = (api, opts, rootOpts) => {
// 渲染模板
api.render("./template");
api.afterInvoke(() => {
const { EOL } = require('os');
const fs = require('fs');
// 找到App.vue文件
const appVue = fs.readFileSync(api.resolve("src/App.vue"), { encoding: 'utf-8' });
const appLines = appVue.split(/\r?\n/g);
// 找到标记的字符位置
const appRenderIndex = appLines.findIndex(line => line.match(/--replace_router--/));
// 找到main.js文件
const mainJs = fs.readFileSync(api.resolve("src/main.js"), { encoding: 'utf-8'});
const mainLines = mainJs.split(/\r?\n/g);
// 找到标记的字符位置
const importRouterIndex = mainLines.findIndex(line => line.match(/\/\/router_import/));
const useRouterIndex = mainLines.findIndex(line => line.match(/\/\/route_use/));
if (opts.router) {
// 用户需要创建路由
appLines[appRenderIndex] = ` <router-view/>`;
mainLines[importRouterIndex] = `import router from './router';`;
mainLines[useRouterIndex] = `router,`;
} else {
// 用户不需要创建路由
appLines[appRenderIndex] = `<h1>这是没有使用路由创建出来的</h1>`;
mainLines[importRouterIndex] = ``;
mainLines[useRouterIndex] = ``;
}
// 将内容写入文件
fs.writeFileSync("src/App.vue", appLines.join(EOL), { encoding: 'utf-8' });
fs.writeFileSync("src/main.js", mainLines.join(EOL), { encoding: 'utf-8' });
})
if (opts.router) {
// 需要路由package.json添加路由的依赖
api.extendPackage({
dependencies: {
"vue-router": "^3.0.2"
}
})
} else {
// 不需要路由删除路由相关的文件夹
api.postProcessFiles((files) => {
Object.keys(files).forEach((name) => {
if (/^src\/(layout|router)[/$]/.test(name)) {
delete files[name]
}
})
})
}
};
vue-cli-plugin
并没有提供可自定义修改部分内容的方法,可能是因为我没有找到吧,不是没有,也看了编辑指定模板,感觉是通过指定外部文件去替换,并达不到我想要的效果,也没有去深入研究,如果有人发现有这个方法也可以选择告诉我。
这里我使用一个小操作,就是通过注释来标记需要替换的内容,然后使用nodejs
进行操作,这里也是通过官网上面知道的,只不过我做了一定的拓展,这种小技巧也不妨是一个好方法,毕竟做开发的是要解决问题的,脑袋一定要灵活。
然后我们还需要修改对应的main.js
和App.vue
文件。
main.js
修改后如下:
import Vue from 'vue';
import App from './App';
//router_import
new Vue({
el: '#app',
//route_use
render: h => h(App)
});
App.vue
修改后如下,本来想用注释的,但是觉得自己想出来的得要有特色才行,所以就用了前后两个短横线,js文件只能用注释,不然打不过去,不信大家可以去试试😅
<template>
--replace_router--
</template>
<script>
export default {}
</script>
然后还需要创建对应的路由文件,这里我创建了两个,至少也是需要两个的,一个router
文件夹,下面index.js
内容如下
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router);
export const constantRoutes = [
{
path: '/',
component: () => import('../layout')
}
];
const createRouter = () => new Router({
scrollBehavior: () => ({
y: 0
}),
routes: constantRoutes
});
export default createRouter();
一个layout
文件夹,下面index.vue
,内容如下
<template>
<div>
<h1>这是使用路由创建出来的</h1>
</div>
</template>
<script>
export default {
name: "Layout",
data() {
return {}
}
}
</script>
都是在template
下面的src
目录下创建的哦,不要弄错了。
验证
最后就到了我们的验证环节了
在test-app
目录下打开终端控制台,输入vue invoke vue-cli-plugin-first
指令
后面的就不需要我继续教你们了吧?做猿还是需要有点探索精神的。
终(这里开始可以不看,避免浪费时间)
今天写完这篇的时候发现已经没有什么可以讲的了,还有就是ui
模块,就是给开发者提供一个可视化的页面进行安装插件,我个人不是特别想去研究这个,因为感觉没必要,我通过控制台上下键和回车键就能做到的事,放到页面上说不定还要多点几次,然后还要更多的代码,主要是看着控制台输出比较有逼格,其实也是懒得研究,我上面的也是跟着官网上面一步一步来的,所以感兴趣的同学可以自己看看,提升一下主动性和自主学习能力也是不错的。
然后上面框架目前需要完成也就是把整个模板给创建完成,将功能逐步完善,这对个人来说,如果完成了可能就是一个质的提升,这里就留给大家自行完成吧,总不能要我直播敲代码吧。
还有最后的一步就是将这个插件上传到npm
,本来想写一篇这个的,发现也没啥好写的,无非就是注意点规范,然后注册账号,发布项目,去npm
上面去看看你的项目,最后用一下,没啥技术含量。
有人可能就问了,对话还有那么多类型的例子为啥不讲讲呢?还有对话里面的能使用函数,为啥不讲讲呢?还有你这个还没有创建store
,eslint
,typescript
等等的示例呢?还有修改webpack
的示例没讲。
我在这里回答一下:
-
类型的示例和函数式使用为啥不讲了
因为你们要自己去探索啊,我在这讲一遍,你跟着做一遍就能记住吗?只有自己切身体会的才能记忆深刻,我觉得学习应该就是我告诉你有这个东西,然后自己去学习,你学不动了,再去请教,这也是我的学习方法,可能不适用所有人,但是我是这样的。 -
其他依赖引用为啥不讲了
我觉得你应该学会举一反三,上面路由的例子难道不够参考的吗?其他的都是同样的道理呀。 -
修改
webpack
的示例为啥不讲了
webpack
的修改其实和在package.json
添加内容类似,这篇里面其实是有在package.json
里面添加内容的,只是没有详细的介绍罢了,package.json
你只需要知道在哪写开发依赖,在哪写生产环境依赖就行了,如果你知道更多其他的,那更好,我上面的东西你应该全都通了。
不同的是你需要掌握webpack
相关的知识,上面说的package.json
你只需要了解就可以了,所以我如果讲的话肯定不会在这里讲webpack
的配置,在这里讲那就混乱了不是。
最后的感觉其实觉得vue-cli-plugin
还是挺简单的,完全可以将你之前做的项目的框架抠出来,然后放到这里里面去,都不需要做什么过多的修改,直接渲染就好,如果逻辑能力没那么强的同学,甚至可以准备几套框架,让用户去选择使用那套进行开发。
就比如这章,如果我选择不用脑子,那么我将会选择创建两套模板文件,一套路由版的,一套没有路由版的,用户选择用路由版的那就渲染路由版的,反之亦然。
毫无技术含量,甚至做得很垃圾,那岂不是让人笑话,那我还发什么博客,不被骂死才怪。
废话说了太多,看到这里的人可能比较闲,好了,拜拜。
代码上传到码云了了