原来Redux的原理这么简单

熟悉React的开发者并不陌生Redux是什么,一个状态管理容器,简单来说就是帮助前端存储交互的数据的容器。
笔者今天聊的就是如何手写一个Redux,破除那些把Redux神话的同学么。

Tip: 一定要熟悉JavaScript的原理哦

了解Redux的运行原理

三大原则

  • 单一数据流
  • State只读
  • 只能使用纯函数来执行修改
    工作原理

    从图片中我们可以看到,Redux的工作原理Action Creators通过派发方法dispatch ,然后派发的action会被Reducer层拿到进行处理,最后行成新的Store数据,最后被React Component绑定, 页面发生改变

声明Store

我们需要创建一个Store的函数

function createStore(reducer){
    let state;
    let getState = () => JSON.parse(JSON.stringify(state))
    return {
        getState,

    }
}

这里的reducer是一个回调函数

const CHANGE_TITLE = "change_title"
function reducer(state={title: "标题"}, action){
    switch(action.type){
        // 一下的reducer就是我们具体实现存储值的逻辑
        case CHANGE_TITLE:
            return {...state, title: action.data}
        default: 
            break;
    }
    return state
}

看到这里是不是有那味了,我想对于React+Redux的使用者来说这个reducer是不是非常熟悉了,那么每一次的派发我们只需要重新reducer一次,就可以把派发的值给重新修改了
diapatch方法

function dispatch(action){
      state = reducer(state, action)
}

ok派发方法有了,创建Store函数有了,我们还差render方法,来让页面同步更新

创建render方法我们必须明白,当state发生改变的时候,页面就应该响应了,那么这里不得不使用一个订阅发布者模式来完成

创建render方法并优化dispatch方法

function createStore(reducer){
    let state;
    function dispatch(action){
        state = reducer(state, action)
        // 每一个改变值之后去重新渲染监听器中的函数
        listeners.forEach(item => item())
    }
    // 优化思路 加入发布订阅模 式
    let listeners = []
    let subscribe = (fn) => {
        listeners.push(fn)
        // 加入取消绑定的函数
        return () => {
            listeners => listeners.filter(item > item!=fn)
        } 
    }

    // 第一次创建需要覆盖自身的对象
    dispatch({})
    let getState = () => JSON.parse(JSON.stringify(state))
    return {
        getState,
        dispatch,
        subscribe
    }
}
let CHANGE_TITLE = "change_title"
let store = createStore(reducer)
function reducer(state={title: "标题"}, action){
    switch(action.type){
        // 一下的reducer就是我们具体实现存储值的逻辑
        case CHANGE_TITLE:
            return {...state, title: action.data}
        default: 
            break;
    }
    return state
}
// 定义一个渲染页面节点的函数
function render(){
    document.querySelector(".title").innerHTML = store.getState().title
}

这里我们可以看到,render方法看似比较简单的,但是如果让render方法可以比较聪明的渲染呢?

image.png
他们就是关键。首先subscribe是一个订阅函数,用来传入每一个render,然后将每一个render函数放入监听数组中,等待下一次dispatch改变值的时候来循环调用数组中的render函数,这样就形成了,数据已发生改变就可以立即响应页面的功能了,我们来看看完整的代码是什么样子的

/**
 * 核心概念store state reducer dispatch action
 */
function createStore(reducer){
    let state;
    function dispatch(action){
        state = reducer(state, action)
        // 每一个改变值之后去重新渲染监听器中的函数
        listeners.forEach(item => item())
    }
    // 优化思路 加入发布订阅模 式
    let listeners = []
    let subscribe = (fn) => {
        listeners.push(fn)
        // 加入取消绑定的函数
        return () => {
            listeners => listeners.filter(item >= item!=fn)
        } 
    }

    // 第一次创建需要覆盖自身的对象
    dispatch({})
    let getState = () => JSON.parse(JSON.stringify(state))
    return {
        getState,
        dispatch,
        subscribe
    }
}
let CHANGE_TITLE = "change_title"
let store = createStore(reducer)
function reducer(state={title: "标题"}, action){
    switch(action.type){
        // 一下的reducer就是我们具体实现存储值的逻辑
        case CHANGE_TITLE:
            return {...state, title: action.data}
        default: 
            break;
    }
    return state
}
// 定义一个渲染页面节点的函数
function render(){
    document.querySelector(".title").innerHTML = store.getState().title
}
// 第一次页面加载的render方法
render()
// 拥有监听器之后我们只需要
let unsubscribe = store.subscribe(render)
// 每次更新值的render方法
setInterval(() => {
    store.dispatch({
        type: CHANGE_TITLE,
        data: "我是重新改变之后的title"+ Math.random() * 100
    })
    // 上面渲染完成之后直接将没有用的监听函数取消
    unsubscribe()
}, 2000)
// 大致上面实现思路就是一个最简单的redux的实现过程

大家可以把它放在一个页面之中运行就可以看到效果了,本文的环境基于node v14,就不做效果演示了.😜😜😜

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值