手把手教你搭建屬於自己的前端腳手架工具

前面的話

以前使用vue-clivue init webpack project-name一行命令就可以初始化一個我們自己的項目,小柒覺得好神奇,之後研究啦一下vue-cli,其實就是一個高級版的克隆。然後就決定自己實現一套屬於自己的腳手架(xq-cli),目前xq-cli腳手架已經實現了有一段時間了,決定拿出來分享。

xq-cli腳手架功能

創建項目

  • 詢問用戶:詢問用戶新建項目的基本信息
  • 下載模板:根據用戶的選擇下載對應的模板
  • 生成配置文件:根據用戶的選擇生成對應的配置文件
  • 生成新項目:在目錄中生成新項目

初始化項目

項目創建好後,我們要對它進行初始化操作:

  • 自動創建一個GitHub倉庫,將我們的項目推送至倉庫
  • 安裝依賴

啓動項目

  • 本地啓動瀏覽
  • 支持熱更新

打包項目

  • 打包我們的項目

前置知識

在開始實現時,我們必須要先了解一些工具:

commander:
  • 作用:命令行工具,用來編寫指令和處理命令行的。

  • 用法:

    const program = require('commander')
    
    program
        .command('init')
        .alias('i')
        .description('初始化')
        .option('-x, --xxx', 'xxx') // 有參數時使用
        .action( () => {
         // 回調函數
        }
        })
        
    program.parse(process.argv)
    

vue init 就是這麼來滴。

  • commander API 介紹
    • command - 自定義執行的命令,後面跟上自己定義的命令名稱
    • alias - 定義更短的命令行(配置別名)(懶人必備)
    • description - 描述,它會在 --help裏面顯示
    • option - 定義參數,接受四個參數
    • action - 註冊一個callback函數(執行命令之後,就會執行這個回調)
    • usage:用戶使用提示
    • parse - 解析命令行,注意這個方法一定要放到最後調用
inquirer
  • 作用:這是一個強大的交互式命令行工具,詢問用戶就靠它啦。
  • 用法:
    onst inquirer = require('inquirer');
    inquirer
      .prompt([
        // 一些交互式的問題
      ])
      .then(answers => {
        // 回調函數,answers 就是用戶輸入的內容,是個對象
      });
    
    想一下,使用vue init webpack project-name時,是不是都要問你幾個問題,項目名稱、作者、描述等。
  • inquirer 功能簡介
    • input - 輸入
    • validate - 驗證
    • list - 列表選項
    • comfirm - 提示
    • checkbox - 複選框
chalk
  • 作用:美化我們滴命令行,一個顏色插件,用來修改命令行輸出樣式,通過顏色區分 info、error 日誌,清晰直觀
  • 用法:
    const chalk = require('chalk');
    console.log(chalk.green('success')); 
    console.log(chalk.red('error'));
    
ora
  • 作用:用於顯示加載效果,轉圈圈滴那種

  • 用法:

    const ora = require('ora')
    let loading = ora('downloading template ...')
    loading.start()
    
log-symbols
  • 作用:控制檯彩色日誌符號,用來顯示√ 或者 × 等圖標。(搭配chalk使用)
  • 使用:
import symbol from 'log-symbols';
console.log(symbol.error, chalk.red('請輸入項目名'));
console.log(symbol.success, chalk.green('配置文件更新完成'));
download-git-repo
  • 作用:用來下載遠程模板的

  • 用法:

    	import downloadGit from 'download-git-repo';
         downloadGit(repository , ProjectName, options ,callback);
        // repository : 爲遠程倉庫地址 
        // ProjectName:是存放下載的文件路徑,可以直接是文件名,默認是當前目錄
        // options: 一些選項,比如`{clone: boolean}`表示用http download
        // 還是 git clone 的形式下載
        // callback: 回調函數
    

準備工作

1、先創建一個文件夾,小柒這裏叫xq-cli
2、創建.babelrc文件,支持es6語法。
3、在該目錄下執行npm init,生成package.json文件,然後下載上面的依賴

"dependencies": {
	"babel-cli": "^6.26.0",
    "babel-env": "^2.4.1",
    "chalk": "^3.0.0",
    "commander": "^5.0.0",
    "download-git-repo": "^1.1.0",
    "inquirer": "^7.1.0",
    "ora": "^4.0.3",
    "log-symbols": "^3.0.0",
    "download-git-repo": "^3.0.2",
}

