如何開發一款 JSSDK

本文作者系 360 奇舞團前端開發工程師

JSSDK 的定義與分類

什麼是 JSSDK

SDK 是Software Development Kit(軟件開發工具包)的縮寫,JSSDK是爲了幫助前端實現特定需求,而向開發者暴露的一些JS-API的集合,開發者可以通過它在網頁中集成和使用某些特定的功能,例如社交分享、地圖服務、支付功能等. 它通常包括一下模塊:

  1. JavaScript庫:這是 JSSDK 的核心部分,包含了一系列預先編寫好的JavaScript代碼,這些代碼實現了一些特定的功能,如用戶認證、數據分析、社交分享等。

  2. API 文檔:這部分文檔詳細描述瞭如何使用 JSSDK 中的各個功能。它通常會包含函數和方法的列表,以及如何使用這些函數和方法。

  3. 示例代碼:這部分代碼展示瞭如何在實際項目中使用 JSSDK。通過閱讀和運行示例代碼,開發者可以更好地理解如何使用 JSSDK

JSSDK 的分類

JSSDK 的分類主要取決於它們提供的功能和服務。通常根據其功能不同分爲:UI 組件庫JS 工具庫分析統計 SDK社交媒體 SDK

如何設計 JSSDK

設計 SDK 要遵循的原則

  1. 單一職責原則:SDK 一般都是爲了滿足一類業務的需要,所以設計之初要明確業務範圍,如果功能過於複雜,可以拆改成幾個獨立的 SDK,在主 SDK 中引入相關邏輯就 OK 了,這樣便於功能解耦和測試。

  2. 最小可用、最少依賴性原則: 能用確定的方法實現,就不要再去搞複雜的內容。SDK 要減少依賴,一些工具庫能不用就不用。儘可能自行實現必要的方法,或者引入儘量少的庫。否則會導致 SDK 打包後過大,或者更新版本帶來的兼容問題。

  3. 穩定性原則: 保持 SDK 足夠的穩定性,一方面要保持 API 方法的穩定,另一方面在設計 API 的入參時,參數要用對象類型,這樣便於後續擴展添加更多參數。如果 SDK 有升級要考慮兼容舊版本。新功能要新增 API,舊的 API 不要刪除。

  4. 易用性原則: 要滿足易用性原則,首先要有簡潔的 API,明確的方法註釋、文檔跟示例代碼很重要,不管 SDK 寫的多漂亮,使用者首先接觸的是接入文檔,然後是覆蓋所有 API 的示例代碼,一個好的實例代碼能起到事半功倍的效果。

語言與構建工具選擇

  1. 語言的選擇 語言無非是選擇TS還是JS, 這裏推薦使用TS來編寫 SDK 代碼因爲相比JS而言他有如下優勢:
  1. 構建工具的選擇 推薦使用Rollup來作爲 SDK 項目的打包構建工具

SDK 核心功能的實現

需求

假設我們開發一款問卷投放 SDK,滿足問卷的自動投放、代碼控制投放、事件觸發投放、問卷回收等邏輯。注意單一職責原則的應用,只做問卷的投放、回收邏輯。問卷裏面內容展示,以及內部相關邏輯不涉及。

項目結構

src: 源代碼

API 的設計

SDK API 的設計根據功能分爲兩大類:生命週期方法實例方法.

需要注意的是在設計對外 API 時,每個 API 都要有方法執行完成後回調,便於用戶執行後續邏輯。

項目架構

  1. 單例模式來創建實例

  2. 接受用戶的必要入參與參數校驗

  3. 設置路由變化監聽

  4. 完成與服務端的對接邏輯

rollup.config.mjs
import ts from "rollup-plugin-typescript2";
import dts from "rollup-plugin-dts";
import commonjs from '@rollup/plugin-commonjs'
// es6 轉 es5
import babel from '@rollup/plugin-babel'
// 模塊自動導入並導出
import { importExportPlugin } from 'rollup-plugin-import-export'
import postcss from 'rollup-plugin-postcss'; // 處理css 的插件
// 代碼壓縮
import terser from '@rollup/plugin-terser'
// 配置環境變量
import replace from 'rollup-plugin-replace';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import json from '@rollup/plugin-json';

import path from "path"
import { fileURLToPath } from 'url'
const __filenameNew = fileURLToPath(import.meta.url)
const __dirnameNew = path.dirname(__filenameNew)

const isProduction = process.env.NODE_ENV === 'production';

