从工程化角度讨论如何快速构建可靠React组件

原文连接javascript

前言

React 的开发也已经有2年时间了,先从QQ的家校群,转成作互动直播,主要是花样直播这一块。切换过来的时候,业务很是繁忙,接手过来的业务比较凌乱,也没有任何组件复用可言。css

为了提升开发效率,去年10月份也开始有意识地私下封装一些组件,而且于今年年初在项目组里发起了百日效率提高计划,其中就包含组件化开发这一块。html

本文并非要谈如何去写一个 React 组件,这一块已经有很多精彩的文章。本文关键词是三个,工程化、快速和可靠。工程化是手段和工具,快速和可靠,是咱们但愿达到的目标。前端

前端工程化不外乎两点,规范和自动化。java

读文先看此图,能先有个大致概念:
node

default

规范

目录与命令规范

规范,主要就是目录规范和代码规范。跟同事合做,通过将近20个的组件开发后,咱们大概造成了必定的目录规范,如下是咱们大体的目录约定。哪里放源码,哪里放生产代码,哪里是构建工具,哪里是例子等。有了这些的约定,往后开发和使用并一目了然。react

__tests__ -- 测试用例
|
example -- 真实demo
|
dist -- 开发者使用代码
|
src -- 源代码
|
config -- 项目配置
|------project.js -- 项目配置,主要被 webpack,gulp 等使用
|      
|   
tools  -- 构建工具
|  
|——————start.js -- 开发环境执行命令
|——————start.code.js -- 开发环境生成编译后代码命令
|
package.json复制代码

命令咱们也进行了规范,以下,webpack

// 开发环境,服务器编译
npm start 或者 npm run dev

// 开发环境,生成代码
npm run start.code

// 生产环境
npm run dist

// 测试
npm test

// 测试覆盖率
npm run coverage

// 检查你的代码是否符合规范
npm run lint复制代码

代码规范

代码规范,主要是写 js,csshtml 的规范,基本咱们都是沿用团队以前制定好的规范,若是以前并无制定,例如 React 的 jsx 的写法,那么咱们就参考业界比较优秀的标准,并进行微调,例如 airbnbJavaScript 规范,是不错的参考。git

自动化

开发与发布自动化

规范是比较人性的东西,凭着人对之的熟悉就能够提升效率了,至于那些工做繁复的流程,单凭人的熟悉也会达到极限,那么咱们就须要借助自动化的工具去突破这重极限。es6

例如代码规范,单凭人的肉眼难以识别全部不合规范的代码,并且效率低下,借助代码检测工具就可以让人卸下这个重担。如 css ,咱们推荐使用 stylelint ,js 则是 eslint。有这种自动化的工具协助开发者进行检查,能更好地保障咱们的代码质量。

自动化最为重要的任务是,去保证开发过程良好的体验还有发布生产代码。实际上,开发和发布组件的整个过程跟平时开发一个任务很像,但却又略有差别。

首先是开发过程当中,咱们但愿一边开发的时候,咱们开发的功能可以显示出来,这时最好能搭建一个demo,咱们把 demo 放到了 example 目录下,这点对 UI 组件(像toast, tips等组件) 尤其重要,逻辑组件(像ajax, utils等组件),能够有 demo,也能够采起测试驱动开发的方式,先制定部份测试用例,而后边开发边进行测试验证。

