Programming

Vuex 액션 vs 돌연변이

procodes 2020. 7. 19. 18:43
반응형

Vuex 액션 vs 돌연변이


Vuex에서 "동작"과 "돌연변이"를 갖는 논리는 무엇입니까?

구성 요소의 논리가 스마트하게 보이는 상태를 수정할 수는 없지만 동작과 변이를 모두 갖는 것은 다른 함수를 트리거하고 상태를 변경하기 위해 하나의 함수를 작성하는 것처럼 보입니다.

"동작"과 "돌연변이"의 차이점은 무엇이며 어떻게 작동합니까? 또한 Vuex 개발자가 왜 이런 식으로 결정했는지 궁금합니다.


질문 1 : Vuejs 개발자가 왜 그렇게하기로 결정 했습니까?

대답:

  1. 응용 프로그램이 커지고이 프로젝트를 수행하는 개발자가 여러 명인 경우 "상태 관리"(특히 "전역 상태")가 점점 더 복잡해집니다.
  2. vuex 방식 (react.js의 Redux와 마찬가지로 )은 상태를 관리하고 상태를 유지하며 "저장하고 추적 할 수있는"새로운 메커니즘을 제공합니다 (즉, 상태를 수정하는 모든 작업은 디버그 도구 로 추적 할 수 있음을 의미합니다 : vue-devtools )

질문 2 : "action"과 "mutation"의 차이점은 무엇입니까?

공식적인 설명을 먼저 보자 :

돌연변이 :

Vuex 돌연변이는 본질적으로 사건입니다 : 각 돌연변이에는 이름과 핸들러가 있습니다.

import Vuex from 'vuex'

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    INCREMENT (state) {
      // mutate state
      state.count++
    }
  }
})

액션 : 액션은 돌연변이를 보내는 함수입니다.

// the simplest action
function increment (store) {
  store.dispatch('INCREMENT')
}

// a action with additional arguments
// with ES2015 argument destructuring
function incrementBy ({ dispatch }, amount) {
  dispatch('INCREMENT', amount)
}

위의 설명은 다음과 같습니다.

  • 돌연변이상태를 수정 하는 유일한 방법입니다
  • 돌연변이 는 비즈니스 로직에 관심이없고 단지 "상태"에 관심이있다
  • 행동 은 비즈니스 로직
  • 액션한 번에 둘 이상의 돌연변이를 파견 할 수 있으며 비즈니스 로직 만 구현하며 데이터 변경에 신경 쓰지 않습니다 (돌연변이로 관리)

돌연변이는 동기식이지만 동작은 비동기식 일 수 있습니다.

다른 방법으로 말하자면 : 작업이 동기식 일 경우에는 조치가 필요하지 않으며, 그렇지 않으면 구현하십시오.


나는 돌연변이와 행동의 동기에 대한 이해가 어느 때와 어떻게 사용할 것인지 더 잘 판단 할 수 있다고 믿습니다. 또한 "규칙"이 희미 해지는 상황에서 프로그래머가 불확실성의 부담을 덜어줍니다. 각각의 목적에 대해 약간의 추론을 한 후에, 나는 행동과 돌연변이를 사용하는 잘못된 방법이있을 수 있지만, 정식 접근법이 있다고 생각하지 않습니다.

먼저 왜 우리가 돌연변이 또는 행동을 겪는 지 이해하려고 노력합시다.

왜 처음부터 상용구를 통과해야합니까? 구성 요소에서 직접 상태를 변경하지 않는 이유는 무엇입니까?

엄밀히 말하면 state구성 요소 에서 직접 변경할 수 있습니다 . state단지 자바 스크립트 객체이며, 당신이 그것을 만드는 것이 변화를 되돌아갑니다 아무것도 마법이있다.

// Yes, you can!
this.$store.state['products'].push(product)

