davestewart / vue-class-store

Universal Vue stores you write once and use anywhere
https://github.com/davestewart/vue-class-store-demos
MIT License
277 stars 13 forks source link

Global store discussion #13

Open davestewart opened 3 years ago

davestewart commented 3 years ago

Starting this thread to prevent #12 from getting cluttered.

@Mootook @djmaze

davestewart commented 3 years ago

It may be too early to declare victory, but I appear to have gotten a nice TypeScript workflow going using:

Note: I'm setting this up in an external project, so some of the imports may look a little funky

The root store class looks like this:

export class RootStore {
  public $add (name: string, store: any): void {
    if (name in this) {
      throw new Error(`Unable to add local store "${name}" as property already exists`)
    }
    this[name] = store
  }

  public $remove (name: string): void {
    if ((name in this) && !name.startsWith('$')) {
      delete this[name]
    }
  }

  constructor (modules: Record<string, any> = {}) {
    Object.keys(modules).forEach(key => {
      this.$add(key, modules[key])
    })
  }
}

Install looks like this:

export function install (Vue: VueConstructor, modules: Record<string, any> = {}) {
  Vue.prototype.$store = new RootStore(modules)
}

My project's store/index.ts looks like this:

import Vue from 'vue'
import { RootStore, install } from '@/packages/store'

// local modules
import app, { AppStore } from './app'
import ui, { UiStore } from './ui'

// tell typescript about modules
declare module '@/packages/store/root' {
  interface RootStore {
    app: AppStore,
    ui: UiStore,
  }
}

// tell vue about the built-in store
declare module 'vue/types/vue' {
  interface Vue {
    $store: RootStore
  }
}

// install and add modules
Vue.use(install, {
  app,
  ui,
})

// ensure other modules importing store directly know what it is
export default Vue.prototype.$store as RootStore

You can add modules individually using a combination of instantiation and augmentation:

// import root store
import store from '@/store'

// augment root store with the new property 
declare module '@/packages/store/root' {
  interface RootStore {
    tools: ToolsStore,
  }
}

// local vue class store setup
@VueStore
class ToolsStore {
  resize () {
    bus.call('tools/windows/resize')
  }
}

// physically add the local store to the root store
store.$add('tools', new ToolsStore())

Autocomplete works great in .vue code and templates:

image

image

I know some of the TS boilerplate looks a little funky, but:

Happy to hear any thoughts!

Mootook commented 3 years ago

@davestewart -- Took me a bit as I'm not as familiar with TypeScript, but I think I get the gist. Looks nice. Would this replace the entire provide/inject usage of the current release?

setup () {
  const { tools } = useStore()
  return { tool }
  // vs. 
  return { tools: this.$store.tools }
}

Globally attaching the store would be great and eliminate some boilerplate at the component level. Also, just a note, Vue is no longer exposed in the vue3 package. It would instead have to be attached to the app instance...something like:

app.config.globalProperties.$store = new RootStore(modules) // or something
davestewart commented 3 years ago

Hmmm. We have inverse knowledge-sets!

I can only really comment regarding Vue 2 right now, but I'm finding that this.$store.model just feels a little "more global" than this.$model, but you can do both, of course.

Vue 3 wise, I don't suppose it makes any difference, but you would have more choice.

Seeing as Vue 3's setup function does make it easier to type variables, perhaps it's not even needed in Vue 3?

Mootook commented 3 years ago

I agree with this.$store.anything feeling more global. And it does steer clear of the risk of assigning some variable to a nested property or destructuring an object and losing reactivity. I know even vue3 has some guidelines on that especially regarding props in the composition API. 

Though, I have found a nice workflow with the current vue-class-store partnered with a jsconfig and jsdoc, which allows for typing "guides" but not necessarily strict type checking like what TS offers.

Screen Shot 2020-12-18 at 9 41 29 AM Screen Shot 2020-12-18 at 9 41 16 AM

I think the way you posted works really well and keeps in tune with how a lot of things are packaged into Vue2 apps. So perhaps the best idea is just to press on with that. Vue3 might have to be its own discussion.