成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

搭建一個(gè)通用的腳手架

187J3X1 / 3543人閱讀

摘要:在年年底的時(shí)候,同事聊起腳手架。由于公司業(yè)務(wù)的多樣性前端的靈活性讓我們不得不思考更通用的腳手架。針對(duì)開(kāi)發(fā)使用的腳手架針對(duì)項(xiàng)目創(chuàng)建項(xiàng)目通用腳手架是一款強(qiáng)壯的且有一系列工具的通用型腳手架,但發(fā)布指定名稱,和用其開(kāi)發(fā)工具。

在16年年底的時(shí)候,同事聊起腳手架。由于公司業(yè)務(wù)的多樣性,前端的靈活性,讓我們不得不思考更通用的腳手架。而不是伴隨著前端技術(shù)的發(fā)展,不斷的把時(shí)間花在配置上。于是chef-cli誕生了。 18年年初,把過(guò)往一年的東西整理和總結(jié)下,重新增強(qiáng)了原有的腳手架project-next-cli, 不單單滿足我們團(tuán)隊(duì)的需求,也可以滿足其他人的需求。

project-next-cli

面向的目標(biāo)用戶:

公司業(yè)務(wù)雜,但有一定的積累

愛(ài)折騰的同學(xué)和團(tuán)隊(duì)

借助github大量開(kāi)發(fā)模板開(kāi)發(fā)

發(fā)展

前端這幾年(13年-15年)處于高速發(fā)展,主要表現(xiàn):

備注:以下發(fā)展過(guò)程出現(xiàn),請(qǐng)不要糾結(jié)出現(xiàn)順序 [捂臉]

庫(kù)/框架:jQuery, backbone, angular,react,vue

模塊化:commonjs, AMD(CMD), UMD, es module

任務(wù)管理器:npm scripts, grunt, gulp

模塊打包工具: r.js, webpack, rollup, browserify

css預(yù)處理器:Sass, Less, Stylus, Postcss

靜態(tài)檢查器:flow/typescript

測(cè)試工具:mocha,jasmine,jest,ava

代碼檢測(cè)工具:eslint,jslint

開(kāi)發(fā)

當(dāng)我們真實(shí)開(kāi)發(fā)中,會(huì)遇到各種各樣的業(yè)務(wù)需求(場(chǎng)景),根據(jù)需求和場(chǎng)景選用不同的技術(shù)棧,由于技術(shù)的進(jìn)步和不同瀏覽器運(yùn)行時(shí)的限制,不得不配置對(duì)應(yīng)的環(huán)境等,導(dǎo)致我們從而滿足業(yè)務(wù)需求。

畫(huà)了一張圖來(lái)表示,業(yè)務(wù),配置(環(huán)境),技術(shù)之間的關(guān)系

前端配置工程師

于是明見(jiàn)流傳了一個(gè)新的職業(yè),前端配置工程師 O(∩_∩)O~

社區(qū)現(xiàn)狀 專一的腳手架

社區(qū)中存在著大量的專一型框架,主要針對(duì)一個(gè)目標(biāo)任務(wù)做定制。比如下列腳手架

vue-cli

vue-cli提供利用vue開(kāi)發(fā)webpack, 以及 遠(yuǎn)程克隆生成文件等 pwa等模板,本文腳手架參考了vue-cli的實(shí)現(xiàn)。

dva-cli

dva-cli 針對(duì)dva開(kāi)發(fā)使用的腳手架

think-cli

think-cli 針對(duì) thinkjs項(xiàng)目創(chuàng)建項(xiàng)目

通用腳手架

yeoman

yeoman是一款強(qiáng)壯的且有一系列工具的通用型腳手架,但yeoman發(fā)布指定package名稱,和用其開(kāi)發(fā)工具。具體可點(diǎn)擊這里查看yeoman添加生成器規(guī)則

開(kāi)發(fā)初衷和目標(biāo)

由于公司形態(tài)決定了,業(yè)務(wù)類型多樣,前端技術(shù)發(fā)展迭代,為了跟進(jìn)社區(qū)發(fā)展,更好的完成下列目標(biāo)而誕生。

完成業(yè)務(wù):專心,穩(wěn)定,快速

團(tuán)隊(duì)規(guī)范:代碼規(guī)范,測(cè)試流程,發(fā)布流程