그러나이 작업을 수행하면 모든 곳에서 상태 돌연변이가 분산됩니다. 상태를 수용하는 단일 모듈을 열 수있는 능력을 잃어 버리고 어떤 종류의 작업을 적용 할 수 있는지 한 눈에 알 수 있습니다. 중앙 집중식 돌연변이가 있으면 일부 상용구 비용이 들지만이를 해결합니다.

// so we go from this
this.$store.state['products'].push(product)

// to this
this.$store.commit('addProduct', {product})

...
// and in store
addProduct(state, {product}){
    state.products.push(product)
}
...

보일러 플레이트로 짧은 것을 교체하면 보일러 플레이트도 작게 만들고 싶습니다. 따라서 돌연변이는 비즈니스 로직이 거의없는 상태에서 네이티브 작업을 둘러싼 매우 얇은 래퍼라고 생각합니다. 다시 말해서, 돌연변이는 주로 세터와 같이 사용되는 것을 의미한다.

이제 돌연변이를 중앙 집중화 했으므로 상태 변경에 대한 개요가 개선되었으며 툴링 (vue-devtools)도 해당 위치를 인식하므로 디버깅이 쉬워집니다. 또한 많은 Vuex 플러그인은 상태를 직접 추적하여 변경 사항을 추적하지 않고 돌연변이에 의존한다는 점을 명심해야합니다. 따라서 상태에 대한 "경계를 벗어난"변경 사항은 보이지 않습니다.

그래서 mutations, actions차이 어쨌든 무엇입니까?

돌연변이와 같은 조치도 상점의 모듈에 상주하며 state오브젝트를 수신 할 수 있습니다 . 그것들 직접 돌연변이 시킬 수도 있음을 의미합니다 . 그렇다면 둘 다 갖는 요점은 무엇입니까? 돌연변이를 작고 단순하게 유지해야한다고 생각한다면보다 정교한 비즈니스 로직을 수용 할 수있는 대체 수단이 필요하다는 것을 의미합니다. 행동은이를위한 수단입니다. 이전에 설정 한 것처럼 vue-devtools 및 플러그인은 Mutations를 통한 변경 사항을 알고 있으므로 일관성을 유지하려면 작업에서 Mutations를 계속 사용해야합니다. 더욱이, 액션은 모두 포함되어야하고, 캡슐화하는 로직은 비동기적일 수 있으므로 액션은 처음부터 단순히 비동기식으로 만들어 질 것입니다.

행동은 비동기적일 수 있지만 돌연변이는 일반적으로 그렇지 않다는 것이 종종 강조된다. 돌연변이는 동기적인 모든 것에 (그리고 비동기적인 것에 대한 행동) 사용되어야한다는 표시로 구별을 보도록 결정할 수있다. 그러나 예를 들어 둘 이상의 돌연변이를 (동 기적으로) 커밋해야하거나 돌연변이 함수가 Getters 또는 Mutations를 인수로받지 않기 때문에 돌연변이에서 Getter와 함께 작업 해야하는 경우 몇 가지 어려움에 처하게됩니다 ...

... 흥미로운 질문으로 이어집니다.

돌연변이는 왜 Getter를받지 않습니까?

아직이 질문에 대한 만족스러운 답변을 찾지 못했습니다. 나는 핵심 팀이 내가 mo을 발견했다고 설명했다. 사용법을 요약하면 Getter는 상태에 대한 확장을 계산 (및 종종 캐시)해야합니다. 다시 말해서, 기본적으로 여전히 상태이지만 일부 선행 계산이 필요하지만 일반적으로 읽기 전용입니다. 그것은 적어도 그들이 사용하도록 권장되는 방법입니다.

