react服务端/客户端,同构代码心得
发布于 8 年前 作者 webkixi 4299 次浏览 来自 分享

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/pagireact/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组件
    

原子组件

  1. List List source
  2. Item Item source
  3. Item 算法部分 Item算法实现

综上所述,做到两端同构的话,需要有一个全局的眼光,从基础的目录结构开始,到组件的结构,其实还有css的结构,html的结构,这里就不一一说明了,希望能抛砖引玉

^^! 欢迎关注我的个人站 http://www.agzgz.com
^
^! github fkpjs https://github.com/webkixi/FKP-REST

回到顶部