日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

走进 San CLI(下):实现原理

發布時間:2024/10/12 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 走进 San CLI(下):实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:胡粵

上期我們討論了 San CLI 的使用,這期我們再深入一點,來看看 San CLI 的實現原理。

核心模塊和核心概念

為了方便理解下文的 San CLI 的整體工作流程(主流程),我們先來看下 San CLI 的核心模塊和核心概念。

核心模塊
San CLI 的核心模塊包括:

  • san-cli:負責 San CLI 的主流程和實現核心功能;
  • san-cli-service:Service 層,負責 Service 流程;
  • san-cli-command-init:實現 san init 命令的 Command 插件;
  • san-cli-plugin-*:Service 插件;
  • san-cli-utils:工具庫,在插件中也可以直接使用;
  • san-cli-webpack:webpack build 和 dev-server 的通用邏輯,webpack 自研插件等。

核心概念
核心概念主要有流程和插件,其中,流程又分為主流程和 Service 流程,插件又分為 Command 插件 和 Service 插件:

  • 流程:San CLI 的流程分為兩段,主流程和 Service 流程:
  • 主流程: san-cli/index.js 的流程,是整個 San CLI 的工作流程。當我們在命令行輸入 San CLI 相關的命令后,比如 san init、 san serve(對應 npm start) 和 san build(對應 npm run build),就會進入主流程,主流程會執行對應命令的 handler。有的命令的 handler 不直接包含處理邏輯,而是引入 san-cli-service,比如 san serve 和 san build,輸入這類命令時,就會從主流程進入 Service 流程。
  • Service 流程: san-cli-service/Service.js 的流程,主要處理 Webpack 構建相關的邏輯。
  • 插件:用于擴展 San CLI 的功能:
  • Command 插件:用于主流程,執行 Command 插件定義的命令后主流程就會執行對應的 Command 插件的 hanlder;
  • Service 插件:用于 Service 流程,處理 Webpack 構建相關的邏輯。

整體工作流程

