ホームページ >ウェブフロントエンド >htmlチュートリアル >DOM 操作から Vue&React のフロントエンドのコンポーネント化を確認し、React の demo_html/css_WEB-ITnose を補足します。
上の続き: フロントエンドのコンポーネント化における「コンポーネント」についての私の理解をお話しし、Vue と React のデモを書きます
前回のブログを書き終えた後、何人かの友人がこう言いました。最初の内容は少し深かったので、混乱しているように見えました。2番目に、Vue&reactのソースコード分析を直接記述する方が良いと思いました。ここで理解すること。
まず第一に、ソースコード分析を書くのは本質的に難しいため、一般的にはコードではなく実装のアイデアを理解する必要があります
第二に、開発者にはそれぞれ独自のスタイルがあるため、一人の人のコードを徹底的に理解することは、その時点での作成者と同じ問題の解決策を常に探し、常にリファクタリングしていなければ、ユーザーの意図を理解することは容易ではありません。
私たちが最後に行ったのは、実際の実務経験に基づいて、コードの堅牢性と優雅さが追いつくことができなかったものの、他の作者と同じように、同じ問題を解決することを考えた計画でした。前回は遅すぎて急いで終了しました。実際、デモのプロセス中に、ビジネスコードはほぼ同じですが、細部が異なるため、製品の品質を決定するのは依然として開発者であるという事実を発見しました。ビジネス コードの機能を強化するために、フレームワークは単なる補助にすぎません。
この種のコードは大規模なアプリケーション向けであるため、React や Vue のようなコンポーネント化されたフレームワークを使用しても、実際にはコードをうまく整理することはできません。 、それは人の建築能力をさらにテストするので、誰もが内面の育成の向上にもっと注意を払う必要があります。
今日の本題に移りましょう。ここで、理解を助けるためにいくつかの情報を提供します。
コード アドレス: https://github.com/yexiaochai/module/
デモ アドレス: http:/ /yexiaochai.github .io/module/me/index.html
記事内のいくつかのコードについて混乱している場合は、次の記事を比較して読むことができます:
[インタビュー] JavaScript の継承についてもう一度話します
[モバイルフロントエンド開発実践】ゼロから知っておきたいこと(統計、リクエスト、MVC、モジュール化)より H5開発指南
【コンポーネントベース開発】フロントエンド上級編:保守性とアップグレード性の高いコードの書き方
最初のデモは Vue 用であり、React も同様であるはずです。 前のコードを比較すると、重要な違いが見つかりました:
DOM操作真的完全没有了!!!
はい、これはまったく DOM 操作がありません。素晴らしいのは、2 つの場所があると思うからです。 DOM 操作を取り除くのは難しいです:
① コンポーネントをどのコンテナに配置する必要があるか? 次のような位置決め要素が必要です:
1 this.sortModule = new SortModule({2 view: this,3 selector: '.js_sort_wrapper',4 sortEntity: this.sortEntity5 });
コンテナにどちらを配置するかを明確に指示します。コンポーネントが属します
② このようなリストについては、さらに混乱しています このタイプのイベントをどのように処理するか、次のような必要なパラメータがイベントに基づいて取得されるためです:
1 listItemClick: function (e) {2 var el = $(e.currentTarget);3 //根据el做一些事情4 }
これに関して、Vue の作者は、イベントがハンドラーはインライン化して表示ステートメントを作成する必要があります:
你可能注意到这种事件监听的方式违背了传统理念 “separation of concern”。不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护困难。实际上,使用 v-on 有几个好处:扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
<button v-on:click="say('hello!', $event)">Submit</button>
1 methods: {2 say: function (msg, event) {3 // 现在我们可以访问原生事件对象4 event.preventDefault()5 }6 }
一般的に使用される操作 (radioList など) がもう 1 つあります。現在のオプションをクリックして項目を選択します。一般的なアプローチは次のとおりです。
1 setIndex: function (i) {2 this.index = i;3 this.$('li').removeClass(this.curClass);4 this.$('li[data-index="' + i + '"]').addClass(this.curClass);5 }
これは相対的なものです。シンプルですが、問題が発生します。つまり、データと dom 表現のプロセスが変更されています。正しいプロセスはインデックス変更です。dom は、Vue:
1 setIndex: function (i) {2 this.index = i;3 //这部分逻辑Vue会自动实现4 //this.$('li').removeClass(this.curClass);5 //this.$('li[data-index="' + i + '"]').addClass(this.curClass);6 }
Before などのデータに基づいて更新されます。パフォーマンスを向上させるためには、単純な選択機能としてデータに基づいてリスト全体を直接再レンダリングしますが、Vue&React は部分的なレンダリングを実現しています。これはコア アルゴリズムの部分になると思います。これを理解する必要があります。時間があるときに後で深くしてください。
上記の部分的な解釈に基づいて、次の 2 つの条件が満たされる限り DOM 操作を削除できるという結論に達します:
① コンポーネントが配置されているコンテナを知る
② に基づいてページをレンダリングするデータ
追記: ここにあるのは、コンポーネントのネストやコンポーネント通信などの過度に複雑な問題を考慮していない、非常に単純な 1 つのリングです
したがって、上記の条件が満たされている場合、ビジネス ロジックに DOM 操作を含めなくてもよいでしょうか?次に試してみましょう。
これは、実際には、以前の複雑すぎるビジネス ロジックを使用せずに、検証を行うデモ クラスの試みです。ここで、me ディレクトリを一緒にコピーし、元のコードを基礎となる依存関係として使用します。リストと最上位のソート関数については、実装を簡素化し、コードの再利用を維持するために、エンティティ モジュールを直接再利用する必要があります。最初のステップは、データ内のオブジェクトがエンティティ インスタンスである必要があります。ここでは list モジュール module を抽象化するため、メイン コントロールは次のようになります。実際、現時点では DOM 操作はありません。
1 initEntity: function () { 2 //实例化排序的导航栏的实体 3 this.sortEntity = new SortEntity(); 4 this.sortEntity.subscribe(this.renderList, this); 5 }, 6 7 initModule: function () { 8 //view为注入给组件的根元素 9 //selector为组件将要显示的容器10 //sortEntity为注入给组件的数据实体,做通信用11 //这个module在数据显示后会自动展示12 this.sortModule = new SortModule({13 view: this,14 selector: '.js_sort_wrapper',15 sortEntity: this.sortEntity16 });17 this.listModule = new ListModule({18 view: this,19 selector: '.js_list_wrapper',20 entity: this.sortEntity21 });22 },23 24 propertys: function ($super) {25 $super();26 27 this.initEntity();28 this.initModule();29 this.viewId = 'list';30 this.template = layoutHtml;31 this.events = {};32 }
実際の list コンポーネントの実装を簡単に見てみましょう。 、元のルートの位置を変更することです ビューコード:
1 define([ 2 'ModuleView', 3 'pages/list.data', 4 'text!pages/tpl.list.html' 5 6 ], function (ModuleView, 7 listData, 8 tpl) { 9 return _.inherit(ModuleView, { 10 11 //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG 12 initData: function () { 13 14 this.template = tpl; 15 this.entity.subscribe(this.render, this); 16 17 }, 18 19 _timeSort: function (data, sort) { 20 data = _.sortBy(data, function (item) { 21 item = item.from_time.split(':'); 22 item = item[0] + '.' + item[1]; 23 item = parseFloat(item); 24 return item; 25 }); 26 if (sort == 'down') data.reverse(); 27 return data; 28 }, 29 30 _sumTimeSort: function (data, sort) { 31 data = _.sortBy(data, function (item) { 32 return parseInt(item.use_time); 33 }); 34 if (sort == 'down') data.reverse(); 35 return data; 36 }, 37 38 _priceSort: function (data, sort) { 39 data = _.sortBy(data, function (item) { 40 return item.min_price; 41 }); 42 if (sort == 'down') data.reverse(); 43 return data; 44 }, 45 46 //获取导航栏排序后的数据 47 getSortData: function (data) { 48 var tmp = []; 49 var sort = this.entity.get(); 50 51 for (var k in sort) { 52 if (sort[k].length > 0) { 53 tmp = this['_' + k + 'Sort'](data, sort[k]) 54 return tmp; 55 } 56 } 57 }, 58 59 //复杂的业务数据处理,为了达到产品的需求,这段代码逻辑与业务相关 60 //这段数据处理的代码过长(超过50行就过长),应该重构掉 61 formatData: function (data) { 62 var item, seat; 63 var typeMap = { 64 'g': 'g', 65 'd': 'd', 66 't': 't', 67 'c': 'g' 68 }; 69 70 //出发时间对应的分钟数 71 var fromMinute = 0; 72 73 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了 74 var d = 1464192000000; 75 var date = new Date(); 76 var now = parseInt(date.getTime() / 1000); 77 date.setTime(d); 78 var year = date.getFullYear(); 79 var month = date.getMonth(); 80 var day = date.getDate(); 81 var toBegin; 82 var seatName, seatIndex, iii; 83 84 //处理坐席问题,仅显示二等座,一等座,特等座 无座 85 // 二等座 一等座 商务座 无座 动卧 特等座 86 var my_seats = {}; 87 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座']; 88 89 for (var i = 0, len = data.length; i < len; i++) { 90 fromMinute = data[i].from_time.split(':'); 91 fromMinute[0] = fromMinute[0] + ''; 92 fromMinute[1] = fromMinute[1] + ''; 93 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1); 94 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1); 95 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0); 96 fromMinute = parseInt(date.getTime() / 1000) 97 toBegin = parseInt((fromMinute - now) / 60); 98 99 data[i].toBegin = toBegin;100 101 //处理车次类型问题102 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';103 104 seat = data[i].seats;105 //所有余票106 data[i].sum_ticket = 0;107 //最低价108 data[i].min_price = null;109 110 for (var j = 0, len1 = seat.length; j < len1; j++) {111 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);112 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);113 114 //坐席问题如果坐席不包括上中下则去掉115 seatName = seat[j].seat_name;116 //去掉上中下117 seatName = seatName.replace(/上|中|下/g, '');118 if (!my_seats[seatName]) {119 my_seats[seatName] = parseInt(seat[j].seat_yupiao);120 } else {121 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);122 }123 }124 //这里myseat为对象,需要转换为数组125 //将定制坐席转为排序后的数组126 data[i].my_seats = [];127 for (iii = 0; iii < seatSort.length; iii++) {128 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({129 name: seatSort[iii],130 yupiao: my_seats[seatSort[iii]]131 });132 }133 134 my_seats = {};135 }136 137 return data;138 },139 140 //完成所有的筛选条件,逻辑比较重141 getViewModel: function () {142 var data = this.formatData(listData);143 data = this.getSortData(data);144 return {data: data};145 }146 147 });148 149 });View Code
この簡単な変更により、DOM 操作から解放されるように見えますが、実際には、ページのすべてのステータスをデータによって制御できます。ここには「タグ付き」がありませんが、あまり良くないと思われるので、タグ付きコードに変換できるかどうか試してみましょう。
我们这里的业务代码(module与entity)没有什么需要改动的,这里主要在底层做改造,这里在我看来是提供了一种“语法糖”的东西,这里的具体概念后续阅读Vue源码再深入了解,这里先照着做,这里看结果想实现,也是我们常用的一种设计方案,首先我们的index编程了这个样子:
1 <article class="cm-page page-list" id="main">2 <div class="js_sort_wrapper sort-bar-wrapper">3 <mySortBar :entity="sortEntity"></mySortBar>4 </div>5 <myList :entity="listEntity" :sort="sort"></myList>6 </article>
1 (function () { 2 require.config({ 3 paths: { 4 'text': 'libs/require.text', 5 6 'AbstractView': 'js/view', 7 'AbstractEntity': 'js/entity', 8 'ModuleView': 'js/module' 9 }10 });11 12 require(['pages/list.label'], function (List) {13 var list = new List();14 list.show();15 });16 })();
PS:里面的js钩子基本无用了
这里标签化带来的好处是,根View中有一段实例代码可以不用与选择器映射了,比如这个:
1 this.sortModule = new SortModule({2 //view: this,3 //selector: '.js_sort_wrapper',4 //sortEntity: this.sortEntity5 });
因为处于组件中,其中所处位置已经定了,view实例或者entity实例全部是跟View显示注入的,这里根View中参考Vue的使用,新增一个$components与$entities属性,然后增加一$watch对象。
大家写底层框架时,私有属性或者方法使用_method的方式,如果是要释放的可以是$method这种,一定要“特殊化”防止被实例或者继承覆盖
1 define([ 2 'AbstractView', 'pages/en.sort', 'pages/mod.sort', 'pages/mod.list' 3 ], function (AbstractView, SortEntity, SortModule, ListModule) { 4 return _.inherit(AbstractView, { 5 propertys: function ($super) { 6 $super(); 7 this.$entities = { 8 sortEntity: SortEntity 9 };10 this.$components = {11 mySortBar: SortModule,12 listModule: ListModule13 };14 this.$watch = {15 16 };17 this.viewId = 'list';18 this.template = layoutHtml;19 this.events = {};20 }21 });22 });
他这种做法,需要组件在显示后框架底层将刚刚的业务代码实现,使用组件生成的html代码将原来标签的占位符给替换掉。
这里在组件也需要明示根View需要注入什么给自己:
PS:事实上这个可以不写,写了对后续属性的计算有好处
//记录需要根View注入的属性props:[sortEntity],
PS:底层什么时候执行替换这个是有一定时机的,我们这里暂时放到根View展示后,这里更好的实现,后续我们在Vue与React中去找寻
因为我们这里是demo类实现,为降低难度,我们为每一个组件动态增加一个div包裹层,于是,我们在跟View中,在View展示后,我们另外多加一段逻辑:
1 //实例化实体,后面要用2 this._initEntity();3 //新增标签逻辑4 this._initComponent();
然后将实体与组件的实例化放到框架底层,这里实体的实例化比较简单(如果有特殊数据需求再说,这里只考虑最简单情况):
1 _initEntity: function() {2 var key, entities = this.$entities;3 //这里没有做特殊化,需要注意4 for(key in entities) {5 this[key] = new entities[key]();6 }7 },
而实例化组件的工作复杂许多,因为他需要将页面中的自定义标签替换掉,还需要完成很多属性注入操作:
1 _initComponent: function() {2 var key, components = this.$components;3 for(key in components) {4 //这里实例化的过程有点复杂,首先将页面的标签做一个替换5 var s = ''6 }7 },
1 _initComponent: function() { 2 var key, components = this.$components; 3 var el, attributes, attr, param, clazz, i, len, tmp, id, name; 4 5 //这里实例化的过程有点复杂,首先将页面的标签做一个替换 6 for(key in components) { 7 param = {}; 8 clazz = components[key]; 9 //由原型链上获取根元素要注入给子组件的属性(这个实现好像不太好)10 attributes = clazz.prototype.props;11 12 //首先获取标签dom元素,因为html是不区分大小写的,这里将标签小写13 el = this.$(key.toLowerCase());14 if(!el[0]) continue;15 16 if(attributes) {17 for (i = 0, len = attributes.length; i < len; i++) {18 attr = attributes[i];19 name = el.attr(':' + attr);20 param[attr] = this[name] || name;21 }22 }23 24 //创建一个空div去替换原来的标签25 id = _.uniqueId('componenent-id-');26 tmp = $('<div component-name="' + key + '" id="' + id + '"></div>');27 tmp.insertBefore(el);28 el.remove();29 param.selector = '#' + id;30 param.view = this;31 this[key] = new components[key](param);32 }33 34 },
于是这个标签便能正常展示了:
1 <article class="cm-page page-list" id="main">2 <div class="js_sort_wrapper sort-bar-wrapper">3 <mySortBar :entity="sortEntity" :myname="111"></mySortBar>4 </div>5 <myList :entity="sortEntity" :sort="sort"></myList>6 </article>
后面想要把这段代码去掉也十分轻易,我这里就不进行了:
1 'click .js_sort_item li ': function (e) {2 var el = $(e.currentTarget);3 var sort = el.attr('data-sort');4 this.entity['set' + sort]();5 }
这里首先根据上次Vue的demo产生了一些思考,并且以简单的demo验证了这些思考,楼主在使用过程中发现Vue很多好的点子,后续应该会深入研究,并且以实际项目入手,这里回到今天的正题,我们使用React实现上次遗留的demo。
在我最初接触React的时候,React Native还没出现,所以很多人对React的关注不高,当时做移动端直接放弃了angular,以体量来说,React也不在我们的考虑范围内,谁知道野心勃勃的Facebook搞出了React Native,让React彻底的跟着火了一把,事实上只要有能力以JavaScript统一Native UI的公司,这个实现就算换个框架依旧会火,虽然都已经这么火了,但是React的文档却不怎样,我后续也有试水React Native的兴趣,届时再与各位分享。
PS:根据之前的反馈,这次demo稍微做简单点,也只包含顶部导航和列表即可:
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui"/> 6 <meta content="yes" name="apple-mobile-web-app-capable"/> 7 <meta content="black" name="apple-mobile-web-app-status-bar-style"/> 8 <meta name="format-detection" content="telephone=no"/> 9 <link href="./static/css/global.css" rel="stylesheet" type="text/css"/> 10 <link href="./pages/list.css" rel="stylesheet" type="text/css"/> 11 <title>组件化</title> 12 </head> 13 <body> 14 <div class="cm-header"> 15 <h1 class="cm-page-title js_title"> 组件化Demo </h1> 16 </div> 17 18 <article class="cm-page page-list" id="main"> 19 </article> 20 21 22 <script src="./libs/react-with-addons.js"></script> 23 <script src="./libs/JSXTransformer.js"></script> 24 <script type="text/javascript" src="./pages/list.data.js"></script> 25 <script type="text/javascript" src="./libs/underscore.js"></script> 26 <script type="text/jsx"> 27 28 29 30 var MySortBar = React.createClass({ 31 getInitialState: function() { 32 return { 33 time: 'up', 34 sumTime: '', 35 price: '' 36 }; 37 }, 38 39 resetData: function () { 40 this.setState({ 41 time: '', 42 sumTime: '', 43 price: '' 44 }); 45 }, 46 47 setTime: function () { 48 this._setData('time'); 49 }, 50 51 setSumTime: function () { 52 this._setData('sumTime'); 53 }, 54 55 setPrice: function () { 56 this._setData('price'); 57 }, 58 59 _setData: function (key) { 60 var param = {}; 61 62 //如果设置当前key存在,则反置,否则清空筛选,设置默认值 63 if (this.state[key] != '') { 64 if (this.state[key] == 'up') param[key] = 'down'; 65 else param[key] = 'up'; 66 } else { 67 this.resetData(); 68 param[key] = 'down'; 69 } 70 this.setState(param); 71 }, 72 73 _getClassName: function(icon) { 74 return 'icon-sort ' + icon; 75 }, 76 77 render: function () { 78 return ( 79 <ul className="bus-tabs sort-bar js_sort_item"> 80 <li className="tabs-item" onClick={this.setTime} >出发时间<i className={this._getClassName(this.state.time)}></i></li> 81 <li className="tabs-item" onClick={this.setSumTime} >耗时<i className={this._getClassName(this.state.sumTime)}></i></li> 82 <li className="tabs-item" onClick={this.setPrice} >价格<i className={this._getClassName(this.state.price)}></i></li> 83 </ul> 84 ); 85 } 86 87 }); 88 89 var Seat = React.createClass({ 90 render: function () { 91 var seat = this.props.seat; 92 93 return ( 94 <span >{seat.name}({seat.yupiao }) </span> 95 ); 96 } 97 }); 98 99 var Item = React.createClass({100 render: function () {101 102 var item = this.props.item;103 var mapping = {104 'g': '高速',105 't': '特快',106 'd': '高速动车',107 'c': '城际高铁',108 'z': '直达'109 };110 111 var seats = item.my_seats.map(function(item){112 return <Seat seat={item}/>;113 });114 115 116 return (117 <li className="bus-list-item ">118 <div className="bus-seat">119 <span className=" fl">{item.train_number } | {mapping[item.my_train_number] || '其它'} </span>120 <span className=" fr">{parseInt(item.use_time / 60) + '小时' + item.use_time % 60 + '分'}</span>121 </div>122 <div className="detail">123 <div className="sub-list set-out">124 <span className="bus-go-off">{item.from_time}</span> <span className="start"><span className="icon-circle s-icon1">125 </span>{item.from_station }</span> <span className="fr price">¥{item.min_price}起</span>126 </div>127 <div className="sub-list">128 <span className="bus-arrival-time">{item.to_time}</span> <span className="end"><span className="icon-circle s-icon2">129 </span>{item.to_station}</span> <span className="fr ">{item.sum_ticket}张</span>130 </div>131 </div>132 <div className="bus-seats-info" >133 {seats}134 </div>135 </li>136 );137 }138 });139 140 141 var MyList = React.createClass({142 143 formatData: function (data) {144 145 var item, seat;146 var typeMap = {147 'g': 'g',148 'd': 'd',149 't': 't',150 'c': 'g'151 };152 153 //出发时间对应的分钟数154 var fromMinute = 0;155 156 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了157 var d = 1464192000000;158 var date = new Date();159 var now = parseInt(date.getTime() / 1000);160 date.setTime(d);161 var year = date.getFullYear();162 var month = date.getMonth();163 var day = date.getDate();164 var toBegin;165 var seatName, seatIndex, iii;166 167 //处理坐席问题,仅显示二等座,一等座,特等座 无座168 // 二等座 一等座 商务座 无座 动卧 特等座169 var my_seats = {};170 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];171 172 for (var i = 0, len = data.length; i < len; i++) {173 fromMinute = data[i].from_time.split(':');174 fromMinute[0] = fromMinute[0] + '';175 fromMinute[1] = fromMinute[1] + '';176 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);177 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);178 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);179 fromMinute = parseInt(date.getTime() / 1000)180 toBegin = parseInt((fromMinute - now) / 60);181 182 data[i].toBegin = toBegin;183 184 //处理车次类型问题185 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';186 187 seat = data[i].seats;188 //所有余票189 data[i].sum_ticket = 0;190 //最低价191 data[i].min_price = null;192 193 for (var j = 0, len1 = seat.length; j < len1; j++) {194 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);195 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);196 197 //坐席问题如果坐席不包括上中下则去掉198 seatName = seat[j].seat_name;199 //去掉上中下200 seatName = seatName.replace(/上|中|下/g, '');201 if (!my_seats[seatName]) {202 my_seats[seatName] = parseInt(seat[j].seat_yupiao);203 } else {204 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);205 }206 }207 //这里myseat为对象,需要转换为数组208 //将定制坐席转为排序后的数组209 data[i].my_seats = [];210 for (iii = 0; iii < seatSort.length; iii++) {211 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({212 name: seatSort[iii],213 yupiao: my_seats[seatSort[iii]]214 });215 }216 217 my_seats = {};218 }219 220 return data;221 },222 223 render: function () {224 225 var main;226 var data = this.formatData(this.props.data);227 228 229 main = data.map(function(item) {230 return <Item item={item}/>;231 });232 233 return (234 <ul className="bus-list js_bus_list ">235 {main}236 </ul>237 );238 }239 240 });241 242 var data = getListData();243 244 React.render(245 <div>246 <div className="js_sort_wrapper sort-bar-wrapper">247 <MySortBar />248 </div>249 <MyList data={data} />250 </div>,251 document.getElementById('main')252 );253 254 </script>255 256 257 </body>258 </html>View Code
他这个语法据说是让开发变得更简单了,我反正是不喜欢,这里有个不好的地方,之前数据实体全部是在根View上实例化的,然后注入给子View,React这里属性完全独享了,现在我触发了状态的改变,如何通知到list组件重新渲染排序呢?
这里React子组件之间如何通信暂没有研究出来,所以将需要通信的数据做到了父组件中
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui"/> 6 <meta content="yes" name="apple-mobile-web-app-capable"/> 7 <meta content="black" name="apple-mobile-web-app-status-bar-style"/> 8 <meta name="format-detection" content="telephone=no"/> 9 <link href="./static/css/global.css" rel="stylesheet" type="text/css"/> 10 <link href="./pages/list.css" rel="stylesheet" type="text/css"/> 11 <title>组件化</title> 12 </head> 13 <body> 14 <div class="cm-header"> 15 <h1 class="cm-page-title js_title"> 组件化Demo </h1> 16 </div> 17 18 <article class="cm-page page-list" id="main"> 19 </article> 20 21 22 <script src="./libs/react-with-addons.js"></script> 23 <script src="./libs/JSXTransformer.js"></script> 24 <script type="text/javascript" src="./pages/list.data.js"></script> 25 <script type="text/javascript" src="./libs/underscore.js"></script> 26 <script type="text/jsx"> 27 28 29 30 var MySortBar = React.createClass({ 31 _getClassName: function(icon) { 32 return 'icon-sort ' + icon; 33 }, 34 35 render: function () { 36 var state = this.props.state; 37 return ( 38 <ul className="bus-tabs sort-bar js_sort_item"> 39 <li className="tabs-item" onClick={this.props.setTime} >出发时间<i className={this._getClassName(state.time)}></i></li> 40 <li className="tabs-item" onClick={this.props.setSumTime} >耗时<i className={this._getClassName(state.sumTime)}></i></li> 41 <li className="tabs-item" onClick={this.props.setPrice} >价格<i className={this._getClassName(state.price)}></i></li> 42 </ul> 43 ); 44 } 45 46 }); 47 48 var Seat = React.createClass({ 49 render: function () { 50 var seat = this.props.seat; 51 52 return ( 53 <span >{seat.name}({seat.yupiao }) </span> 54 ); 55 } 56 }); 57 58 var Item = React.createClass({ 59 render: function () { 60 61 var item = this.props.item; 62 var mapping = { 63 'g': '高速', 64 't': '特快', 65 'd': '高速动车', 66 'c': '城际高铁', 67 'z': '直达' 68 }; 69 70 var seats = item.my_seats.map(function(item){ 71 return <Seat seat={item}/>; 72 }); 73 74 75 return ( 76 <li className="bus-list-item "> 77 <div className="bus-seat"> 78 <span className=" fl">{item.train_number } | {mapping[item.my_train_number] || '其它'} </span> 79 <span className=" fr">{parseInt(item.use_time / 60) + '小时' + item.use_time % 60 + '分'}</span> 80 </div> 81 <div className="detail"> 82 <div className="sub-list set-out"> 83 <span className="bus-go-off">{item.from_time}</span> <span className="start"><span className="icon-circle s-icon1"> 84 </span>{item.from_station }</span> <span className="fr price">¥{item.min_price}起</span> 85 </div> 86 <div className="sub-list"> 87 <span className="bus-arrival-time">{item.to_time}</span> <span className="end"><span className="icon-circle s-icon2"> 88 </span>{item.to_station}</span> <span className="fr ">{item.sum_ticket}张</span> 89 </div> 90 </div> 91 <div className="bus-seats-info" > 92 {seats} 93 </div> 94 </li> 95 ); 96 } 97 }); 98 99 var MyList = React.createClass({100 101 formatData: function (data) {102 103 var item, seat;104 var typeMap = {105 'g': 'g',106 'd': 'd',107 't': 't',108 'c': 'g'109 };110 111 //出发时间对应的分钟数112 var fromMinute = 0;113 114 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了115 var d = 1464192000000;116 var date = new Date();117 var now = parseInt(date.getTime() / 1000);118 date.setTime(d);119 var year = date.getFullYear();120 var month = date.getMonth();121 var day = date.getDate();122 var toBegin;123 var seatName, seatIndex, iii;124 125 //处理坐席问题,仅显示二等座,一等座,特等座 无座126 // 二等座 一等座 商务座 无座 动卧 特等座127 var my_seats = {};128 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];129 130 for (var i = 0, len = data.length; i < len; i++) {131 fromMinute = data[i].from_time.split(':');132 fromMinute[0] = fromMinute[0] + '';133 fromMinute[1] = fromMinute[1] + '';134 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);135 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);136 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);137 fromMinute = parseInt(date.getTime() / 1000)138 toBegin = parseInt((fromMinute - now) / 60);139 140 data[i].toBegin = toBegin;141 142 //处理车次类型问题143 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';144 145 seat = data[i].seats;146 //所有余票147 data[i].sum_ticket = 0;148 //最低价149 data[i].min_price = null;150 151 for (var j = 0, len1 = seat.length; j < len1; j++) {152 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);153 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);154 155 //坐席问题如果坐席不包括上中下则去掉156 seatName = seat[j].seat_name;157 //去掉上中下158 seatName = seatName.replace(/上|中|下/g, '');159 if (!my_seats[seatName]) {160 my_seats[seatName] = parseInt(seat[j].seat_yupiao);161 } else {162 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);163 }164 }165 //这里myseat为对象,需要转换为数组166 //将定制坐席转为排序后的数组167 data[i].my_seats = [];168 for (iii = 0; iii < seatSort.length; iii++) {169 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({170 name: seatSort[iii],171 yupiao: my_seats[seatSort[iii]]172 });173 }174 175 my_seats = {};176 }177 178 return data;179 },180 181 _timeSort: function (data, sort) {182 data = _.sortBy(data, function (item) {183 item = item.from_time.split(':');184 item = item[0] + '.' + item[1];185 item = parseFloat(item);186 return item;187 });188 if (sort == 'down') data.reverse();189 return data;190 },191 192 _sumTimeSort: function (data, sort) {193 data = _.sortBy(data, function (item) {194 return parseInt(item.use_time);195 });196 if (sort == 'down') data.reverse();197 return data;198 },199 200 _priceSort: function (data, sort) {201 data = _.sortBy(data, function (item) {202 return item.min_price;203 });204 if (sort == 'down') data.reverse();205 return data;206 },207 208 //获取导航栏排序后的数据209 getSortData: function (data) {210 var tmp = [];211 var sort = this.props.state;212 213 for (var k in sort) {214 if (sort[k].length > 0) {215 tmp = this['_' + k + 'Sort'](data, sort[k])216 return tmp;217 }218 }219 },220 221 render: function () {222 223 var main;224 var data = this.formatData(this.props.data);225 data = this.getSortData(data);226 227 main = data.map(function(item) {228 return <Item item={item}/>;229 });230 231 return (232 <ul className="bus-list js_bus_list ">233 {main}234 </ul>235 );236 }237 238 });239 240 var App = React.createClass({241 getInitialState: function() {242 return {243 time: 'up',244 sumTime: '',245 price: ''246 };247 },248 249 resetData: function () {250 this.setState({251 time: '',252 sumTime: '',253 price: ''254 });255 },256 257 setTime: function () {258 this._setData('time');259 },260 261 setSumTime: function () {262 this._setData('sumTime');263 },264 265 setPrice: function () {266 this._setData('price');267 },268 269 _setData: function (key) {270 var param = {};271 272 //如果设置当前key存在,则反置,否则清空筛选,设置默认值273 if (this.state[key] != '') {274 if (this.state[key] == 'up') param[key] = 'down';275 else param[key] = 'up';276 } else {277 this.resetData();278 param[key] = 'down';279 }280 this.setState(param);281 },282 283 formatData: function (data) {284 285 var item, seat;286 var typeMap = {287 'g': 'g',288 'd': 'd',289 't': 't',290 'c': 'g'291 };292 293 //出发时间对应的分钟数294 var fromMinute = 0;295 296 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了297 var d = 1464192000000;298 var date = new Date();299 var now = parseInt(date.getTime() / 1000);300 date.setTime(d);301 var year = date.getFullYear();302 var month = date.getMonth();303 var day = date.getDate();304 var toBegin;305 var seatName, seatIndex, iii;306 307 //处理坐席问题,仅显示二等座,一等座,特等座 无座308 // 二等座 一等座 商务座 无座 动卧 特等座309 var my_seats = {};310 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];311 312 for (var i = 0, len = data.length; i < len; i++) {313 fromMinute = data[i].from_time.split(':');314 fromMinute[0] = fromMinute[0] + '';315 fromMinute[1] = fromMinute[1] + '';316 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);317 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);318 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);319 fromMinute = parseInt(date.getTime() / 1000)320 toBegin = parseInt((fromMinute - now) / 60);321 322 data[i].toBegin = toBegin;323 324 //处理车次类型问题325 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';326 327 seat = data[i].seats;328 //所有余票329 data[i].sum_ticket = 0;330 //最低价331 data[i].min_price = null;332 333 for (var j = 0, len1 = seat.length; j < len1; j++) {334 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);335 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);336 337 //坐席问题如果坐席不包括上中下则去掉338 seatName = seat[j].seat_name;339 //去掉上中下340 seatName = seatName.replace(/上|中|下/g, '');341 if (!my_seats[seatName]) {342 my_seats[seatName] = parseInt(seat[j].seat_yupiao);343 } else {344 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);345 }346 }347 //这里myseat为对象,需要转换为数组348 //将定制坐席转为排序后的数组349 data[i].my_seats = [];350 for (iii = 0; iii < seatSort.length; iii++) {351 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({352 name: seatSort[iii],353 yupiao: my_seats[seatSort[iii]]354 });355 }356 357 my_seats = {};358 }359 360 return data;361 },362 363 render: function () {364 365 var main;366 var data = this.formatData(this.props.data);367 main = data.map(function(item) {368 return <Item item={item}/>;369 });370 371 return (372 <div>373 <div className="js_sort_wrapper sort-bar-wrapper">374 <MySortBar state={this.state} setTime={this.setTime} setSumTime={this.setSumTime} setPrice={this.setPrice}/>375 </div>376 <MyList data={data} state={this.state} />377 </div>378 );379 }380 381 });382 383 var data = getListData();384 385 React.render(386 <App data={data}/>,387 document.getElementById('main')388 );389 390 </script>391 392 393 </body>394 </html>View Code
react的中文文档整理较差,很多资料找不到,jsx语法比较怪异,不是所有人能接受,我去找模板循环时候压根就没找到,所以jsx有个特点,他让你不得不去拆分你的组件,在我写React代码中,感觉React代码控制力度要重一点,但是如果没有良好的架构能力,我可以毫不夸张的说,你依旧写不好业务代码。
至于React与Vue的优劣,这个方面见仁见智吧,好了今天的文章到此为止。
后续我们可能会深入分析下Vue的实现,在React Native上做深入,有兴趣的同学可以持续关注。
文章有任何不足错误,请您提出,因为小钗也是第二次使用React写demo,如果使用不当请多包涵