博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端状态管理简易实现(以vuex为例)
阅读量:7115 次
发布时间:2019-06-28

本文共 7754 字,大约阅读时间需要 25 分钟。

1.状态管理到底是什么?有什么用?

       状态管理说白了就是一个 全局变量,var obj = {} 这样的东西。

       使用全局变量的缺点:

             无法追踪数据的更改情况,任意的操作都可导致数据变量,无法所追踪数据的变化过程,大型应用中不太容易定位bug

       状态管理:

              可以追踪到数据变化的全局变量。

        使用场景:

       在多页面中,状态管理应用不大,因为这个变量仅仅存与内存中,当跳转页面时,数据就会清除。当然,如果对状态进行存储(比如sessionStroge),倒也可以实现状态的复用,但是想想,每次数据都需存储,相比较sessionStroge api级别的存储,状态管理会更复杂一些,带来应用的复杂性。

        在单页面中,跳转系统转化为路由系统,路由跳转无需熟悉页面,保存到内存中的状态数据生命周期更长,使用也更加广泛。但依然要注意在web端,刷新操作的频率,刷新页面会使的数据全部丢失,所以需在代码中完成丢失情况的处理,使的代码更加健壮性。

2.那如何追踪一个全局变量的数据变化?

     如果我们有使用vuex的经验,我们知道在vuex里的数据,无法通过store.state.a = 1这样的赋值操作完成。因为这样,无法追踪到数据的变化。

     问题:追踪一个全局变量的数据变化?

     方法1:Object.defineProperty();这个应该很熟悉,vue双向绑定就是通过这里的setter getter实现的。

    方法2:Proxy ES6新语法,vue3的底层双向绑定会使用它。(本文我们以Proxy做demo)

学习Proxy,请点击 

3.在写一个简易的状态管理,需要我们准备什么?

         前提:本文只讨论如何简易状态管理,所以性能方面不会考虑,也就是说不会使用Vnode diff算法等知识。

         查看上面的图,有几个问题我们需要考虑:

          1.只要state发生变化,view层自动更新?

          我们增加个订阅-发布器, 每个需要state的view订阅数据变化,当数据发生变化时,发布这个变化,state也会跟着变化。

         2.这个指令是什么?是vuex中的actions mutations吗?

         我们模拟vuex实现,所以认为指定就是这两个,actions是异步操作,mutation是同步操作。

4.代码实现:

        本文代码大部分来自

        本文是在上文中总结学习写出的。

        本文目录结构如下:

              

