前端腳手架構建實踐

前面的話

在前端工程化過程當中,爲了解決多項目中,類似度高的工做,便誕生許多前端腳手架,這裏記錄下本身實現一個簡易前端腳手架過程的實踐。主要是解決多個頁面類似內容的複製粘貼問題,功能相似於Webstorm的Live template,或者Vscode的Snippets。前端

思路
  • 預先配置頁面模板,預留關鍵字變量
  • 用戶填寫關鍵字變量,生成頁面模板,輸出到制定目錄
用到的包
  • fs

    讀寫文件模塊,這裏主要用於讀入用戶配置文件,輸出模板到文件node

  • commander

    NodeJs命令行工具,提供了用戶命令行輸入和參數解析,用戶解析用戶輸入react

  • inquirer

    NodeJs交互式命令行工具,詢問操做者問題,獲取用戶輸入,校驗回答的合法性npm

  • metalsmith

    文件處理,讀寫操做json

  • handlebars

    將模板中的變量替換爲用戶輸入,編譯模板,相似框架如:artTemplate,Jade前端工程化

  • path

    NodeJs的路徑操做庫,如合併路徑babel

  • chalk

    命令行輸出樣式美化框架

具體實現
  1. 首先在一個新的文件夾,如xxx-tools下 npm init 建立一個node項目,由於是要作成一個npm包的腳手架,因此在包的取名上必定要惟一,即package.jsonname字段,避免在發包的時候和網上已經存在的npm包重名,報403沒有權限的錯。
  2. 在xxx-tools文件夾下建立bin文件夾,同時在bin文件夾下建立腳本tempTool文件,內容以下:
#!/usr/bin/env node

console.log('Hello World');

注意哦,#!/usr/bin/env node 這個是Linux規範,用來指明瞭這個執行腳本的解釋程序,要是沒有這一行,默認用當前Shell去解釋這個腳本less

  1. package.json中增長bin配置:
"bin": {
    "tempTool": "./bin/tempTool"
  },
  1. 到目前爲止,一個簡單的前端腳手架實現了,在npm官網註冊,在項目裏執行npm login登陸,以後npm publish若是一切順利,npm包提交完畢,能夠在其它項目中執行npm i -g xxx-tools,安裝這個包,執行xxx-tools命令,輸出 Hello World,腳手架開發過程當中,也涉及到在本地調試,能夠直執行node ./bin/xxx-tools
  2. 如今來加入具體的開發流程,用戶的輸入,輸入信息的讀取等等,bin文件修改以下
#!/usr/bin/env node

const program = require('commander');
const chalk = require('chalk');
const { loadTemplate } = require('../src/lib/writeTemp');

const log = data => console.log(chalk.green(data));

log('初始化模板配置');

program
  .command('create')
  .description('create template')
  .option('-d')
  .action(async function () {
    const result = await loadTemplate();
    result ? null : log('配置完畢');
  });

program.parse(process.argv);

用戶執行create命令,在這裏調用了loadTemplate函數,看一下這個函數async

// 把模板中的變量替換爲用戶輸入的變量,輸出模板到制定文件夾
const Metalsmith = require('metalsmith');
const Handlebars = require('handlebars');
const path = require('path');
const fs = require('fs');
const { askQuestion } = require('./askQuestion');

const loadTemplate = async () => {
  // 從toolrc.json文件讀取配置
  const dirPath = process.cwd();
  if (!fs.existsSync('toolrc.json')) {
    throw new Error('toolrc.json配置文件不存在');
  }
  const configJson = path.join(dirPath, 'toolrc.json');
  const config = fs.readFileSync(configJson);
  const { source, dist, questionConfig } = JSON.parse(config);
  const answer = await askQuestion(questionConfig);
  const metalsmith = Metalsmith(__dirname);

  metalsmith
    .metadata(answer)
    .source(path.join(dirPath, source))
    .destination(path.join(dirPath, dist))
    .use(function (files, metalsmith, done) {
      //遍歷替換模板
      Object.keys(files).forEach(fileName => {
        const fileContentsString = files[fileName].contents.toString();
        //Handlebar compile 前須要轉換爲字符串
        files[fileName].contents = new Buffer(Handlebars.compile(fileContentsString)(metalsmith.metadata()));
      });
      done();
    }).build(function (err) {
    if (err) throw err;
  });
};

module.exports.loadTemplate = loadTemplate;

爲了方便用戶配置,須要用戶自行配置一個toolrc.json文件,指明模板文件的輸入輸出目錄,和須要用到的
詢問變量,示例配置以下:

{
  "source": "/src/template",
  "dist": "/build",
  "questionConfig": {
    "name": {
      "type": "string",
      "required": true,
      "label": "Module name"
    },
    "description": {
      "type": "string",
      "required": true,
      "label": "Module description"
    },
    "namespace": {
      "type": "string",
      "required": true,
      "label": "dva model namespace"
    }
  }
}

source配置了模板文件的位置,dist爲輸出文件的位置,questionConfig爲模板中的關鍵字,須要用戶在交互的命令行中輸入,下面這段爲利用inquirer包,實現命令行交互。

// 遍歷問題模板,輸出提問

const inquirer = require('inquirer');

const askQuestion =  async (prompts)=> {
  let promptsArr = Object.keys(prompts).map(key => ({
    name: key,
    ...prompts[key]
  }));
  return inquirer.prompt(promptsArr);
}

module.exports.askQuestion = askQuestion

效果以下:
圖片描述

由於用了handlebars包,模板的定義須要符合其規範,模板文件以下:

import React, { Component } from 'react';
import { connect } from 'dva';
import './style.less';

@connect(state => ({ loading: state.loading }))
class {{name}} extends React.Component {
  state = {};

  componentWillReceiveProps = (nextProps) => {

  };

  render() {
    return ();
  }

}

export default {{name}};

最終輸出到 dist 目錄的文件,會替換其中雙括號裏的內容

結束的話

這裏只是簡單的例子,能夠沉澱一些業務場景的模板,經過命令行的方式快速的建立,避免複製粘貼,其實本意是學習一下Node的腳手架工具的實現,有興趣的同窗能夠看看babel-cli的源碼。

相關文章
相關標籤/搜索