├── .editorconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── class
├── index.d.ts
└── index.js
├── helpers
├── index.d.ts
└── index.js
├── index.d.ts
├── index.js
├── jsconfig.json
├── package.json
├── test
├── unit
│ ├── class.test.js
│ ├── helpers.test.js
│ └── index.test.js
└── utils.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | yarn-error.log
3 |
4 | .cache/
5 | .parcel-cache/
6 | node_modules/
7 | dist/
8 | coverage/
9 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .editorconfig
2 |
3 | npm-debug.log
4 | yarn-error.log
5 | package-lock.json
6 | yarn.lock
7 |
8 | .travis.yml
9 |
10 | node_modules/
11 | example/
12 | coverage/
13 | .cache/
14 | dist/
15 | test/
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache: yarn
3 | node_js:
4 | - node
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | ## 3.0.0-beta.2
6 |
7 | ### Breaking Changes
8 |
9 | #### Installation process has changed
10 |
11 | To install StoreonVue to Vue app instance, pass the store instance to `createStoreonPlugin` function.
12 |
13 | ```js
14 | import { createApp } from 'vue'
15 | import { createStoreonPlugin } from '@storeon/vue'
16 | import App from './App.vue'
17 | import { store } from './store'
18 |
19 | const app = createApp(App)
20 |
21 | app.use(createStoreonPlugin(store))
22 |
23 | app.mount('#app')
24 | ```
25 |
26 | ## 2.0.0
27 | - Drop support for storeon < v3 and node < v10 ([a9a27c](https://github.com/storeon/vue/commit/a9a27c3ca76e678b11748eb7a56fc91b750d0d3c))
28 |
29 | ## 1.1.0
30 | - Add support for Composition API ([6e77e0](https://github.com/storeon/vue/commit/3e65191fa4d8586c1197a64852b0cb39d6fe6fdf))
31 |
32 | ## 1.0.0
33 | - Add support for Class Components ([6e77e0](https://github.com/storeon/vue/commit/6e77e000118f52aa8506a70e583f19a192080d63))
34 | - Add helper functions ([532121](https://github.com/storeon/vue/commit/532121764a7fa5e61d37e3f8325eccfea4deebf9))
35 |
36 | ## 0.5.0
37 | - Use dual-publish for ESModules ([a1a76c](https://github.com/storeon/vue/commit/a1a76c0b3c7197d661e133f64cc4ea770e50c247))
38 |
39 | ## 0.4.0
40 | - Move to Storeon org ([7bd0a9](https://github.com/storeon/vue/commit/7bd0a95f31e018b3cefcf1a2fd6e769db13d29f7))
41 | - Optimise perfomance ([bf23f3](https://github.com/storeon/vue/commit/bf23f3b7809ca815a839bb8350acce0b457ad036))
42 |
43 | ## 0.3.0
44 | - Use named export ([3fd201](https://github.com/storeon/vue/commit/3fd201ea0e199f82fccc0df5f733fa18f16f463a))
45 |
46 | ## 0.2.2
47 | - Remove unnecessary type declarations ([d67d76](https://github.com/storeon/vue/commit/d67d765e1d09470b4260cfd9be20b61a6f4d2143))
48 |
49 | ## 0.2.1
50 | - Add type definitions ([3bd245](https://github.com/storeon/vue/commit/3bd245319cc3c7f76d924f322d814f5fba683434))
51 |
52 | ## 0.2.0
53 | - Simplify API ([773929](https://github.com/storeon/vue/commit/773929714f27dd0ca78ed72b6f8ade6d4bde5f37))
54 |
55 | ## 0.1.0
56 | - change api to avoid component re-render ([2b9a97](https://github.com/storeon/vue/commit/2b9a9750763bfdab7585851500defd512f3a8422))
57 |
58 | ## 0.0.1
59 |
60 | - Initial release.
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 distolma
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Storeon Vue
2 |
3 | [](https://www.npmjs.com/package/@storeon/vue)
4 | [](https://travis-ci.org/storeon/vue)
5 |
6 |
7 |
8 | **This is the Vue 3 compatible version of the package. For the Vue 2 support, see the [2.0 branch](https://github.com/storeon/vue/tree/2.0).**
9 |
10 | [Storeon] is a tiny event-based Redux-like state manager without dependencies. `@storeon/vue` package helps to connect store with [Vue] to provide a better performance and developer experience while remaining so tiny.
11 |
12 | - **Size**. 160 bytes (+ Storeon itself) instead of ~3kB of [Vuex] (minified and gzipped).
13 | - **Ecosystem**. Many additional [tools] can be combined with a store.
14 | - **Speed**. It tracks what parts of state were changed and re-renders only components based on the changes.
15 |
16 | Read more about Storeon [article].
17 |
18 | [vue]: https://github.com/vuejs/vue
19 | [vuex]: https://github.com/vuejs/vuex
20 | [storeon]: https://github.com/storeon/storeon
21 | [tools]: https://github.com/storeon/storeon#tools
22 | [vue]: https://github.com/vuejs/vue
23 | [size limit]: https://github.com/ai/size-limit
24 | [demo]: https://codesandbox.io/s/throbbing-sunset-x27qc
25 | [article]: https://evilmartians.com/chronicles/storeon-redux-in-173-bytes
26 | [vscode]: https://github.com/microsoft/vscode
27 | [vscodium]: https://github.com/VSCodium/vscodium
28 |
29 | ## Install
30 |
31 | ```sh
32 | npm install @storeon/vue -S
33 | ```
34 | or
35 | ```sh
36 | yarn add @storeon/vue
37 | ```
38 |
39 | ## How to use ([Demo])
40 |
41 | Create a store with `storeon` as you do it usually. You must explicitly install plugin `@storeon/vue` via `app.use()`.
42 |
43 | #### `store.js`
44 |
45 | ```js
46 | import { createStoreon } from 'storeon'
47 |
48 | let counter = store => {
49 | store.on('@init', () => ({ count: 0 }))
50 | store.on('inc', ({ count }) => ({ count: count + 1 }))
51 | store.on('dec', ({ count }) => ({ count: count - 1 }))
52 | store.on('incBy', ({ count }, amount) => ({ count: count + amount }))
53 | }
54 |
55 | export const store = createStoreon([counter])
56 | ```
57 |
58 | #### `index.js`
59 |
60 | Library provides a mechanism to "inject" the store into all child components from the root component with the `store` option:
61 |
62 | ```js
63 | import { createApp } from 'vue'
64 | import { createStoreonPlugin } from '@storeon/vue'
65 | import App from './App.vue'
66 | import { store } from './store'
67 |
68 | const app = createApp(App)
69 |
70 | app.use(createStoreonPlugin(store))
71 |
72 | app.mount('#app')
73 | ```
74 |
75 | By providing the `store` option to the root instance, the store will be injected
76 | into all child components of the root and will be available on them as `this.$storeon`.
77 |
78 | #### `App.vue`
79 |
80 | ```html
81 |
82 |
83 |
The count is {{$storeon.state.count}}
84 |
85 |
86 |
87 |
88 |
89 |
101 | ```
102 |
103 | Or use Composition API with `useStoreon` hook to get state and dispatch function
104 |
105 | #### `App.vue`
106 |
107 | ```html
108 |
109 |
110 |
The count is {{count}}
111 |
112 |
113 |
114 |
115 |
116 |
136 | ```
137 |
138 | ### The `mapState` Helper
139 |
140 | When a component needs to make use of multiple store state properties, declaring all these computed properties can get repetitive and verbose. To deal with this we can make use of the `mapState` helper which generates computed getter functions for us, saving us some keystrokes:
141 |
142 | ```js
143 | import { mapState } from '@storeon/vue/helpers'
144 |
145 | export default {
146 | computed: mapState({
147 | // arrow functions can make the code very succinct!
148 | count: state => state.count,
149 | // passing the string value 'count' is same as `state => state.count`
150 | countAlias: 'count',
151 | // to access local state with `this`, a normal function must be used
152 | countPlusLocalState (state) {
153 | return state.count + this.localCount
154 | }
155 | })
156 | }
157 | ```
158 |
159 | We can also pass a string array to `mapState` when the name of a mapped computed property is the same as a state sub-tree name.
160 |
161 | ```js
162 | import { mapState } from '@storeon/vue/helpers'
163 |
164 | export default {
165 | computed: mapState([
166 | // map this.count to storeon.state.count
167 | 'count'
168 | ])
169 | }
170 | ```
171 |
172 | ### The `mapDispatch` Helper
173 |
174 | You can dispatch actions in components with `this.$storeon.dispatch('xxx')`, or use the `mapDispatch` helper which maps component methods to `store.dispatch` calls:
175 |
176 | ```js
177 | import { mapDispatch } from '@storeon/vue/helpers'
178 |
179 | export default {
180 | methods: {
181 | ...mapDispatch([
182 | // map `this.inc()` to `this.$storeon.dispatch('inc')`
183 | 'inc',
184 | // map `this.incBy(amount)` to `this.$storeon.dispatch('incBy', amount)`
185 | 'incBy'
186 | ]),
187 | ...mapDispatch({
188 | // map `this.add()` to `this.$storeon.dispatch('inc')`
189 | add: 'inc'
190 | })
191 | }
192 | }
193 |
194 | ```
195 |
196 | ## Using with Class Components
197 |
198 | If you would like to write as more class-like style, use decorators from `@storeon/vue/class`
199 |
200 | ```js
201 | import { Vue } from 'vue-class-component'
202 | import { State, Dispatch } from '@storeon/vue/class'
203 |
204 | export default class extends Vue {
205 | @State count
206 | @Dispatch('inc') inc
207 | @Dispatch('dec') dec
208 | }
209 | ```
210 |
211 | ## Using with TypeScript
212 |
213 | Plugin adds to Vue’s global/instance properties and component options. In these cases, type declarations are needed to make plugins compile in TypeScript. We can declare an instance property `$storeon` with type `StoreonStore`. You can also declare a component options `store`:
214 |
215 | #### `typing.d.ts`
216 |
217 | ```ts
218 | import { ComponentCustomProperties } from 'vue'
219 | import { StoreonStore } from 'storeon'
220 | import { StoreonVueStore } from '@storeon/vue'
221 | import { State, Events } from './store'
222 |
223 | declare module '@vue/runtime-core' {
224 | interface ComponentCustomProperties {
225 | $storeon: StoreonVueStore
226 | }
227 | }
228 | ```
229 |
230 | To let TypeScript properly infer types inside Vue component options, you need to define components with `defineComponent` function:
231 |
232 | ```diff
233 | -export default {
234 | +export default defineComponent({
235 | methods: {
236 | inc() {
237 | this.$storeon.dispatch('inc')
238 | }
239 | }
240 | };
241 | ```
242 |
243 | :warning: To enable type checking in your template use this flag in the `settings.json` of your [VSCode] or [VSCodium] with `Vetur` plugin. For more information see [Vetur documentation](https://vuejs.github.io/vetur/interpolation.html#generic-language-features)
244 |
245 | ```json
246 | {
247 | "vetur.experimental.templateInterpolationService": true
248 | }
249 | ```
250 | ## TODO
251 | - Add examples
252 |
--------------------------------------------------------------------------------
/class/index.d.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | declare type StateTransformer = (state: any) => any;
4 | declare type VuexDecorator = (proto: V, key: string) => void;
5 | interface BindingHelper {
6 | (proto: V, key: string): void;
7 | (type: string): VuexDecorator;
8 | }
9 | interface StateBindingHelper extends BindingHelper {
10 | (type: StateTransformer): VuexDecorator;
11 | }
12 |
13 | export declare const State: StateBindingHelper;
14 | export declare const Dispatch: BindingHelper;
15 |
--------------------------------------------------------------------------------
/class/index.js:
--------------------------------------------------------------------------------
1 | const { createDecorator } = require('vue-class-component')
2 |
3 | const { mapDispatch, mapState } = require('../helpers')
4 |
5 | const State = createBindingHelper('computed', mapState)
6 | const Dispatch = createBindingHelper('methods', mapDispatch)
7 |
8 | module.exports = { State, Dispatch }
9 |
10 | function createBindingHelper (bindTo, mapFn) {
11 | function makeDecorator (map) {
12 | return createDecorator((componentOptions, key) => {
13 | if (!componentOptions[bindTo]) {
14 | componentOptions[bindTo] = {}
15 | }
16 |
17 | let mapObject = { [key]: map }
18 |
19 | componentOptions[bindTo][key] = mapFn(mapObject)[key]
20 | })
21 | }
22 |
23 | function helper (proto, key) {
24 | if (typeof key === 'string') {
25 | return makeDecorator(key)(proto, key)
26 | }
27 |
28 | return makeDecorator(proto)
29 | }
30 |
31 | return helper
32 | }
33 |
--------------------------------------------------------------------------------
/helpers/index.d.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { StoreonDispatch } from 'storeon';
3 |
4 | type Computed = () => any;
5 | type ActionMethod = (...args: any[]) => Promise;
6 | type InlineComputed = T extends (...args: any[]) => infer R ? () => R : never
7 | type InlineMethod any> = T extends (fn: any, ...args: infer Args) => infer R ? (...args: Args) => R : never
8 | type CustomVue = Vue & Record;
9 |
10 | interface Mapper {
11 | (map: Key[]): { [K in Key]: R };
12 |