前端腳手架開發(fā)

腳手架是什么

創(chuàng)建項(xiàng)目基礎(chǔ)結(jié)構(gòu),提供項(xiàng)目規(guī)范和約定。
在眾多的項(xiàng)目中,我們發(fā)現(xiàn)他們總是有著

  • 相同的組織結(jié)構(gòu)
  • 相同的開發(fā)范式
  • 相同的模塊依賴
  • 相同的工具配置
  • 相同的基礎(chǔ)代碼

腳手架就是用來解決這一些重復(fù)問題的。

常用的腳手架工具

我們常見的腳手架工具大都是為了特定項(xiàng)目類型服務(wù)的,比如vue-cli,create-react-app,只能創(chuàng)建對(duì)應(yīng)的特定項(xiàng)目類型。還有通用的腳手架工具Yeoman,較為靈活易擴(kuò)展。(CLI一般指命令行界面)

Yeoman

Yeoman是一款創(chuàng)建現(xiàn)代化web引用的腳手架工具。老牌,通用,強(qiáng)大,相比vue-cli的特定,他有更多值得我們學(xué)習(xí)的地方。它可以通過我們自定義的generator,創(chuàng)建自己想要的項(xiàng)目。

Yeoman的使用

全局范圍安裝yo
cnpm install -g yo

安裝對(duì)應(yīng)的generator (這里以想創(chuàng)建node項(xiàng)目的腳手架為例)
cnpm install generator-node -g

通過yo運(yùn)行g(shù)enerator
mkdir my-module
yo node

最后通過命令行交互填寫項(xiàng)目結(jié)構(gòu),一些描述,作者什么的

Sub Generator

有時(shí)候我們只是在原有的項(xiàng)目上添加些配置文件,例如eslint配置文件,bable配置文件等,這些文件都有些基礎(chǔ)代碼,如果自己手動(dòng)配的話也容易出錯(cuò),這就需要生成器自動(dòng)幫我們生成,這種需求就適合通過Sub Generator來實(shí)現(xiàn)。

cd my-module
yo node:readme
generator-node中的sub generator說明

并不是說有g(shù)enerator都有sub generator, 比如generator-vue就沒有

總結(jié)常規(guī)使用yeoman的步驟

  1. 明確你的需求
  2. 找到合適的generator(通過yeoman官網(wǎng)找)
  3. 全局范圍安裝你找到的generator
  4. 通過Yo運(yùn)行此generator
  5. 通過命令行交互填寫項(xiàng)目結(jié)構(gòu)
  6. 生成你所需要的項(xiàng)目結(jié)構(gòu)

自定義generator,搭建自己的腳手架

比如vue官方的vue-cli,創(chuàng)建完成后只包含vue結(jié)構(gòu),如果我們還想集成vue-router,vuex,那我們就可以自行搭建自己的腳手架。

創(chuàng)建generator模塊

generator本質(zhì)上就是一個(gè)npm模塊


generator基本結(jié)構(gòu)

如果想添加Sub Generator,目錄就如下


多了一個(gè)component子生成器

還有Generator的模塊名稱必須是generator-<name>
mkdir generator-sample && cd generator-sample

創(chuàng)建一個(gè)package.json
npm init

這個(gè)模塊會(huì)為我們提供一個(gè)generator生成器的基類,基類中提供了一些工具函數(shù),可以讓我們寫生成器的時(shí)候更加便捷
npm install yeoman-generator

按照上述圖的目錄標(biāo)準(zhǔn),創(chuàng)建generators/app/index.js文件
// index.js
// 此文件作為 Generator 的核心入口
// 需要導(dǎo)出一個(gè)繼承自 Yeoman Generator 的類型
// Yeoman Generator 在工作時(shí)會(huì)自動(dòng)調(diào)用我們?cè)诖祟愋椭卸x的一些生命周期方法
// 我們?cè)谶@些方法中可以通過調(diào)用父類提供的一些工具方法實(shí)現(xiàn)一些功能,例如文件寫入

