你是个啥 發表於 2019-5-9 21:15:00

React架构之路

<p>笔者本人其实对react的项目经验很少,主要是用Angular框架。前段时间和同学合作做了一个酒店加盟平台项目,我负责后台管理系统,采用的是Angular框架。他负责微信小程序,采用react框架。但随着项目的进行,我发现他的项目文件我一时难以理清,整个项目结构比较零散。只有他自己对自己的项目很熟悉。我也提出了一些疑问,说react架构为何如此松散。当然,后续的故事就不赘述了。</p>
<p>笔者现在在校答辩中,利用空余时间对react做了较为深入的一些研究。以下是笔者的个人心得分享。</p>
<p>很多开发者都知道react并不是一个完整的前端框架,它仅仅是一个UI层面的框架。如果我们需要用react来进行开发,那作为一个开发者必须整合react周边的生态,自己搭建出一个完整的框架。例如我们通常需要redux进行数据管理,需要特定的HTTP模块进行前后端通信,需要react-router来进行路由管理等等。这带来一个问题:react让我有更多的选择的同时也带来的架构松散性的问题。正因为react过于开放的环境,使得不同开发者搭建出来的框架结构也是不尽相同。如果一个开发者缺乏一定的经验,他很可能写出难以维护的代码结构出来。</p>
<p>在经过反复思考过后,笔者按照自己在实际项目中的经验搭建了一个react的架构。笔者很欣赏Angular严谨的架构,所以在架构React的时候参照了很多Angular的架构设计。</p>
<p>&nbsp;首先让我们来看看整体项目结构。</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509190239272-1454183381.png"></p>
<p>通常一个应用可以先拆分为三个部分:登陆页、注册页、主体业务页面。这三个功能模块是平级的,对应图中login、regist、pc三个文件夹。其中pc文件就是主体业务模块,个人喜好根据终端类型来命名如pc、mobile。也有很多人喜欢用类似pages、home来命名。根据个人和团队喜好而定。</p>
<p>我们要写的绝大部分页面都放在主体页面下,即pc文件下。现在pc文件夹下有hotel(酒店模块)、order(订单模块)、room(房间模块)。我们先不管这些模块的细节,只需要知道当前主体业务模块下有这几个模块。它们在页面上表现如下:</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509191004975-1182973364.png"></p>
<p>我们重点来看pc文件夹下的pc.ui.tsx、pc.css、pc.component.tsx、pc.router.tsx、pc.reducer.tsx这几个文件里都有什么,以及它们各自的作用。这里我采用了typescript。采用typescript的原因是强类型在多人协作开发方面能带来很大好处,类型检测可以防止不同的开发者不按项目规范写代码造成项目混乱,同时类型提示也方便不同开发者默契地交流。</p>
<p>pc.ui.tsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> import * as React from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">2</span> import { NavLink } from 'react-router-dom'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">3</span> import PcRouter from './pc.router'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">4</span> import "./pc.ui.css"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">5</span> import { Menu, Icon, Layout, Avatar, Row, Col} from "antd"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">6</span>
<span style="color: rgba(0, 128, 128, 1)">7</span>
<span style="color: rgba(0, 128, 128, 1)">8</span> const { Header, Content, Footer, Sider } =<span style="color: rgba(0, 0, 0, 1)"> Layout;
</span><span style="color: rgba(0, 128, 128, 1)">9</span> const SubMenu =<span style="color: rgba(0, 0, 0, 1)"> Menu.SubMenu;
</span><span style="color: rgba(0, 128, 128, 1)"> 10</span> const height =<span style="color: rgba(0, 0, 0, 1)"> document.body.clientHeight;
</span><span style="color: rgba(0, 128, 128, 1)"> 11</span> const Index =<span style="color: rgba(0, 0, 0, 1)"> [
</span><span style="color: rgba(0, 128, 128, 1)"> 12</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 13</span>   icon: 'pie-chart'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 14</span>   path: '/pc/order'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 15</span>   name: '订单管理'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 16</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)"> 17</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 18</span>   icon: 'desktop'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 19</span>   path: '/pc/room'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 20</span>   name: '房态管理'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 21</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)"> 22</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 23</span>   icon: 'desktop'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 24</span>   path: '/pc/hotel'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 25</span>   name: '酒店管理'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 26</span> <span style="color: rgba(0, 0, 0, 1)">    children: [
</span><span style="color: rgba(0, 128, 128, 1)"> 27</span> <span style="color: rgba(0, 0, 0, 1)">      {
</span><span style="color: rgba(0, 128, 128, 1)"> 28</span>         path: '/pc/hotel/qualification'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 29</span>         name: '资质管理'
<span style="color: rgba(0, 128, 128, 1)"> 30</span> <span style="color: rgba(0, 0, 0, 1)">      },
</span><span style="color: rgba(0, 128, 128, 1)"> 31</span> <span style="color: rgba(0, 0, 0, 1)">      {
</span><span style="color: rgba(0, 128, 128, 1)"> 32</span>         path: '/pc/hotel/info'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 33</span>         name: '信息管理'
<span style="color: rgba(0, 128, 128, 1)"> 34</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)"> 35</span> <span style="color: rgba(0, 0, 0, 1)">    ]
</span><span style="color: rgba(0, 128, 128, 1)"> 36</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 37</span> <span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 128, 1)"> 38</span>
<span style="color: rgba(0, 128, 128, 1)"> 39</span> <span style="color: rgba(0, 0, 0, 1)">interface Props {
</span><span style="color: rgba(0, 128, 128, 1)"> 40</span> <span style="color: rgba(0, 0, 0, 1)">index: string[];
</span><span style="color: rgba(0, 128, 128, 1)"> 41</span>   getIndex: () =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 42</span>   getList: () =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 43</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 44</span> <span style="color: rgba(0, 0, 0, 1)">interface State {
</span><span style="color: rgba(0, 128, 128, 1)"> 45</span>   collapsed: <span style="color: rgba(0, 0, 255, 1)">boolean</span>
<span style="color: rgba(0, 128, 128, 1)"> 46</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 47</span>
<span style="color: rgba(0, 128, 128, 1)"> 48</span> class Pc extends React.Component&lt;Props, State&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 49</span> <span style="color: rgba(0, 0, 0, 1)">constructor(props: Props, state: State) {
</span><span style="color: rgba(0, 128, 128, 1)"> 50</span> <span style="color: rgba(0, 0, 0, 1)">    super(props);
</span><span style="color: rgba(0, 128, 128, 1)"> 51</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> state;
</span><span style="color: rgba(0, 128, 128, 1)"> 52</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 53</span>
<span style="color: rgba(0, 128, 128, 1)"> 54</span>   change = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 55</span>   <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
</span><span style="color: rgba(0, 128, 128, 1)"> 56</span>       collapsed: !<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.collapsed
</span><span style="color: rgba(0, 128, 128, 1)"> 57</span> <span style="color: rgba(0, 0, 0, 1)">    })
</span><span style="color: rgba(0, 128, 128, 1)"> 58</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 59</span>
<span style="color: rgba(0, 128, 128, 1)"> 60</span> <span style="color: rgba(0, 0, 0, 1)">render() {
</span><span style="color: rgba(0, 128, 128, 1)"> 61</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 62</span>       &lt;Layout style={{ height: height }}&gt;
<span style="color: rgba(0, 128, 128, 1)"> 63</span>         &lt;<span style="color: rgba(0, 0, 0, 1)">Sider
</span><span style="color: rgba(0, 128, 128, 1)"> 64</span>         breakpoint="lg"
<span style="color: rgba(0, 128, 128, 1)"> 65</span>         collapsedWidth="0"
<span style="color: rgba(0, 128, 128, 1)"> 66</span>         &gt;
<span style="color: rgba(0, 128, 128, 1)"> 67</span>         &lt;div className="logo" /&gt;
<span style="color: rgba(0, 128, 128, 1)"> 68</span>         &lt;<span style="color: rgba(0, 0, 0, 1)">Menu
</span><span style="color: rgba(0, 128, 128, 1)"> 69</span>             defaultSelectedKeys={["1"<span style="color: rgba(0, 0, 0, 1)">]}
</span><span style="color: rgba(0, 128, 128, 1)"> 70</span>             defaultOpenKeys={["sub1"<span style="color: rgba(0, 0, 0, 1)">]}
</span><span style="color: rgba(0, 128, 128, 1)"> 71</span>             mode="inline"
<span style="color: rgba(0, 128, 128, 1)"> 72</span>             theme="dark"
<span style="color: rgba(0, 128, 128, 1)"> 73</span>             inlineCollapsed={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.collapsed}
</span><span style="color: rgba(0, 128, 128, 1)"> 74</span>         &gt;
<span style="color: rgba(0, 128, 128, 1)"> 75</span>             {Index.map((i) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 76</span>               <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (i.children) {
</span><span style="color: rgba(0, 128, 128, 1)"> 77</span>               <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 78</span>                   &lt;<span style="color: rgba(0, 0, 0, 1)">SubMenu
</span><span style="color: rgba(0, 128, 128, 1)"> 79</span>                     key=<span style="color: rgba(0, 0, 0, 1)">{i.path}
</span><span style="color: rgba(0, 128, 128, 1)"> 80</span>                     title=<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 81</span>                     &lt;span&gt;
<span style="color: rgba(0, 128, 128, 1)"> 82</span>                         &lt;Icon type={i.icon} /&gt;
<span style="color: rgba(0, 128, 128, 1)"> 83</span>                         &lt;span&gt;{i.name}&lt;/span&gt;
<span style="color: rgba(0, 128, 128, 1)"> 84</span>                     &lt;/span&gt;
<span style="color: rgba(0, 128, 128, 1)"> 85</span>                     }&gt;
<span style="color: rgba(0, 128, 128, 1)"> 86</span> <span style="color: rgba(0, 0, 0, 1)">                  {
</span><span style="color: rgba(0, 128, 128, 1)"> 87</span>                     i.children.map((child) =&gt;
<span style="color: rgba(0, 128, 128, 1)"> 88</span>                         &lt;Menu.Item key={child.path}&gt;
<span style="color: rgba(0, 128, 128, 1)"> 89</span> <span style="color: rgba(0, 0, 0, 1)">                        {child.name}
</span><span style="color: rgba(0, 128, 128, 1)"> 90</span>                           &lt;NavLink to={child.path}&gt;&lt;/NavLink&gt;
<span style="color: rgba(0, 128, 128, 1)"> 91</span>                         &lt;/Menu.Item&gt;
<span style="color: rgba(0, 128, 128, 1)"> 92</span> <span style="color: rgba(0, 0, 0, 1)">                      )
</span><span style="color: rgba(0, 128, 128, 1)"> 93</span> <span style="color: rgba(0, 0, 0, 1)">                  }
</span><span style="color: rgba(0, 128, 128, 1)"> 94</span>                   &lt;/SubMenu&gt;
<span style="color: rgba(0, 128, 128, 1)"> 95</span> <span style="color: rgba(0, 0, 0, 1)">                )
</span><span style="color: rgba(0, 128, 128, 1)"> 96</span>               } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 97</span>               <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 98</span>                   &lt;Menu.Item key={i.path}&gt;
<span style="color: rgba(0, 128, 128, 1)"> 99</span>                     &lt;Icon type={i.icon} /&gt;
<span style="color: rgba(0, 128, 128, 1)">100</span>                     &lt;span&gt;{i.name}&lt;/span&gt;
<span style="color: rgba(0, 128, 128, 1)">101</span>                     &lt;NavLink to={i.path}&gt;&lt;/NavLink&gt;
<span style="color: rgba(0, 128, 128, 1)">102</span>                   &lt;/Menu.Item&gt;
<span style="color: rgba(0, 128, 128, 1)">103</span> <span style="color: rgba(0, 0, 0, 1)">                )
</span><span style="color: rgba(0, 128, 128, 1)">104</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">105</span> <span style="color: rgba(0, 0, 0, 1)">            })}
</span><span style="color: rgba(0, 128, 128, 1)">106</span>         &lt;/Menu&gt;
<span style="color: rgba(0, 128, 128, 1)">107</span>         &lt;/Sider&gt;
<span style="color: rgba(0, 128, 128, 1)">108</span>         &lt;Layout&gt;
<span style="color: rgba(0, 128, 128, 1)">109</span>         &lt;Header style={{ background: '#fff', padding: 0 }} &gt;
<span style="color: rgba(0, 128, 128, 1)">110</span>               &lt;Row&gt;
<span style="color: rgba(0, 128, 128, 1)">111</span>               &lt;Col span={1} offset={1}&gt;
<span style="color: rgba(0, 128, 128, 1)">112</span>                   &lt;Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" /&gt;
<span style="color: rgba(0, 128, 128, 1)">113</span>               &lt;/Col&gt;
<span style="color: rgba(0, 128, 128, 1)">114</span>               &lt;Col span={2} &gt;
<span style="color: rgba(0, 128, 128, 1)">115</span> <span style="color: rgba(0, 0, 0, 1)">                   yangkai.sun
</span><span style="color: rgba(0, 128, 128, 1)">116</span>               &lt;/Col&gt;
<span style="color: rgba(0, 128, 128, 1)">117</span>               &lt;/Row&gt;
<span style="color: rgba(0, 128, 128, 1)">118</span>         &lt;/Header&gt;
<span style="color: rgba(0, 128, 128, 1)">119</span>         &lt;Content style={{ margin: '24px 16px 0' }}&gt;
<span style="color: rgba(0, 128, 128, 1)">120</span>               &lt;div style={{ padding: 24, background: '#fff', minHeight: 360 }}&gt;
<span style="color: rgba(0, 128, 128, 1)">121</span>               &lt;PcRouter&gt;&lt;/PcRouter&gt;
<span style="color: rgba(0, 128, 128, 1)">122</span>             &lt;/div&gt;
<span style="color: rgba(0, 128, 128, 1)">123</span>         &lt;/Content&gt;
<span style="color: rgba(0, 128, 128, 1)">124</span>         &lt;Footer style={{ textAlign: 'center' }}&gt;
<span style="color: rgba(0, 128, 128, 1)">125</span>             住行科技©2018<span style="color: rgba(0, 0, 0, 1)"> Created by sun.yangkai
</span><span style="color: rgba(0, 128, 128, 1)">126</span>         &lt;/Footer&gt;
<span style="color: rgba(0, 128, 128, 1)">127</span>         &lt;/Layout&gt;
<span style="color: rgba(0, 128, 128, 1)">128</span>       &lt;/Layout&gt;
<span style="color: rgba(0, 128, 128, 1)">129</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)">130</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">131</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">132</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> Pc;</pre>
</div>
<p>&nbsp;pc.ui.tsx这个文件很显然就是UI组件,它只负责UI层面的展现。这里的命名范式是:模块名.功能名.tsx。pc.ui.tsx这个文件里放了一个侧边栏&lt;Sider&gt;&lt;/Sider&gt;标签和主体内容标签&lt;Content&gt;&lt;/Content&gt;。重点请看&lt;Content&gt;&lt;/Content&gt;:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span>&lt;Content style={{ margin: '24px 16px 0' }}&gt;
<span style="color: rgba(0, 128, 128, 1)">2</span>               &lt;div style={{ padding: 24, background: '#fff', minHeight: 360 }}&gt;
<span style="color: rgba(0, 128, 128, 1)">3</span>                   &lt;PcRouter&gt;&lt;/PcRouter&gt;
<span style="color: rgba(0, 128, 128, 1)">4</span>             &lt;/div&gt;
<span style="color: rgba(0, 128, 128, 1)">5</span> &lt;/Content&gt;</pre>
</div>
<p>里面有一个我们自定义的组件标签&lt;PcRouter&gt;&lt;/PcRouter&gt;。在Angular里它被称之为路由出口,现在我们在react里实现了一个路由出口。pc模块下的所有路由组件将会被渲染到&lt;PcRouter&gt;&lt;/PcRouter&gt;标签里。我们可以看到这个标签来自于&nbsp;import PcRouter from './pc.router'; 即pc.router.tsx文件。</p>
<p>pc.router.tsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import * as React from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { Route} from 'react-router-dom'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> import Order from './order/order.component'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> import Room from './room/room.ui'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> import Hotel from './hotel/hotel.ui'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> const routes =<span style="color: rgba(0, 0, 0, 1)"> [
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">            {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>               path: '/pc/order'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">                component: Order,
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">            },
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">            {
</span><span style="color: rgba(0, 128, 128, 1)">14</span>               path: '/pc/room'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">                component: Room,
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">            },
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">            {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>               path: '/pc/hotel'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">                component: Hotel,
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">            },
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 128, 1)">22</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">class PcRouter extends React.Component{
</span><span style="color: rgba(0, 128, 128, 1)">24</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">    render() {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>         <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">27</span>             routes.map((route) =&gt;
<span style="color: rgba(0, 128, 128, 1)">28</span>               &lt;Route key={route.path} path={route.path} component={route.component}&gt;   
<span style="color: rgba(0, 128, 128, 1)">29</span>               &lt;/Route&gt;
<span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">            )
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">      )
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">34</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> PcRouter;</pre>
</div>
<p>pc.router.tsx是路由配置文件,它导入了pc模块下的三个路由组件Room、Hotel、Order并按照react-router的语法配置渲染。最终导出一个PcRouter的路由标签供Pc模块使用。</p>
<p>pc.component.tsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import { connect } from 'react-redux'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> importPc from './pc.ui'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import{ State } from '../reducer'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> import { actionType } from './pc.reducer'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> import { HTTPS } from '../network/network'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> const mapStateToProps = (state: State) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">      index: state.pc.index
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> const mapDispatchToProps = (dispatch) =&gt;<span style="color: rgba(0, 0, 0, 1)"> ({
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   getIndex: () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      dispatch({
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">            type: actionType.pc_first
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">      })
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">    },
</span><span style="color: rgba(0, 128, 128, 1)">21</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>   getList: () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">23</span>         HTTPS.post('/getList', {id: 2<span style="color: rgba(0, 0, 0, 1)">}).subscribe({
</span><span style="color: rgba(0, 128, 128, 1)">24</span>             next: (res) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">                dispatch({
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">                  type: actionType.pc_getList,
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">                  list: res.data
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">                })
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">            },
</span><span style="color: rgba(0, 128, 128, 1)">30</span>             error: (e) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">31</span>
<span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">      })
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">36</span>
<span style="color: rgba(0, 128, 128, 1)">37</span>export <span style="color: rgba(0, 0, 255, 1)">default</span> connect(mapStateToProps, mapDispatchToProps)(Pc);</pre>
</div>
<p>pc.component.tsx就是容器组件,可以看到它导入了pc.ui.tsx并且将之连接形成一个完整的组件:export default connect(mapStateToProps, mapDispatchToProps)(Pc);</p>
<p>在这里,我们编写一个组件的主要逻辑,例如Ajax请求函数。这里的&nbsp;mapStateToProps, mapDispatchToProps两个函数都是redux的语法规则,笔者就不再赘述。本文主要研究react架构。</p>
<p>值得注意的是并非任何函数都要写在容器组件里,例如在pc.ui.tsx中有一个 change函数就写在ui组件中。因为此函数修改的数据并不在redux提供的全局store中。这个change函数事实上是一个控制侧边栏是否收起的函数。显然图中的collapsed变量是存放在pc组件本身所维护的state当中。也就是说一些数据只需要组件自己维护就可以了,不需要借助redux来维护其状态。通常来将这些数据都是决定组件本身状态的数据。我们在处理这样的数据时应该把它当成一个UI层面的事件,自然我们也应该把这些函数写在UI组件里。</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509195642024-922388434.png"></p>
<p>pc.reducer.tsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const initialState =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   index: ['test'<span style="color: rgba(0, 0, 0, 1)">]
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">export interface PcState {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">    index: string[]
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> export const actionType =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   pc_first: 'pc_first'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   pc_getList: 'pc_getList'
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> const pc = (state: PcState = initialState, action: any) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (action.type) {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>         <span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> actionType.pc_first:
</span><span style="color: rgba(0, 128, 128, 1)">17</span>             <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">                ...state,
</span><span style="color: rgba(0, 128, 128, 1)">19</span>               index: ['sun', 'yang', 'kai'<span style="color: rgba(0, 0, 0, 1)">]
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">21</span>         
<span style="color: rgba(0, 128, 128, 1)">22</span>         <span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> actionType.pc_getList:
</span><span style="color: rgba(0, 128, 128, 1)">23</span>             <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">                ...state,
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">                index: action.list
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">27</span>         <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 128, 128, 1)">28</span>             <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> state;
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">31</span>   
<span style="color: rgba(0, 128, 128, 1)">32</span> export { pc };</pre>
</div>
<p>pc.reducer.tsx很显然就是专门用于维护组件数据的文件。它负责去修改和更新store树上的数据。store树也是redux的概念,这里也不再赘述。修改store树上的数据的唯一方式是发起一个action,这是redux的规则。随着我们的action越来越多,我们需要对action进行范式化的命名。例如这里的pc_getList。它代表pc模块下的getList函数,也就是pc模块下获取一个列表的函数。</p>
<p>然后我们来看整个应用的store是如何构成的:</p>
<p>首先找到根reducer:reducer.tsx</p>
<p>reducer.tsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import { combineReducers } from 'redux'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { pc, PcState } from './pc/pc.reducer'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import { pc_order, PcOrderState } from './pc/order/order.reducer'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> combineReducers({
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">    pc,
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">    pc_order
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">export interface State {
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    pc: PcState,
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    pc_order: PcOrderState
</span><span style="color: rgba(0, 128, 128, 1)">13</span> }</pre>
</div>
<p>State接口包含我们整个应用的数据,它定义redux的store的类型. 我们可以看到reducer.tsx导入了pc模块和pc_order模块下各自的reducer并用combineReducers将它们合并。根据我们的范式化设计可以看出pc_order就是pc模块下的order模块。这里的store树的设计依旧是按照范式化扁平化的设计原则,为的也是提高store树的性能。如果我们将pc_order命名为order并将之嵌套 在pc下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">export interface State {
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">    pc: {
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)">      order: PcOrderState
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">5</span>
<span style="color: rgba(0, 128, 128, 1)">6</span> }</pre>
</div>
<p>随着业务越来越复杂,order模块下可能还会有其它其它模块,order下又会嵌套更多的对象。最终整个store树层级太深变得臃肿不堪,会影响性能。所以采取范式化扁平化的设计会提高store树的性能。</p>
<p>同时请注意我们在reducer.tsx导入各个模块的reducer时还导入了它们的类型如PcState、PcOrderState。我们利用这些类型完整地定义了整个全局State类型。所以今后无论我们在哪个地方操作store树只需要导入此State接口,就能对整个store树地结构一目了然,因为typescript会有类型提示。</p>
<p>例如我们在order组件中操纵全局的store, 根据导入的State类型提示,我们清楚地知道store的每一个细节:</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509202527287-1002410846.png"></p>
<p>我们已经讨论完pc模块下的文件划分以及它们的功能。其它任何文件都可以按照这样的方式划分。最后我们总结一下划分思路:我们首先在第一维度上是按照业务层次(页面层次)上来划分文件夹,如pc文件夹代表主体页面,这个页面下包含订单页面、酒店页面、房间页面。所以我们又在pc文件夹下划分了三个文件夹room、order、hotel。这样整体业务层次划分就非常清晰。其次在第二维度上,我们根据页面元素可以再次拆分一个页面。例如pc页面。通常首页包含的元素有很多,例如轮播图、导航栏、页眉页脚等等。我们可以将这些元素拆分出来放进pc下的components文件夹中。当然这里笔者偷了懒,components文件夹是空的。对于任何一个页面我们都可以这样做,像order订单页面,我们也可以在其下建一个components文件夹用于存放拆分出来的页面元素。</p>
<p>最后做一点补充:network文件夹里存放了网络相关的配置。这里只是简单得封装了一个post请求,用的rxjs。我不喜欢promise,明明rxjs功能更加强悍。</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509203543955-1127113167.png"></p>
<p>mock文件夹下模拟了后台服务得接口,这里简单写了几个接口:</p>
<p><img src="https://img2018.cnblogs.com/blog/1281172/201905/1281172-20190509203625216-1181554298.png"></p>
<p>&nbsp;在order.ui.tsx中,借助immutable.js进行了渲染优化,利用react提供的shouldComponentUpdate函数避免不必要的渲染。感兴趣得同学可以自己研究一下</p>
<div class="cnblogs_code">
<pre>import * as React from "react"<span style="color: rgba(0, 0, 0, 1)">;
import { Collapse, Button } from </span>'antd'<span style="color: rgba(0, 0, 0, 1)">;
import { Order as OrderProps } from </span>'./order.reducer'<span style="color: rgba(0, 0, 0, 1)">;
import { is, Map } from</span>'immutable'<span style="color: rgba(0, 0, 0, 1)">;

const Panel </span>=<span style="color: rgba(0, 0, 0, 1)"> Collapse.Panel;

interface OrderItemProps {
    order: OrderProps
}
interface OrderItemState {}

class OrderItem extends React.Component</span>&lt;OrderItemProps, OrderItemState&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    constructor(props: OrderItemProps, state: OrderItemState) {
      super(props);
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {}
    }
    shouldComponentUpdate(nextProps,nextState){
      const thisProps </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props;
      const thisState </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state;
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">is(Map({...thisProps.order}), Map({...nextProps.order}))) {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (const key <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> nextState) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (thisState !== nextState &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">is(thisState, nextState)) {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    render() {
      const order </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.order;
      console.log(</span>'render: ' +<span style="color: rgba(0, 0, 0, 1)"> order.name);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
            </span>&lt;span&gt;{order.name}&lt;/span&gt;
<span style="color: rgba(0, 0, 0, 1)">      )
    }
}


interface State {}
interface Props {
    orders: OrderProps[],
    getOrders: () </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">
}
class Order extends React.Component</span>&lt;Props, State&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    constructor(props: Props, state: State) {
      super(props);
    }
    render() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
            </span>&lt;div&gt;
            &lt;Button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.props.getOrders}&gt;获取新订单&lt;/Button&gt;
            &lt;Collapse defaultActiveKey={['key0']}&gt;<span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.orders.map((order, index) =&gt;
                        &lt;Panel header={order.name} key={'key' + index}&gt;
                           &lt;OrderItem order={order} &gt;&lt;/OrderItem&gt;
                        &lt;/Panel&gt;
<span style="color: rgba(0, 0, 0, 1)">                  )
                }
            </span>&lt;/Collapse&gt;
            &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">      )
    }
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Order;</pre>
</div>
<p>最后做一点总结吧。我认为架构好一个前端应用需要从页面层次上清晰地划分整个应用,再从页面元素层次上清晰地划分每一个页面。另外关于框架的选择问题,网上也有很多讨论。但是往往他们只是罗列了一大堆各个框架的特性,最终并没有给出一个明确的建议。总是以它们各有格的特点为由不给出答案。我其实并不完全这样认为。拿React和Angular来说,React更适合有一定经验的团队,Angular更适合没有经验的团队。因为React它不是一个完整的框架却有着庞大的生态环境,如果你和你的团队足够老练,那么你们可以随心所欲地架构起适合自己项目的框架,这样就非常地灵活,所构建地应用也和当前项目契合度很高。Angular是一个完整地框架,它把一切都规定好限制好了,虽然它很优秀,但对于一个有经验地团队实在是限制过度了。打个比方,Angular就像是倚天屠龙剑,一个初出茅庐的小子拿着它也能和各路江湖高手过上几招,但是如果你重度依赖它,自己是很难突破自我提升能力的,厉害的是剑而不是使用者。而一个熟练使用React的人,他就像一个武器大师,草木皆为剑。没有固定的武器,但是他能在不同的境地找到适合自己的武器,他真正的提升了自己的能力而不是依赖武器本身。</p>
<p>项目github地址:https://github.com/sunyangkai/ReactDemo</p>
<p>以上就是笔者对react架构方面的一些思考。文中若有不当之处还请各位少侠不吝赐教!</p><br><br>
来源:https://www.cnblogs.com/sunyangkai123/p/10840390.html
頁: [1]
查看完整版本: React架构之路