开发过程当中的这个 demo, 跟平时开发项目基本一致,咱们就是经过配置,把 html,js, css 都搭建好,并且咱们是开发 React 组件,引入热替换的功能令整个开发流程很是流畅。这里分别是 webpack 和配合 `webpack 开发的静态资源服务器的两份配置: webpack & server

可是发布组件的这个过程跟开发项目却又很不一样。开发项目,咱们须要把全部的依赖都打包好,而后一并发布。但对于组件来讲,咱们只须要单独将它的功能发布就行了,它的相关依赖能够在实际开发项目中引用时一并再打包。所以这里的 package.json 写的时候也要有所区分。跟只跟开发流程、构建、测试相关的,咱们一概放在 devDependencies 中,组件实际依赖的库,则主要放在 dependencies 中。

鉴于咱们项目通常采用 webpack 打包,所以咱们通常只须要 es6 import 的引入方式,那咱们直接用 babel 帮咱们的项目进行生产代码的编译打包就能够了,这样能有效减小冗余代码。配置好 .babelrc,而后配置 package.json 的打包命令便可。要注意的是,你的组件可能含有样式文件,配置命令的时候要记得将样式文件也复制过去,像下面的命令,--copy-files 参数就是为了将样式文件直接拷贝到 dist 目录下。

babel src --out-dir dist --copy-files复制代码

但有时候,你也想组件能兼容多种引用方式,即 umd,那 babel 的这种打包就不够了。这时你能够借助 webpack 打包 library 的能力。可参考此 配置。主要是配置 output.libraryoutput.libraryTarget

output: {
       // other config
        library: "lib",   // 表示以什么名字输出,这里,会输出为如 exports["lib"]
        libraryTarget: "umd", // 表示打包的方式
},复制代码

另外一点要注意的是,咱们只需打包组件的逻辑就行了,那些依赖,能够等实际生产项目的时候再进行解析。但 webpack 默认会将依赖也打包进行,为了不这点,你须要将这些依赖一一配置成为 external,这就告诉了 webpack 它们是外部引用的,能够不用打包进来。

打包完成以后,根据指引进行 npm publish 就能够了。这里大致总结了一下咱们开发组件的一些流程和注意事项

测试自动化

上述讲的都跟如何提高开发效率有关的,即知足 “快速” 这个目标,对 ”可靠“ 有必定帮助,如稳定的流程和良好的代码规范,但并无很是好地保证组件地稳定可靠。须要 ”可靠“的组件,还须要测试来保证。

很多开发者作测试会使用 mocha,若是是 UI 组件可能会配置上 karma。而 React 组件测试还有一个更好的选择,就是官方推荐的 jest + enzyme

jestjasmine 有点相似,将一个测试库的功能大部份集成好了(如断言等工具),一键安装 babel-jest 能够用 es6 直接写测试用例,搭配 jest-environment-jsdomjsdom 可以模拟浏览器环境,结合 airbnb 写的 react 测试库 enzyme, 基本能知足大部份的 React 测试需求。确实符合官方的宣传语 painless,这是一个无痛的测试工具。

测试逻辑组件问题倒不大,UI组件对于大部份的状况均可以,许多事件均可以经过enzyme 模拟事件进行测试。但这里举的例子, react-list-scroll 组件,一个 React 的滚动列表组件,碰巧遇到一种比较难模拟的状况,就是对 scroll 事件的模拟。这里想展开说一下。

对于 Reactscroll 事件而言,必需要绑定在某个元素里才能进行模拟,不巧,对于安卓手机来讲,大部份 scroll 事件都是绑定在 window 对象下的。这就很是尴尬了,须要借助到 jsdom 的功能。经过 jest-environment-jsdom,它可以将 jsdom 注入到 node 运行环境中,所以你能够在测试文件中直接使用 window 对象进行模拟。例以下面代码,模拟滚动到最底部:

test('scroll to bottom', (done) => {

    const wrapper = mount(<Wrapper />);

    window.addEventListener('scroll', function(e) {
        setTimeout(() => {
            try {
                // expect 逻辑
                done();
            }
            catch(err) {
                done.fail(err);
            }
        }, 100);
        jest.runAllTimers();

    });

    let scrollTop = 768;
    window.document.body.scrollTop = scrollTop; // 指明当前 scrollTop到了哪一个位置
    window.dispatchEvent(new window.Event('scroll', {
        scrollTop: scrollTop
    }));

});复制代码

细心的你会发现,上图还有一些定时器的逻辑。缘由是在组件中会有一些截流的逻辑,滚动时间隔一段时间才去检测滚动的位置,避免性能问题,所以加一个定时器,等待数据的返回,而 jest.runAllTimers(); 则是用于告诉定时器立刻跑完。

除此以外,定时器里还有个 try catch 的逻辑,主要是若是 expect 验证不经过,jest 会报告错误,这时需用错误捕获的办法将错误传给 done (异步测试的回调),这样才能正常退出这一个测试用例,不然会返回超时错误。

安卓测完了,那iPhone呢?iPhone 的 scroll 事件是绑定在具体某个元素里的,但我这里又不是经过 React 的 onScroll 来绑定。首先咱们得经过 window.navigator.userAgent 来区分手机类型。但因为 userAgent 只有 getter 函数,直接设置值会报错,所以咱们要添加一个 setter 函数给它,用这段示例代码:

Object.defineProperty(window.navigator, "userAgent", (function(_value){
  return {
    get: function _get() {
      return _value;
    },
    set: function _set(v) {
        _value = v;
    }
  };
})(window.navigator.userAgent));

let str = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1";
window.navigator.userAgent = str;复制代码

而后,去找到这个绑定的元素,进行事件监听和分发就行了:

const wrapper = mount(<Wrapper />),
      scrollComp = wrapper.find(Scroll),
      scrollContainer = scrollComp.nodes[0].scrollContainer;

scrollContainer.addEventListener('scroll', function(e) { //... });

scrollContainer.dispatchEvent(// ... );复制代码

总结

本文主要是提取了开发组件工程化的一些关键要点,具体的开发脚手架能够参考 steamer-react-component,里面主要举了pure-render-deepCompare-decoratorreact-list-scroll,一个逻辑组件,一个UI组件,共两个示例,对照着脚手架的文档,从目录规范、开发流程、发布都写得较为清楚,你们开发组件的时候,能够根据状况作些调整。

若有谬误,恳请斧正。


本文对你有帮助?欢迎扫码加入前端学习小组微信群:

相关文章
相关标签/搜索