const API_URL_MAP = {
    production: 'https://prod.***',
    pre: 'https://pre.*****',
    development: 'https://dev.***'
};

function getEnvApiUrl() {
    return API_URL_MAP[process.env.NODE_ENV] || API_URL_MAP.production;
}

export default [
    {
        //入口文件
        input: './src/core/index.ts',
        output: [
             //打包esModule
            {
                file: path.resolve(__dirnameNew, "./dist/index.esm.js"),
                format: "es", //輸出類型 ESM 支持通過import export引入
                sourcemap: false,
            },
             //打包common js 支持 require exports 引入
            {
                file: path.resolve(__dirnameNew, "./dist/index.cjs.js"),
                format: "cjs",
                sourcemap: false,
            },
            //打包 AMD CMD UMD global 引入 將依賴包注入到全局變量中 支持<script>引入
            {
                file: path.resolve(__dirnameNew, "./dist/index.js"),
                format: "umd",
                name: "QaSdk",
                sourcemap: false,
            },
        ],
         //配置ts
        plugins: [
            nodeResolve(),
            json(),
            postcss(),
            ts(),
            importExportPlugin(),
            babel(),
            commonjs(),
            isProduction ? terser() : null, // 生成環境啓用壓縮
            replace({
                'process.env.API_URL': JSON.stringify(getEnvApiUrl()),
                'process.env.API_URL_MAP': JSON.stringify(API_URL_MAP),
                'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
            })
        ],
    },
    {
        //打包聲明文件
        input: "./src/core/index.ts",
         output: {
            file: path.resolve(__dirnameNew, "./dist/index.d.ts"),
            format: "es",
          },
          plugins: [dts()],
    }
]

rollup.config.mjsRollop 構建工具的核心配置文件,相當於 webpack.config.js 文件在 Webpack 構建工具中的作用。用於定義構建過程的各種設置。例如,入口文件、輸出文件、模塊解析規則、插件等。

-input: 入口文件

定義了三種輸出格式:

es:這是ES模塊格式。這種格式的模塊可以通過 importexport 關鍵字在現代瀏覽器或 Node.js 中使用。

cjs:這是CommonJS格式。這種格式的模塊可以通過 requiremodule.exportsNode.js 中使用。

umd:這是通用模塊定義格式。這種格式的模塊可以在各種環境中使用,包括瀏覽器(通過 標籤引入)和 Node.js

環境變量的設置
plugins: [
    nodeResolve(),
    json(),
    postcss(),
    ts(),
    importExportPlugin(),
    babel(),
    commonjs(),
    isProduction ? terser() : null, // 生成環境啓用壓縮
    replace({
        'process.env.API_URL': JSON.stringify(getEnvApiUrl()),
        'process.env.API_URL_MAP': JSON.stringify(API_URL_MAP),
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    })
]

環境變量的配置使用rollup-plugin-replace這個Rollup插件,它的作用是在源代碼中查找並替換特定的字符串,我們常用它進行處理環境變量。

比如:要根據不通的環境,請求不同的服務,可以在代碼中使用 process.env.API_URL 。在打包過程中,rollup-plugin-replace 插件,會根據當前的環境將這個字符串替換爲實際的值。

注意點
  1. 做好入參校驗,你不知道用戶是否傳遞以及傳遞何種類型的參數,做好入參校驗與錯誤提示。

  2. 對外 API 保持入參的數據結構統一。最好是都用對象來作爲入參,便於後續擴展更多參數

  3. 有些方法要有onCompleted 回調,比如init初始化完成後,有回調,便於用戶處理一些其他邏輯

  4. 下面是兩個自動寫註釋的 Vscode 插件

發佈、維護、更新

發佈到 CDN 或是 NPM

[名稱][prod/test][版本].js

將打包好的 js 文件進行發佈到 CDN,用戶通過引入鏈接的形式進行訪問

維護與更新

SDK 的維護更新關鍵是要做好兩個關鍵文檔的書寫:SDK 接入文檔SDK 維護文檔,這兩個文檔一個是對外的,便於開發者接入的文檔,一個是對內的,便於項目開發者的維護文檔,這兩個文檔需要包含一下內容:

總結

開發一款 JavaScript SDK 需要考慮到諸多方面,除了以上提到的,SDK 還需要考慮到用戶友好性、性能、安全性、易用性等方面,這裏就不展開了。另外,一個 SDK 寫的再好不如有個好文檔,特別是對外的 SDK 接入文檔,它可以爲開發者提供更好的開發體驗。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Sg5FsGvXjFzVFWPy-QBoVQ