1.订阅-发布模式 js/lib/pubsub.jsexport default class PubSub{    constructor(){        this.events = {};    }    // 订阅    subscribe(type,callback){        if(!this.events.hasOwnProperty(type)){            this.events[type] = [];        }        // 返回该type下订阅了多少个回掉函数        // push返回的是当前数组的length        return this.events[type].push(callback);    }    // 发布    publish(type,data={}){        if(!this.events.hasOwnProperty(type)){            return [];        }        return this.events[type].map((callback)=>{            callback(data)        })    }}复制代码

2.实现状态管理的关键  js/store/store.jsimport pubSub from '../lib/pubsub.js'export default class Store {    // 默认参数    /*    * 以vuex的写法举例    * new Vuex.store({    *   state:{    *    *   },    *   mutations:{},    *   actions:{}    * })    * */    constructor(params) {        let self = this;        self.mutations = params.mutations ? params.mutations : {};        self.actions = params.actions ?  params.actions : {} ;        this.events = new pubSub();        self.state = new Proxy((params.state || {}), {            set(target,key,value){                target[key] = value;                console.log(`stateChange: ${key}: ${value}`);                // 当数据变化时,进行发布操作                self.events.publish('stateChange',self.state);                // 如果数据不是有mutation方式改变,则发出警告                if(self.status !== 'mutation'){                    console.warn(`数据应该用mutation方式提交`);                }                self.status = 'resting';                return true;            }        });        self.status = 'resting';    }    /*    * dispatch():提交action,action是一个异步方法    * */    dispatch(actionType,payload){        let self = this;        if(typeof self.actions[actionType] !== 'function'){            console.log(`action ${actionType} 不存在`);            return false;        }        console.groupCollapsed(`ACTION: ${actionType}`);        self.status = 'action';        self.actions[actionType](self,payload);        console.groupEnd();        return true;    }    commit(mutataionType,payload){        let self = this;        if(typeof self.mutations[mutataionType] !== 'function'){            console.log(`Mutation "${mutataionType}" 不存在`);            return false;        }        console.log(`Mutation: ${mutataionType}`)        self.status = 'mutation';        let newState = self.mutations[mutataionType](self.state, payload);        self.state = Object.assign(self.state,newState);        return true;    }}复制代码

实例化Store  js/store/index.jsimport Store from './store.js'export default new Store({    state:{        items:[            1,2        ]    },    mutations:{        addItem(state,payload){            state.items.push(payload);            return state;        },        clearItem(state,payload){            state.items.splice(payload.index,1);            return state;        }    },    actions:{        addItem(context,payload){            context.commit('addItem',payload)        },        clearItem(context,payload){            context.commit('clearItem',payload)        }    }})复制代码

view层:js/lib/component.jsimport Store from '../store/store.js'export default class Component{    constructor(props={}) {        let self = this;        this.render = this.render || function () {                    }        // 关键:这个是通用的组件类,可对需要使用state的组件,进行数据订阅。        if(props.store instanceof Store){            props.store.events.subscribe('stateChange',self.render.bind(self))        }        if(props.hasOwnProperty('el')){            self.el = props.el;        }    }}复制代码

组件类:List      js/components/List.jsimport component from '../lib/component.js'import store from '../store/index.js'export default class List extends component{    constructor() {        super({            store,            el: document.querySelector('.js-items')        })    }        render(){            let self = this;            if(store.state.items.length === 0) {                self.el.innerHTML = `

You've done nothing yet 😢

`; return; } self.el.innerHTML = `
    ${store.state.items.map(item => { return `
  • ${item}
  • ` }).join('')}
`; self.el.querySelectorAll('button').forEach((button, index) => { button.addEventListener('click', () => { store.dispatch('clearItem', { index }); }); }); }}复制代码

Count组件   js/components/Count.jsimport store from '../store/index.js'import component from '../lib/component.js'export default  class Count extends component{    constructor() {        super({            store,            el:document.querySelector('.js-count')        })    }    render(){        let suffix = store.state.items.length !== 1 ? 's' : '';        let emoji = store.state.items.length > 0 ? '🙌' : '😢';        this.el.innerHTML = `            You've done            ${store.state.items.length}            thing${suffix} today ${emoji}        `;    }}复制代码

主函数:main.jsimport store from './store/index.js';import Count from './components/Count.js';import List from './components/List.js';const formElement = document.querySelector('.js-form');const inputElement = document.querySelector('#new-item-field');formElement.addEventListener('submit', evt => {    evt.preventDefault();    let value = inputElement.value.trim();    if(value.length) {        store.dispatch('addItem', value);        inputElement.value = '';        inputElement.focus();    }});const countInstance = new Count();const listInstance = new List();countInstance.render();listInstance.render();复制代码

入口html:    
Vanilla State Management

Done list

A list of things that you have achieved today

Note: The data isn't stored, so it will disappear if you reload!

What you've done

复制代码

5.总结

以上,也就实现了一下简易的状态管理,其实状态管理就是将{a:1}的数据修改都通过指定的指令去修改,可以追踪到数据变化,所以不要把状态管理想的多么复杂,多么高大上。时刻牢记,所谓状态管理就是一个全局对象,只是这个对象的属性变化要经过规定一套流程。

如果你不喜欢状态管理,而且项目不大的情况下,可以不用。sessionstorage这些也是很好的选择。

转载地址:http://ydzel.baihongyu.com/

你可能感兴趣的文章
JavaScript的第一次小结
查看>>
Google Maps 如何接地气地本地化 “两轮车模式” ?
查看>>
转-Java基础全面解析——Java语言基础
查看>>
模糊查询json数组
查看>>
8.使用下拉列表框进行多选
查看>>
不安装执行apk的方法(参考地址)
查看>>
oracle中的递归查询(start with/connect by)
查看>>
Xenserver命令大全
查看>>
随堂练习_电梯
查看>>
自动化测试基础篇--Selenium cookie操作
查看>>
再思linux内核在中断路径内不能睡眠/调度的原因(2010)【转】
查看>>
TCP/UDP区别&&心跳包机制【转】
查看>>
通信协议中的转义字符【转】
查看>>
input 上报流程图
查看>>
cppcheck代码检测
查看>>
vs 2010 sn 序列号
查看>>
js获取下拉框的value值
查看>>
LeetCode OJ:Permutations(排列)
查看>>
云科技时代:阿里云创造者写了《在线》,这是一本怎样的书?
查看>>
PyQt5:常用控件
查看>>