react服务端/客户端,同构代码心得javascript
服务端,客户端同构一套代码,
大前端
的梦想,为了省略重复的代码,却平添了很多烦恼,头发也白了,。。。。,妹子还在家等我.css
咱们引用了不少的库,在开发前端代码的时候,习惯性的咱们不会考虑到node端对于库的引用,这就是开始同构最大的痛点。整个目录结构须要调整。html
好比说开发前端时,有一个libs的库,在react的前端组件开发时,咱们屡次调用到libs里面的若干方法,这个时候,为了同构,须要将libs库作一个抽离,既是从前端代码中抽离到中间的部分。前端
这么说有点很差理解,简单配一个图吧。java
FKP原来的结构node
node -> fed -> libs -> component -> pagesreact
大体上咱们原来的结构都相似于这样,调整好以后的结构,以下:webpack
node <-> libs <-> fed -> component -> pagesgit
这样,咱们将libs抽离到中间的部分,相对来讲,在同构时,require的层级少了不少。可是还不够,为了 将react同构,咱们还须要调整component的结构,以下:github
node <-> libs <-> component <-> fed -> pages
如此这般,大体的结构算调整好了,接下来解决require的坑,让webpack和node端require作到无缝切换。 让require('libs/index'),这种引用兼容于两端。 在这里FKPJS用到了一个好用的包文件app-module-path
,指定node端require的目录优先级,及自写了一个 include的方法(封装require),来简化require的调用深度。 并对libs库作更细化的抽象与提取,最后,FKPJS的libs结构作到如上所述。
解决了目录结构问题后,为了作到同构,咱们须要合理的组件结构,以方便两端的调用,通过本人的实践,FKPJS将组件分为三层,原子组件
,组合组件
,组件封装
,以下图
原子 -> 组合 -> 封装
适用node/fed,复用型组件,最小粒度化,产出纯结构,纯粹的react组件,封装了对数据的处理
适用node/fed,组合不一样的原子组件,并引入相关mixins,实现like redux
,产出纯结构,纯react组件,传输数据
适用于前端,最表层,处理配置文件,可导入JQ等库实现内部逻辑、效果,并响应由业务层传导进来的方法,数据等等。
在FKPJS中封装的比较好的有两个组件,react/modules/pagination/pagi
,react/modules/list/base_list.jsx
,list组件有点复杂,咱们先说下 pagi这个组件吧
pagi这个组件,用于分页,可先后端同构
varPagi=require('modules/pagination/pagi'),// 初始化分页数据 pageData ={ total:60, per:20, url:'/', query:'page='}Pagi(pageData,{ container:'pagi',begin:{ start:0, off:5}, itemMethod: bindItem })
// pages/pagi.jsvar _props ={ itemMethod:false, listMethod:false, itemClass:'', listClass:'pagenation wid-12', data:{ total:60, per:20, url:'/', query:'page='},begin:{ start:0, off:5}}var reactHtml =yield react2html('react/modules/pagination/pagi', _props) reactHtml[0]='<div class="pagi" id="pagi" >'+reactHtml[0]+'</div>' oridata.pagi = reactHtml[0]return.....
// 封装方法function pagination(data, opts ){// 处理配置文件 var noop =false, dft ={ container:'', globalName:'_Pagi', itemMethod: noop, listMethod: noop, itemClass:'', listClass:'pagenation wid-12', data:{ total:200, per:10, url:'/', query:'page='},begin:{ start:0, off:7}} dft = _.assign(dft, opts)if(!dft.container)returnfalse;if(data){ dft.data = data }// fkp redux// 初始化组件数据// FKPJS使用SA代替redux// 须要在组合组件中引入,store的minxin SA.set(dft.globalName,{ data: data,begin: dft.begin})// fkp redux // 将组建的action放到 SA 的全局名字中// 须要在 _Pagi组件中引入 store 这个mixinsvarPagi=_Pagi(dft.globalName)// 渲染组件 render(<Pagi data={data}begin={dft.begin} itemDefaultMethod={idm} itemMethod={dft.itemMethod} listMethod={dft.listMethod} itemClass={dft.itemClass} listClass={dft.listClass}/>, document.getElementById(dft.container))}// 服务端同构,执行这个部分 pagination.server =function(){return_Pagi(true)};module.exports = pagination
这里不贴出全部代码,部分
varList=require('../../../widgets/listView/list');varStore=require('../../../mixins/store');//引入这个就完成了reduxvar _storeName;var _jump =false;// List的item组件varPageItem=React.createClass({ componentDidMount:function(){var ele =React.findDOMNode(this), mtd =this.props.itemMethod, dmtd =this.props.itemDefaultMethod;if(dmtd &&typeof dmtd==='function'){ dmtd.call(ele, _storeName, mtd);}},.......
组合组件-算法部分
render:function(){if(this.state.data){var data =this.state.data, newData =[], pages = data.total/data.per, pre, aft, half,begin=this.state.begin,........
组合组件-实现部分
function actRct( storeName ){// 根据storeName,能够实现多个组件,并redux化// for serverif(storeName===true){returnReact.createClass( pagenation );}// for client _storeName = storeName||'_Pagi';var _rct = _.cloneDeep(pagenation);if( _rct.mixins && _rct.mixins.length ){ _rct.mixins.push(Store( _storeName ))//实现redux}else{ _rct.mixins =[Store( _storeName )]}returnReact.createClass( _rct );//返回react组件
综上所述,作到两端同构的话,须要有一个全局的眼光,从基础的目录结构开始,到组件的结构,其实还有css的结构,html的结构,这里就不一一说明了,但愿能抛砖引玉