項目地址: dianahtml
文檔地址: muyunyun.cn/diana/前端
爲啥已經有如此多的前端工具類庫還要本身造輪子呢?我的認爲有如下幾個觀點吧:node
拋開內部方法(寫相應的專題效果可能會更好,因此這裏先略過),下面分享一些開發 diana 庫 時的一些心得:webpack
├── LICENSE 開源協議
├── README-zh_en.md 英文說明文檔
├── README.md 中文說明文檔
├── coverage 代碼覆蓋率文件
├── docs 文檔目錄
│ └── static-parts
│ ├── index-end.html 靜態文檔目錄結尾文件
│ └── index-start.html 靜態文檔目錄開頭文件
├── karma.conf.js karma 配置文件
├── lib
│ ├── diana.back.js 服務端引用入口
│ └── diana.js 瀏覽器引用入口
├── package.json
├── script
│ ├── build.js 構建文件
│ ├── check.js 結合 pre-commit 進行 eslint 校驗
│ ├── tag-script.js 自動生成文檔的標籤
│ ├── web-script.js 自動生成文檔
│ ├── webpack.browser.js 瀏覽器端 webpack 配置文件
│ └── webpack.node.js 服務器端 webpack 配置文件
├── snippets
├── src
│ ├── browser 瀏覽器端方法
│ ├── common 共用方法
│ ├── node node 端方法
│ └── util.js 庫內通用方法
├── tag_database 文檔標籤
└── test 測試文件
├── browserTest
├── commonTest
├── index.js
└── nodeTest
複製代碼
目錄結構也隨着方法的增多在不停迭代當中,建議直接到庫中查看最新的目錄結構。git
相應地,具體的方法會隨着時間迭代,因此首先推薦查看文檔,點擊以下圖的 Ⓢ 就能查看源碼。es6
咱們能夠經過以下方法來判斷模塊當前是運行在 Node.js 仍是瀏覽器中,而後使用不一樣的方式實現咱們的功能。github
// Only Node.JS has a process variable that is of [[Class]] process
const isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'
複製代碼
但若是用戶使用了模塊打包工具,這樣作會致使 Node.js 與瀏覽器的實現方式都會被包含在最終的輸出文件中。針對這個問題,開源社區提出了在 package.json 中添加 browser 字段的提議,目前 webpack 和 rollup 都已經支持這個字段了。web
給 browser 字段提供一個文件路徑做爲在瀏覽器端使用時的模塊入口,但須要注意的是,打包工具會優先使用 browser 字段指定的文件路徑做爲模塊入口,因此你的 main 字段 和 module 字段會被忽略,可是這會致使打包工具不會優化你的代碼。詳細信息請參考這個問題。編程
在 diana 庫 爲了在不一樣環境中使用適當的文件,在 package.json 中進行了以下聲明:json
"browser": "lib/diana.js",
"main": "lib/diana.back.js", // 或者 "module": "lib/diana.back.js",
複製代碼
這樣一來,在 node 環境中,引用的是 lib/diana.back.js
文件,在瀏覽器環境中,引用的是 lib/diana.js
文件。而後就能愉快地在瀏覽器端和 node 端愉快地使用本身特有的 api 了。
另外爲了使 diana 庫 的打包文件兼容 node 端、以及瀏覽器端的引用,選擇了 UMD 規範進行打包,那麼爲何要選擇 UMD 規範呢?讓咱們看下如下幾種規範之間的異同:
CommonJs 是服務器端模塊的規範,Node.js 採用了這個規範
。這些規範涵蓋了模塊、二進制、Buffer、字符集編碼、I/O流、進程環境、文件系統、套接字、單元測試、服務器網關接口、包管理等。
根據 CommonJS 規範,一個單獨的文件就是一個模塊。加載模塊使用 require
方法,該方法讀取一個文件並執行,最後返回文件內部的 exports
對象。
CommonJS 加載模塊是同步的。像 Node.js 主要用於服務器的編程,加載的模塊文件通常都已經存在本地硬盤,因此加載起來比較快,不用考慮異步加載的方式,因此 CommonJS 規範比較適用。但若是是瀏覽器環境,要從服務器加載模塊,這是就必須採用異步模式。因此就有了 AMD、CMD 解決方案。
// AMD 默認推薦的是
define(['./a', './b'], function(a, b) {
a.doSomething()
b.doSomething()
...
})
複製代碼
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b')
b.doSomething()
...
})
複製代碼
UMD 是 AMD 和 CommonJS 的結合。由於 AMD 是以瀏覽器爲出發點的異步加載模塊,CommonJS 是以服務器爲出發點的同步加載模塊,因此人們想出了另外一個更通用的模式 UMD,來解決跨平臺的問題。
diana 庫 選擇了以 umd 方式進行輸出,來看下 UMD 作了啥:
(function (root, factory) {
if (typeof exports === 'object' && typeof module === 'object') { // UMD 先判斷是否支持 Node.js 的模塊(exports)是否存在,存在則使用 CommonJS 模式
module.exports = factory()
} else if (typeof define === 'function' && define.amd) { // 接着判斷是否支持 AMD(define是否存在),存在則使用 AMD 方式加載模塊。
define([], factory)
} else if (typeof exports === 'object') { // CommonJS 的另外一種形式
exports['diana'] = factory()
} else
root['diana'] = factory() // Window
})(this, function() {
return module
})
複製代碼
單元測試的代碼覆蓋率統計,是衡量測試用例好壞的一個的方法。但凡是線上用的庫,基本上都少不了高質量的代碼覆蓋率的檢測。以下圖爲 diana 庫的測試覆蓋率展現。
能夠看到覆蓋率分爲如下 4 種類型,
最初的版本, 僅僅用到 mocha 進行測試 *.test.js 文件,而後在 codecov 獲得測試覆蓋率。
若是僅僅測試 es五、es6 的語法,其實用 mocha 就已經夠用了,可是涉及到測試 Dom 操做的語法等就必須創建一個瀏覽器,在上面進行測試。karma 的做用其實就是自動幫咱們創建一個測試用的瀏覽器環境。
爲了讓瀏覽器支持 Common.js 規範,中間用了 karma + browserify,儘管測試用例都跑通了,可是最後的代碼覆蓋率的文件裏只有各個方法的引用路徑。最後只能又回到 karma + webpack 來,這裏又踩到一個坑,打包編譯JS代碼覆蓋率問題,踩了一些坑後,終於實現了能夠查看編譯前代碼的覆蓋率。圖以下:
經過這幅圖咱們能清晰地看到源代碼中測試用例跑過各行代碼的次數(左側的數字),以及測試用例沒有覆蓋到的代碼(圖中紅色所示)。而後咱們就能改善相應的測試用例從而提升測試覆蓋率。
配置文件,核心部分以下:
module.exports = function(config) {
config.set({
files: ['test/index.js'], // 需載入瀏覽器的文件
preprocessors: { // 預處理
'test/index.js': ['webpack', 'coverage']
},
webpack: {
module: {
rules: [{
test: /\.js$/,
use: { loader: 'sourcemap-istanbul-instrumenter-loader' }, // 這裏用 istanbul-instrumenter-loader 插件的 0.0.2 版本,其它版本有坑~
exclude: [/node_modules/, /\.spec.js$/],
}],
}
},
coverageReporter: {
type: 'lcov', // 貌似只能支持這種類型的讀取
dir: 'coverage/'
},
remapIstanbulReporter: { // 生成 coverage 文件
reports: {
'text-summary': null,
json: 'coverage/coverage.json',
lcovonly: 'coverage/lcov.info',
html: 'coverage/html/',
}
},
reporters: ['progress', 'karma-remap-istanbul'], // remap-isbanbul 也報了一個未找到 sourcemap 的 error,直接註釋了 remap-istanbul 包的 CoverageTransformer.js 文件的 169 行,之後有機會再搗鼓吧。(心累)
...
})
}
複製代碼
本文圍繞 diana 庫 對造輪子的意義,模塊兼容性,測試用例進行了思考總結。後續會對該庫流程自動化以及性能上作些分享。 該庫參考學習了不少優秀的庫,感謝 underscore、outils、ec-do、30-seconds-of-code 等庫對個人幫助。
最後歡迎各位大佬在 issues 盡情吐槽。