目前較爲流行的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
轉載請註明出處!!!