> 웹 프론트엔드 > View.js > 상태 관리의 미스터리를 밝히기 위해 Vuex를 예로 들어보겠습니다.

상태 관리의 미스터리를 밝히기 위해 Vuex를 예로 들어보겠습니다.

藏色散人
풀어 주다: 2021-12-16 15:47:17
앞으로
1888명이 탐색했습니다.

Vuex를 가이드로 사용하여 전체 상태 관리 그림을 엿볼 수 있습니다

우리 모두 알고 있듯이 Vuex는 Vue의 공식 상태 관리 솔루션입니다.

Vuex의 사용법과 API는 어렵지 않으며, 공식 홈페이지 소개도 간결하고 명확합니다. 덕분에 Vuex를 프로젝트에 신속하게 통합하는 것이 매우 쉽습니다. 그러나 유연한 사용법으로 인해 많은 학생들이 Vuex의 디자인과 사용에 다소 혼란스러워합니다.

사실, 사용하기 전에 잠시 멈춰서 몇 가지 질문에 대해 생각해 보는 것이 좋습니다.

  • 상태 관리란 무엇입니까?
  • 왜 Vuex를 사용해야 하나요?
  • 컴포넌트 내부 상태와 Vuex 상태를 어떻게 배포하나요?
  • Vuex를 사용할 때 잠재적인 문제는 무엇입니까?

이 질문에 대해 모호한 경우 축하합니다. 이 기사가 필요할 수 있습니다.

아래에서 저와 함께 Vuex를 예로 들어 근원부터 시작하는 상태 관리의 신비를 밝혀보세요.

개요 미리보기

이 글에서 소개하는 내용은 다음과 같은 내용을 담고 있습니다.

  • 상태와 컴포넌트의 탄생
  • 상태관리는 꼭 관리해야 하나요?
  • 단일 데이터 소스
  • 상태 업데이트 방법
  • 비동기 업데이트?
  • 상태 모듈화
  • 모듈화 슬롯
  • 다음 단계

상태 및 구성 요소의 탄생

세 가지 주요 프레임워크가 탄생한 이후 그들이 공유하는 두 가지 기능이 Jquery에 완전히 적용되었습니다. 이 두 가지 기능은 다음과 같습니다.

  1. 데이터 중심 보기
  2. 구성 요소화

데이터 중심 보기를 사용하면 DOM 운영에만 의존하여 페이지를 업데이트할 수 있는 시대에 작별을 고할 수 있습니다. 더 이상 찾기 레이어를 통해 DOM을 찾은 다음 페이지를 업데이트할 때마다 해당 속성과 콘텐츠를 수정할 필요가 없습니다. 데이터를 조작하여 이러한 작업을 수행할 수 있습니다.

물론, 우리 프론트엔드의 관점에서 보면 데이터는 기본적으로 다양한 데이터 형태를 저장하는 것으로 이해될 수 있습니다. 变量。在 数据驱动 이 개념이 등장한 이후에는 일부 변수에도 특별한 의미가 부여되었습니다.

먼저 JQ시대와 다르지 않고 데이터를 저장하는 용도로만 사용되는 일반 변수입니다. 또한, 반응형 효과를 갖는 변수 유형이 있습니다. 이러한 변수는 변수가 변경되면 해당 변수에 연결된 뷰도 해당 업데이트를 트리거합니다. 이러한 유형의 변수를 상태 변수라고 합니다. .

소위 데이터 기반 뷰는 엄밀히 말하면 상태 변수가 뷰를 구동한다는 의미입니다. Vue와 React의 인기로 인해 프런트엔드 개발자의 초점은 점차 DOM 운영에서 데이터 운영으로 이동했으며 상태 변수가 핵심이 되었습니다.

상태 변수, 이제는 모두가

상태라고 부르는 것을 선호하는 것 같습니다. 우리는 종종 상태와 상태 관리에 대해 이야기합니다. 실제로 이 상태는 상태 변수를 나타냅니다. 아래에 언급된 상태는 상태 변수를 의미하기도 합니다.

상태가 나오면 구성품도 옵니다.

