react服务端/客户端,同构代码心得
服务端,客户端同构一套代码,
大前端
的梦想,为了省略重复的代码,却平添了不少烦恼,头发也白了,。。。。,妹子还在家等我.
目录结构问题
我们引用了很多的库,在开发前端代码的时候,习惯性的我们不会考虑到node端对于库的引用,这就是开始同构最大的痛点。整个目录结构需要调整。
减少调用层级
比如说开发前端时,有一个libs的库,在react的前端组件开发时,我们多次调用到libs里面的若干方法,这个时候,为了同构,需要将libs库做一个抽离,既是从前端代码中抽离到中间的部分。
这么说有点不好理解,简单配一个图吧。
FKP原来的结构
node -> fed -> libs -> component -> pages
大致上我们原来的结构都类似于这样,调整好之后的结构,如下:
node <-> libs <-> fed -> component -> pages
这样,我们将libs抽离到中间的部分,相对来说,在同构时,require的层级少了很多。但是还不够,为了 将react同构,我们还需要调整component的结构,如下:
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将组件分为三层,原子组件
,组合组件
,组件封装
,如下图
原子 -> 组合 -> 封装
1. 原子组件(react/widgets)
适用node/fed,复用型组件,最小粒度化,产出纯结构,纯粹的react组件,封装了对数据的处理
2. 组合组件(react/modules/xxx/_component/xxx)
适用node/fed,组合不同的原子组件,并引入相关mixins,实现like redux
,产出纯结构,纯react组件,传输数据
3. 组件封装(react/modules/xxx/yyy)
适用于前端,最表层,处理配置文件,可导入JQ等库实现内部逻辑、效果,并响应由业务层传导进来的方法,数据等等。
在FKPJS中封装的比较好的有两个组件,react/modules/pagination/pagi
,react/modules/list/base_list.jsx
,list组件有点复杂,我们先说下pagi这个组件吧,pagi这个组件,用于分页,可前后端同构
Demo
前端业务中实现的代码
var Pagi = 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.js
var _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) return false;
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 这个mixins
var Pagi = _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
组合组件
这里不贴出所有代码,部分
var List = require('../../../widgets/listView/list');
var Store = require('../../../mixins/store'); //引入这个就完成了redux
var _storeName;
var _jump = false;
// List的item组件
var PageItem = 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 server
if (storeName===true){
return React.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 ) ]
}
return React.createClass( _rct ); //返回react组件
原子组件
- List List source
- Item Item source
- Item 算法部分 Item算法实现
综上所述,做到两端同构的话,需要有一个全局的眼光,从基础的目录结构开始,到组件的结构,其实还有css的结构,html的结构,这里就不一一说明了,希望能抛砖引玉
^^! 欢迎关注我的个人站 http://www.agzgz.com
^^! github fkpjs https://github.com/webkixi/FKP-REST