Molecule 在構建工具中的選擇

来源:https://www.cnblogs.com/dtux/archive/2023/10/31/17799946.html
-Advertisement-
Play Games

我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。 本文作者:修能 朝聞道,夕死可矣 何為 Molecule? 輕量級的 Web IDE UI 框架——Molecule 我們開源了一個輕量的 Web IDE UI 框架 ...


我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。

本文作者:修能

朝聞道,夕死可矣

何為 Molecule?
輕量級的 Web IDE UI 框架——Molecule
我們開源了一個輕量的 Web IDE UI 框架
Molecule實現數棧至簡前端開發新體驗

前言

構建通常指的是把源代碼轉換成發佈到線上的可執行 JavaScrip、CSS、HTML 代碼。在前端發展的過程中,源代碼的模塊體系在不斷的更新,最終產物也在不斷的更新。而隨之也使得構建工具也在不斷更新換代。

而目前來看,基於前端的細化領域下,針對不同領域下的構建工具也日新月異。來看看 Molecule 該如何選擇構建工具呢?

Molecule 的需求

首先,我們需要分析 Molecule 對構建工具的需求有什麼?

老版本的問題

  1. 本地開發和 build 的構建工具不同,不得不增加 web 命令來執行一個預覽的任務,確保 build 後的產物沒問題。
  2. 慢,由於使用 tsc 作為編譯,所以編譯較慢。
  3. 部分變數無法復用,導致重覆定義。

代碼編譯

由於 Molecule 的代碼是用 ESM 的模塊書寫,且 Molecule 面向的是 Web 應用。通常來說面向 Web 應用的依賴庫是需要提供 ESM 的代碼實現 tree shaking 的作用的。
所以我們這裡需要把 ESM 書寫的 Molecule 代碼通過構建工具編譯成 ESM。

思考:為什麼要把 ESM 代碼編譯成 ESM?

  1. 將 TypeScript 編譯成 JavaScript
  2. 將高級語法編譯成低級語法

除此之外,由於我們考慮到 Node.js 後續發展以 Pure ESM 為主,且 Molecule 針對 CommonJS 的場景較少,故我們不考慮輸出 CommonJS 的產物。

類型

需要支持輸出類型。

樣式

Molecule 中使用 BEM 作為類名規範,通常情況下使得需要在 Sass 中和 JavaScript 中都定義相同變數名。而類 Sass-in-JS 使得我們可以從 Sass 中導出變數名,在 JS 文件中使用。
這就使得構建工具不僅要支持 Sass 的編譯,同時還需要支持插件,允許我們做 Sass-in-JS 的需求。

其他

其他相關文件,例如 JSON,PNG 等文件需要支持拷貝至相關指定目錄。

調研構建工具

Webpack

Webpack 是目前構建工具中的老大哥了,作為頂級老牌構建工具,幾乎所有場景都能適用。
缺點也僅僅是冗餘代碼較多,配置項太多,體積較大等。

Rollup

作為面向 JS 類庫而出現的構建工具。其和 Webpack 相比,在打包後產生的冗餘代碼少,體積較小,功能專註。缺點僅僅是不支持 HMR。

Vite

直接排除

Parcel

Parcel 目前看作是面向 Web 應用的零配置,高速度的 Webpack。其有一個致命的弱點是,自定義插件支持不如 Webpack。這會讓我們無法實現 Sass-in-JS。
2.0 可能有所改善,我不清楚。不予評價

swc

swc 在某種程度上,是 babel 和 tsc 的競品,屬於比較底層的構建工具。和 esbuild 同類型,只是 esbuild 基於 Go,swc 基於 Rust。

esbuild

extremely fast JavaScript Compiler

babel

很好,就是慢

tsc

很好,就是更慢。有一個優點,只有 tsc 能支持輸出類型。

方案實施

由於大多數的構建工具都是 bundler,並不符合 Molecule 的定位。故採取的方案是 esbuild + Sass + tsc 的方案。
esbuild 取其作為 Compiler 的部分,Sass 取其編譯 SCSS 文件的部分,tsc 負責編譯出類型文件。

tsx 相關文件輸出

 transformCtx = await esbuild.context({
        entryPoints,
        bundle: false,
        format: 'esm',
        outdir: dist,
        jsx: 'automatic',
        plugins: [
            {
                name: 'alias',
                setup(build) {
                    build.onLoad({ filter: /.*/ }, async (args) => {
                        const source = await fs.promises.readFile(args.path, 'utf8');
                        const contents = sassLoader(alias(source, args.path));
                        return {
                            contents,
                            loader: args.path.endsWith('.tsx') ? 'tsx' : 'ts',
                        };
                    });
                },
            },
        ],
    });
    await transformCtx.watch();

做兩件事

  1. 別名重定位
  2. 將文件中的樣式文件改為 css

樣式文件輸出

/**
 *
 * @param {string} entry
 */
