npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hami-vuex

v0.1.5

Published

哈密瓜味的Vuex! State management for Vue.js

Downloads

28

Readme

Hami-Vuex 🍈 (hami melon flavored)

查看中文文档

Hami melon flavored Vuex! State management for Vue.js

Features:

  • Build on Vuex, compatible with Vuex 3 & 4
  • Also compatible with Vue 2 and Vue 3
  • Modular by design, no more fat store
  • Completely TypeScript intelligence support
  • Unit tests line coverage: 100%

Usage

npm install --save hami-vuex

Create Hami Vuex instance

// store/index.js
import { createHamiVuex } from 'hami-vuex'
export const hamiVuex = createHamiVuex({
    vuexStore: /* the Vuex Store instance, created according to Vuex docs */
})

Or use the default empty Vuex Store instance, Vue 2 + Vuex 3 Writing:

import Vue from 'vue'
import Vuex from 'vuex'
import { createHamiVuex } from 'hami-vuex'

Vue.use(Vuex)

export const hamiVuex = createHamiVuex({
    /* Optional:Vuex Store constructor options */
})

And Vue 3 + Vuex 4 Writing:

import { createApp } from 'vue'
import { createHamiVuex } from 'hami-vuex'

export const hamiVuex = createHamiVuex({
    /* Optional:Vuex Store constructor options */
})

export const app = createApp()
app.use(hamiVuex)

The Hami Vuex instance is for create and manage Hami Store instances, all business logics should implemented by Hami Store instances.

Create a Counter Store

// store/counter.js
import { hamiVuex } from '@/store/index'

// Internal:dynamic registered Vuex module
export const counterStore = hamiVuex.store({

    // unique name of the store, will appear in devtools
    $name: 'counter',

    // define the state with plain object(will auto deep copy it)
    $state: {
        count: 0,
    },

    // or a function that returns a fresh state
    $state() {
        return { count: 0 }
    }

    // define a getter, similar to Vue computed
    get double() {
        return this.count * 2
    },

    // define watcher (event listener), similar to Vuex watch
    onChange(callback) {
        // $watch is builtin method, used for watch changes
        // $watch(source: Function, callback: Function): Function
        unwatch = this.$watch(() => this.count, (newValue, oldValue) => {
            console.log('onChange', newValue, oldValue)
            unwatch()
        })
    }

    // define an action, similar to Vuex action
    increment() {
        // $patch is builtin Vuex mutation, used for update state
        // it accepts K:V object, will shallow asign to state
        this.$patch({
            count: this.count + 1
        })
        // for complex operation, can use a mutator function
        this.$patch(state => {
            state.count = this.count + 1
        })
    },

    // also define an async action, similar to Vuex action
    async query() {
        let response = await http.get('/counter')
        this.$patch({
            count: response.count
        })
    }
})

We don't need define mutations, the builtin $patch mutation can handle all tasks.

Use Counter Store in Vue component

import { counterStore } from '@/store/counter'

export default {
    computed: {
        // map state properties
        count: () => counterStore.count,
        // map getters
        double: () => counterStore.double
    },
    methods: {
        // map actions
        increment: counterStore.increment,
    },
    async mounted() {
        // call actions, or use properties
        await counterStore.query()
        console.log(counterStore.count)
    },
    destroyed() {
        // the $reset method will reset to initial state
        counterStore.$reset()
    }
}

Congratulations, you can enjoy the hami melon flavored Vuex!

TypeScript Support

Hami-Vuex completely support TypeScript, you can configure TypeScript to make type inference smarter (better code intelligence).

// tsconfig.json
{
  "compilerOptions": {
    // this aligns with Vue's browser support
    "target": "es5",
    // this enables stricter inference for data properties on `this`
    "strict": true,
  }
}

Detailed instructions: TypeScript Support - Vue.js

Advanced usage

If you don't need Vue SSR, you can skip the following advanced usages (which are not required in most cases).

In the above basic usage, the Store object is a stateful singleton, and using such an object in Vue SSR requires setting the runInNewContext parameter to true, which incurs a relatively large server performance overhead.

Advanced usage avoids "stateful singletons" and is suitable for use in the Vue 3 setup method, as well as for Vue optional writing.

Define a Counter Store

import { defineHamiStore } from 'hami-vuex'

// Note: Here is the capitalization of the initials CounterStore !
export const CounterStore = defineHamiStore({
    /* Here the parameters are the same as hamiVuex.store() */
})

Usage 1: Bind a Vuex Store instance

const vuexStore = /* Vuex Store instance */
const counterStore = CounterStore.use(vuexStore)
await counterStore.query()
console.log(counterStore.count)

Usage 2: Used in the Vue setup method

export default {
    setup() {
        const counterStore = CounterStore.use()
        await counterStore.query()
        console.log(counterStore.count)
    },
}

Usage 3: Used in Vue computed

export default {
    computed: {
        // Similar to Vuex mapActions, mapGetters
        counterStore: CounterStore.use,
        count: CounterStore.count,
        query: CounterStore.query,
    },
}

Usage 4: Use each other in the Store

const otherStore = defineHamiStore({
    get counterStore(){
        return CounterStore.use(this)
    },
    get count(){
        return this.counterStore.count
    }
})

References and acknowledgements

Special thanks to the above projects and materials for bringing me a lot of inspiration, and hope that Hami Vuex can bring new inspiration to the community!

Design considerations

Why use "stateful singleton"?

Stateful singletons are simpler and more convenient to use, and only perform poorly in terms of Vue SSR. Vue SSR is not required for most single-page applications, so you can use "stateful singleton" with confidence and switch to advanced usage if necessary.

Why write getters and actions at the same level?

Since TypeScript is difficult to type infer from the Vue optional interface, writing at the same level avoids this problem.

For specific reasons, please refer to the following information:

  • https://github.com/microsoft/TypeScript/pull/14141
  • https://github.com/microsoft/TypeScript/issues/13949
  • https://github.com/microsoft/TypeScript/issues/12846
  • https://github.com/microsoft/TypeScript/issues/47150

Why are $name and $state prefixed with $?

Because all fields need to avoid name conflicts after writing them at the same level, special fields are prefixed with $.

Why not use mutations anymore?

The advantage of using mutations is that it is more convenient to debug in devtools, the disadvantage is that the code is slightly more cumbersome. Another problem is that after writing all fields at the same level, it is difficult to distinguish between actions and mutations. After weighing it, it was decided not to use mutations.

Why not use Pinia?

After using Pinia for a while, I felt that the devtools plugin support was not very good and unstable. In addition, Pinia is not written in a way that is friendly enough for Vue optional usage, and should be caused by supporting Vue SSR.

Why is it based on Vuex?

I think Vuex itself does a good job, and improving the interface design can achieve the effect I want, without having to make the wheels repeatedly. And based on the Vuex implementation, it can be compatible with old code to the greatest extent, and it is easy to do smooth migration.