Однажды в Наггетс я увидел статью js state management, написанную иностранцем, который многим поделился.Прочитав ее, я решил реализовать ее сам.Цель понять внутренний механизм управления состоянием.
Большинство текущих проектов разрабатываются в компонентах, а библиотека управления состоянием делает управление состоянием между компонентами очень удобным.
1. Подпишитесь на публикацию модуля
Этот модуль на самом деле является шаблоном наблюдателя, который представляет собой зависимость «один ко многим».. Когда определенное состояние объекта изменяется, все объекты, которые зависят от него, будут уведомлены, и будут инициированы зарегистрированные события.
в темеSubjectпервый класс определенthis.eventListСохраняйте события, которые необходимо зарегистрировать, и добавляйте их по очередиsubscribe(подписка),unsubscribe(отписаться),publish(публикация и подписка) и другие методы
subscribeа такжеunsubscribeДва параметра:nameСобытие представляет собой регистрацию уникального имени,fnдля мероприятияnameФункция обратного вызова, указывающая, что всеfnметоды зарегистрированы наnameпод коллекцию
class Subject {
constructor() {
this.eventList = []
}
/**
* 订阅主题
* @param {string} name 事件名称
* @param {function} fn 事件方法
*/
subscribe(name, fn) {
if (!this.eventList.hasOwnProperty(name)) {
this.eventList[name] = []
}
this.eventList[name].push(fn)
console.log('this.eventList: ', this.eventList);
}
/**
* 取消订阅主题
* @param {string} name 事件名称
* @param {function} fn 事件方法
*/
unsubscribe(name, fn) {
var fns = this.eventList[name];
if (!fns || fns.length == 0) { // 如果没有订阅该事件,直接返回
return false
}
if (!fn) { // 如果传入具体函数,表示取消所有对应name的订阅
fns.length = 0
} else {
for (var i = 0; i < fns.length; i++) {
if (fn == fns[i]) {
fns.splice(i, 1);
}
}
}
}
/**
* 发布主题,触发订阅事件
*/
publish() {
var name = Array.prototype.shift.call(arguments) // 获取事件名称
var fns = this.eventList[name]
if (!fns || fns.length == 0) { // 没有订阅该事件
return false
}
for (var i = 0, fn; i < fns.length; i++) {
fn = fns[i]
fn.apply(this, arguments)
}
}
}
Для класса наблюдателя передаются субъект, имя события и метод события, и цель состоит в том, чтобы зарегистрировать событие в соответствующем субъекте:
class Observer {
constructor(subject, name, fn) {
this.subject = subject
this.name = name
this.subject.subscribe(name, fn)
}
}
2. ЯдроLibStoreДобрый
основнойLibStoreКлассу необходимо ввести класс темы модуля подписки и публикации выше.Управление состоянием лично понимается как отдельная тема, и все события состояния подписываются и публикуются в одной и той же теме, поэтому создайте экземпляр один разSubjectВот и все. В то же время необходимоstateКонтролируйте и назначайте данные, создавайтеLibStoreКласс должен передавать параметрыparams, полученный из параметровactions,mutations, или по умолчанию {}
constructor(params){
var _self = this
this._subject = new Subject()
this.mutations = params.mutations ? params.mutations : {}
this.actions = params.actions ? params.actions : {}
}
судитьLibStoreСостояние объекта в любой момент времени должно быть определеноstatusДля записи существует три состояния:
this.status = 'resting';
this.status = 'mutation';
this.status = 'action';
хранить данныеstateтакже изparamsвходящие, но для прослушиванияLibStoreКогда данные, хранящиеся в магазине, меняются, мы вводим проксиProxy, сделать каждый визит и изменитьstateИзменения данных отслеживаются и изменяютсяstateКогда данные срабатывают, тема публикуется, и все зависимости выполняются.stateChangeметод события.
// 代理状态值,监听状态变化
this.state = new Proxy(params.state || {}, {
get(state, key) {
return state[key]
},
set(state, key, val) {
if (_self.status !== 'mutation') {
console.warn(`需要采用mutation来改变状态值`);
}
state[key] = val
console.log(`状态变化:${key}:${val}`)
_self._subject.publish('stateChange', _self.state)
_self.status = 'resting';
return true
}
})
Изменятьstateданные черезcommitилиdispatchСпособ сделать
/**
* 修改状态值
* @param {string} name
* @param {string} newVal
*/
commit(name, newVal) {
if (typeof (this.mutations[name]) != 'function') {
return fasle
}
console.group(`mutation: ${name}`);
this.status = 'mutation'; // 改变状态
this.mutations[name](this.state, newVal);
console.groupEnd();
return true;
}
/**
* 分发执行action的方法
* @param key 的方法属性名
* @param newVal 状态的新值
*/
dispatch(key, newVal) {
if (typeof (this.actions[key]) != 'function') {
return fasle
}
console.group(`action: ${key}`);
this.actions[key](this, newVal);
self.status = 'action';
console.groupEnd();
return true
}
Наконец, субъект, который будет создан_subjectвыставляется для последующей регистрацииstateChangeиспользовать в случае
getSubject() {
return this._subject
}
3. Создайте ядроLibStoreкомпоненты
использоватьvuexУчащиеся должны быть знакомы с этим компонентом, главным образом с конфигурациейstate,mutations,actions, и передать параметры в ядроLibStoreВ экземпляре класса компонента
import libStore from "./libStore";
let state = {
count: 0
}
let mutations = {
addCount(state, val) {
state.count = val
},
}
let actions = {
updateCount(context, val) {
context.commit('addCount', val);
}
}
export default new libStore({
state,
mutations,
actions
})
4. ЗарегистрируйтесьstateChangeмероприятие
StoreChangeКласс будет использоваться как класс наследования компонента приложения, целью является использование регистрации компонентаstateChangeсобытие, при получении унаследованного классаupdateметод, который будетstateСрабатывает при изменении данных.
импортировать только что созданный экземплярLibStoreОбъектstoreи подпишитесь на класс наблюдателя в модуле публикации и зарегистрируйтесьstateChangeСобытия и обратные вызовыupdateметод
import store from '@/assets/lib/store'
import { Observer } from './subject'
class StoreChange {
constructor() {
this.update = this.update || function () {};
new Observer(store.getSubject(), 'stateChange', this.update.bind(this))
}
}
5. Примеры применения
Экземпляр будет принимать два компонентаIndexа такжеDetail, соответственно представляют две страницы, черезhashМаршрут переключения монтируется для достижения прыжков.Следует отметить, что перед монтированием каждого компонента необходимо очистить регистры, которые были прописаны в синглтонной теме объекта состояния.stateChangeспособ избежать двойной регистрации.
- Index
<!-- 页面art模板 -->
<div class="index">
<h1>首页</h1>
<hr>
<button id="btn1">增加数量</button>
<button id="btn2">减少数量</button>
<h3 id='time'><%= count%></h3>
</div>
// 组件Js
import StateChange from '@/assets/lib/stateChange'
import store from '@/assets/lib/store'
export default class Index extends StateChange{
constructor($root){
super()
this.$root = $root
this.render()
document.querySelector('#btn1').addEventListener('click',this.add.bind(this))
document.querySelector('#btn2').addEventListener('click',this.minus.bind(this))
}
render(){
var indexTmpl = require('./index.art')
this.$root.innerHTML =indexTmpl({count:store.state.count})
}
update(){
document.querySelector('#time').textContent = store.state.count
}
add(){
var count = store.state.count
store.commit('addCount',++count)
}
minus(){
var count = store.state.count
store.dispatch('updateCount',--count)
}
}
- Detail
<!-- 页面art模板 -->
<div class="detail">
<h1>详情</h1>
<hr>
<h3 id="count"><%= count%></h3>
</div>
import StateChange from '@/assets/lib/stateChange'
import store from '@/assets/lib/store'
export default class Index extends StateChange {
constructor($root){
super()
this.$root = $root
this.render()
}
render(){
var detailTmpl = require('./detail.art')
this.$root.innerHTML = detailTmpl({count:store.state.count})
}
}
Ссылка на статьюСобственный JavaScript реализует систему управления состоянием
Наконец, спасибо оригинальному автору и автору за то, что поделились! Посмотреть полный кодGithub, добро пожаловать на обмен и звезду!