JQ 시대에는 프런트 엔드 페이지가 단지 HTML일 뿐이고 "컴포넌트"라는 개념이 없습니다. 페이지의 공개 부분을 우아하게 재사용하는 것은 그리 어렵지 않습니다. 다행스럽게도 세 가지 주요 프레임워크는 매우 성숙한 구성 요소 디자인을 가져왔습니다. DOM 조각을 구성 요소로 추출하기 쉽고 구성 요소가 내부적으로 자체 상태를 유지할 수 있어 더욱 독립적입니다.

구성 요소의 중요한 특징은 이러한 내부 상태가 외부와 격리되어 있다는 것입니다. 부모 컴포넌트는 자식 컴포넌트의 내부 상태에 접근할 수 없으나, 자식 컴포넌트는 부모 컴포넌트가 전달한 상태(Props)에 접근하여 변경 사항에 따라 자동으로 응답할 수 있습니다.

이 기능은 상태가 모듈화되어 있다고 이해하면 됩니다. 이것의 장점은 현재 설정 상태가 다른 구성 요소에 영향을 미칠 것이라는 점을 고려할 필요가 없다는 것입니다. 물론, 컴포넌트 상태를 완전히 분리하는 것은 비현실적입니다. 상태를 공유하기 위해 여러 컴포넌트가 필연적으로 필요합니다. 이 경우, 해결책은 해당 컴포넌트에 가장 가까운 상위 컴포넌트로 상태를 추출하여 Props를 통해 전달하는 것입니다. .

위의 상태 공유 방식은 일반적으로 문제가 없으며 공식적으로 권장되는 모범 사례이기도 합니다.

그러나 페이지가 복잡하다면 여전히 부족하다는 것을 알게 될 것입니다. 예:

    구성 요소 계층 구조가 너무 깊어서 상태를 공유해야 합니다. 이때 상태는 레이어별로 전달되어야 합니다.
  • 하위 구성 요소가 상태를 업데이트할 때 여러 상위 구성 요소가 형제 구성 요소와 공유되어 구현하기 어려울 수 있습니다.
이 경우 "

상위 구성 요소로 상태 추출" 방법을 계속 사용하면 매우 복잡하다는 것을 알 수 있습니다. 그리고 구성요소 수가 증가하고 중첩 수준이 깊어질수록 복잡성은 더욱 높아집니다. 연관된 상태와 복잡한 전송이 많기 때문에 구성 요소가 설명할 수 없이 업데이트되거나 구성 요소가 업데이트되지 않는 등의 문제가 발생하기 쉽고 비정상적인 문제 해결이 어렵습니다.

이를 고려하여 이 복잡한 상황을 처리하려면 보다 우아한 솔루션이 필요합니다.

상태 관리가 필요하신가요?

이전 섹션에서 페이지가 복잡해지면서 구성 요소 간 공유 상태를 구현하는 데 까다로운 문제가 발생했다고 언급했습니다.

그래서 해결책이 있나요? 물론 있고, 지역사회 지도자들의 노력 덕분에 하나 이상의 계획이 있습니다. 그러나 이러한 솔루션은 모두 2년 전에 우리가 매우 집중적으로 논의한 상태 관리라는 공통 이름을 가지고 있습니다.

상태 관리는 실제로 전역 상태 관리로 이해될 수 있습니다. 여기서 상태는 구성 요소 내부의 상태와 다릅니다. 이는 구성 요소와 독립적으로 유지되며 어떤 방식으로든 이 상태가 필요한 구성 요소와 연결됩니다.

상태 관리에는 자체 구현 계획이 있습니다. Vue에는 Vuex가 있고 React에는 Redux, Mobx가 있으며 물론 다른 솔루션도 있습니다. 그러나 그들은 모두 동일한 문제, 즉 구성 요소 간 상태 공유 문제를 해결합니다.