async function _transform(entry) {
    const res = await sass.compileAsync(entry);
    const regex = /^:export {(\n|.)+}$/m;
    const target = entry.replace(/src\//, 'esm/').replace(/.scss/, '.css');
    const dirname = path.dirname(target);
    if (!fs.existsSync(dirname)) {
        fs.mkdirSync(dirname, { recursive: true });
    }
    const css = res.css.replace(regex, '');
    fs.writeFileSync(target, css);
    if (regex.test(res.css)) {
        const exportModules = res.css.match(regex)[0];
        fs.writeFileSync(
            path.join(dirname, styleVariablesFileName),
            exportModules
                .replace(':export', 'export default')
                .replace(/: .*;/gm, (substring) => {
                    const stringLiteral = /(?<="|')\S+(?="|')/g;
                    if (!stringLiteral.test(substring)) {
                        const startIdx = substring.indexOf(':');
                        const endIdx = substring.indexOf(';');
                        return `:"${substring.substring(startIdx + 1, endIdx).trim()}",`;
                    } else {
                        return substring.replace(';', ',');
                    }
                })
        );
    }
}

做兩件事

  1. :export幹掉
  2. :export的內容放到當前目錄下的style__variables.js的目錄中

類型文件輸出

類型文件非同步輸出,防止阻塞

async function transformTyping() {
    typingCtx = spawn('tsc && (concurrently "tsc -w" "tsc-alias -w")', {
        stdio: 'inherit',
        shell: true,
    });
}

其他文件輸出

/**
 *
 * @param {string} filePath
 */
function _copyFile(filePath) {
    const dest = filePath.replace(/src\//, 'esm/');
    const dirname = path.dirname(dest);
    if (!fs.existsSync(dirname)) {
        fs.mkdirSync(dirname, { recursive: true });
    }
    fs.createReadStream(filePath, 'utf-8').pipe(fs.createWriteStream(dest));
}

遺留問題

  • 增量編譯的問題
  • 代碼壓縮

歡迎大家就以上問題留言討論!

最後

歡迎關註【袋鼠雲數棧UED團隊】~
袋鼠雲數棧UED團隊持續為廣大開發者分享技術成果,相繼參與開源了歡迎star


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 哈嘍大家好,我是鹹魚 我們知道預設情況下,Linux 終端提示符都是簡單的黑白色 這種黑白提示一方面看久了容易視覺疲勞,另一方面由於沒有高亮顯示,看著很不方便,視覺體驗極差 所以我們需要修改我們的終端顯示顏色,像下麵這樣子 PS1(提示語句) 在 Linux 中,當前的 Bash 提示設置保存在一個 ...
  • kube-promethues配置釘釘告警 前置:k8s部署kube-promethues 一.配置釘釘機器人 打開釘釘的智能群助手,點擊添加機器人 選擇自定義機器人 勾選加簽,複製後保存 複製webhook地址後點擊保存 二.編寫dingtalk的yaml部署文件 vi dingtalk.yaml ...
  • Redis是基於記憶體的K-V鍵值對記憶體資料庫 淺談Redis7新特性 主要是自身底層性能和資源利用率上的提高和優化。 多AOF文件支持 config命令增強 限制客戶端記憶體使用 listpack緊湊列表調整 訪問安全性增強 Redis Functions(要搶Lua腳本的飯碗) RDB保存時間調整, ...
  • 本文記錄如何在Linux系統上進行MongoDB資料庫的導出和導入(備份和還原),Windows系統上的命令基本一樣,僅文件路徑不同。 ...
  • 本文分享自華為雲社區《華為雲GaussDB城市沙龍活動走進安徽,助力金融行業數字化轉型》,作者: GaussDB 資料庫 。 近日,華為雲GaussDB資料庫城市沙龍·安徽站圈層活動順利舉行。活動邀請了金融行業代表及伙伴,一起圍繞資料庫展開了金融行業數字化轉型解決方案與成功實踐交流,共同推動國內數據 ...
  • 背景 1、在ArkTS的架構中,沒有明確的可管理的載入請求狀態的腳手架,在進行網路請求過程中,無法簡單的進行交互響應。 2、參考Android中的LoadState寫了一個簡單的腳手架,以便在日常開發過程中,管理載入請求狀態和UI交互。 腳手架說明與源碼 1、狀態機LoadState 使用一個狀態機 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一,問題起因 最新在開發小程式的時候,調用微信小程式來獲取用戶信息的時候經常報錯一個問題 fail api scope is not declared in the privacy agreement,api 更具公告,是微信更新對應的隱 ...
  • 在建設博客的初期,我採用GitBook構建了編碼專家的專欄系統。GitBook是基於Node.js的靜態頁構建組件,可以將Markdown文檔渲染為靜態頁,支持插件擴展,使用非常簡單。由於它不支持深度的定製,使用了一段時間後,無法滿足我的要求了。有一天我看到某博客採用VuePress,簡潔美觀、功能... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...