играть плохо Vuex

Vue.js Vuex

Концепции Vuex

Что такое Векс?

Vuex — это приложение, разработанное специально для Vue.js.режим управления состоянием. он принимаетЦентрализованное хранилище управляет состоянием всех компонентов приложения, и с соответствующимправилоСостояние гарантированно изменится предсказуемым образом.

Что такое «модель управления государством»?

С точки зрения разработки программного обеспечения, это концепция управления и работы с глобальными общими данными о состоянии с унифицированным соглашением и критерием. Вы должны следовать этой философии дизайна и архитектуре для CRUD данных общего состояния в вашем проекте. Таким образом, так называемый «режим управления состоянием» является архитектурным режимом (идеей) разработки программного обеспечения.

Зачем нам нужно применять этот «шаблон управления состоянием» к проекту?

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

Однако в большом количестве бизнес-сценариев разные модули (компоненты) действительно нуждаются в совместном использовании данных, и их также необходимо модифицировать. Это также приводит к противоречиям в разработке программного обеспечения:Данные должны быть разделены между модулями (компонентами)а такжеДанные могут быть произвольно изменены, что приведет к непредсказуемым результатам.

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

Vuex — это продукт архитектурных шаблонов и дизайнерских идей Flux, Redux и The Elm Architecture.

Vuex运行机制

Когда мне следует использовать Vuex?

Если вы не планируете разрабатывать большие одностраничные приложения, использование Vuex может быть утомительным и избыточным. Приложение достаточно простое, Vuex лучше не использовать. Простойglobal event bus (связь между родительским и дочерним компонентами, родительский компонент управляет требуемым состоянием данных) достаточно для того, что вам нужно. Создавая среднее или большое одностраничное приложение, вы, вероятно, задумаетесь о том, как лучше управлять состоянием вне компонентов, и Vuex станет естественным выбором.

Личное мнение, когда использовать? Независимо от ваших малых, средних и крупных приложений, я просто хочу их использовать.Для проекта небольшого приложения, построенного в течение длительного времени, кто знает, как будут выглядеть требования к проекту в будущем.В конце концов, в этом бурная эпоха, спрос такой же быстрый, как Сычуаньская опера. В конце концов, изучение Vuex не сразу применимо к реальному проекту, вы никогда не сможете приоткрыть завесу Vuex. Чем больше вы используете его в своем проекте, тем больше вы будете знать, когда использовать управление состоянием, а когда нет. Как говорится в старой поговорке, практика делает совершенным, что вы думаете? (Квадратные скобки — сначала разберитесь с некоторыми базовыми концепциями Vuex, а затемсобственный проектПосле использования в Китае, вы можете использовать его в проекте вашей компании.Не будьте так грубы, чтобы использовать его, как только он появится~)

Основное использование Vuex

Установить

npm i vuex -S

Чтобы использовать Vuex в любом месте глобального проекта, вам необходимо зарегистрировать Vuex с экземпляром Vue:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

Тот, перед тем, как Vuex сядет в машину 🌰

Правила посадки:

  1. Ядром каждого приложения Vuex является хранилище (склад), и в проекте должен быть только один экземпляр хранилища. Содержит большую часть состояния в вашем приложении.
  2. Состояние в магазине нельзя изменить напрямую. Единственный способ изменить состояние в хранилище — это явно зафиксировать мутации.

Vuex отличается от чистого глобального объекта следующими двумя способами: (1) Хранилище состояния Vuex является реактивным. Когда компонент Vue считывает состояние из хранилища, если состояние в хранилище изменяется, соответствующий компонент будет эффективно обновлен соответствующим образом. (2) Состояние в хранилище нельзя изменить напрямую. (Важные вещи приходят снова)

На 🌰:

<div id="app">
  <p>{{ count }}</p>
  <p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </p>
</div>

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex) // Vuex 注册到 Vue 中

// 创建一个 store

const store = new Vuex.Store({
  // 初始化 state   
  state: {
    count: 0
  },
 // 改变状态唯一声明处
  mutations: {
  	increment: state => state.count++,
    decrement: state => state.count--
  }
})

new Vue({
  el: '#app',
  // 从根组件将 store 的实例注入到所有的子组件  
  store,  
  computed: {
    count () {
        // Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态,
        // 每当状态 count 发生变化,都会重新求取计算
	    return this.$store.state.count
    }
  },
  methods: {
    increment () {
      this.$store.commit('increment')
    },
    decrement () {
    	this.$store.commit('decrement')
    }
  }
})

