baidu / san

A fast, portable, flexible JavaScript component framework
https://baidu.github.io/san/
MIT License
4.72k stars 549 forks source link

我想实现一个类似vue的KeepAlive组件,可以给一些建议或思路吗 #780

Closed jingxin2015 closed 5 months ago

jingxin2015 commented 5 months ago

主要是用于实现同时打开多个选项卡页面的功能的,选项卡想通过 s-is 实现,如何保持状态?

jingxin2015 commented 5 months ago

@errorrik 能给点建议思路吗?

errorrik commented 5 months ago

没必要。你要解决的是状态保存的问题。

看起来这个状态和 parent 和 owner 都无关,看起来这个粒度是 by 组件的,是自己组件内聚的。以及,我不确定你是依赖某个父环境存在,还是出现在任何地方都需要保持之前的状态。这是你需要考虑的问题,这些问题框架并不帮你解决,KeepAlive 是一个无脑封装

我还是建议自己封装解决。下面是我的一个思路,场景是,在某个爹一直存活的生命周期内,状态能被保存。但这不一定适应你的状态保存场景,仅供参考。

function keepAlive(Component) {
    let key = (new Date).getTime() + Math.random();
    let initData = Component.prototype.initData;
    let attached = Component.prototype.attached;
    let proto = {
        initData() {
            if (this.owner && this.owner.__keepAlive && this.owner.__keepAlive[key]) {
                return this.owner.__keepAlive[key];
            }

            return initData.call(this);
        },

        attached() {
            this.data.listen(change => {
                if (this.owner) {
                    if (!this.owner.__keepAlive) {
                        this.owner.__keepAlive = {};
                    }

                    this.owner.__keepAlive[key] = this.data.get();
                }
            });

            attached && attached.call(this);
        }
    }

    return san.defineComponent(proto, Component);
}

const ManualCounter = san.defineComponent({
    template: '<div>{{count}}<button on-click="add">+</button></div>',

    add() {
        this.d.count++;
    },

    initData() {
        return {
            count: 0
        };
    }
});

const AutoCounter = san.defineComponent({
    template: '<div>{{count}}</div>',

    attached() {
        this.interval = setInterval(() => {
            this.d.count++;
        }, 1000)
    },

    detached() {
        clearInterval(this.interval);
    },

    initData() {
        return {
            count: 0
        };
    }
});

const Entry = san.defineComponent({
    components: {
        'c-manual': keepAlive(ManualCounter),
        'c-auto': keepAlive(AutoCounter)
    },

    template: `
        <div>
            <input type="radio" value="c-manual" checked="{= counter =}"  name="counter">manual
            <input type="radio" value="c-auto" checked="{= counter =}"  name="counter">auto
                <c s-is="counter" />
        </div>
    `,

    initData() {
        return {
            counter: 'c-manual'
        };
    }

});
(new Entry).attach(document.body)
jingxin2015 commented 3 months ago

我的需求就是想在打开多个选项卡时,在已经打开的选项卡之间切换时需要保持状态,这些选项卡是通过 san-router 来打开的,上面的代码能够实现初始化数据的状态保持,但是在切换的时候还是会执行 attached 方法,这样就会调用后台方法获取数据,能否实现不去重新调用后端方法加载数据呢?