如何開發一款 JSSDK
本文作者系 360 奇舞團前端開發工程師
JSSDK 的定義與分類
什麼是 JSSDK
SDK 是Software Development Kit
(軟件開發工具包)的縮寫,JSSDK
是爲了幫助前端實現特定需求,而向開發者暴露的一些JS-API
的集合,開發者可以通過它在網頁中集成和使用某些特定的功能,例如社交分享、地圖服務、支付功能等. 它通常包括一下模塊:
-
JavaScript
庫:這是JSSDK
的核心部分,包含了一系列預先編寫好的JavaScript
代碼,這些代碼實現了一些特定的功能,如用戶認證、數據分析、社交分享等。 -
API
文檔:這部分文檔詳細描述瞭如何使用JSSDK
中的各個功能。它通常會包含函數和方法的列表,以及如何使用這些函數和方法。 -
示例代碼
:這部分代碼展示瞭如何在實際項目中使用JSSDK
。通過閱讀和運行示例代碼,開發者可以更好地理解如何使用JSSDK
。
JSSDK 的分類
JSSDK
的分類主要取決於它們提供的功能和服務。通常根據其功能不同分爲:UI 組件庫、JS 工具庫、分析統計 SDK、社交媒體 SDK
-
UI組件庫
:通過封裝一系列組件,通過配置幫助開發者調用,實現一些 UI 效果,例如:Antd
、ElementUI
、Vant
、Bootstrap
等 -
JS類庫
:通過實現一類常用的方法,便於開發處理數據,也不用再考慮兼容性,常用的如:lodash
、moment
、axios
、jQuery
等 -
監控統計工具
:通過 API,來監聽前端系統的報錯、統計數據,以及數據上報等,例如:Sentry
、百度統計、Google Analytics JSSDK
等 -
其他第三方的SDK
:微信 SDK、支付寶 SDK、新浪微博 SDK,Facebook JSSDK
等
如何設計 JSSDK
設計 SDK 要遵循的原則
-
單一職責原則
:SDK 一般都是爲了滿足一類業務的需要,所以設計之初要明確業務範圍,如果功能過於複雜,可以拆改成幾個獨立的 SDK,在主 SDK 中引入相關邏輯就 OK 了,這樣便於功能解耦和測試。 -
最小可用、最少依賴性原則
: 能用確定的方法實現,就不要再去搞複雜的內容。SDK 要減少依賴,一些工具庫能不用就不用。儘可能自行實現必要的方法,或者引入儘量少的庫。否則會導致 SDK 打包後過大,或者更新版本帶來的兼容問題。 -
穩定性原則
: 保持 SDK 足夠的穩定性,一方面要保持 API 方法的穩定,另一方面在設計 API 的入參時,參數要用對象類型,這樣便於後續擴展添加更多參數。如果 SDK 有升級要考慮兼容舊版本。新功能要新增 API,舊的 API 不要刪除。 -
易用性原則
: 要滿足易用性原則,首先要有簡潔的 API,明確的方法註釋、文檔跟示例代碼很重要,不管 SDK 寫的多漂亮,使用者首先接觸的是接入文檔,然後是覆蓋所有 API 的示例代碼,一個好的實例代碼能起到事半功倍的效果。
語言與構建工具選擇
- 語言的選擇 語言無非是選擇
TS
還是JS
, 這裏推薦使用TS
來編寫 SDK 代碼因爲相比JS
而言他有如下優勢:
-
類型系統
:TypeScript
的最大特點就是它的類型系統。這個類型系統可以在編譯階段捕獲許多常見的錯誤,如類型錯誤、未定義的函數或屬性等,從而提高代碼的質量和可維護性。 -
更好的工具支持
:由於TypeScript
的類型信息,IDE
和編輯器可以提供更好的代碼補全、自動提示等功能。
- 構建工具的選擇 推薦使用
Rollup
來作爲 SDK 項目的打包構建工具
-
Rollup
的配置通常比Webpack
更簡單,更易於理解。這對於新手或者希望快速開始項目的開發者來說是一個優點。 -
更好的
Tree-shaking
:Rollup
的Tree-shaking
功能通常比Webpack
更有效。Tree-shaking
是一種只包含實際使用的代碼,而去除未使用代碼的優化技術。這可以幫助減小最終打包文件的大小。 -
Rollup
是以ES6
模塊標準爲中心設計的,這使得它在處理ES6
模塊時更加高效。 -
適合庫的打包:由於
Rollup
的特性,它通常更適合用於打包庫(library)或者工具,而Webpack
則更適合用於打包應用(application
)。
SDK 核心功能的實現
需求
假設我們開發一款問卷投放 SDK,滿足問卷的自動投放、代碼控制投放、事件觸發投放、問卷回收等邏輯。注意單一職責原則的應用,只做問卷的投放、回收邏輯。問卷裏面內容展示,以及內部相關邏輯不涉及。
項目結構
src
: 源代碼
-
api/index.t
s: 網絡請求的 API 封裝 -
core/index.ts
: 核心入口文件 -
core/dependencies.ts
: 核心邏輯實現文件 -
style/index.css
: 樣式文件 -
types/index.ts
: 聲明的類型文件 -
utils/constants.ts
: 常量文件 -
utils/http.ts
: 網絡請求封裝庫 -
utils/index.ts
: 工具函數 -
utils/pv.ts
: 路由監聽處理函數
API 的設計
SDK API
的設計根據功能分爲兩大類:生命週期方法
與 實例方法
.
-
生命週期方法:主要是 SDK 的生命週期回調函數:從 init 初始化方法,問卷的打開與關閉回調。
-
實例方法:負責具體的業務實現
需要注意的是在設計對外 API 時,每個 API 都要有方法執行完成後回調,便於用戶執行後續邏輯。
項目架構
Index
入口文件裏面主要是對外的 SDK 生命週期方法與實例方法。首先調用 initQaSDK, 完成問卷 SDK 實例的創建, 裏面主要包含:
-
單例模式來創建實例
-
接受用戶的必要入參與參數校驗
-
設置路由變化監聽
-
完成與服務端的對接邏輯
Dependencies
:裏面是核心邏輯層的實現。單獨抽出一個文件的目的是,便於方法的複用。再下面是對Dom
的操作:Dom
元素的創建、更新、與移除邏輯,最底下是依賴語言與構建工具
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.mjs
是 Rollop
構建工具的核心配置文件,相當於 webpack.config.js
文件在 Webpack
構建工具中的作用。用於定義構建過程的各種設置。例如,入口文件、輸出文件、模塊解析規則、插件等。
-input
: 入口文件
output
: 輸出配置項
定義了三種輸出格式:
es
:這是ES
模塊格式。這種格式的模塊可以通過 import
和 export
關鍵字在現代瀏覽器或 Node.js 中使用。
cjs
:這是CommonJS
格式。這種格式的模塊可以通過 require
和 module.exports
在 Node.js
中使用。
umd
:這是通用模塊定義格式。這種格式的模塊可以在各種環境中使用,包括瀏覽器(通過 標籤引入)和 Node.js
-
plugins
: 依賴的插件列表 -
external
: 忽略打包模塊列表 -
cache
: 構建緩存,通過增量構建提高構建速度
環境變量的設置
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
插件,會根據當前的環境將這個字符串替換爲實際的值。
注意點
-
做好入參校驗,你不知道用戶是否傳遞以及傳遞何種類型的參數,做好入參校驗與錯誤提示。
-
對外 API 保持入參的數據結構統一。最好是都用對象來作爲入參,便於後續擴展更多參數
-
有些方法要有
onCompleted
回調,比如init
初始化完成後,有回調,便於用戶處理一些其他邏輯 -
下面是兩個自動寫註釋的 Vscode 插件
發佈、維護、更新
發佈到 CDN 或是 NPM
- 爲了便於用戶使用,SDK 要發佈 CDN 與 NPM 上,打包完成後,js 文件要有一定的命名規範:
[名稱][prod/test][版本].js
將打包好的 js 文件進行發佈到 CDN,用戶通過引入鏈接的形式進行訪問
-
Npm
發佈需要注意的是,如果要將包發佈到 npm 上,需要設置
package.json
的幾個關鍵參數:•
name
: 名稱•
version
:版本:遵循Major. Minor. Patch
規範•
description
:描述•
main
:主文件•
keywords
:SDK 搜索關鍵詞•
maintainers
: 列表,維護者信息可以是多個最後執行:
npm login npm publish
進行包的發佈
維護與更新
SDK 的維護更新關鍵是要做好兩個關鍵文檔的書寫:SDK 接入文檔與 SDK 維護文檔,這兩個文檔一個是對外的,便於開發者接入的文檔,一個是對內的,便於項目開發者的維護文檔,這兩個文檔需要包含一下內容:
-
SDK 接入文檔(對外)
• SDK 簡介
• SDK 支持的平臺
• SDK 接入方式:
cdn & npm
• SDK API 的介紹:API 簡介 & 示例 & 參數說明
• 示例代碼:
example
-
SDK 維護更新文檔(對內)
• SDK 目錄文件說明
• 如何運行調試 SDK
• 打包發佈 SDK
• SDK 更新記錄:版本 & 發佈時間 & cdn 地址 & 更新內容
總結
開發一款 JavaScript SDK
需要考慮到諸多方面,除了以上提到的,SDK 還需要考慮到用戶友好性、性能、安全性、易用性等方面,這裏就不展開了。另外,一個 SDK 寫的再好不如有個好文檔,特別是對外的 SDK 接入文檔,它可以爲開發者提供更好的開發體驗。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Sg5FsGvXjFzVFWPy-QBoVQ