(有了package.josn文件,這樣我們就可以發佈我們的npm包了)。

4、新建一個bin文件夾,並在bin目錄下新建一個無後綴名的cmd文件:

#!/usr/bin/env node
console.log('hello'); // 測試用

這個文件是我們整個腳手架的入口文件

再去package.json文件中配置bin參數,專門放置用戶的自定義命令:
在這裏插入圖片描述
使用npm link命令將xq-clixqc軟連接至全局,這樣就可以全局使用xq-cli或者xqc這個命令來啓動了。
在這裏插入圖片描述
(這裏只是簡單的測試一下)。

實際代碼爲:

#!/usr/bin/env node
require('../dist/main.js'); // 編譯後的main.js文件作爲入口文件

5、修改 package.json 中的 scripts 參數,指定可執行命令,實時編譯腳本,讓 node 能夠識別並執行,開啓watch實時監控。
在這裏插入圖片描述

6、補全目錄結構
在這裏插入圖片描述

入口文件main.js

在整個腳手架的入口文件bin/cmd中,我們引入了require('../dist/main.js');這個編譯後的文件,那我們就從src/main.js文件開始:

這個文件就是使用commander工具來定義我們的命令,主要代碼:

  program
        .command(action)
        .description(actionMap[action].description)
        .alias(actionMap[action].alias)
        .action(()=>{
            switch (action) {
                case 'create':
                    create(...process.argv.slice(3));
                    break;
                case 'init':
                    init(program.username, program.token);
                    break;
                case 'dev': 
                    dev(program.port);
                    break;
                case 'build': 
                    build();
                    break;
                default:
                    break;
            }
        })
 });
// 項目版本
program
    .version(require('../package.json').version, '-v --version')
    .parse(process.argv);

action就是我們的命令,這裏用四個命令,也就是對應腳手架的4個功能:create、init、dev 、build它們對應的回調函數分別在create.jsinit.jsdev.jsbuild.js。下面分別來實現這個幾個功能。

創建項目(xq-cli create)

項目創建思路:

  • 項目創建命令必須輸入新建項目名稱(必須做一層判斷,避免創建的項目名稱覆蓋已有項目)

    // 文件是否存在
    let isExist = async(name) => {
        return new Promise((resolve) => {
            if(fs.existsSync(name)) {
                console.log(symbol.error, chalk.red('文件夾名已被佔用,請更換名字重新創建'))
            }else{
                resolve();
            }
        });
    }
    
    
  • 詢問用戶,引導用戶輸入配置信息

    // 詢問用戶
    let promptList = [
        {
            type: 'list',
            name: 'frame',
            message: 'please choose this project template',
            choices: ['vue', 'react']
        },
        {
            type: 'input',
            name: 'description',
            message: 'Please enter the project description:'
        },
        {
            type: 'input',
            name: 'author',
            message: 'Please enter the author name:'
        }
    ];
    
    let prompt = ()=>{
        return new Promise((resolve)=>{
            // inquirer提供prompt函數來實現詢問,其參數爲數組,詢問將按數組的順序來
            inquirer.prompt(promptList)
                .then(answer => {
                    resolve(answer);
                })
        });
    }
    
  • 下載模板,下載模板比較耗時,可以通過ora提示用戶 正在下載模板,下載完畢之後給出提示。

    // 項目模塊遠程下載
    let downLoadTemplate = async (ProjectName ,api) => {
        return new Promise((resolve, reject) => {
            downloadGit(api, ProjectName, {clone: true}, (err) => {
                if(err) {
                    reject(err);
                }else{
                    resolve();
                }
            });
        });
    };
    
  • 項目下載完畢後,更新配置文件

    // 更新json配置文件
    let updateJsonFile = (fileName, obj) => {
        return new Promise((resolve) => {
            if(fs.existsSync(fileName)) {
                // 讀出模板下的package.json文件
                const data = fs.readFileSync(fileName).toString();
                // 轉爲json對象
                let json = JSON.parse(data);
                // 將用戶輸入的更新到模板package.json文件
                Object.keys(obj).forEach(key => {
                    json[key] = obj[key];
                });
                // 重寫模板下的package.json文件
                fs.writeFileSync(fileName, JSON.stringify(json, null, '\t'), 'utf-8');
                resolve();
            }
        });
    }
    
    

    上面的工具方法都在unit.js代碼中,具體調用都在create.js中,可以到create.js文件中查看源碼。

