一份 2.5k star 的《React 开发思想纲领》

Posted 小李的前端小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一份 2.5k star 的《React 开发思想纲领》相关的知识,希望对你有一定的参考价值。

翻译自:2.5k star
原文作者:
已获作者授权

时的一些思考

  • 每当我 review 他人或自己的代码时自然而然会思考的东西
  • 仅仅作为参考和建议,并非严格的要求
  • 会随着我的经验不断更新
  • 大多数技术点是基础的重构方法论SOLID 原则以及极限编程等思想的变体,仅仅是在 React 中的实践而已

    React jQuery公用组件celling的实现

      目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想。还有可复用组件化的思想等等。加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试。其实组件化的思想一直在提,原来的开发中也会抽一些公共的模块出来。但是react带来的思想冲击是革命性的,套用一句可能不太合适的话来,描述:万事万物皆组件,在这种思想的影响下,不管什么框架都可以抽一些公共的模块出来,应该秉持一种心态:任何代码都尽量不要重复写两遍,如果存在那么就可以考虑封装起来作为组件。当然不是一味的提倡盲目抽离,这个度还是要把握好的。

      但是,人无无人,何况物乎。react虽好,但是目前国内的现状是还存在一个让人颇为头疼的浏览器系列——微软的ie系列。虽然微软最新推出的ie系列已经不再那么特立独行了,但是用户不一定也会升级呀。我一个浏览器能用就行,干嘛费那个劲随时升级,而且有这个想法的不在少数。所以面向用户的系统,根据我们自己统计的浏览器使用情况(随手安利一下自己的浏览器统计工具https://github.com/future-team/cat-browser)。ie6这个货排名还不太靠后,遑论ie7-9!

      要兼容这些特立独行的文艺青年,react真的有点力不从心了。虽然有一些办法可以解决一些问题,例如引入es-shims转换es6语法的不备支持现象。但是整体来说还是不能用的。现身说法,前段时间一个项目使用react来开发,要求兼容ie8,但是react路由的hash值在ie8下面竟然会丢失。。。。最后还是用一些其他方式绕过了。所以jquery还是有存在的必要性的。

      要是开发两套组件,成本还是蛮大的,并且重复的工作量也不小。所以就有个想法能不能开发一个公用组件,jquery和react技术栈都可以使用。刚开始的时候也觉得不太现实,毕竟两种技术的定位和开发模式存在很大差异。不过空想是没什么用的,动手实践一下才是王道。(今天的前言说的有点多了。。。)所以打算实现一个celling组件这个名字还真不好想,就是实现简单的吸顶、吸底和中间特定条件下的吸顶的一个定位组件。

      首先分析一下实现的可能性:

      一、分析一下两者的不同:

      1、在于html的渲染:jquery毕竟只是个类库,具体dom元素还是要html来渲染或者其他方式插入,react通过自己的jsx语法将两者放在一起通过虚拟dom来渲染

      2、操作dom的方式:jquery通过直接操作真实dom来实现需求, react各个组件作为状态机,需要通过改变状态重新渲染自己的虚拟dom,通过diff算法决定更新于否

      3、事件的绑定处理方式的不同:jquery有自己的一套事件处理系统,react也同样。概而言之,两者都是在原生js的基础上进行了不同封装而已。

        综上而言,其实不同的主要在于与view相关的部分。但是作为一个相同功能的组件而言逻辑部分是相同的。这说明我们的封装时完全可以实现的。

      二、目前有利的技术: 

       虽然es6还未正式发布,但是babel的存在已经解决了这个问题。es6提出的一些新特性和语法糖,极大的简洁了某些繁琐的写法。特别是class和继承属性的增加,使得es6的继承已经变得相对优雅一些了。(相关es6可参考阮一峰的文章)

      三、实现思路和设计:

      原本设想的是抽离一个公共base类出来存放公共逻辑部分,jquery适用的类Jq和react适用的类Re 继承于该类,各自实现ui相关的部分。如下图所示(忽略现在已经不会画类图的细节):

        

      但是忽然后来想起来刚才提到的react,万事万物皆组件。。。。既然都是组件那么他们都继承于react提供的类Componet。因为多重继承是不现实的。所以当时就感觉有点暗无天日了。后来经过老司机提醒想起来,有一种模式就好像是为解决其而生的,那就是装饰者模式。

      关于装饰着模式这里只简单说两句,Decorators让我们能够在设计时对类、属性等进行标注和修改成为了可能。Decorators利用了ES5的Object.defineProperty来实现这一特性。简而言之装饰者处理传入的对象,为其增添一些静态或者实例方法或属性。在需要增加属性的class中通过@的方式来实现注入。修饰者模式具体可以参考http://www.cnblogs.com/whitewolf/p/details-of-ES7-JavaScript-Decorators.html

      现在的实现方式做了改变,react的class继承于component,jquery的不继承任何基类。相同的部分通过装饰者模式注入,如下图所示:

       

      装饰者具体实现示例如下:

      在具体的class注入公共的方法:

      四、具体的实现:

      基本思路通了之后,剩下的就是代码编写了:

      1、options.js:简单的配置文件,root指定要实现的元素,position:指定top,bottom,middle等方位.classnames.js匹配不同的position,获取不同的class。代码如下:

          

    let options = {
        root:\'\',
        position:\'top\'
    };
    
    let className = {
        top:\'fix-top\',
        bottom:\'fix-bottom\',
        middle:\'fix-top\'
    };

      2、关于公共Minix的抽离:因为实现组件只是很简单的定位组件,所以整体逻辑不多。主要增加了一些实例方法,获取不同定位的class,生成唯一key等,为了统一操作class,这里没有适用jq的方式,而是自己封装了一些方法,使得react和jquery通用。

     1 import options from \'./options.js\';
     2 import classNames from \'./className.js\';
     3 export default objs=>{
     4     objs.prototype.test= function(){
     5         console.log(\'test1\');
     6     };
     7     objs.prototype.getCName= function(pos = \'top\'){
     8         return classNames[pos];
     9     };
    10     objs.prototype.isMiddle= function(pos = \'top\'){
    11         return pos == \'middle\';
    12     };
    13     /**
    14      * 获取唯一的id
    15      * */
    16     objs.prototype.getUniq=function(){
    17         return \'cell\'+Math.floor(Math.random()*100);
    18     };
    19     /**
    20      * 是否有某class
    21      * */
    22     objs.prototype.hasClass = function(obj,cls){
    23         return obj.className.match(new RegExp(\'(\\\\s|^)\' +cls+ \'(\\\\s|$)\'));
    24     };
    25 
    26     /**
    27      * 增加classs
    28      * */
    29     objs.prototype.addClass = function(obj,cls){
    30         if (!this.hasClass(obj, cls)) {
    31             obj.className = (obj.className + " " + cls).replace(/\\s{2,}/g, " ");
    32         }
    33     };
    34     /**
    35      * 删除class
    36      * */
    37     objs.prototype.removeClass = function(obj,cls){
    38         if (this.hasClass(obj,cls)) {
    39             let reg = new RegExp(\'(\\\\s|^)\' + cls + \'(\\\\s|$)\');
    40             obj.className = arguments[0].className.replace(reg, \' \').split(" ").join(" ");
    41         }
    42     };
    43     /**
    44      * 取反
    45      * @param bool
    46      * */
    47     objs.prototype.getInvert = function(isBool){
    48         return !isBool;
    49     };
    50     /**
    51      * 获取初始的offsetTop
    52      * @param dom
    53      * */
    54     objs.prototype.getDTop = function(obj){
    55         return obj.offsetTop;
    56     };
    57 }

      3、各class的具体实现就比较简单了,通过@注入minix,然后实现特定的方法。这里以react为例:

     1 /**
     2  * react 适用
     3  * */
     4 import React, {PropTypes,Component} from \'react\';
     5 import ReactDom from \'react/lib/ReactDOM\';
     6 import CellMin from \'./utils/CellMixin.js\';
     7 import \'../css/cell-react.less\';
     8 import options from \'./utils/options.js\';
     9 @CellMin
    10 class ForReact extends Component{
    11     constructor(props,context) {
    12         super(props,context);
    13         this.uniquRef = this.getUniq();
    14     }
    15     static defaultProps = options;
    16     componentDidMount(){
    17         this.isMiddle && this.addEvent();
    18     }
    19     render(){
    20         return(
    21             <div ref={this.uniquRef} className={
    22                 this.getClass()
    23             }>
    24                 {this.props.children}
    25             </div>
    26         )
    27     }
    28     getClass(){
    29         let pos = this.props.position;
    30         this.isMiddle = this.isMiddle(pos);
    31         this.cls = this.getCName(pos);
    32         return !this.isMiddle ? this.cls :\'\';
    33     }
    34     /**
    35      * 监听滚动事件
    36      * */
    37     addEvent(){
    38         let cellDom = ReactDom.findDOMNode(this.refs[this.uniquRef]);
    39         this.isReset = true;
    40         let deTop = this.getDTop(cellDom);
    41         document.addEventListener("scroll",()=>{
    42           /**
    43            * 不再一一列出
    44            * */
    45         })
    46     }
    47 }
    48 module.exports = ForReact;

      到这里,适用于jquery和react的celling组件就基本完成了。引入的时候只需要单独引入需要的版本即可。

      总结一下,这种开发方式开始的时候确实不太适应。感觉如果熟练了,应该比开发不同框架的两套组件要省力。可能的隐患在于本文的组件只是功能很简单的例子,涉及到的逻辑和操作不是很多,目前看还算可以。但如果是功能比较复杂的组件,是否也可以做到比较彻底的封装抽离,就有待商榷了。参考文章http://www.cnblogs.com/whitewolf/p/details-of-ES7-JavaScript-Decorators.html

      此文还是抛砖,希望能得到大神的意见。最后源码地址https://github.com/future-team/multiple-celling

      转载请注明出处!!!

    以上是关于一份 2.5k star 的《React 开发思想纲领》的主要内容,如果未能解决你的问题,请参考以下文章

    26.9K Star,「程序员做饭指南」冲上热榜

    分享一份非常强势的多家BAT大厂的Android面试题(美团,滴滴)

    爆火的深度学习面试书现可白嫖!GitHub上线2周1.5K Star,之前售价146元

    源码解析 GitHub 上 14.1k Star 的 RocketMQ

    React-Native 开发 android & ios App,共享一份代码

    勿以 star 数论高低!React 在前端框架世界仍是领先地位