沉淀:專人做專事,持續(xù)穩(wěn)定的迭代更新,跟進(jìn)時(shí)代

效益:少加班,少造輪子,完成kpi,做更有意義的事兒

實(shí)現(xiàn)準(zhǔn)備

依托于Github,根據(jù)Github API來(lái)實(shí)現(xiàn),如下:

獲取項(xiàng)目

curl -i https://api.github.com/orgs/project-scaffold/repos

獲取版本

curl -i https://api.github.com/repos/project-scaffold/cli/tags
實(shí)現(xiàn)邏輯

根據(jù)github api獲取到項(xiàng)目列表和版本號(hào)之后,根據(jù)輸入的名稱,選擇對(duì)應(yīng)的版本下載到本地私有倉(cāng)庫(kù),生成到執(zhí)行目錄下。核心流程圖如下:。

總體設(shè)計(jì)

規(guī)范

使用Node進(jìn)行腳手架開(kāi)發(fā),版本選擇 >=6.0.0

選用async/await開(kāi)發(fā),解決異步回調(diào)問(wèn)題

使用babel編譯

使用ESLint規(guī)范代碼

功能

遵守單一職責(zé)原則,每個(gè)文件為一個(gè)多帶帶模塊,解決獨(dú)立的問(wèn)題??梢宰杂山M合,從而實(shí)現(xiàn)復(fù)用。以下是最終的目錄結(jié)構(gòu):

├── LICENSE
├── README.md
├── bin
│?? └── project
├── package.json
├── src
│?? ├── clear.js
│?? ├── config.js
│?? ├── helper
│?? │?? ├── metalAsk.js
│?? │?? ├── metalsimth.js
│?? │?? └── render.js
│?? ├── index.js
│?? ├── init.js
│?? ├── install.js
│?? ├── list.js
│?? ├── project.js
│?? ├── search.js
│?? ├── uninstall.js
│?? ├── update.js
│?? └── utils
│??     ├── betterRequire.js
│??     ├── check.js
│??     ├── copy.js
│??     ├── defs.js
│??     ├── git.js
│??     ├── loading.js
│??     └── rc.js
└── yarn.lock
配置和主框架 使用babel-preset-env保證版本兼容
{
  "presets": [
    ["env", {
      "targets": {
        "node": "6.0.0"
      }
    }]
  ]
}
使用eslint管理代碼

eslint demo

{
  "parserOptions": {
      "ecmaVersion": 7,
      "sourceType": "module",
      "ecmaFeatures": {
          "jsx": true
      }
  },
  "extends": "airbnb-base/legacy",
  "rules": {
      "consistent-return": 1,
      "prefer-destructuring": 0,
      "no-mixed-spaces-and-tabs": 0,
      "no-console": 0,
      "no-tabs": 0,
      "one-var":0,
      "no-unused-vars": 2,
      "no-multi-spaces": 2,
      "key-spacing": [
        2,
        {
          "beforeColon": false,
          "afterColon": true,
          "align": {
            "on": "colon"
          }
        }
      ],
      "no-return-await": 0
  },
  "env": {
      "node": true,
      "es6": true
  }
}
使用husky檢測(cè)提交

使用husky, 來(lái)定義git-hooks, 規(guī)范git代碼提交流程,這里只做 commit校驗(yàn)

package.json配置如下:

"husky": {
    "hooks": {
      "pre-commit": "npm run lint"
    }
}
入口

統(tǒng)一配置和入口,分發(fā)到不同單一文件,執(zhí)行輸出。核心代碼

function registerAction(command, type, typeMap) {
  command
    .command(type)
    .description(typeMap[type].desc)
    .alias(typeMap[type].alias)
    .action(async () => {
      try {
        if (type === "help") {
          help();
        } else if (type === "config") {
          await project("config", ...process.argv.slice(3));
        } else {
          await project(type);
        }
      } catch (e) {
        console.log(e);
        help();
      }
    });

  return command;
}
本地配置讀和寫(xiě)

配置用來(lái)獲取腳手架的基本設(shè)置, 如registry, type等基本信息。

使用

project config set registry koajs # 設(shè)置本地倉(cāng)庫(kù)下載源