따라서 돌연변이가 Getter에 직접 액세스하는 것을 방지한다는 것은 우리가 전자에서 제공하는 일부 기능을 전자에서 액세스해야하는 경우 이제 세 가지 중 하나가 필요하다는 것을 의미합니다. (1) Getter가 제공 한 상태 계산이 액세스 가능한 곳에 복제됩니다 (2) 계산 된 값 (또는 관련 Getter 자체)이 Mutation (펑키)에 대한 명시적인 인수로 전달되거나 (3) Getter의 논리 자체가 Mutation 내에서 직접 복제 됨 Getter (stench)에서 제공하는 캐싱의 추가 이점없이

다음은 (2)의 예입니다. 제가 경험 한 대부분의 시나리오에서 "최소 불량"옵션으로 보입니다.

state:{
    shoppingCart: {
        products: []
    }
},

getters:{
    hasProduct(state){
        return function(product) { ... }
    }
}

actions: {
    addProduct({state, getters, commit, dispatch}, {product}){

        // all kinds of business logic goes here

        // then pull out some computed state
        const hasProduct = getters.hasProduct(product)
        // and pass it to the mutation
        commit('addProduct', {product, hasProduct})
    }
}

mutations: {
    addProduct(state, {product, hasProduct}){ 
        if (hasProduct){
            // mutate the state one way
        } else {
            // mutate the state another way 
        }
    }
}

저에게 위의 내용은 약간 복잡 할뿐만 아니라 다소 "누설"인 것 같습니다. 액션에 존재하는 일부 코드는 분명히 돌연변이의 내부 논리에서 나오기 때문입니다.

내 생각에 이것은 타협의 표시입니다. 돌연변이가 Getter를 자동으로 수신하도록 허용하면 몇 가지 문제가 있다고 생각합니다. Vuex 자체의 설계 나 툴링 (vue-devtools 등), 또는 이전 버전과의 호환성을 유지하거나 명시된 모든 가능성의 조합 일 수 있습니다.

내가 믿지 않는 것은 Getters를 돌연변이에 전달하는 것이 반드시 무언가 잘못하고 있다는 표시입니다. 프레임 워크의 단점 중 하나를 단순히 "패치"하는 것으로 간주합니다.


TLDR의 대답은 돌연변이가 동기 / 트랜잭션이어야한다는 것입니다. 따라서 Ajax 호출을 실행하거나 다른 비동기 코드를 수행해야하는 경우 Action에서 해당 작업을 수행 한 후 새 상태를 설정하기 위해 변이를 커밋해야합니다.


에 따르면 docs

행동변이 와 유사하지만 차이점은 다음과 같습니다.

  • 상태 변경 하는 대신 작업 돌연변이를 저지 릅니다 .
  • 작업 에는 임의의 비동기 작업 이 포함될 수 있습니다 .

다음 스 니펫을 고려하십시오.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++               //Mutating the state. Must be synchronous
    }
  },
  actions: {
    increment (context) {
      context.commit('increment') //Committing the mutations. Can be asynchronous.
    }
  }
})

조치 핸들러 ( increment )는 상점 인스턴스에서 동일한 메소드 / 특성 세트를 노출하는 컨텍스트 오브젝트를 수신하므로 context.commit을 호출하여 돌연변이를 커미트하거나 context.state 및 context.getters를 통해 상태 및 게터에 액세스 할 수 있습니다.


면책 조항-방금 vuejs를 사용하기 시작 했으므로 디자인 의도를 외삽하는 것입니다.

