前端腳手架,聽起來玄乎,實際呢?

開篇

第一次據說腳手架, 是我剛接觸Vue,跟着網上大佬的文章,用Vue-cli從0搭建了一個Vue項目,一步一步配置,而後npm i, npm run dev,打開連接,一個網頁就這麼寫好了,當時對於npm,webpack這些前端工程化一無所知,嘴裏不自覺的吐出了兩個字:'NB'。一年之後,一位新猿在Segmentfault上發出了這樣的提問css

clipboard.png

而後我就裝着知道的樣子就去回答了一下,答案是這樣的:html

clipboard.png

粗狂的講,這樣的回答,好像沒什麼毛病。但既然是本着學習的態度,那此次就好好的講一講前端腳手架存在的意義,究竟是個什麼鬼,以及寫一個腳手架到底有多難?因此接下來,文章將圍繞下面幾部分來討論:前端

  • 前端腳手架存在的意義
  • 腳手架的實質
  • 寫一個屬於本身的腳手架有多難

前端腳手架存在的意義

隨着前端工程化的概念愈來愈深刻人心,腳手架的出現就是爲減小重複性工做而引入的命令行工具,擺脫ctrl + c, ctrl + v,此話zenjiang? 如今新建一個前端項目,已經不是在html頭部引入css,尾部引入js那麼簡單的事了,css都是採用Sass或則Less編寫,在js中引入,而後動態構建注入到html中;除了學習基本的js,css語法和熱門框架,還須要學習構建工具webpack,babel這些怎麼配置,怎麼起前端服務,怎麼熱更新;爲了在編寫過程當中讓編輯器幫咱們查錯以及更加規範,咱們還須要引入ESlint;甚至,有些項目還須要引入單元測試(Jest)。對於一個更入門的人來講,這無疑會讓人望而卻步。而前端腳手架的出現,就讓事情簡單化,一鍵命令,新建一個工程,再執行兩個npm命令,跑起一個項目。在入門時,無需關注配置什麼的,只須要開心的寫代碼;另外,對於不少系統,他們的頁面類似度很是高,因此就能夠基於一套模板來搭建,雖然是不一樣的人開發,但用腳手架來搭建,相同的項目結構與代碼書寫規範,是很利於項目的後期維護的;以上就是爲何腳手架存在的意義, 讓項目從"搭建-開發-部署"更加快速以及規範 (出自於某乎達人)。vue

腳手架的實質

如今流行的前端腳手架都是基於NodeJs編寫,好比前面提到的Vue-CLI,比較火的create-react-app,還有Dva-CLI和我司本身curie,都是熱門框架react和vue的項目腳手架,其功能都是生成一個通用的目錄結構,並配上構建、編譯、檢查等工程環境。大體流程以下:node

  1. 解析用戶輸入的命令;
  2. 生成一些配置化文件,如package.json, 或webpack.config.js等;
  3. 根據用戶的輸入生成對應的模板項目;(高級一點的ctrl + c, ctrl + v)
  4. 安裝該模板所須要的環境。

先以create-react-app爲例,網上有不少讀create-react-app源碼的文章,能夠網上搜索一下,推薦一篇。它的代碼很少,讀起來也比較容易,最主要的是create-react-app在某種程度上來說它只作了1,2步的事情(固然4也作了一些),第3步是由react-scripts完成的,固然其還有一個重要做用,就是做爲這個項目的構建編譯工具,因此你在package.json中還能看到下圖靠右這樣的命令(熟悉的nmp start, 有木有):react

clipboard.png

因此咱們知道,create-react-app主要解析命令,執行文件的操做,react-scripts主要提供模板與模板所須要的項目工程化配置,如上圖左側所示,咱們能看到其包含了webpack與jest測試的相關配置文件。
而vue-cli的實現與create-react-app稍微有點不同。首先vue-cli新建工程是那種一步一步問答式命令來進行個性化定製的,然後者是一鍵搞定的;vue-cli的項目模板來源於github,支持多種模板(可經過vue list查看),經過git下載的,具體模板配置參照這裏,然後者模板來自於react-scripts目錄下的兩個文件夾(上圖中的template與template-typescript);vue-cli構建的項目其構建編譯是直接依賴於browserify或webpack這樣的構建工具,配置是徹底暴露給使用者的,能夠再次自定義,然後者是基於webpack進行了一層封裝,而後將其暴露爲一種新的構建命令,固然也能夠運行npm run eject將配置暴露出來。dva是一個比較成熟的react解決方案,比較適合中後臺系統,dva-cli與前二者具備較強的類似性,採用roadhog做爲其構建編譯工具,開發人員,無須關心構建配置,只需關心業務代碼的實現(因此大廠的碼農本身不注意的話容易發展成碼畜)。其還有一個擴展功能,就是項目生成後,能夠採用命令添加一個頁面,這樣確實也能減小一部分的ctrl + c, ctrl + v。
綜上,前端腳手架的實質包含兩項,命令式的構建項目(解析命令,拷貝項目到本地),提供項目的配置(構建,編譯,代碼規範檢查)。webpack

寫一個屬於本身的腳手架有多難

整理思路