項目初始化(xq-cli init )
curl -u username:token https://api.github.com/user/repos -d "{\"name\": \"project name \"}"

使用這條命令就可以自動生成倉庫,不用手動再去創建了。這裏的username 就是你Github的用戶名,token在Github上生成:(這裏我的是windows系統,不支持單引號,所以要用轉義符)
在這裏插入圖片描述
然後會有一個一長串數字字符串就是token了。

初始化項目的思路:

  • Git初始化
  • 創建Github倉庫
  • 關聯Github倉庫
  • 更新package.json的repository配置
  • 提交代碼到GitHub倉庫
  • 安裝依賴
let init = async (username, token)=>{
    
    try {
        await loadCmd(`git init`,'git初始化');
        if(username === '' || token === '') {
            console.log(symbol.warning, chalk.yellow('缺少參數無法創建遠程倉庫'));
        }else {
            const projectName = process.cwd().split('\\').slice(-1)[0];
            await loadCmd(`curl -u qjk-xiaoqi:a97fc56cbefe4bdc092490067bb1a9727615a583 https://api.github.com/user/repos -d "{\"name\": \"kkk\"}"`, 'Github倉庫創建');
            // curl -u qjk-xiaoqi:a97fc56cbefe4bdc092490067bb1a9727615a583 https://api.github.com/user/repos -d "{\"name\": \"auto\"}"
            await loadCmd(`git remote add origin https://github.com/${username}/${projectName}.git`, '關聯遠端倉庫')
            let loading = ora();
            loading.start(`package.json更新repository:命令執行中...`);
            await updateJsonFile('package.json', {
                "repository": {
                    "type": "git",
                    "url": `https://github.com/${username}/${projectName}.git`
                }
            }).then(() => {
                loading.succeed(`package.json更新repository: 命令執行完成`);
            });
            await loadCmd(`git add .`,"執行 git add");
            await loadCmd(`git commit -a -m "init"`, '執行git commit')
            await loadCmd(`git push --set-upstream origin master`, '執行git push')           
        }
        await loadCmd(`npm install`,"安裝依賴");
    }catch(err) {
        console.log(symbol.error, chalk.red('初始化失敗'));
        console.log(symbol.error, chalk.red(err));
        process.exit(1);
    }

}

let loadCmd = async(cmd, text) =>{
    let loading = ora();
    loading.start(`${text}: 命令執行中...`);
    await child.exec(cmd);
    loading.succeed(`${text}: 命令執行完成`)
}

相關代碼在:init.js文件中。

運行命令:xq-cli init -u xxx -t xxx就可以了:
在這裏插入圖片描述

項目啓動 (xq-cli dev )

項目啓動就要用我們的Webpack啦,將項目的webpack.config.js 配置好。使用open-browser-webpack-plugin插件來打開了頁面, 並且支持熱更新。

const config = require('./webpack.config.js');

let dev = (port) => {
    // 啓動項目後自動打開瀏覽器
    config.plugins.push(new OpenBrowserPlugin({ url: `http://localhost:${port}` }))
    // 創建一個小型服務器
    new WebpackDevServer(webpack(config), {
        contentBase: './public',// 配置http服務器的文件目錄
        hot: true, // 開啓模塊熱替換
        historyApiFallback: true // 開啓html5 History API網頁
    }).listen(port, 'localhost', function (err, result) {
        if (err) {
            console.log(err);
        }
    });
}

在這裏插入圖片描述

項目打包(xq-cli build)

這個就比較簡單了,直接只用webpack

let build = () => {
    // 打包
    webpack(config, function(err, stats) {
        process.stdout.write(stats.toString({
            colors: true,
            modules: false,
            children: false,
            chunks: false,
            chunkModules: false
        }))
        console.log( symbol.success, chalk.green(' 打包完成'));
        process.exit(1);
    })
}

執行命令:xq-cli build
在這裏插入圖片描述

發佈npm包

最後就可以發佈我們自己的npm包啦,刪掉創建的文件,去官網創建一個賬號,就可以發佈啦。

npm login登錄,npm publish發佈,之後就可以去官網上查看了:
(xq-cli已經被使用了,所以該爲xqc)
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章