Time Machine 디버깅은 상태의 스냅 샷을 사용하고 작업 및 변이의 타임 라인을 보여줍니다. 이론적으로 우리는 actions돌연변이를 동시에 기술하기 위해 상태 설정자와 게터의 기록과 함께 있을 수 있었다 . 하지만:

  • 세터와 게터를 유발하는 입력이 잘못되었습니다 (비동기 결과). 논리적으로 따라 가기가 어려우며 다른 비동기 세터와 게터가 놀랍게 상호 작용할 수 있습니다. mutations트랜잭션에서 여전히 발생할 수 있지만 작업에서 경쟁 조건이 아닌 트랜잭션을 개선해야한다고 말할 수 있습니다. 비동기 프로그래밍은 깨지기 쉽고 어렵 기 때문에 작업 내에서 익명의 돌연변이가 이러한 종류의 버그를 더 쉽게 다시 나타낼 수 있습니다.
  • 상태 변경에 대한 이름이 없으므로 트랜잭션 로그를 읽기가 어렵습니다. 그것은 훨씬 더 코드와 유사하고 영어가 적을 것이고, 돌연변이의 논리적 그룹이 누락되었습니다.
  • 변이 함수 호출 전후에 동 기적으로 정의 된 차이 지점이있는 위치와 달리, 데이터 객체에 대한 변이를 기록하는 것은 까다 롭고 성능이 떨어질 수 있습니다. 그것이 얼마나 큰 문제인지 잘 모르겠습니다.

다음 트랜잭션 로그를 명명 된 돌연변이와 비교하십시오.

Action: FetchNewsStories
Mutation: SetFetchingNewsStories
Action: FetchNewsStories [continuation]
Mutation: DoneFetchingNewsStories([...])

명명 된 돌연변이가없는 트랜잭션 로그를 사용하는 경우 :

Action: FetchNewsStories
Mutation: state.isFetching = true;
Action: FetchNewsStories [continuation]
Mutation: state.isFetching = false;
Mutation: state.listOfStories = [...]

이 예제에서 액션 내부의 비동기 및 익명 돌연변이의 잠재적 인 복잡성을 추가 할 수 있기를 바랍니다.

https://vuex.vuejs.org/en/mutations.html

이제 앱을 디버깅하고 devtool의 돌연변이 로그를보고 있다고 상상해보십시오. 기록 된 모든 돌연변이에 대해 devtool은 상태의 "이전"및 "이후"스냅 샷을 캡처해야합니다. 그러나 위의 예제 돌연변이 내부의 비동기 콜백은 불가능합니다. 돌연변이가 커밋 될 때 콜백이 아직 호출되지 않고 콜백이 실제로 언제 호출되는지 devtool이 알 수있는 방법이 없습니다-콜백에서 수행 된 상태 돌연변이 본질적으로 추적 할 수 없습니다!


동작과 돌연변이의 주요 차이점 :

  1. 내부 작업에서는 비동기 코드를 실행할 수 있지만 돌연변이는 실행할 수 없습니다. 따라서 비동기 코드에는 동작을 사용하고 그렇지 않으면 돌연변이를 사용하십시오.
  2. 내부 액션은 게터, 상태, 돌연변이 (커밋), 돌연변이의 액션 (디 패칭)에 액세스 할 수 있습니다. 따라서 상태에만 액세스하려면 돌연변이를 사용하고 그렇지 않으면 동작을 사용하십시오.

이것은 나도 혼란스러워서 간단한 데모를 만들었습니다.

component.vue

<template>
    <div id="app">
        <h6>Logging with Action vs Mutation</h6>
        <p>{{count}}</p>
        <p>
            <button @click="mutateCountWithAsyncDelay()">Mutate Count directly with delay</button>
        </p>
        <p>
            <button @click="updateCountViaAsyncAction()">Update Count via action, but with delay</button>
        </p>
        <p>Note that when the mutation handles the asynchronous action, the "log" in console is broken.</p>
        <p>When mutations are separated to only update data while the action handles the asynchronous business
            logic, the log works the log works</p>
    </div>
</template>

<script>

        export default {
                name: 'app',

                methods: {

                        //WRONG
                        mutateCountWithAsyncDelay(){
                                this.$store.commit('mutateCountWithAsyncDelay');
                        },

                        //RIGHT
                        updateCountViaAsyncAction(){
                                this.$store.dispatch('updateCountAsync')
                        }
                },

                computed: {
                        count: function(){
                                return this.$store.state.count;
                        },
                }

        }
</script>

store.js