San CLI 的整體工作流程,即主流程,在 san-cli/index.js 中,大致如下:

  • 檢查 node 版本;
  • 檢查 san-cli 版本;
  • 調用 san-cli/lib/Commander.js 創建命令實例:
    a.添加全局 option;
    b.添加中間件:
    i.設置全局 logLevel;
    ii.設置 NODE_ENV 環境變量;
    iii.給 argv 添加日志等屬性和方法。
  • 加載內置命令: init、 serve、 build、 inspect、 ui 等;
  • 加載 package.json 和 sanrc.json 里聲明的預置命令(自定義命令);
  • sanrc.json 是配置 San CLI 的文件,和 san.config.js 不同,后者是配置項目的文件,詳見 San CLI
    官方文檔。

  • 觸發命令的 hanlder,開始 San CLI 的正式執行。

    結合核心模塊的主流程如下圖所示:
  • san init

    san init 命令用于初始化項目,用法在上期已有所介紹,這期我們來看該命令是怎么做到初始化一個項目的。

    流程
    san init 初始化項目的原理,簡單來說,就是通過 git 命令遠程拉取項目腳手架模板的代碼庫到本地,或者直接使用本地的項目腳手架模板的代碼庫,然后使用 vinyl-fs 將拉取到的代碼庫的文件依次處理,處理完成就得到了一個初始化好的項目。

    vinyl-fs 是 gulp 的核心。

    san init 主要由四步串行任務組成:

  • 檢查目錄和離線包狀態:檢查項目腳手架模板的本地路徑和離線包是否可用;
  • 下載項目腳手架模板:從 Github 等遠程倉庫下載項目腳手架模板到模板緩存目錄;
  • 生成項目目錄結構:使用 vinyl-fs 把項目腳手架模板從緩存目錄遍歷處理到開發者指定的項目目錄;
  • 安裝項目依賴:詢問開發者是否安裝 package.json 里的依賴。
  • 對應的流程圖如下圖所示:

    其中,檢查目錄和離線包狀態的流程圖如下:

    設計思路
    san init 的具體實現在 san-cli-command-init 模塊中, san-cli-command-init 模塊是一個 Command 插件,其核心是一個 TaskList 類, san init 的執行過程的本質就是:傳入上述 4 個任務組成的數組來實例化 TaskList 并調用實例的 run 方法。

    我們來看下 TaskList 的內部實現。

    當調用 TaskList 實例的 run 方法時,首先會對執行實例化 TaskList 時傳入的任務列表的第一個任務進行處理。

    因為這些任務本質上都是一個個函數,所以先給第一個任務加上一些方法,比如表示這個任務已完成的 complete 方法,加完方法后,就調用第一個任務函數。

    調用第一個任務函數,就意味著第一個任務開始執行了,當第一個任務該做的事情都做完后,最后就會在這個任務函數中調用之前給這個任務函數加上的 complete 方法。

    complete 方法就做一件事情,讓 TaskList 實例開始處理下一個任務,處理方式和上面說的一樣,只是簡單地重復。

    最后, TaskList 實例發現沒有下一個任務了,就收工了。

    TaskList 源碼簡化版
    我們用簡化版的源碼來看下 TaskList 的使用和實現。

    TaskList 的使用:

    // 任務列表,4 個任務函數分別對應 san init 的 4 個串行任務 const taskList = [checkStatus, download, generator, installDep]; // 傳入任務列表來實例化 TaskList const tasks = new TaskList(taskList); // 按任務列表的順序依次執行任務 tasks.run();

    以 checkStatus 為例,看下任務函數的實現:

    function checkStatus(task) {// 檢查目錄和離線包狀態// ……// 檢查完了,告訴 tasks 這個任務完成了task.complete(); }

    TaskList 的實現:

    class TaskList {constructor(taskList) {this._taskList = taskList;// 當前任務的索引this._index = 0;}run() {const currentTask = this._taskList[this._index];// 給當前任務的函數加上 complete 方法currentTask.complete = () => {// 當前任務完成,執行下一個任務this.next();};// 真正開始執行當前任務// 把任務函數作為入參是用于在任務函數里調用 complete 方法,具體可見上面的 checkStatus 任務函數的實現currentTask(currentTask);}next() {this._index++;if (this._index >= this._taskList.length) {// 所有任務都完成了,收工return;} else {// 還有任務沒完成,繼續執行下一個任務this.run();}} }

    我們可以看出, san init 的設計模式還是比較優秀的,舉個栗子,如果我們想給 san init 添加多一個任務,那只需要關注該任務本身的實現,實現好后放入實例化 TaskList 時傳入的任務列表中,就可以了,可擴展性非常好。

    插件機制

    San CLI 的插件分為 Command 插件和 Service 插件,在上期我們以實際例子討論了具體怎么開發一個 Command 插件或 Service 插件了,這期我們就來看看 San CLI 的插件機制。

    Command 插件
    Command 插件相對 Service 插件來說,機制比較簡單。

    Command 插件實際是 yargs 的插件系統的擴展,yargs 是一個 npm 包,用它我們可以定義我們自己的命令行命令。

    回顧下我們在上期創建的 Command 插件:

    // san-command-hello.js exports.command = 'hello'; exports.builder = {name: {type: 'string'} }; exports.desc = '熱情地向給定對象打招呼'; exports.handler = argv => {console.log(`${argv.name},你好呀!`); };

    之所以要這么寫,是因為這是 yargs 對定義一個命令的要求。定義好命令后,就在項目的 package.json 里聲明這個命令。

    當我們執行任何一個 san 的命令時 —— 注意,是任何一個 —— 在真正執行這個命令之前,San CLI 會先去讀取 package.json 里聲明的命令,然后找到命令的定義并傳入 yargs,此時,yargs 就知道了都有些什么命令,在此之后,San CLI 才把我們執行的命令的名字和參數傳如 yargs,yargs 拿到命令的名字和參數后,就回去執行對應命令的 hanlder。

    Command 插件的機制就是這樣。

    另外還值得注意的是,在 san-cli/lib/commander.js 里定義了一個名為 Command 的類,這個類對 yargs 插件做了一些定制,比如通過中間件機制添加了常用的方法和屬性到 argv 對象中,方便下游 handler 直接使用。

    Service 插件

    San CLI 的 Service 插件機制借鑒了 Vue CLI 的 Service 插件機制,但有一些不同之處:

  • Vue CLI 注冊一個新命令是通過 Service 插件來完成的,具體是使用 Service 插件的
    registerCommandAPI 方法實現;而 San CLI 把注冊一個新命令的邏輯從 Service
    插件里分離了出來,成為了一個獨立的部分,也就是前面介紹過的 Command 插件。

  • Vue CLI 的一個命令對應一個或多個 Service 插件,也就是說,一個命令的實現由一個或多個 Service 插件來完成;而
    San CLI 的一個命令對應零個或所有 Service 插件(引入的),一個命令對應零個的情況是這個命令是一個純的 Command
    插件,一個命令對應所有 Service 插件的情況是這個命令在它對應的 Command 插件的邏輯里觸發了 Service 流程,而
    Service 流程會依次注冊并執行所有的 Service 插件。

  • 下面我們會以 san serve 命令為例,分別看下 Service 流程、Service 插件的設計思路和 Service 類的簡化版源碼

    Service 流程

    Service 流程,即 Service 的整個工作流程:

  • San CLI 在主流程解析輸入命令行的 san serve 命令,進入 san-cli/commands/serve 的
    handler;
  • san serve 命令的 handler 主要是實例化 Service,實例化會將配置項和 Service 插件進行處理;
  • 執行 service.run(callback),進入 Service 流程,Service 流程的實現主要在 service.run 中:
    a.loadEnv:加載 env 文件;
    b.loadProjectOptions:加載 san.config.js;
    c.init:service 啟動:
    i.初始化插件,即依次執行插件;
    ii.依次執行 webpackChain 回調棧;
    iii.依次執行 webpackConfig 回調棧;
  • 執行 callback。
  • 對應的流程圖如下:

    我們自定義的 Service 插件的具體執行時機是在 3-1-1 “初始化插件,即依次執行插件” 這一步,對應上圖中的 “初始化插件(插件.apply)” 這一步。

    上圖中的 “初始化 plugin 變量并加載傳入的 plugin” 這一步和 “加載 config 中 plugins 里設置的插件” 這一步,其實都是在加載 Service 插件,只不過前者是在加載內置插件和 sanrc.json 里預設的插件,而后者主要是在加載 san.config.json 里的插件。

    加載 Service 插件的流程圖如下:

    設計思路
    輸入 san serve 命令,觸發對應的 handler。 handler 主要就做兩件事情:一是實例化 Service,二是調用 Service 實例的 run 方法。

    實例化 Service 時,會加載內置 Service 插件和 sanrc.json 里預設的 Service 插件。如果我們自定義的 Service 插件預設在了 sanrc.json 里,比如上期的 san-cli-plugin-get-entry,這個時候就會被加載了。

    實例化完 Service,就調用 Service 實例的 run 方法。

    調用 run 方法時,首先會加載 san.config.js 里的 Service 插件,當然也包括我們放在 san.config.js 里的自定義 Service 插件;然后,該加載的 Service 插件都加載完了,這時就準備依次執行它們了。

    在執行每個 Service 插件之前,會先實例化 PluginAPI。 PluginAPI 實例給 Service 插件提供了用于處理 Webpack 構建相關邏輯的方法,比如 configWebpack,通過這個方法我們可以在 Service 插件里獲取和修改 Webpack 配置,比如在上期我們寫的 Service 插件示例里,就用這個方法獲取了網站的入口文件名。

    最后,把 PluginAPI 實例作為入參來調用 Service 插件定義的 apply 函數,就正式開始了 Service 插件的執行。

    Service 源碼簡化版
    Service 的使用:

    // san serve 命令的 handler exports.handler = argv => {// 實例化 Service// 這里傳入的 argv.plugins 是內置 Service 插件和 sanrc.json 里預設的 Service 插件// 之所以能通過 argvs 獲取到這些插件,是因為正如在上文的“Command 插件”一節里所說,我們對 yargs 插件做了一些定制const service = new Service(argv.plugins);// 調用 Service 實例的 run 方法service.run(); };

    Service 的實現:

    class Service {constructor(plugins) {// 加載內置 Service 插件和 `sanrc.json` 里預設的 Service 插件// 這是第一批 Service 插件this.plugins = this.loadPlugin(plugins);}run() {// 獲取 san.config.js 里的配置const projectOptions = this.loadProjectOptions();// 加載 san.config.js 里配置的 Service 插件// 這是第二批 Service 插件const morePlugins = this.loadPlugin(projectOptions.plugins);// 合并兩批 Service 插件this.plugins = [...this.plugins, ...morePlugins];// 依次處理 Service 插件this.plugins.forEach(plugin => {// 實例化 PluginAPI,得到用于處理 Webpack 構建相關邏輯的方法const pluginApi = new PluginAPI();// 把用于處理 Webpack 構建相關邏輯的方法傳入 Service 插件定義的 apply 函數,正式開始了Service 插件的執行plugin.apply(pluginApi);});}loadPlugin() {// 主要是統一各路 Service 插件的形式,方便后續調用// 具體做了些什么可見前面的加載 Service 插件的流程圖// ……}loadProjectOptions() {// 實現獲取 san.config.js 里的配置// ……} }

    PluginAPI 的實現:

    class PluginAPI {configWebpack(fn) {// 實現 configWebpack// ……} }

    最后

    感謝你閱讀到了這里,以上便是《走進 San CLI(下):實現原理》的全部內容。

    如果你都看懂了,請收下我的膝蓋:

    作為 San 生態系列文章的第二彈的《走進 San CLI》,也告一段落了,敬請期待下周同樣精彩的 San 生態系列文章之 San CLI UI !

    新的一年San-CLI還會有持續的開發和優化,比如eject功能、CLI和Service要不要分離,想了解后續的更新,可以關注San CLI 的 GitHub,歡迎 star、歡迎 issues、歡迎 pr。地址:https://github.com/ecomfe/san-cli

    作者:胡粵

    總結

    以上是生活随笔為你收集整理的走进 San CLI(下):实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。