stateОбъект управляет всеми состояниями приложения, единственный источник данных (SSOT, Единый источник правды), должен быть определен в ранней инициализации, иначе настройки будут изменены позжеstate, программа не поймаетmutations(突变的意思)Поэтому данные никак не обновятся, и компоненты не отразятся.

Получите состояние управления хранилищем, поскольку объект хранилища Vuex внедряется при создании экземпляра Vue, поэтому к нему можно получить доступ в любом месте черезthis.$storeдобраться до магазина,this.$store.stateчтобы получить объект состояния,this.$store.commitдля запуска методов в ранее определенных мутациях

this.$store.state('count') // => 0

this.$store.commit('increment') // => 1

отправивmutationобразом, а не напрямую изменятьstore.state.count, использоватьcommitЭтот метод позволяет Vuex четко отслеживать изменения состояния, что способствует постобслуживанию и отладке.

понимаяstate(статус, данные) иmutations(Единственное место, где данные изменяются, подобно операторам SQL). Знайте две наиболее важные основные части Vuex, а затем сделайте Vuex более инженерным и рациональным, освоив gttter, action и module, чтобы адаптироваться к управлению состоянием более крупных проектов. .

Вспомогательная функция mapState

mapStateЧто может быть сделано? буквальное значениекарта штата,пройти черезmapStateЭто может помочь нам быстрее и удобнее сгенерировать вычисляемые свойства.Возьмите приведенный выше пример, чтобы продемонстрировать:

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

// 使用 mapState

import { mapState } from 'vuex' // 需要先导入

computed: mapState([
    // 箭头函数方式
	count: state => state.count ,
    
    // or 传字符串参数方式, 'count' 等同于 state => state.count
    countAlias: 'count'
    
    // 获取状态后,你还需要和当前组件别的属性值时,就必须使用常规函数的写法了, 只有这样才能获取到当前组件的 this
    countPlusLocalState (state) {
    	return state.count + this.localCount
    } 
])

Когда имя текущего вычисляемого свойства и имя состояния совпадают, вы можете передать массив строк:

computed: mapState([
    // 映射 `this.count` 为 `this.$store.state.count`
	'count'
])

Используйте вышеmapStateПосле вспомогательной функции всяcomputedВычисляемые свойства сталиstateУправление состоянием сбора, не все компоненты при расчете свойств должны иметь управление состоянием, есть еще много вычислительных данных атрибутов, не требующих управления состоянием, и как оно будет смешиваться с локальным вычислительный атрибут, чтобы использовать его?

потому чтоmapStateФункция возвращает объект. Таким образом, мы можем использовать оператор распространения объекта для объединения локальных вычисляемых свойств сmapStateОбъект, возвращаемый функцией, объединяется.

computed: {
	...mapState({
		count: state => state.count    
    }),
    
   localComputed () {
   	   /* ... */ 
   }     
}

⚠️ Примечание: Оператор распространения объектов в настоящее время находится на стадии 4 предложения ECMASCript (будет добавлен в следующий ежегодный выпуск), поэтому его необходимо установить для использования в проекте.babel-plugin-transform-object-rest-spreadПодключите или установите среду пресетов как версию env с этапом 1.babel-preset-stage-1и измените файл конфигурации babelrc

.babelrc

{
    "presets": [
        "env",
        "stage-1" // 添加此项
    ],
    "plugins": [
        "transform-vue-jsx",
        "syntax-dynamic-import"
    ]
}

Основная идея

Состояние было описано выше, поэтому здесь не так много пояснений.

Getter

Getterто естьStoreВычисляемые свойства на уровне управления состоянием, получение источникаStateПосле этого я надеюсь его немного завернуть и вернуть компоненту для использования. который непосредственно получитStateпозжеcomputedВ фильтре вся логика упаковки извлекается и помещается вGetterЭто улучшает возможность повторного использования и читабельность кода.

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

извлечь вGetter:


const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  //  `Getter` 默认第一个参数为 `state`:  
  getters: {
    doneTodos: state => {
      return state.todos.filter( todo => todo.done )
    }
  }
})


//  组件中获取,通过属性访问

computed: {
	doneTodosCount () {
    	return this.$store.getters.doneTodos.length
    } 
}

⚠️ Примечание: геттеры кэшируются как часть системы реактивности Vue при доступе через свойства.

Геттеры также принимают другие геттеры в качестве второго параметра.


const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter( todo => todo.done )
    },
    // 第二个参数为 getters  
    doneTodosLength: (state, getters) => {
    	return getters.doneTodos.length
    }  
  }
})

