Объясните принцип реализации Redux самым простым способом.

JavaScript

написать впереди

Объясните принцип реализации Redux самым простым способом? Грубо говоря, мои возможности ограничены, и я могу использовать только самый элементарный метод объяснения, для того, чтобы объяснить это более понятно, статья может быть отложена. Тем не менее, я считаю, что студенты, которые не очень хорошо знают Redux, обязательно что-то получат после прочтения статьи, которой я поделился сегодня!

Что такое Редукс?

Сегодня я не об этом, интересно, что такое ReduxНажмите на портал

Начинать

Прежде чем я начну, я хочу поговорить об общем шаблоне проектирования: шаблоне Observer. Сначала позвольте мне сказать观察者模式Личное понимание: шаблон наблюдателя (публикация/подписка). Для студентов, которые хорошо разбираются в этом режиме, следующий код можно пропустить. Если вам все еще не ясно, вы можете попробовать ввести следующий код вручную! !

Шаблон наблюдателя

Шаблон наблюдателя, основанный на канале темы/события, объекты, которые хотят получать уведомления (называемые подписчиками), подписываются на пользовательские события. Прочтите тему и получите уведомление о том, как доставщик публикует событие темы. Так же, как пользователи подписываются на общедоступные учетные записи WeChat, пока они публикуются, пользователи могут получать новейший контент.

/**
 * describe: 实现一个观察者模式
 */
let data = {
    hero: '凤凰',
};
//用来储存 订阅者 的数组
let subscribers = [];
//订阅 添加订阅者 方法
const addSubscriber = function(fn) {
    subscribers.push(fn)
}
//发布
const deliver = function(name) {
    data.hero = name;
    //当数据发生改变,调用(通知)所有方法(订阅者)
    for(let i = 0; i<subscribers.length; i++){
        const fn = subscribers[i]
        fn()
    }
}
//通过 addSubscriber 发起订阅
addSubscriber(() => {
    console.log(data.hero)
})
//改变data,就会自动打印名称
deliver('发条') 
deliver('狐狸')
deliver('卡牌')

Эта публикация и подписка сохраняет подписчиков (метод fn) через addSubscriber. Когда данные изменяются путем вызова доставки, он автоматически проходит через addSubscriber для выполнения внутри метода fn.

Зачем говорить об этой модели публикации-подписки? Поскольку вы понимаете этот паттерн, вы будете чувствовать себя более ясно, когда будете читать эту статью.

Начало работы с Redux

首先我们把上面那个发布订阅代码优化一下,顺便改一下命名,为什么要改命名?主要是紧跟 Redux 的步伐。让同学们更加眼熟。

let state = {hero: '凤凰'};
let subscribers = [];
//订阅 定义一个 subscribe 
const subscribe = (fn) => {
    subscribers.push(fn)
}
//发布
const dispatch = (name) => {
    state.hero = name;
    //当数据发生改变,调用(通知)所有方法(订阅者)
    subscribers.forEach(fn=>fn())
}
//通过 subscribe 发起订阅
subscribe(() => {
    console.log(state.hero)
})
//改变state状态,就会自动打印名称
//这里要注意的是,state状态不能直接去修改
dispatch('发条') 
dispatch('狐狸')
dispatch('卡牌')

Теперь это изменение выглядит знакомым?Да, это идея, похожая на редукс, изменяющий состояние. Но публикации и подписки недостаточно, невозможно изменить состояние и нужно определить так много методов. Итак, мы инкапсулируем его.

метод creatStore

const creatStore = (initState) => {
    let state = initState;
    let subscribers = [];
    //订阅 定义一个 subscribe 
    const subscribe = (fn) => {
        subscribers.push(fn)
    }
    //发布
    const dispatch = (currentState) => {
        state = currentState;
        //当数据发生改变,调用(通知)所有方法(订阅者)
        subscribers.forEach(fn=>fn())
    }
    // 这里需要添加这个获取 state 的方法
    const getState = () => {
        return state;
    }
    return {
        subscribe,
        dispatch,
        getState,
    }
}