project config get registry # 獲取本地倉(cāng)庫(kù)設(shè)置的屬性

project config delete registry # 刪除本地設(shè)置的屬性

邏輯

判定本地設(shè)置文件存在 ===> 讀/寫(xiě)

本地配置文件, 格式是 .ini
若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示

核心代碼

switch (action) {
    case "get":
      console.log(await rc(k));
      console.log("");
      return true;

    case "set":
      await rc(k, v);
      return true;

    case "remove":
      await rc(k, v, true);
      return true;

    default:
      console.log(await rc());

下面每個(gè)命令的實(shí)現(xiàn)邏輯。

下載

使用

project i

邏輯

Github API ===> 獲取項(xiàng)目列表 ===> 選擇一個(gè)項(xiàng)目 ===> 獲取項(xiàng)目版本號(hào) ===> 選擇一個(gè)版本號(hào) ===> 下載到本地倉(cāng)庫(kù)

獲取項(xiàng)目列表

https://api.github.com/orgs/p...

獲取tag列表

若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示

請(qǐng)求代碼

request

function fetch(api) {
  return new Promise((resolve, reject) => {
    request({
      url    : api,
      method : "GET",
      headers: {
        "User-Agent": `${ua}`
      }
    }, (err, res, body) => {
      if (err) {
        reject(err);
        return;
      }

      const data = JSON.parse(body);
      if (data.message === "Not Found") {
        reject(new Error(`${api} is not found`));
      } else {
        resolve(data);
      }
    });
  });
}

下載代碼

download-git-repo

export const download = async (repo) => {
  const { url, scaffold } = await getGitInfo(repo);

  return new Promise((resolve, reject) => {
    downloadGit(url, `${dirs.download}/${scaffold}`, (err) => {
      if (err) {
        reject(err);
        return;
      }
      resolve();
    });
  });
};

核心代碼

  // 獲取github項(xiàng)目列表
  const repos = await repoList();

  choices = repos.map(({ name }) => name);
  answers = await inquirer.prompt([
    {
      type   : "list",
      name   : "repo",
      message: "which repo do you want to install?",
      choices
    }
  ]);
  // 選擇的項(xiàng)目
  const repo = answers.repo;

  // 項(xiàng)目的版本號(hào)劣幣愛(ài)哦
  const tags = await tagList(repo);

  if (tags.length === 0) {
    version = "";
  } else {
    choices = tags.map(({ name }) => name);

    answers = await inquirer.prompt([
      {
        type   : "list",
        name   : "version",
        message: "which version do you want to install?",
        choices
      }
    ]);
    version = answers.version;
  }
  // 下載
  await download([repo, version].join("@"));
生成項(xiàng)目

使用

project init

邏輯

獲取本地倉(cāng)庫(kù)列表 ===> 選擇一個(gè)本地項(xiàng)目 ===> 輸入基本信息 ===> 編譯生成到臨時(shí)文件 ===> 復(fù)制并重名到目標(biāo)目錄

若中間每一步 數(shù)據(jù)為空/文件不存在/生成目錄已重復(fù) 則給予提示

核心代碼

  // 獲取本地倉(cāng)庫(kù)項(xiàng)目
  const list = await readdir(dirs.download);

  // 基本信息
  const answers = await inquirer.prompt([
    {
      type   : "list",
      name   : "scaffold",
      message: "which scaffold do you want to init?",
      choices: list
    }, {
      type   : "input",
      name   : "dir",
      message: "project name",
      // 必要的驗(yàn)證
      async validate(input) {
        const done = this.async();

        if (input.length === 0) {
          done("You must input project name");
          return;
        }

        const dir = resolve(process.cwd(), input);

        if (await exists(dir)) {
          done("The project name is already existed. Please change another name");
        }

        done(null, true);
      }
    }
  ]);
  const metalsmith = await rc("metalsmith");
  if (metalsmith) {
    const tmp = `${dirs.tmp}/${answers.scaffold}`;
    // 復(fù)制一份到臨時(shí)目錄,在臨時(shí)目錄編譯生成
    await copy(`${dirs.download}/${answers.scaffold}`, tmp);
    await metal(answers.scaffold);
    await copy(`${tmp}/${dirs.metalsmith}`, answers.dir);
    // 刪除臨時(shí)目錄
    await rmfr(tmp);
  } else {
    await copy(`${dirs.download}/${answers.scaffold}`, answers.dir);
  }

其中模板引擎編譯實(shí)現(xiàn)核心代碼如下:

// metalsmith邏輯
function metal(answers, tmpBuildDir) {
    return new Promise((resolve, reject) => {
    metalsmith
      .metadata(answers)
      .source("./")
      .destination(tmpBuildDir)
      .clean(false)
      .use(render())
      .build((err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(true);
      });
  });
}
// metalsmith render中間件實(shí)現(xiàn)
function render() {
    return function _render(files, metalsmith, next) {
    const meta = metalsmith.metadata();

    /* eslint-disable */
    
    Object.keys(files).forEach(function(file){
      const str = files[file].contents.toString();

      consolidate.swig.render(str, meta, (err, res) => {
        if (err) {
          return next(err);
        }

        files[file].contents = new Buffer(res);
        next();
      });
    })
    
  }
}
升級(jí)/降級(jí)版本

使用

project update

邏輯

獲取本地倉(cāng)庫(kù)列表 ===> 選擇一個(gè)本地項(xiàng)目 ===> 獲取版本信息列表 ===> 選擇一個(gè)版本 ===> 覆蓋原有的版本文件

若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示

核心代碼

  // 獲取本地倉(cāng)庫(kù)列表
  const list = await readdir(dirs.download);

  // 選擇一個(gè)要升級(jí)的項(xiàng)目
  answers = await inquirer.prompt([
    {
      type   : "list",
      name   : "scaffold",
      message: "which scaffold do you want to update?",
      choices: list,
      async validate(input) {
        const done = this.async();

        if (input.length === 0) {
          done("You must choice one scaffold to update the version. If not update, Ctrl+C");
          return;
        }

        done(null, true);
      }
    }
  ]);

  const repo = answers.scaffold;

  // 獲取該項(xiàng)目的版本信息
  const tags = await tagList(repo);

  if (tags.length === 0) {
    version = "";
  } else {
    choices = tags.map(({ name }) => name);

    answers = await inquirer.prompt([
      {
        type   : "list",
        name   : "version",
        message: "which version do you want to install?",
        choices
      }
    ]);
    version = answers.version;
  }
  // 下載覆蓋文件
  await download([repo, version].join("@"))
搜索

搜索遠(yuǎn)程的github倉(cāng)庫(kù)有哪些項(xiàng)目列表

使用

project search

邏輯

獲取github項(xiàng)目列表 ===> 輸入搜索的內(nèi)容 ===> 返回匹配的列表

若中間每一步 數(shù)據(jù)為空 則給予提示

核心代碼

 const answers = await inquirer.prompt([
    {
      type   : "input",
      name   : "search",
      message: "search repo"
    }
  ]);

  if (answers.search) {
    let list = await searchList();

    list = list
      .filter(item => item.name.indexOf(answers.search) > -1)
      .map(({ name }) => name);

    console.log("");
      if (list.length === 0) {
          console.log(`${answers.search} is not found`);
      }
      console.log(list.join("
"));
      console.log("");
  }
總結(jié)

以上是這款通用腳手架產(chǎn)生的背景,針對(duì)用戶以及具體實(shí)現(xiàn),該腳手架目前還有一些可以優(yōu)化的地方:

不同源,存儲(chǔ)不同的文件

支持離線功能

硬廣:如果您覺(jué)得project-next-cli好用,歡迎star,也歡迎fork一塊維護(hù)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101986.html

相關(guān)文章

  • webpack4詳細(xì)教程,從無(wú)到有搭建react手架(一)

    摘要:是一個(gè)現(xiàn)代應(yīng)用程序的靜態(tài)模塊打包器,前端模塊化的基礎(chǔ)。作為一個(gè)前端工程師切圖仔,非常有必要學(xué)習(xí)。官網(wǎng)的文檔非常的棒,中文文檔也非常給力,可以媲美的文檔。建議先看概念篇章,再看指南,然后看和配置總覽。 webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器,前端模塊化的基礎(chǔ)。作為一個(gè)前端工程師(切圖仔),非常有必要學(xué)習(xí)。 showImg(https://segment...

    zhkai 評(píng)論0 收藏0
  • 使用webpack從0搭建多入口網(wǎng)站手架,可復(fù)用導(dǎo)航欄/底部通欄/側(cè)邊欄,根據(jù)頁(yè)面文件自動(dòng)更改配置

    摘要:官方推薦不寫(xiě)重復(fù)的配置,即把本地和生產(chǎn)環(huán)境共用的配置放到一個(gè)文件,然后通過(guò)進(jìn)行合并我們可以看到,通過(guò)插件,將共用配置和開(kāi)發(fā)的配置進(jìn)行合并定義了全局變量這個(gè)插件是為了在我們?cè)试S后,自動(dòng)打開(kāi)頁(yè)面,避免每次都手動(dòng)打開(kāi)。 之前只知道webpack很強(qiáng)大,但是一直沒(méi)有深入學(xué)習(xí)過(guò),這次從頭看了一下教程,然后從0開(kāi)始搭建了一個(gè)多入口網(wǎng)站的開(kāi)發(fā)腳手架,期間遇到過(guò)很多問(wèn)題,所以有心整理一下,希望能給大家...

    isLishude 評(píng)論0 收藏0
  • 使用webpack從0搭建多入口網(wǎng)站手架,可復(fù)用導(dǎo)航欄/底部通欄/側(cè)邊欄,根據(jù)頁(yè)面文件自動(dòng)更改配置

    摘要:官方推薦不寫(xiě)重復(fù)的配置,即把本地和生產(chǎn)環(huán)境共用的配置放到一個(gè)文件,然后通過(guò)進(jìn)行合并我們可以看到,通過(guò)插件,將共用配置和開(kāi)發(fā)的配置進(jìn)行合并定義了全局變量這個(gè)插件是為了在我們?cè)试S后,自動(dòng)打開(kāi)頁(yè)面,避免每次都手動(dòng)打開(kāi)。 之前只知道webpack很強(qiáng)大,但是一直沒(méi)有深入學(xué)習(xí)過(guò),這次從頭看了一下教程,然后從0開(kāi)始搭建了一個(gè)多入口網(wǎng)站的開(kāi)發(fā)腳手架,期間遇到過(guò)很多問(wèn)題,所以有心整理一下,希望能給大家...

    jaysun 評(píng)論0 收藏0
  • vue和react差異

    摘要:而中實(shí)現(xiàn)原理是利用高階函數(shù)通過(guò)將多個(gè)函數(shù)組合成一個(gè)可執(zhí)行執(zhí)行函數(shù)關(guān)鍵步驟代碼如下所示。和都是基于更新差異元素。 引言 平時(shí)開(kāi)發(fā)單頁(yè)項(xiàng)目應(yīng)用基于vue,目前另外兩個(gè)比較熱的庫(kù)還有angular和react,angular 1系列用過(guò),進(jìn)入公司后由于基于vue技術(shù)棧就沒(méi)在關(guān)注了。一直在關(guān)注react,目的不是學(xué)習(xí)用法,只是為了拓展自己的視野和思維,通過(guò)了解一些使用上的差異性,來(lái)進(jìn)一步的思考...

    OnlyLing 評(píng)論0 收藏0
  • 如何構(gòu)建大型前端項(xiàng)目

    摘要:如何構(gòu)建大型的前端項(xiàng)目搭建好項(xiàng)目的腳手架一般新開(kāi)發(fā)一個(gè)項(xiàng)目時(shí),我們會(huì)首先搭建好一個(gè)腳手架,然后才會(huì)開(kāi)始寫(xiě)代碼。組件化一般分為項(xiàng)目?jī)?nèi)的組件化和項(xiàng)目外的組件化。 如何構(gòu)建大型的前端項(xiàng)目 1. 搭建好項(xiàng)目的腳手架 一般新開(kāi)發(fā)一個(gè)項(xiàng)目時(shí),我們會(huì)首先搭建好一個(gè)腳手架,然后才會(huì)開(kāi)始寫(xiě)代碼。一般腳手架都應(yīng)當(dāng)有以下的幾個(gè)功能: 自動(dòng)化構(gòu)建代碼,比如打包、壓縮、上傳等功能 本地開(kāi)發(fā)與調(diào)試,并有熱替換與...

    lykops 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

187J3X1

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<