Вы также можете получить определенные данные, передав параметры в Getter


getters: {
	// ...
    
    getTodoById: state => id => {
    	return state.todos.find( todo => todo.id === id )
    }
}

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


this.$store.getters.getTodoById(2) // => { id: 2, text: '...', done: false }

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

Функция помощника MapGetters

СпередиmapStateВспомогательные функции в основном одинаковы по функциям и использованию.


import { mapGetters } from 'vuex'

// getter 名称和 计算属性名称相同的情况下,可以传递字符串数组

export default {
  // ...
  computed: {
  	...mapGetters([
    	'doneTodos'
    ])
  }
}

// 传递对象的方式

export default {
  // ...
  computed: {
  	...mapGetters({
    	doneTodos: 'doneTodos',
        getTodoById: 'getTodoById' // 此处传递回来的是一个函数,所以在使用的时候 => {{ getTodoById(2) }}
    })
  }
}

Mutation

Состояние нельзя изменить напрямую, и его необходимо изменить с помощью методов Mutations, объявленных в хранилище Vuex.Единственный способ изменить состояние в хранилище Vuex — зафиксироватьmutation.mutationэто объект, содержащий строкуТип события (тип)с однимфункция обратного вызова (обработчик). Функция обратного вызова — это место, где мы фактически изменяем состояние и по умолчанию принимает состояние в качестве первого параметра.

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    // increment 事件类型(type)名称,increment() 回调函数
    // increment: function (state) {}  原本写法
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

mutation handlerНевозможно вызвать напрямую, нужно пройтиstore.commit()чтобы уведомить меня о том, что мне нужно вызватьmutation handler.

this.$store.commit('increment')

mutationПараметров для получения должно быть только два, а лишнее получить нельзя, рекомендуется передавать объект для второго параметра, чтобы получить больше информации.

this.$store.commit('increment', 10) 

// or

this.$store.commit('increment', { num: 10 }) 

Представление стиля объекта

this.$store.commit({
	type: 'increment',
    num: 10
})

Мутация должна следовать правилам ответа Vue.

Состояние в хранилище Vuex является реактивным, поэтому, когда мы меняем состояние, компонент Vue, отслеживающий состояние, также будет автоматически обновляться. Это также означает, что мутации в Vuex также должны подчиняться тем же предостережениям, что и при использовании Vue:

  1. Лучше всего заранее инициализировать все необходимые свойства (свойства в состоянии) в вашем магазине.
  2. Когда вам нужно добавить новое свойство к объекту, вы должны вернуть новый объект * используйте Vue.set(obj, 'newProp', 123) или * Замените старый объект новым. Например:Object.assgin({}, state.obj, newProps), оператор распространения объектаstate.obj = {...state.obj, newProp: 123 }

Вспомогательная функция mapMutation

как использоватьmapStateа такжеmapGettersв основном то же самое.

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    // 传递字符串数组,同名哦~  
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
      
    // 传递对象  
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

Назовите это какmethodsКак и другие обычные методы,this.<methodName>звонить.

⚠️ Примечание:мутация должна быть синхронной функциейИзменение состояния должно быть синхронно, синхронно, синхронно. Если это асинхронно, когда запутана мутация, функция внутреннего обратного вызова не была вызвана, и трудно отслеживать проблему, не зная, где существует фактическое исполнение. (По сути, любые изменения состояния, сделанные в функции обратного вызова, неразрешиваются. Vuex также обеспечивает решение для асинхронных операций, которые необходимо извлечь и вводить в действие для работы. И мутация несет только заСинхронная транзакция.

Action

Как упоминалось ранее, Action используется для обработки асинхронных операций. Вот подробное описание основного использования Action.

Действие похоже на мутацию, разница в следующем:

  • Действия вызывают мутации, а не изменения состояния напрямую. (Не изменяйте состояние напрямую, изменение состояния по-прежнему требует мутации)
  • Действие может содержать произвольные асинхронные операции.
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
     // 实践中,经常用到参数解构来简化代码, increment ({commit}) { commit('') }
    increment (context) {
      context.commit('increment')
    }
  }
})

Функция действий принимает хранилище экземпляров одинаковых методов и свойств объекта контекста (не реальный сам по себе) может быть вызванstore.commitсделать коммит-мутацию или черезcontext.stateа такжеcontext.gettersполучитьstateа такжеgetters.

Триггерное действие

Пропуск действияstore.dispatchТриггер метода:

this.$store.dispatch('increment')

