最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue|简易版vue-cli实现二三事儿

    正文概述 掘金(南方小菜)   2021-03-11   542

    前言

    创建一个脚手架,本质上就是实现一个全局包,只不过其职能是根据配置项创建项目

    实现思路

    1. 配置可执行命令 可以基于commander
    2. 要实现脚手架 先做一个命令行交互的功能 可以基于inquirer
    3. 项目模板下载 可以基于 download-git-repo
    4. 根据用户的选择动态的生成内容 可以基于metalsmith

    具体实现

    首先构建npm包的基本配置
    创建可执行的脚本 并指明运行环境是node

    /bin/start.js

    #! /usr/bin/env node
    
    将项目执行脚本指向创建的脚本

    配置package.json中的bin字段

    将包链接到全局下,以直接用命令方式执行
    npm link
    

    此时 这个包就变成了全局包,执行命令为name属性对应的值,执行时会默认执行指定的脚本文件

    继而配置可执行命令

    安装依赖

    npm i command
    

    在执行脚本start.js中实现逻辑

    // 1、配置可执行命令 commander 
    const program  = require('commander');
    
    
    // 配置文件相关
    program
            .command('config [value]')
            .description('inspect and modify the config')
            .option('-g, --get <path>','get value from option')
            .option('-s, --set <path> <value>')
            .option('-d, --delete <path>','delete option from config')
            .action((value,opts)=>{
                console.log(opts);
                // console.log(value,cleanArgs(cmd));
            })
    
    // 创建ui
    program
            .command('ui')
            .description('start and open wzy-cli ui')
            .option('-p,--port <port>','Port used for the UI Server')
            .action(cmd => {
                console.log(cmd);
            })
    // 查看版本  `wzy-cli --version`
    program
            .version(`zhufeng-cli ${require('../package.json').version}`)
            .usage(`<command> [option]`)
    
    // 当用户输入`wzy-cli --help`时 执行回调 提示用户可以查看具体命令详情
    program.on('--help',function (){
        console.log();
        console.log(`Run ${chalk.cyan('wzy-cli <command> --help ')} show details>`);
        console.log();
    })
    
    // 解析命令执行的参数
    program.parse(process.argv);
    

    中间如果存在交互式配置,比如新建项目时目标文件路径已经存在,则需问询用户是否进行覆盖,则可以使用inquier

    npm i inquier
    

    start.js中逻辑

    const path = require('path');
    const fs = require('fs-extra');
    const Inquirer = require('inquirer');
    
    // 需要支持强制创建功能  创建项目
    program
            .command('create <app-name>')
            .description('create a new project')
            .option('-f, --force','overwrite target directory if it exists')
            .action((name,opts) => {
                const cwd = process.cwd(); // 获取当前命令执行时的工作目录
                const targetDir = path.join(cwd,projectName); // 目标目录
                async function overWrite() {
                     // 先移除
                    return await fs.remove(targetDir)
                }
                // 首先判断项目是否已经存在
                if(fs.existsSync(targetDir)){
                    // 继而判断是否是强制覆盖模式
                    if(opts.force){
                       await overWrite()
                    }else {
                        // 提示用户是否确定覆盖
                        let {action} = await Inquirer.prompt([
                            {
                                name: 'action',
                                type: 'list', // 还有很多其他类型
                                message: 'Target directory already exited Pick an action',
                                choices: [
                                    {
                                        name: 'Overwrite', value: 'overwrite'
                                    },
                                    {name: 'cancel', value: false}
                                ]
                            }
                        ]);
    
                        console.log(action);
                        if(!action){
                            return
                        }else if(action === 'overwrite') {
                            await overWrite()
                        }
                    }
                }
    
            })
    
    功能完善:选择指定模板(仓库和tag)

    架子已经搭建起来了,其实我们这时候就是在不同的命令下去执行不同的逻辑处理,重点关注实现

    • 创建项目以及根据用户的选择动态的生成项目的内容

    承接上文,我们很自然能想到,目前只需要实现下载模板的逻辑就可以了,我们可以抽离出来一个创建类,用于下载模板项目,大体思路如下:

    // 1. 先拉取当前组织下的模板
    		let repo = await this.fetchRepo();
    // 2. 在通过模板找到版本号  
    		let tag = await this.fetchTag();
    
    // 3. 下载
    		let downloadUrl = await this.download(repo,tag);
    
    // 4. 编译模板
    
    

    相应的,我们只需要找到模板项目存储的平台对外暴露的API进行下载即可,中间需要做两步

    • 实现选择功能,下载模板列表后,让用户自行选择下载哪个模板
    • 实现失败重发功能,下载时可能遇到网络等不可控影响导致下载失败,此时要添加loading态并重新发起下载请求

    第一点,我们依然可以使用inquirer

    第二点,我们可以使用一个第三方库ora,其作用是添加loading态,重传的实现我们可以进行tryCatch检测。

    Show me Code

    Creator.js

    const inquirer = require("inquirer");
    const { fetchRepoList } = require("./request");
    const { wrapLoading } = require("./util");
    
    class Creator {
        constructor (projectName,target){
            this.name = projectName;
            this.target = target;
        }
    
        async fetchRepo(){
            // 新增loading 以及 失败重新获取
            let repos = await wrapLoading(fetchRepoList,'waiting fetch template');
            // let repos = await fetchRepoList();
            // // console.log(repos,'仓库列表');
            if(!repos) return;
            repos = repos.map(item => item.name);
            let { repo } = await inquirer.prompt({
                name: 'repo',
                type: 'list',
                choices: repos,
                message: 'please choose a template to create a project'
            })
            // // console.log(repo);
        }
    
        async fetchTag(){
    
        }
    
        async download(){
    
        }
    
        async create(){
            // 1. 先拉取当前组织下的模板
            let repo = await this.fetchRepo();
            // 2. 在通过模板找到版本号  
            let tag = await this.fetchTag();
    
            // 3. 下载
            let downloadUrl = await this.download(repo,tag);
    
            // 4. 编译模板
    
        }
    }
    
    module.exports = Creator;
    

    util.js

    const ora = require('ora')
    // 挂起函数
    export async function sleep(n) {
        return new Promise((resolve,reject) => {
            setTimeout(resolve,n)
        })
    }
    
    // loading + 失败重新发起fetch
    export async function wrapLoading(fn,message) {
        const spinner = ora(message);
        spinner.start(); // 开始加载
    
        try {
            let repos = await fn();
            spinner.succeed(); // 成功
            return repos;
        } catch (error) {
            spinner.fail('request failed, refetch...')
            await sleep(1000);
            console.log(error);
            return wrapLoading(fn,message);
        }   
    }
    
    功能完善:下载选择好的模板至指定位置

    此处从github上下载仓库我们可以借助一个第三方包download-git-repo

    npm i download-git-repo
    

    然后在下载函数中执行如下逻辑

    1. 拼接处下载路径
    2. 将资源下载到指定的位置 (后续亦可增加缓存功能)
    Show me Code

    Creator.js

     // 根据仓库和tag下载指定模板
        async download(repo,tag){
            // 1. 拼接处下载路径
            let requestUrl = `zhu-cli/${repo}${tag ? '#' + tag : ''}`;
            // 2. 将资源下载到指定的位置 (后续可以增加缓存功能 w-todo)
            await wrapLoading(this.downloadGitRepo,'waiting down repo', requestUrl,path.resolve(process.cwd(),`${repo}@${tag}`));
    
            return this.target;
        }
    

    总结

    至此,我们就完成了一个简易版的cli工具,可以看到,其实也就是包的使用+思路的理清,完整版代码的仓库地址,如果对您有任何帮助吧,赏小菜个star吧,谢谢阅读


    起源地下载网 » vue|简易版vue-cli实现二三事儿

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元