Это создает метод createStore. В этом нет ничего нового, просто перейдите в начальное состояние, а затем верните три метода подписки, отправки и getState. Вот новый метод getState, код очень прост для возврата состояния, чтобы получить состояние.

использование creatStore

Реализован createStore Попробуем как пользоваться, далее возьмем тот самый классический случай - счетчик попробовать

let initState = {
    num: 0,
}
const store = creatStore(initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})
// +1
store.dispatch({
   num: store.getState().num + 1
})
//-1
store.dispatch({
   num: store.getState().num - 1
})

Это выглядит немного ближе к внешнему виду Redux. Но существует проблема. Если вы используете метод store.dispatch, будет больше проблем, если вы сделаете ошибку или передадите что-то еще посередине. Как следующее:

На самом деле, я думаю, что +1, +1, -1 должно быть 1 в конце (начальное число равно 0)! Но из-за неправильного написания следующее будет неправильным. И у него еще проблема в том, что он может дать новое состояние по желанию. Тогда это не кажется таким простым. Например следующее:

Поскольку num злонамеренно изменен на тип String, диспетчеризация используется позже.Поскольку num больше не имеет тип Number, печатается NaN, а это не то, что нам нужно. Поэтому мы должны преобразовать его, чтобы упростить отправку. Как это сделать? Давайте попросим менеджера помочь нам с этим, давайте назовем его редьюсером.

Почему он называется редуктором?

Я нашел следующее введение в редуктор на официальном сайте редуктора.

Что ты имеешь в виду, для такого английского у меня просто есть способ перевести

Конечно, этот перевод не работает.

Поискав официальный сайт китайского Redux, он сказал следующее:

Такая функция называется редуктором, потому что она имеет тот же тип, что и функция обратного вызова, переданная в Array.prototype.reduce(reducer, ?initialValue) . Очень важно поддерживать редуктор в чистоте. Никогда не выполняйте эти операции в редукторах.

Эй, этот перевод кажется намного яснее. Как сказал комментатор ниже, это вдохновлено методом сокращения в массиве, который является своего рода композицией операций. Итак, здесь я представлю сокращение.

что такое уменьшить

Нечего сказать, сразу к коду

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

/*该减速作用有四个参数:
*累加器(acc)
*当前价值(cur)
*当前指数(idx)
*源数组(src)
*您的reducer函数的返回值被分配给累加器,其值在整个阵列的每次迭代中被记住,并最终成为最终的单个结果值。
*/

Введение в конкретные параметры

callback
  函数在数组中的每个元素上执行,有四个参数:
accumulator
  累加器累加回调的返回值; 它是先前在回调调用中返回的累计值,或者initialValue,如果提供(参见下文)。
currentValue
  当前元素在数组中处理。
currentIndex可选的
  数组中正在处理的当前元素的索引。如果initialValue提供了an,则从索引0开始,否则从索引1开始
array可选的
  该阵列reduce()被召唤。
initialValue可选的
  用作第一次调用的第一个参数的值callback。如果未提供初始值,则将使用数组中的第一个元素。调用reduce()没有初始值的空数组是一个错误。

Этот метод сложнее для понимания, чем forEach, map, filter. См. также MDNArray.prototype.reduce()Подробное введение

Примечание: Прежде всего, я хотел бы поблагодарить нижеприведенного комментатора panda080 за его руководство.По его совету я пошел на официальный сайт Rudex, чтобы найти его снова. Изучая себя, я лучше понимаю редуктор и уменьшаюофициальный сайт редуктора

ps:理解完之后,其实个人觉得 reducer 这个命名从翻译过来的角度总觉得很怪异。可能英语有限,或许他有更加贴切的意思我还不知道。

что такое редуктор