지난 2년 동안 '상태 관리'라는 개념이 인기를 끌면서 애플리케이션 개발에 없어서는 안 될 부분이 된 것 같았던 기억이 납니다. Vue를 예로 들면, 프로젝트를 생성하면 필연적으로 상태 관리를 위해 Vuex가 도입됩니다. 그러나 많은 사람들은 상태 관리를 왜, 언제, 어떻게 사용해야 하는지 모르고, 무작정 추세를 따르기 때문에 상태 관리를 남용하는 사례가 많이 있었습니다.

이것을 보면 상태 관리가 필요하지 않다는 것을 알아야 합니다. 왜 나타나는지, 어떤 문제를 해결하는지에 대해서는 기본적으로 위에 설명되어 있습니다. 이해가 안 되면 잠시 멈추고 처음부터 다시 읽어보세요. 기술적 솔루션이 탄생하는 배경이 중요하지 않다고 생각하지 마세요. 솔루션이 어떤 문제를 해결하도록 설계되었는지 이해하지 못한다면 그 역할을 제대로 수행할 수 없습니다.

Redux의 저자는 다음과 같은 유명한 말을 했습니다.

Redux(상태 관리)가 필요한지 모른다면 필요하지 않습니다.

좋습니다. 상태 관리를 사용 중이거나 문제 해결을 위해 상태 관리를 사용해야 한다면 계속 읽어보겠습니다.

Vuex

Vue는 중국, 특히 중소 규모 팀에서 널리 사용되므로 대부분의 사람들이 접하는 첫 번째 상태 관리 솔루션은 Vuex여야 합니다.

그렇다면 Vuex는 컴포넌트 간 상태 공유 문제를 어떻게 해결합니까? 함께 살펴보겠습니다.

스토어 만들기

위에서 언급했듯이 일반적인 컴포넌트 공유 상태의 경우 공식적인 권장 사항은 "

가장 가까운 상위 컴포넌트로 상태 추출"입니다. Vuex는 한 단계 더 나아가 모든 상태를 루트 구성 요소로 추출하여 모든 구성 요소가 액세스할 수 있도록 합니다.

아마 여러분은 이렇게 물을 것입니다: 이것은 전반적인 상황에 대한 상태를 노출시키는 것이 아닌가? 모듈화의 장점을 완전히 없애는 것이 아닐까?

사실 그렇지 않아요. 이를 수행하는 Vuex의 주요 목적은 모든 구성 요소가 이러한 상태에 액세스할 수 있도록 허용하고 하위 구성 요소의 상태에 액세스할 수 없는 상황을 완전히 방지하는 것입니다. Vuex는

단일 데이터 소스 원칙에 따라 모든 상태 데이터를 하나의 객체에 저장합니다. 그러나 이것이 상태가 스택되어 있다는 의미는 아닙니다. Vuex는 이 단일 상태 트리에 자체 모듈식 솔루션을 구현합니다.

걱정하지 마세요. 먼저 Vuex 사용법을 단계별로 살펴보겠습니다.

Vuex는 Vue용 플러그인으로 존재합니다. 먼저 npm을 사용하여 설치합니다.

$ npm install --save vuex
로그인 후 복사
설치 후 새 src/store 폴더를 만들고 여기에 모든 Vuex 관련 코드를 넣습니다.

src/store 文件夹,在这里放所有 Vuex 相关的代码。

新建 index.js 并写入如下代码。这段代码主要的作用就是用 Vue.use 方法加载 Vuex 这个插件,然后将配置好的 Vuex.Store 实例导出。

import Vue from 'vue'
import Vuex from 'vuex'
// 安装插件
Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
})
로그인 후 복사

上面导出的实例我们通常称之为 store。一个 store 中包含了存储的状态(state)和修改状态的函数(mutation)等,所有状态和相关操作都在这里定义。

最后一步,在入口文件将上面导出的 store 实例挂载到 Vue 上:

import store from './store'

new Vue({
  el: '#app',
  store: store
})
로그인 후 복사

注意:挂载这一步不是必须的。挂载这一步的作用只是为了方便在 .vue 组件中通过 this.$store 访问我们导出的 store 实例。如果不挂载,直接导入使用也是一样的。

单一数据源(state)

