尝试造了个工具类库,名为 Diana

项目地址: diana

文档地址: http://muyunyun.cn/diana/

造轮子的意义

为啥已经有如此多的前端工具类库还要自己造轮子呢?个人认为有以下几个观点吧:

  • 定制性强,能根据自己的需求为主导延伸开发。万一一不小心还能帮到别人(比如 React 库);
  • 纸上得来终觉浅,很多流行的库,只是照着它们的 API 进行使用,其实这些库里蕴含着大量的知识、技巧,最好的办法就是仿照它们来写些小 demo,从而体会这些库的精髓;
  • 造轮子的过程中能让自己体会到与平常业务开发不一样的乐趣;比如和日常业务开发中很大的一个区别是会对测试用例具有比较严格的要求;而且写文档能力提升了。
  • 就先瞎编到这里了。。。

抛开内部方法(写相应的专题效果可能会更好,所以这里先略过),下面分享一些开发 diana 库 时的一些心得:

项目目录结构

├── 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

目录结构也随着方法的增多在不停迭代当中,建议直接到库中查看最新的目录结构。

相应地,具体的方法会随着时间迭代,所以首先推荐查看文档,点击如下图的 ? 就能查看源码。

让模块同时在 Node.js 与浏览器中运行

我们可以通过如下方法来判断模块当前是运行在 Node.js 还是浏览器中,然后使用不同的方式实现我们的功能。

// 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 都已经支持这个字段了。

给 browser 字段提供一个文件路径作为在浏览器端使用时的模块入口,但需要注意的是,打包工具会优先使用 browser 字段指定的文件路径作为模块入口,所以你的 main 字段 和 module 字段会被忽略,但是这会导致打包工具不会优化你的代码。详细信息请参考这个问题

diana 库 为了在不同环境中使用适当的文件,在 package.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

  • CommonJs 是服务器端模块的规范,Node.js 采用了这个规范。这些规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字、单元测试、服务器网关接口、包管理等。
  • 根据 CommonJS 规范,一个单独的文件就是一个模块。加载模块使用 require 方法,该方法读取一个文件并执行,最后返回文件内部的 exports 对象。
  • CommonJS 加载模块是同步的。像 Node.js 主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以 CommonJS 规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD、CMD 解决方案。

AMD、CMD

  • AMD 是 RequireJS 在推广过程中对模块定义的规范化产物。AMD 推崇提前执行。

     // AMD 默认推荐的是
    define(['./a', './b'], function(a, b) {
      a.doSomething()
      b.doSomething()
      ...
    })
  • CMD 是 SeaJS 在推广过程中对模块定义的规范化产物。CMD 推崇依赖就近。
    // CMD
    define(function(require, exports, module) {
      var a = require('./a')
      a.doSomething()
      var b = require('./b')
      b.doSomething()
      ...
    })

UMD

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 种类型,

  • 行覆盖率(line coverage):是否每一行都执行了?
  • 函数覆盖率(function coverage):是否每个函数都调用了?
  • 分支覆盖率(branch coverage):是否每个if代码块都执行了?
  • 语句覆盖率(statement coverage):是否每个语句都执行了?

番外:github 上显示的覆盖率是根据行覆盖率来展示的。

mocha + istanbul

最初的版本, 仅仅用到 mocha 进行测试 *.test.js 文件,然后在 codecov 得到测试覆盖率。

引人 karma

如果仅仅测试 es5、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 库 对造轮子的意义,模块兼容性,测试用例进行了思考总结。后续会对该库流程自动化以及性能上做些分享。
该库参考学习了很多优秀的库,感谢 underscoreoutilsec-do30-seconds-of-code 等库对我的帮助。

最后欢迎各位大佬在 issues 尽情吐槽。

时间: 2024-10-09 06:20:39

尝试造了个工具类库,名为 Diana的相关文章

个人一些小工具类库分享

分享一下个人弄的几个小工具类库 github地址 https://github.com/virtualcca/FoundationLibrary 这几个小类库都在我司各大主要系统里长期运作,效果良好. 主要包含: DapperWrapper ServiceClient DapperWrapper 作用:基于Dapper的实例类封装(用于配合依赖注入等使用Dapper) 参考对象:https://github.com/alterius/DapperWrapper 背景: 首先Dapper是个很优秀