const Generator = require('yeoman-generator')
module.exports = class extends Generator {
  writing () {
    // Yeoman 自動(dòng)在生成文件階段調(diào)用此方法
    // 我們這里嘗試往項(xiàng)目目錄中寫入文件
    this.fs.write( // 這個(gè)fs不是node模塊的fs,這個(gè)更強(qiáng)大
      this.destinationPath('temp.txt'), // 絕對(duì)路徑
      Math.random().toString() //填入隨機(jī)數(shù)
    )
  }
}

這樣一個(gè)簡(jiǎn)單的generator就完成了。

通過命令行把這個(gè)模塊鏈接到全局范圍,使之成為一個(gè)全局模塊包
npm link
mkdir my-sample && cd my-sample
yo sample

=>生成文件啦

根據(jù)模版創(chuàng)建文件

大部分時(shí)候我們要?jiǎng)?chuàng)建的文件較多也較復(fù)雜,所以我們使用模版,可以更便捷一些

創(chuàng)建template/foo.txt
// foo.txt
這是一個(gè)模板文件
內(nèi)部可以使用 EJS 模板標(biāo)記輸出數(shù)據(jù)
例如:<%= title %>

其他的 EJS 語法也支持

<% if (success) { %>
哈哈哈
<% }%>
writing () {
    // 通過模板方式寫入文件到目標(biāo)目錄
    // 模板文件路徑
    const tmpl = this.templatePath('foo.txt')
    // 輸出目標(biāo)路徑
    const output = this.destinationPath('foo.txt')
    // 模板數(shù)據(jù)上下文
    const context = { title: 'Hello zce~', success: true }
    this.fs.copyTpl(tmpl, output, context)
}

html也同理

接收用戶輸入

通過prompting方法,接收命令行交互中用戶輸入的數(shù)據(jù)

prompting () {
        // Yeoman 在詢問用戶環(huán)節(jié)會(huì)自動(dòng)調(diào)用此方法
        // 在此方法中可以調(diào)用父類的 prompt() 方法發(fā)出對(duì)用戶的命令行詢問
        return this.prompt([
          {
            type: 'input',
            name: 'name',
            message: 'Your project name',
            default: this.appname // appname 為項(xiàng)目生成目錄名稱
          }
        ])
        .then(answers => {
          // answers => { name: 'user input value' }
          this.answers = answers
        })
      }
// 在想使用的地方 this.answer.name

Vue-generator案例

開發(fā)一個(gè)vue-cli
https://gitee.com/ericzhouhang/generator-vue-demo

generator發(fā)布

generator本質(zhì)上就是一個(gè)npm模塊,所以就是發(fā)布一個(gè)npm模塊
npm publish

Plop

一個(gè)小型前端腳手架工具,一般用來創(chuàng)建項(xiàng)目中的同類型文件

腳手架工作原理

啟動(dòng)它過后,它會(huì)自動(dòng)的去詢問你一些預(yù)設(shè)的問題,然后將你回答的結(jié)果,結(jié)合模版文件,生成項(xiàng)目的內(nèi)容。 核心代碼:

#!/usr/bin/env node
// Node CLI 應(yīng)用入口文件必須要有這樣的文件頭
//意思是指定用node執(zhí)行腳本文件。

// 腳手架的工作過程:
// 1. 通過命令行交互詢問用戶問題
// 2. 根據(jù)用戶回答的結(jié)果生成文件

const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer') // 命令行交互模塊
const ejs = require('ejs')

inquirer.prompt([
  {
    type: 'input',
    name: 'name',
    message: 'Project name?'
  }
])
.then(anwsers => {
  // console.log(anwsers)
  // 根據(jù)用戶回答的結(jié)果生成文件

  // 模板目錄
  const tmplDir = path.join(__dirname, 'templates') // __dirname:當(dāng)前模塊的目錄名
  // 目標(biāo)目錄
  const destDir = process.cwd() // Node.js 進(jìn)程的當(dāng)前工作目錄。

  // 將模板下的文件全部轉(zhuǎn)換到目標(biāo)目錄
  fs.readdir(tmplDir, (err, files) => {
    if (err) throw err
    files.forEach(file => {
      // 通過模板引擎渲染文件
      ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
        if (err) throw err

        // 將結(jié)果寫入目標(biāo)文件路徑
        fs.writeFileSync(path.join(destDir, file), result)
      })
    })
  })
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容