上一步我们用构造函数 Vuex.Store 创建了 store 实例,大家至少知道该怎么用 Vuex 了。这一步我们来看看 Vuex.Store 构造函数的具体配置。

首先是 state 配置,他的值是一个对象,用来存储状态。Vuex 使用 单一状态树 原则,将所有的状态都放在这个对象上,便于后续的状态定位和调试。

比如说我们有一个初始状态 app_version 表示版本,如下:

new Vuex.Store({
  state: {
    app_version: '0.1.1'
  }
}
로그인 후 복사

现在要在组件中获取,可以这样:

this.$store.state.app_version
로그인 후 복사

但这并不是唯一的获取方式,也可以这样:

import store from '@/store' // @ 表示 src 目录
store.state.app_version
로그인 후 복사

为什么要强调这一点呢?因为很多小伙伴以为 Vuex 只能通过 this.$store 操作。到了非组件内,比如在请求函数中要设置某一个 Vuex 的状态,就不知道该怎么办了。

事实上组件中获取状态还有更优雅的方法,比如 mapStateindex.js를 만들고 다음 코드를 작성하세요. 이 코드의 주요 기능은 Vue.use 메서드를 사용하여 Vuex 플러그인을 로드한 다음 구성된 Vuex.Store 인스턴스를 내보내는 것입니다.

import { mapState } from 'vuex'

export default {
  computed: {
    ... // 其他计算属性
    ...mapState({
      version: state => state.app_version
    })
  }
}
로그인 후 복사
로그인 후 복사
🎜위에서 내보낸 예는 일반적으로 store라고 합니다. 저장소에는 저장된 상태(state)와 상태를 수정하는 함수(mutation)가 포함되어 있습니다. 🎜🎜마지막 단계는 항목 파일에서 Vue로 내보낸 스토어 인스턴스를 마운트하는 것입니다: 🎜
new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state, count) {
      // 变更状态
      state.count += count
    }
  }
})
로그인 후 복사
로그인 후 복사
🎜참고: 🎜이 마운트 단계는 필요하지 않습니다🎜. 이 마운트 단계의 목적은 .vue 구성 요소의 this.$store를 통해 내보낸 저장소 인스턴스에 쉽게 액세스하는 것입니다. 마운트되어 있지 않으면 직접 가져오기와 동일합니다. 🎜🎜단일 데이터 소스(상태)🎜🎜이전 단계에서는 Vuex.Store 생성자를 사용하여 저장소 인스턴스를 만들었습니다. 최소한 Vuex 사용법은 누구나 알고 있습니다. 이 단계에서는 Vuex.Store 생성자의 특정 구성을 살펴보겠습니다. 🎜🎜첫 번째는 state 구성입니다. 해당 값은 상태를 저장하는 데 사용되는 개체입니다. Vuex는 단일 상태 트리 원칙을 사용하여 이 객체에 모든 상태를 배치하여 후속 상태 위치 및 디버깅을 용이하게 합니다. 🎜🎜예를 들어 다음과 같이 버전을 나타내는 초기 상태 app_version이 있습니다. 🎜
this.$store.commit('increment', 2)
로그인 후 복사
로그인 후 복사
🎜이제 이를 구성 요소에 가져오려면 다음과 같이 하면 됩니다. 🎜
new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    add(state) {
      state.count++
    },
    reduce(state) {
      state.count--
    }
  },
  actions: {
    increment(context, data) {
      axios.get('**').then(res => {
        if (data.iscan) {
          context.commit('add')
        } else {
          context.commit('reduce')
        }
      })
    }
  }
})
로그인 후 복사
로그인 후 복사
🎜하지만 그렇지 않습니다. 얻을 수 있는 유일한 방법은 이렇게 얻을 수도 있습니다 :🎜
this.$store.dispatch('increment', { iscan: true })
로그인 후 복사
로그인 후 복사
🎜이 점을 왜 강조해야 할까요? 많은 친구들이 Vuex는 this.$store를 통해서만 운영될 수 있다고 생각하기 때문입니다. 예를 들어, 요청 함수에서 특정 Vuex 상태를 설정하려는 경우와 같이 구성 요소가 아닌 경우 어떻게 해야 할지 알 수 없습니다. 🎜🎜사실 여러 상태를 더 쉽게 얻을 수 있게 해주는 mapState 함수와 같이 구성 요소에서 상태를 얻는 더 우아한 방법이 있습니다. 🎜
import { mapState } from 'vuex'