编写更少量的代码:使用apache commons工具类库

Commons-configuration   Commons-FileUpload   Commons DbUtils   Commons BeanUtils  Commons CLI  Commons Codec   Commons Collections Commons DBCP    Commons HttpClient  Commons IO  Commons JXPath   Commons Lang   Commons Math   Commons Net   Commons Va

Java程序员常用工具类库

有人说当你开始学习Java的时候,你就走上了一条不归路,在Java世界里,包罗万象,从J2SE,J2ME,J2EE三大平台,到J2EE中的13中核心技术,再到Java世界中万紫千红的Framework......等等,你会发现自己总是在学习,但总是有自己不知道的东西. 当你看到代码工程的lib下各种各样的jar文件的时候,你是否能全部知道他们都是干什么用的,为什么要导入到工程下吗? 从Google你能搜索到大量的关于Struts,Spring,Hibernate,iBatis等比较大的框架的资料

WMI使用的WIN32_类库名

WMI使用的WIN32_类库名 包括:硬件类.操作系统类.安装应用程序类.WMI服务管理类.性能计数器类1.硬件类冷却类别Win32_Fan--风扇Win32_HeatPipe--热管Win32_Refrigeration--致冷Win32_TemperatureProbe--温度传感输入设备类别Win32_Keyboard--键盘 Win32_PointingDevice--指示设备(如鼠标) 大容量存储类别Win32_AutochkSetting--磁盘自动检查操作设置 Win32_CDRO

JDK源码简析--java.util包中的工具类库

题记 JDK,Java Development Kit. 我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列所有文章基于的JDK版本都是1.7.16. 本节内容 在本节中,简析java.util包所包

JSON工具类库: alibaba/fastjson 使用记录

JSON工具类库: alibaba/fastjson 使用记录 一.了解JSON JSON标准规范中文文档: http://www.json.org/json-zh.html 最佳实践:http://kimmking.github.io/2017/06/06/json-best-practice/ (JSON的高级使用,特别十分有参考价值) 二.项目地址和Wiki: Git地址: https://github.com/alibaba/fastjson Wiki:https://github.co

前端常用插件、工具类库汇总(下)

前言 对本文感兴趣可以先加个收藏,也可以转发分享给身边的小伙伴,以后遇到类似的场景就来看看具体的插件及其用法. 上一篇<前端常用插件.工具类库汇总(上)>内容摘要: " 动画库 滚动库 轮播图 滚屏 弹出框 消息通知 下拉框 级联选择器 颜色选择器 时间日期处理 表单验证 分页插件 " >>前端常用插件.工具类库汇总(上) 本篇延续上一篇的内容继续给大家带来一系列关于前端插件.工具类的内容. 富文本编辑器 wangEditor http://www.wanged

那些年尝试过的效率工具之Total Commander

昨天电脑文件很乱,想整理一下发现移动.复制文件要来回目录切换很麻烦,突然就又想起了用Total Commander——简称TC,很久之前尝试过但没坚持使用的工具. 借此机会总结一下自己对TC的认识,后续再陆续回顾总结一下尝试用过的其它效率工具. 1.尝试使用 刚工作那会儿,对提升效率软件很痴迷,无意中网上看到善用佳软介绍的TC, 发现资源管理占用系统资源不多,但是功能却可以做的如此强大: 左右双窗口,可以很方便的复制移动文件: 批量重命名,很方便的处理一些有规律的要重命名的场景: 展开目录树,可

五个值得尝试的前端开发工具

在过去的几年时间里,出现了许多全新的网页应用程序,不过,由于应用程序的功能越来越丰富,也导致了前端开发的复杂度大幅增加. 现在也有不少前端开发工具,比如Backbone和EmberJS框架都能提供稳定的App开发解决方案.同时,Javascript的应用也越来越常广泛,而且它还能和Node.JS在后端协同工作,快速搭建易于扩展的网络应用.实际上,为了应对前端开发复杂度所带来的挑战,开发人员创建了许多工具来简化开发流程.从测试框架,到分析工具,前端开发工具已经非常成熟了,正是得益于这些有用的工具才