import 'es6-promise/auto'
import Vuex from 'vuex'
import Vue from 'vue';

Vue.use(Vuex);

const myStore = new Vuex.Store({
    state: {
        count: 0,
    },
    mutations: {

        //The WRONG way
        mutateCountWithAsyncDelay (state) {
            var log1;
            var log2;

            //Capture Before Value
            log1 = state.count;

            //Simulate delay from a fetch or something
            setTimeout(() => {
                state.count++
            }, 1000);

            //Capture After Value
            log2 = state.count;

            //Async in mutation screws up the log
            console.log(`Starting Count: ${log1}`); //NRHG
            console.log(`Ending Count: ${log2}`); //NRHG
        },

        //The RIGHT way
        mutateCount (state) {
            var log1;
            var log2;

            //Capture Before Value
            log1 = state.count;

            //Mutation does nothing but update data
            state.count++;

            //Capture After Value
            log2 = state.count;

            //Changes logged correctly
            console.log(`Starting Count: ${log1}`); //NRHG
            console.log(`Ending Count: ${log2}`); //NRHG
        }
    },

    actions: {

        //This action performs its async work then commits the RIGHT mutation
        updateCountAsync(context){
            setTimeout(() => {
                context.commit('mutateCount');
            }, 1000);
        }
    },
});

export default myStore;

이것을 연구 한 결과, 돌연변이는 업데이트 된 데이터 전후에 문제를 더 잘 분리하고 로깅을 개선하기 위해 데이터를 변경하는 데에만 초점을 둔 관습이라는 결론에 도달했습니다. 동작은 상위 수준의 논리를 처리 한 다음 적절하게 돌연변이를 호출하는 추상화 계층입니다.


돌연변이 :

Can update the state. (Having the Authorization to change the state).

행위:

Actions are used to tell "which mutation should be triggered"

Redux 방식으로

Mutations are Reducers
Actions are Actions

왜 둘 다 ??

When the application growing , coding and lines will be increasing , That time you have to handle the logic in Actions not in the mutations because mutations are the only authority to change the state, it should be clean as possible.


1.From docs:

Actions are similar to mutations, the differences being that:

  • Instead of mutating the state, actions commit mutations.
  • Actions can contain arbitrary asynchronous operations.

The Actions can contain asynchronous operations, but the mutation can not.

2.We invoke the mutation, we can change the state directly. and we also can in the action to change states by like this:

actions: {
  increment (store) {
    // do whatever ... then change the state
    store.dispatch('MUTATION_NAME')
  }
}

the Actions is designed for handle more other things, we can do many things in there(we can use asynchronous operations) then change state by dispatch mutation there.


Because there’s no state without mutations! When commited — a piece of logic, that changes the state in a foreseeable manner, is executed. Mutations are the only way to set or change the state (so there’s no direct changes!), and furthermore — they must be synchronous. This solution drives a very important functionality: mutations are logging into devtools. And that provides you with a great readability and predictability!

One more thing — actions. As it’s been said — actions commit mutations. So they do not change the store, and there’s no need for these to be synchronous. But, they can manage an extra piece of asynchronous logic!


It might seem unnecessary to have an extra layer of actions just to call the mutations, for example:

const actions = {
  logout: ({ commit }) => {
    commit("setToken", null);
  }
};

const mutations = {
  setToken: (state, token) => {
    state.token = token;
  }
};

So if calling actions calls logout, why not call the mutation itself?

The entire idea of an action is to call multiple mutations from inside one action or make an Ajax request or any kind of asynchronous logic you can imagine.

We might eventually have actions that make multiple network requests and eventually call many different mutations.

So we try to stuff as much complexity from our Vuex.Store() as possible in our actions and this leaves our mutations, state and getters cleaner and straightforward and falls in line with the kind of modularity that makes libraries like Vue and React popular.

참고URL : https://stackoverflow.com/questions/39299042/vuex-action-vs-mutations

반응형