從上兩節的描述,大體整理一下即將要實現的功能:git

  1. 命令的解析,這個能夠藉助commander實現;
  2. 文件的操做,複製,粘貼,增長,刪除,文件內容的新增,替換;這個能夠藉助fs-extra實現;
  3. 模板文件,就以本身對前端工程化粗淺的認識,寫一個最牛(cu)逼(lou)的模板項目;
  4. 申請一個npm帳號,這個不算實現的功能,算附屬工做;

以上,感受是否是實現特簡單。不是感受,是確實很簡單。流程以下:github

clipboard.png

實現代碼

命令解析:web

// 四種模板。對應我git倉庫四個倉庫地址
    const tempIndex = {
      react: 'reactTemplate', // react 模板
      vue: 'vueTemplate', // vue 模板
      h5: 'h5Template', // h5模板
      dva: 'dvaTemplate', // dva模板
    };
    
    let projectName;  // 存儲目錄
    let templateName; // 模板名稱
    let inputIndex; // 除了拷貝模板,還支持自定義模板路徑下載,但感受有點畫蛇添足
    const program = new commander.Command(packageJson.name)
      .version('v' + packageJson.version, '-v, --version')
      .arguments('<templateName>')
      .arguments('<projectName>')
      .option('-f, --force', 'force delete the exist director')
      .option('-d, --directly', 'copy the not specified template')
      .alias('cp')
      .description('create-doddle react myProject')
      .action(function (index,name) {
        inputIndex = index;
        // 容許目標項目名和要複製的模板類型名順序顛倒
        if (tempIndex[index] || tempIndex[name]) {
          if (tempIndex[index]) {
            templateName = tempIndex[index];
            projectName = name;
          } else {
            templateName = tempIndex[name];
            projectName = index;
          }
        }
        if (program.directly) {
          templateName = index;
        }
      });
    program.parse(process.argv)
    // 沒有輸入任何參數,報語法錯誤,並打印help
    if (program.args.length === 0) {
      console.log(chalk.red('syntax error'));
      program.help()
    }
    
    if (templateName) {
      excute(templateName, projectName, program.force);
    } else {
      console.log(`the template ${inputIndex} you want download do not exist`);
    }

文件拷貝:

async function create(temp, project, force = false) {
      tempName = temp;
      projectName = project;
      forceDel = force;
      const file = currentPath + projectName;
      try {
        // 檢測項目文件夾是否已存在, 若存在,拋出錯誤
        const res = await fs.pathExists(file);
        if (res) {
          if (forceDel) {
            console.log(green('force remove the exist directory'));
            await fs.remove(file);
            downloadByGit(renameFile, tempName);
          } else {
            // 拋出錯誤,並提示可使用-f參數來強制刪除已存在的項目
            console.log(chalk.red('Error, In this directory, the project name already exsits !'));
            console.log(chalk.green('you can use option -f to force delete the directory !'));
          }
          return;
        }
        // 若不存在,直接從git下載
        downloadByGit(renameFile, tempName);
      } catch (err) {
        console.error(red(err));
      }
    }

關於chalk,這是一款顏色標記插件,將要打印的文字用不一樣的顏色標記出來,像下面這樣:

clipboard.png

git文件下載:

function downloadByGit(callback, template) {
      console.log(green('start download'));
      console.log(`git@github.com:closertb/${template}.git`);
      const result = spawn(
        'git',
        ['clone', `git@github.com:closertb/${template}.git`],
        { stdio: 'inherit' }
      );
      const error = result.error;
      if (error) {
        console.log(red(error));
        return;
      }
      // 定義回調;
      callback && callback();
    }

主要就這三段代碼,就實現了命令的解析,和從git源端拷貝模板到本地。

建立可執行命令

稍微對前端工程化了解的就知道,對於項目,想建立npm run start或npm run dev這樣的可執行命令,只須要在package.json的scripts進行定義。而想要建立vue init webpack myapp或dva init myapp這樣的命令又怎樣作呢?廣告以後,立刻揭曉(自娛自樂中,請忽視O(∩~∩)O!)
第一步:在你項目的package.json中填上一個bin屬性,代表它是可執行的, 並配置好可執行命令和入口文件

clipboard.png

第二步: 在你的入口文件(我這裏是根目錄的index.js)首行加上一段代碼: #!/usr/bin/env node,告訴操做系統執行這個腳本的時候,調用/usr/bin下的node解釋器;

第三步:登陸你的npm帳號,運行npm publish發佈你的npm包,參考過的連接
到此,一個簡易的腳手架就寫好了。

最後

人老了,總喜歡最後嘮叨兩句,其實不論vue-cli,仍是create-react-app,或則dva,其核心功能(最牛逼的)不是命令的解析或者模板的拷貝,這部分只佔了它腳手架很小的部分,看起來多,是由於它作了不少兼容,好比幫助、錯誤檢測、系統網絡環境檢測、回滾這些操做,但這也不是核心。我的以爲核心仍是是react-scripts或則roadhog這些構建編譯腳本,但歸根接地仍是得對babel和webpack這些庫的深刻理解,送給觀看這篇文章到這裏的你,也勉勵一下本身,繼續加油。

項目源碼地址: create-doddle
npm包地址:create-doddle
首發地址:Denzel Blog

相關文章
相關標籤/搜索