В процессе моего исследования я думаю, что редьюсер — это менеджер (может быть, это не правильно), и тогда мы уведомляем менеджера каждый раз, когда хотим что-то сделать, и позволяем ему делать это в соответствии с тем, что мы говорим. Если мы случайно скажем что-то не так, то он этого не сделает. Просто следуйте за вещью по умолчанию. Редуктор Дэн Дэн Дэн Денг уже здесь! !

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                count: state.count + 1
            }
        case 'minus':
            return {
                ...state,
                count: state.count - 1
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}

С добавлением этого менеджера мы должны переписать предыдущий метод createStroe: поместить в него редюсер.

const creatStore = (reducer,initState) => {
    let state = initState;
    let subscribers = [];
    //订阅 定义一个 subscribe 
    const subscribe = (fn) => {
        subscribers.push(fn)
    }
    //发布
    const dispatch = (action) => {
        state = reducer(state,action);
        subscribers.forEach(fn=>fn())
    }
    const getState = () => {
        return state;
    }
    return {
        subscribe,
        dispatch,
        getState,
    }
}

Очень простая модификация, чтобы вам было легче увидеть модификацию и разницу, я перекодировал сравнение двух методов до и после, как показано ниже.

Хорошо, попробуем эффект от добавления менеджера в creatStore.

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                num: state.num + 1
            }
        case 'minus':
            return {
                ...state,
                num: state.num - 1
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}

let initState = {
    num: 0,
}
const store = creatStore(reducer,initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})

Для того, чтобы результат был виден наглядно, рассылку (подписку) я вывожу прямо в консоль, как показано ниже:

Эффект очень хороший, у нас больше не будет NaN или других неописуемых проблем из-за опечаток. Теперь эта рассылка немного чище.

Мы просто даем ему тип и позволяем менеджеру решать, как изменить состояние для нас. Если вы случайно напишете ошибку, или дадите случайный тип и менеджер не сможет его сопоставить, то это действие будет недействительным, и будет возвращено наше собственное состояние по умолчанию.

Хорошо, это в основном то, что я увидел, когда впервые использовал Redux. В то время мне было очень трудно им пользоваться. В то время я едва понял эту демонстрацию счетчика и молча закрыл vs code.

Далее давайте улучшим этот редюсер и добавим к нему еще один метод. И на этот раз мы даем государству еще один

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                num: state.num + 1
            }
        case 'minus':
            return {
                ...state,
                num: state.num - 1
            }
        // 增加一个可以传参的方法,让他更加灵活
        case 'changeNum':
            return {
                ...state,
                num: state.num + action.val
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}

let initState = {
    num: 0,
}
const store = creatStore(reducer,initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})

Консоль снова использует новый метод:

Хорошо, это делает диспетчеризацию более гибкой?

Сейчас мы написали 3 метода в редьюсере, но в реальном проекте их должно быть много, поэтому их написание таким образом должно быть неблагоприятным для разработки и сопровождения. Так что этот вопрос остается для вас, чтобы подумать.

Подсказка: Redux тоже это знает, поэтому он предоставляетcombineReducersдля реализации этого шаблона. Это пример редьюсера более высокого порядка, который принимает объект разделенных функций редуктора и возвращает новую функцию редуктора.

Подумав об этом, вы можете обратиться кредукционная китайская документацияПредставляем combReducers

Суммировать

В проекте Redux есть много очень оригинальных методов, и многие места можно использовать для справки. В конце концов, это звезда 47W+ на github.

Сегодня я рассказал лишь о небольшой его части. Я также усердно учусь и надеюсь поделиться с вами еще большим количеством мнений и подробно обсудить их в будущем.

напиши в конце

Каждый из вышеперечисленных случаев и код размещены на github, и их можно открыть и использовать напрямую, поделившись со всеми.гитхаб-портал

Во всей статье, если есть какие-либо ошибки или неточности, обязательно исправьте их, спасибо!

Ссылаться на: