基于 React 的前端项目开发总结
hans511002
7年前
<h2>技术选型</h2> <p>我们的项目主要选用以下技术开发,再配合一些其它辅助工具。</p> <ul> <li> <p>react</p> </li> <li> <p>react-router</p> </li> <li> <p>redux</p> </li> <li> <p>react-redux</p> </li> </ul> <h2>开发及线上环境</h2> <h2>开发环境</h2> <p>由于项目是前后端分离的,所以我们需要一套完整的开发环境,需要包括以下功能:</p> <ul> <li> <p>数据 Mock</p> </li> <li> <p>Webpack 实施编译刷新</p> </li> <li> <p>方便前后端联调</p> </li> </ul> <p>基于这些需求,我们基于 Express, Webpack, Webpack-dev-middleware 搭建了这套完整的开发环境。</p> <p><img src="https://simg.open-open.com/show/765df558c4416ba6f987928b21316ae0.png"></p> <p>可以看到,浏览器所有的请求都被本地的 Node.js 服务拦截。对于静态资源请求,都委托给 webpack-dev-middleware 来处理,对于接口请求根据不同的环境来决定要做的操作。</p> <h3>本地开发</h3> <p>当 ENV = 'development' 时,也就是开发环境,那么就直接读取本地的 mock 数据来渲染页面。</p> <h3>前后端联调</h3> <p>当 ENV = 'api' 时,也就是我们认为的联调环境,这个时候对于接口请求由 node.js 转发到需要联调的真实后端服务地址上,从而避免直接调用所产生的跨域问题。</p> <p>这样就可以直接用本地开发代码和后端联调,能大大提高效率,省去了每次需要往服务器上构建部署的步骤。</p> <h2>线上环境</h2> <p>前后端是分开部署的,所有的静态资源都放在 CDN (example.cdn.com)上面。</p> <p>也就是说我们的页面在 example.cdn/index.html 这里,但是请求的接口在 example.163.com/api/xxx,我们肯定不能让用户去直接去访问 example.cdn.com/index.html,这样不合理,而且由跨域问题存在。</p> <p>那么访问 example.dai.163.com 的时候,怎么拿到我们的 HTML 页面呢?</p> <p>看下图:</p> <p><img src="https://simg.open-open.com/show/9f5d5a1e40628e78b6404d20f752e0e3.png"></p> <p>在客户端和后台服务之间架设一台 Nginx, 我们访问的 example.dai.163.com 有两种请求:</p> <ul> <li> <p>HTML 页面资源</p> </li> <li> <p>接口请求</p> </li> </ul> <p>这两种请求都先经过 nginx,在这里做判断,如果是页面请求那么由 nginx 转发到 CDN, 否则交给后端服务来响应接口请求。</p> <p>拿到页面以后,其它所有的 css, js 等静态资源都是直接请求到 CDN ,这里没什么说的。</p> <h2>数据流转</h2> <p>我们是借助 redux 来管理数据流的。</p> <p><img src="https://simg.open-open.com/show/a1c77df4f77f7391675f0c63697321c9.png" alt="基于 React 的前端项目开发总结" width="550" height="376"></p> <p>我们来看这张图。</p> <p>首先,通过 middleware 和 reducer 生成 store , 然后获得项目的初始 state ,通过初始 state 去渲染页面的初始状态。</p> <p>以 Home 页面为例,首先 Home 通过 react-redux 提供的 connect 方法拿到初始 state 作为 Home 的 prop 传递给 Home . 而 Home 由多个不同的子组件组成,这些组件的需要数据再由 Home 通过 props 传递给自己的子组件。</p> <p>当 Home 的初始状态加载完毕后,我们需要向后端请求去拿去一些用户数据。这时,我们分发一个下面这种格式的 action :</p> <pre> <code class="language-javascript">{ types: ['home/start','home/success','home/failure'], payload: { api: ... }, meta: { isApi: true } }</code></pre> <p>所有的 action 都会按照我们制定的循序通过一个个 middleware .</p> <p>在这里,我们的 action 会被 callApiMiddleware 通过 meta 里面的 isApi 标识命中,并去做相应的事情。</p> <p>比如在这个中间件里面,我们去做了真实的接口请求,在请求成功或失败的时候分发对应的 action ,以及做一些统一的业务逻辑。比如我们对后端返回的接口中 code 值有统一的约定,假设 1 为成功, 2 为失败, 3 为未登录。那么我们就可以在中间件中去处理这些业务逻辑。</p> <p>当请求成功,并渲染页面后,假设用户点击了一个按钮,这个按钮需要唤起 native 的一些功能,比如说拍照。那么我们分发一个唤起拍照功能的 camera/start 的 action :</p> <pre> <code class="language-javascript">{ types: ['sdk/start','sdk/success','sdk/failure'], payload: { command: ... }, meta: { isSDK: true } }</code></pre> <p>同样的道理,这个 action 会被 EpaySDKMiddleware 所识别并处理,在调起 native 的时候, 为了保证安全性,我们需要向后发起一个请求去拿签名,这个时候就可以在 EpaySDKMiddleware 里面分发一个接口请求的 action ,那么这个 action 同样需要走一遍所有的 middleware . 那么这个接口请求的 action 就会像上面的流程一样,通过 callApiMiddleware 去处理。</p> <p>中间件的存在,使整个流程变得非常清晰,接口的请求的中间件就只做接口请求,调用 native 接口的中间件就只做对 native 的调用,当对 native 接口的调用需要做后端接口请求的时候,去分发一个 action 走接口请求的中间件。</p> <p>每个中间件只专注于自己的事情,既方便后续的维护,同时也提供了一个很好的拓展方式。</p> <h2>一个例子</h2> <p><img src="https://simg.open-open.com/show/0ecfad8edef3b50a902e0d755050d6f7.png"></p> <p>假设我们由如下的一个路由配置。</p> <pre> <code class="language-javascript">{ component: App, path: '/', onEnter: initLogin(store), indexRoute: { getComponent(nextState, cb) { require.ensure([], require => { cb(null, require('../views/Home').default) }, 'Home') }, onEnter: initHome(store) }, childRoutes: [ createActivateRoute(store), { path: 'test', indexRoute: { getComponent(nextState, cb) { require.ensure([], require => { cb(null, require('../views/Test').default) }, 'Test') } } }, ... ] }</code></pre> <p>那结合 react-route 我们来看一个完整的流程。当我们浏览器里面输入 example.dai.163.com/index.html/#/ 的时候。</p> <p>首先,通过最上面 线上环境 一节提到的内容,拿到页面需要 html,css,js.</p> <p>然后渲染 Provide 和 Router 组件,分别提供 store 的注入和路由的控制。</p> <p>此时触发根路径的路由匹配,然后加载根组件 APP , 然后根据路由匹配规则匹配到 IndexRouter , 加载 Home 组件。</p> <p>后面的事情就和前面数据流转一节讲的是一样的了。</p> <h2>总结</h2> <p>在前后端完全分离的基础上,借助一套完善的开发环境,可以大大提高的我们的开发效率,降低前后端联调的成本。</p> <p>同时借助于 Redux 思想,实现单向数据流,让我们可以实现一个非常清晰的数据流向。并且,借助于中间件,我们可以更加有效的控制数据流转的过程。为后面项目的扩展提供无限的想象空间。</p> <p> </p> <p>来自: www.kisnows.com/2017/05/12/react-base-project-summary/</p> <p> </p>