// 以传递额外参数分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

Асинхронные запросы с данными сервера в основном выполняются в действии, а затем используются мутации для синхронизации состояния приложения.state

Вспомогательная функция mapAction

а такжеmapMutionsИспользование в основном такое же.

import { mapActions } from 'vuex'

export default {
  // ...
  methods: { 
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
      
    // 传递对象  
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}

Объединение действий

Действия обычно асинхронны, так как же узнать, что действие завершилось? Что еще более важно, как мы можем объединить несколько действий для обработки более сложных асинхронных процессов?

Действие выполняется путем возврата комбинации множества объектов Promise.


actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

Потом:

store.dispatch('actionA').then(() => {
  // ...
})

использоватьasync / await, мы можем объединить действия следующим образом:


// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

Module

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

Для решения вышеуказанных проблем необходимо расщепить и подразделить внутреннюю часть объекта, а также классифицировать состояние, что приводит кмодульэто понятие. Каждый модуль имеет свое состояние, мутацию, действие, геттер и даже вложенные подмодули — сегментированные таким же образом сверху вниз, разделяя огромную систему на разумные и эффективные функции, следуя концепции единой ответственности, каждый модуль четко определяет свои обязанности и функции.

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

После объявления модуля использование, функция и отсутствие состояния, мутация, действие, геттер и т. д.modulesВнутренний метод объявления в основном такой же, но в детали были внесены некоторые тонкие изменения, такие как: геттер получает состояние параметра по умолчанию, а состояние, полученное в модуле, является состоянием самого модуля, а не глобального one; Еще один должен сообщить, какой модуль получает статус и другие детали различия.

Некоторые различия в состоянии, мутации, действии и геттере в модуле

(1) Мутация и геттер внутри модуля, получен первый параметрstateдалокальный объект состояния модуля. (2) Действие внутри модуля, локальное состояние передается черезcontext.stateвыставлено, состояние корневого узлаcontext.rootState(3) Геттер внутри модуля, состояние корневого узла будет использоваться кактретий параметрнезащищенный

Пространства имен

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

Чтобы модуль имел более высокую инкапсуляцию, возможность повторного использования и независимость, его можно добавить, добавивnamespaced: trueспособ сделать его модулем с пространством имен. В вызов необходимо добавить объявление о том, что геттер, действие и мутация принадлежат какому модулю, в виде пути, чтобы указать, что они принадлежат этому модулю.


const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true, // 开启命名空间

      // 模块内容(module assets)
      state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin'] 调用时以路径的形式表明归属
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

Доступ к глобальному содержимому в модуле с пространством имен

доступ к глобальному состоянию, геттерам и действиям в модуле с пространством имен,rootStateа такжеrootGetterбудут переданы в качестве третьего и четвертого параметровgetter, тоже пройдетcontextСвойства объекта передаются в действие.

необходимо распространять в глобальном пространстве именactionили представитьmutation,Буду{ root: true }передается в качестве третьего параметра вdispatchилиcommitВот и все.


modules: {
  foo: {
    namespaced: true,

    getters: {
      // 在这个模块的 getter 中,`getters` 被局部化了
      // 全局的 state 和 getters 可以作为第三、四个参数进行传入,从而访问全局 state 和 getters
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    actions: {
      // 在这个模块中, dispatch 和 commit 也被局部化了
      // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}

Зарегистрируйте глобальные действия в модулях с пространством имен

Необходимо зарегистрировать глобальные переменные в модулях с пространством имен.action, можете добавитьroot: true, и поместите определение этого действия в обработчик функции. Например:

{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}
Как использовать вспомогательные функции в модулях с пространством имен?

Передайте строку имени пространства модуля в качестве первого аргумента вышеуказанной функции, чтобы все привязки автоматически использовали этот модуль в качестве контекста.

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo',
    'bar'
  ])
}

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

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

Динамическая регистрация модуля

После создания магазина вы можете использоватьstore.registerModuleМодуль регистрации метода:

// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

Функция динамической регистрации модулей позволяет другим плагинам Vue управлять состоянием с помощью Vuex, добавляя новые модули в хранилище. Например,vuex-router-syncПлагин предназначен для динамической регистрации модуля вvue-routerа такжеvuexВ совокупности реализовано управление состоянием маршрутизации приложения.

вы также можете использоватьstore.unregisterModule(moduleName)для динамической выгрузки модулей. Обратите внимание, что вы не можете выгрузить статические модули (т. е. модули, объявленные при создании хранилища) с помощью этого метода.

Будет обновлено~