React jQuery公用組件開發模式及實現

  目前較爲流行的react確實有不少優勢,例如虛擬dom,單向數據流狀態機的思想。還有可複用組件化的思想等等。加上搭配jsx語法和es6,適應以後開發確實快捷不少,值得你們去一試。其實組件化的思想一直在提,原來的開發中也會抽一些公共的模塊出來。可是react帶來的思想衝擊是革命性的,套用一句可能不太合適的話來,描述:萬事萬物皆組件,在這種思想的影響下,無論什麼框架均可以抽一些公共的模塊出來,應該秉持一種心態:任何代碼都儘可能不要重複寫兩遍,若是存在那麼就能夠考慮封裝起來做爲組件。固然不是一味的提倡盲目抽離,這個度仍是要把握好的。css

  可是,人無無人,況且物乎。react雖好,可是目前國內的現狀是還存在一個讓人頗爲頭疼的瀏覽器系列——微軟的ie系列。雖然微軟最新推出的ie系列已經再也不那麼特立獨行了,可是用戶不必定也會升級呀。我一個瀏覽器能用就行,幹嗎費那個勁隨時升級,並且有這個想法的不在少數。因此面向用戶的系統,根據咱們本身統計的瀏覽器使用狀況(隨手安利一下本身的瀏覽器統計工具https://github.com/future-team/cat-browser)。ie6這個貨排名還不太靠後,遑論ie7-9!html

  要兼容這些特立獨行的文藝青年,react真的有點力不從心了。雖然有一些辦法能夠解決一些問題,例如引入es-shims轉換es6語法的不備支持現象。可是總體來講仍是不能用的。現身說法,前段時間一個項目使用react來開發,要求兼容ie8,可是react路由的hash值在ie8下面居然會丟失。。。。最後仍是用一些其餘方式繞過了。因此jquery仍是有存在的必要性的。react

  要是開發兩套組件,成本仍是蠻大的,而且重複的工做量也不小。因此就有個想法能不能開發一個公用組件,jquery和react技術棧均可以使用。剛開始的時候也以爲不太現實,畢竟兩種技術的定位和開發模式存在很大差別。不過空想是沒什麼用的,動手實踐一下才是王道。(今天的前言說的有點多了。。。)因此打算實現一個celling組件這個名字還真很差想,就是實現簡單的吸頂、吸底和中間特定條件下的吸頂的一個定位組件。jquery

  首先分析一下實現的可能性:git

  1、分析一下二者的不一樣:es6

  一、在於html的渲染:jquery畢竟只是個類庫,具體dom元素仍是要html來渲染或者其餘方式插入,react經過本身的jsx語法將二者放在一塊兒經過虛擬dom來渲染github

  二、操做dom的方式:jquery經過直接操做真實dom來實現需求, react各個組件做爲狀態機,須要經過改變狀態從新渲染本身的虛擬dom,經過diff算法決定更新於否算法

  三、事件的綁定處理方式的不一樣:jquery有本身的一套事件處理系統,react也一樣。概而言之,二者都是在原生js的基礎上進行了不一樣封裝而已。瀏覽器

    綜上而言,其實不一樣的主要在於與view相關的部分。可是做爲一個相同功能的組件而言邏輯部分是相同的。這說明咱們的封裝時徹底能夠實現的。babel

  2、目前有利的技術: 

   雖然es6還未正式發佈,可是babel的存在已經解決了這個問題。es6提出的一些新特性和語法糖,極大的簡潔了某些繁瑣的寫法。特別是class和繼承屬性的增長,使得es6的繼承已經變得相對優雅一些了。(相關es6可參考阮一峯的文章)

  3、實現思路和設計:

  本來設想的是抽離一個公共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注入公共的方法:

  4、具體的實現:

  基本思路通了以後,剩下的就是代碼編寫了:

  一、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'
};

  二、關於公共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 }

  三、各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

  轉載請註明出處!!!

相關文章
相關標籤/搜索