export default {
  computed: {
    ... // 其他计算属性
    ...mapState({
      version: state => state.app_version
    })
  }
}
로그인 후 복사
로그인 후 복사

状态更新方式(mutation)

Vuex 中的状态与组件中的状态不同,不能直接用 state.app_version='xx' 这种方式修改。Vuex 规定修改状态的唯一方法是提交 mutation

Mutation 是一个函数,第一个参数为 state,它的作用就是更改 state 的状态。

下面定义一个名叫 increment 的 mutation,在函数内更新 count 这个状态:

new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state, count) {
      // 变更状态
      state.count += count
    }
  }
})
로그인 후 복사
로그인 후 복사

然后在 .vue 组件中触发 increment

this.$store.commit('increment', 2)
로그인 후 복사
로그인 후 복사

这样绑定了 count 的视图就会自动更新。

同步更新

虽然 mutation 是更新状态的唯一方式,但实际上它还有一个限制:必须是同步更新

为什么必须是同步更新?因为在开发过程中,我们常常会追踪状态的变化。常用的手段就是在浏览器控制台中调试。而在 mutation 中使用异步更新状态,虽然也会使状态正常更新,但是会导致开发者工具有时无法追踪到状态的变化,调试起来就会很困难。

再有 Vuex 给 mutation 的定位就是更改状态,只是更改状态,别的不要参与。所谓专人干专事儿,这样也帮助我们避免把更改状态和自己的业务逻辑混起来,同时也规范了函数功能。

那如果确实需要异步更新,该怎么办呢?

异步更新

异步更新状态是一个非常常见的场景,比如接口请求回来的数据要存储,那就是异步更新。

Vuex 提供了 action 用于异步更新状态。与 mutation 不同的是,action 不直接更新状态,而是通过触发 mutation 间接更新状态。因此即便使用 action 也不违背 “修改状态的唯一方法是提交 mutation” 的原则。

Action 允许在实际更新状态前做一些副作用的操作,比如上面说的异步,还有数据处理,按条件提交不同的 mutation 等等。看一个例子:

new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    add(state) {
      state.count++
    },
    reduce(state) {
      state.count--
    }
  },
  actions: {
    increment(context, data) {
      axios.get('**').then(res => {
        if (data.iscan) {
          context.commit('add')
        } else {
          context.commit('reduce')
        }
      })
    }
  }
})
로그인 후 복사
로그인 후 복사

在组件中触发 action:

this.$store.dispatch('increment', { iscan: true })
로그인 후 복사
로그인 후 복사

这些就是 action 的使用方法。其实 action 最主要的作用就是请求接口,拿到需要的数据,然后触发 mutation 修改状态。

其实这一步在组件中也可以实现。我看过一些方案,常见的是在组件内写一个请求方法,当请求成功,直接通过 this.$store.commit 方法触发 mutation 来更新状态,完全用不到 action。

难道 action 可有可无吗?

也不是,在特定场景下确实需要 action 的,这个会在下一篇说。

状态模块化(module)

前面讲过,Vuex 是单一状态树,所有状态存放在一个对象上。同时 Vuex 有自己的模块化方案
,可以避免状态堆砌到一起,变的臃肿。

Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action。虽然状态注册在根组件,但是支持模块分割,相当于做到了与页面组件平级的“状态组件”。

为了区分,我们将被分割的模块称为子模块,暴露在全局的称为全局模块

我们来看基础用法:

new Vuex.Store({
  modules: {
    user: {
      state: {
        uname: 'ruims'
      },
      mutation: {
        setName(state, name) {
          state.name = name
        }
      }
    }
  }
})
로그인 후 복사

上面定义了 user 模块,包含了一个 state 和一个 mutation。在组件中使用方法如下:

// 访问状态
this.$store.state.user.uname
// 更新状态
this.$store.commit('setName')
로그인 후 복사

大家发现了,访问子模块的 state 要通过 this.$store.state.[模块名称] 这种方式去访问,触发 mutation 则与全局模块一样,没有区别。

action 与 mutation 原理一致,不细说。

命名空间

上面说到,子模块触发 mutation 和 action 与全局模块一致,那么假设全局模块和子模块中都有一个名为 setName 的 mutation。在组件中触发,哪个 mutation 会执行呢?

经过试验,都会执行。官方的说法是:为了多个模块能够对同一 mutation 或 action 作出响应。

其实官方做的这个兼容,我一直没遇到实际的应用场景,反而因为同名 mutation 导致误触发带来了不少的麻烦。可能官方也意识到了这个问题,索引后来也为 mutation 和 action 做了模块处理方案。

这个方案,就是命名空间。

命名空间也很简单,在子模块中加一个 namespaced: true 的配置即可开启,如:

new Vuex.Store({
  modules: {
    user: {
      namespaced: true,
      state: {}
    }
  }
})
로그인 후 복사

开启命名空间后,触发 mutation 就变成了:

this.$store.commit('user/setName')
로그인 후 복사
로그인 후 복사

可见提交参数由 '[mutation]' 变成了 '[模块名称]/[mutation]'

模块化的槽点

上面我们介绍了 Vuex 的模块化方案,将单一状态树 store 分割成多个 module,各自负责本模块状态的存储和更新。

模块化是必要的,但是这个模块的方案,用起来总觉得有点别扭

比如,总体的设计是将 store 先分模块,模块下在包含 state,mutation,action。

那么按照正常理解,访问 user 模块下 state 应该是这样的:

this.$store.user.state.uname
로그인 후 복사

但是实际 API 却是这样的:

this.$store.state.user.uname
로그인 후 복사

这个 API 仿佛是在 state 中又各自分了模块。我没看过源码,但从使用体验上来说,这是别扭一。

除 state 外,mutation,action 默认注册在全局的设计,也很别扭

首先,官方说的多个模块对同一 mutation 或 action 作出响应,这个功能暂无找到应用场景。并且未配 namespace 时还要保证命名唯一,否则会导致误触发。

其次,用 namespace 后,触发 mutation 是这样的:

this.$store.commit('user/setName')
로그인 후 복사
로그인 후 복사

这个明显是将参数单独处理了,为什么不是这样:

this.$store.user.commit('setName')
로그인 후 복사

总体感受就是 Vuex 模块化做的还不够彻底。

为什么吐槽

上面说的槽点,并不是为了吐槽而吐槽。主要是感觉还有优化空间。

比如 this.$store.commit 函数可以触发任何 mutation 来更改状态。如果一个组件复杂,需要操作多个子模块的状态,那么就很难快速的找出当前组件操作了哪些子模块,当然也不好做权限规定。

我希望的是,比如在 A 组件要用到 b, c 两个子模块的状态,不允许操作其他子模块,那么就可以先将要用到模块导入,比如这样写:

import { a, b } from this.$store
export default {
  methods: {
    test() {
      alert(a.state.uname) // 访问状态
      a.commit('setName')// 修改状态
    }
  }
}
로그인 후 복사

这样按照模块导入,查询和使用都比较清晰。

下一步

前面我们详细介绍了状态管理的背景以及 Vuex 的使用,分享了关于官方 API 的思考。相信看到这里,你已经对状态管理和 Vuex 有了更深刻的认识和理解。

然而本篇我们只介绍了 Vuex 这一个方案,状态管理的其他方案,以及上面我们的吐槽点,能不能找到更优的实现方法,这些都等着我们去尝试。

下一篇文章我们继续深挖状态管理,对比 Vuex 和 React,Fluter 在状态管理实现上的差异,然后在 Vue 上集成 Mobx,打造我们优雅的应用。

위 내용은 상태 관리의 미스터리를 밝히기 위해 Vuex를 예로 들어보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