├── docs ├── old │ ├── SUMMARY.md │ └── README.md ├── fr │ ├── book.json │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── SUMMARY.md │ ├── strict.md │ ├── installation.md │ ├── structure.md │ ├── hot-reload.md │ ├── forms.md │ ├── getters.md │ └── getting-started.md ├── assets │ ├── CNAME │ ├── circle.yml │ └── vuex.ai ├── ru │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── book.json │ ├── SUMMARY.md │ ├── strict.md │ ├── installation.md │ ├── structure.md │ ├── hot-reload.md │ ├── forms.md │ ├── getting-started.md │ ├── getters.md │ └── plugins.md ├── en │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── book.json │ ├── SUMMARY.md │ ├── strict.md │ ├── installation.md │ ├── structure.md │ ├── hot-reload.md │ ├── forms.md │ ├── getting-started.md │ ├── getters.md │ ├── intro.md │ └── plugins.md ├── ja │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── book.json │ ├── strict.md │ ├── SUMMARY.md │ ├── structure.md │ ├── installation.md │ ├── hot-reload.md │ ├── forms.md │ ├── getting-started.md │ ├── getters.md │ ├── intro.md │ ├── plugins.md │ └── state.md ├── kr │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── book.json │ ├── strict.md │ ├── SUMMARY.md │ ├── structure.md │ ├── installation.md │ ├── hot-reload.md │ ├── forms.md │ ├── getting-started.md │ ├── getters.md │ ├── intro.md │ ├── plugins.md │ ├── state.md │ └── modules.md ├── zh-cn │ ├── README.md │ ├── images │ │ ├── flow.png │ │ └── vuex.png │ ├── book.json │ ├── strict.md │ ├── SUMMARY.md │ ├── structure.md │ ├── installation.md │ ├── hot-reload.md │ ├── forms.md │ ├── getting-started.md │ ├── getters.md │ ├── intro.md │ ├── state.md │ ├── plugins.md │ ├── modules.md │ ├── mutations.md │ └── actions.md ├── LANGS.md ├── deploy.sh └── book.json ├── .babelrc ├── test ├── unit │ ├── .eslintrc.json │ ├── setup.js │ ├── jasmine.json │ ├── module │ │ ├── module.spec.js │ │ └── module-collection.spec.js │ └── util.spec.js └── e2e │ ├── runner.js │ ├── specs │ ├── counter.js │ ├── cart.js │ └── chat.js │ └── nightwatch.config.js ├── types ├── typings.json ├── README.md ├── test │ ├── vue.ts │ ├── tsconfig.json │ └── helpers.ts ├── tsconfig.json ├── vue.d.ts ├── helpers.d.ts └── index.d.ts ├── circle.yml ├── .eslintrc.json ├── examples ├── chat │ ├── store │ │ ├── mutation-types.js │ │ ├── getters.js │ │ ├── actions.js │ │ ├── index.js │ │ └── mutations.js │ ├── index.html │ ├── app.js │ ├── components │ │ ├── Message.vue │ │ ├── App.vue │ │ ├── Thread.vue │ │ ├── ThreadSection.vue │ │ └── MessageSection.vue │ ├── api │ │ ├── index.js │ │ └── mock-data.js │ └── css │ │ └── chat.css ├── counter-hot │ ├── app.js │ ├── store │ │ ├── mutations.js │ │ ├── getters.js │ │ ├── actions.js │ │ └── index.js │ ├── index.html │ └── Counter.vue ├── counter │ ├── app.js │ ├── index.html │ ├── Counter.vue │ └── store.js ├── shopping-cart │ ├── store │ │ ├── actions.js │ │ ├── mutation-types.js │ │ ├── getters.js │ │ ├── index.js │ │ └── modules │ │ │ ├── products.js │ │ │ └── cart.js │ ├── app.js │ ├── index.html │ ├── components │ │ ├── App.vue │ │ ├── ProductList.vue │ │ └── Cart.vue │ ├── api │ │ └── shop.js │ └── currency.js ├── todomvc │ ├── app.js │ ├── store │ │ ├── index.js │ │ ├── plugins.js │ │ └── mutations.js │ ├── index.html │ └── components │ │ ├── Todo.vue │ │ └── App.vue ├── global.css ├── index.html ├── server.js └── webpack.config.js ├── .gitignore ├── .github └── ISSUE_TEMPLATE.md ├── src ├── index.js ├── index.esm.js ├── plugins │ ├── devtool.js │ └── logger.js ├── mixin.js ├── module │ ├── module.js │ └── module-collection.js ├── util.js └── helpers.js ├── bower.json ├── dist ├── logger.d.ts └── logger.js ├── README.md ├── LICENSE └── package.json /docs/old/SUMMARY.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/fr/book.json: -------------------------------------------------------------------------------- 1 | ../book.json -------------------------------------------------------------------------------- /docs/assets/CNAME: -------------------------------------------------------------------------------- 1 | vuex.vuejs.org 2 | -------------------------------------------------------------------------------- /docs/ru/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} -------------------------------------------------------------------------------- /docs/en/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} 2 | -------------------------------------------------------------------------------- /docs/fr/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} 2 | -------------------------------------------------------------------------------- /docs/ja/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} 2 | -------------------------------------------------------------------------------- /docs/kr/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} 2 | -------------------------------------------------------------------------------- /docs/zh-cn/README.md: -------------------------------------------------------------------------------- 1 | {% include "./SUMMARY.md" %} 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"] 3 | } 4 | -------------------------------------------------------------------------------- /test/unit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /types/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex", 3 | "main": "index.d.ts" 4 | } 5 | -------------------------------------------------------------------------------- /docs/assets/circle.yml: -------------------------------------------------------------------------------- 1 | general: 2 | branches: 3 | ignore: 4 | - gh-pages 5 | -------------------------------------------------------------------------------- /docs/assets/vuex.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/assets/vuex.ai -------------------------------------------------------------------------------- /docs/en/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/en/images/flow.png -------------------------------------------------------------------------------- /docs/en/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/en/images/vuex.png -------------------------------------------------------------------------------- /docs/fr/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/fr/images/flow.png -------------------------------------------------------------------------------- /docs/fr/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/fr/images/vuex.png -------------------------------------------------------------------------------- /docs/ja/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/ja/images/flow.png -------------------------------------------------------------------------------- /docs/ja/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/ja/images/vuex.png -------------------------------------------------------------------------------- /docs/kr/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/kr/images/flow.png -------------------------------------------------------------------------------- /docs/kr/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/kr/images/vuex.png -------------------------------------------------------------------------------- /docs/ru/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/ru/images/flow.png -------------------------------------------------------------------------------- /docs/ru/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/ru/images/vuex.png -------------------------------------------------------------------------------- /docs/zh-cn/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/zh-cn/images/flow.png -------------------------------------------------------------------------------- /docs/zh-cn/images/vuex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/vuex/dev/docs/zh-cn/images/vuex.png -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 5 4 | environment: 5 | BABEL_ENV: development 6 | -------------------------------------------------------------------------------- /types/README.md: -------------------------------------------------------------------------------- 1 | This is the TypeScript declaration of Vuex. 2 | 3 | ## Testing 4 | 5 | ```sh 6 | $ tsc -p test/tsconfig.json 7 | ``` 8 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue/dist/vue.common.js' 3 | import Vuex from '../../dist/vuex.js' 4 | 5 | Vue.use(Vuex) 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "sourceType": "module" 6 | }, 7 | "extends": "vue" 8 | } 9 | -------------------------------------------------------------------------------- /examples/chat/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const SWITCH_THREAD = 'SWITCH_THREAD' 2 | export const RECEIVE_ALL = 'RECEIVE_ALL' 3 | export const RECEIVE_MESSAGE = 'RECEIVE_MESSAGE' 4 | -------------------------------------------------------------------------------- /docs/LANGS.md: -------------------------------------------------------------------------------- 1 | * [2.0 - English](en/) 2 | * [2.0 - 简体中文](zh-cn/) 3 | * [2.0 - Français](fr/) 4 | * [2.0 - Русский](ru/) 5 | * [2.0 - 日本語](ja/) 6 | * [2.0 - 한국어(Korean)](kr/) 7 | * [1.0 Docs](old/) 8 | -------------------------------------------------------------------------------- /examples/counter-hot/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from './store' 3 | import Counter from './Counter.vue' 4 | 5 | new Vue({ 6 | el: '#app', 7 | store, 8 | render: h => h(Counter) 9 | }) 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | TODO.md 4 | lib 5 | docs/_book 6 | examples/**/build.js 7 | types/typings 8 | types/test/*.js 9 | explorations 10 | *.log 11 | test/e2e/reports 12 | test/e2e/screenshots 13 | -------------------------------------------------------------------------------- /test/unit/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test/unit", 3 | "spec_files": [ 4 | "**/*.spec.js" 5 | ], 6 | "helpers": [ 7 | "../../node_modules/babel-register/lib/node.js", 8 | "setup.js" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/counter/app.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue' 3 | import Counter from './Counter.vue' 4 | import store from './store' 5 | 6 | new Vue({ 7 | el: '#app', 8 | store, 9 | render: h => h(Counter) 10 | }) 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /examples/counter-hot/store/mutations.js: -------------------------------------------------------------------------------- 1 | export const increment = state => { 2 | state.count++ 3 | state.history.push('increment') 4 | } 5 | 6 | export const decrement = state => { 7 | state.count-- 8 | state.history.push('decrement') 9 | } 10 | -------------------------------------------------------------------------------- /docs/deploy.sh: -------------------------------------------------------------------------------- 1 | rm -rf _book 2 | gitbook build 3 | cp assets/circle.yml _book/circle.yml 4 | cp assets/CNAME _book/CNAME 5 | cd _book 6 | git init 7 | git add -A 8 | git commit -m 'update book' 9 | git push -f git@github.com:vuejs/vuex.git master:gh-pages 10 | -------------------------------------------------------------------------------- /types/test/vue.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("vue"); 2 | import * as Vuex from "../index"; 3 | 4 | const store = new Vuex.Store({ 5 | state: { 6 | value: 1 7 | } 8 | }); 9 | 10 | const vm = new Vue({ 11 | store 12 | }); 13 | 14 | vm.$store.state.value; 15 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-types' 2 | 3 | export const addToCart = ({ commit }, product) => { 4 | if (product.inventory > 0) { 5 | commit(types.ADD_TO_CART, { 6 | id: product.id 7 | }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/todomvc/app.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue' 3 | import store from './store' 4 | import App from './components/App.vue' 5 | 6 | new Vue({ 7 | store, // inject store to all children 8 | el: '#app', 9 | render: h => h(App) 10 | }) 11 | -------------------------------------------------------------------------------- /examples/todomvc/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import { state, mutations } from './mutations' 4 | import plugins from './plugins' 5 | 6 | Vue.use(Vuex) 7 | 8 | export default new Vuex.Store({ 9 | state, 10 | mutations, 11 | plugins 12 | }) 13 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const ADD_TO_CART = 'ADD_TO_CART' 2 | export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST' 3 | export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS' 4 | export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE' 5 | export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS' 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions 12 | } 13 | -------------------------------------------------------------------------------- /examples/shopping-cart/app.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue' 3 | import App from './components/App.vue' 4 | import store from './store' 5 | import { currency } from './currency' 6 | 7 | Vue.filter('currency', currency) 8 | 9 | new Vue({ 10 | el: '#app', 11 | store, 12 | render: h => h(App) 13 | }) 14 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/getters.js: -------------------------------------------------------------------------------- 1 | export const cartProducts = state => { 2 | return state.cart.added.map(({ id, quantity }) => { 3 | const product = state.products.all.find(p => p.id === id) 4 | return { 5 | title: product.title, 6 | price: product.price, 7 | quantity 8 | } 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /examples/todomvc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vuex todomvc example 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/counter-hot/store/getters.js: -------------------------------------------------------------------------------- 1 | export const count = state => state.count 2 | 3 | const limit = 5 4 | 5 | export const recentHistory = state => { 6 | const end = state.history.length 7 | const begin = end - limit < 0 ? 0 : end - limit 8 | return state.history 9 | .slice(begin, end) 10 | .toString() 11 | .replace(/,/g, ', ') 12 | } 13 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex", 3 | "main": "dist/vuex.js", 4 | "description": "state management for Vue.js", 5 | "authors": "Evan You", 6 | "license": "MIT", 7 | "ignore": [ 8 | "examples", 9 | "test", 10 | "docs", 11 | ".gitignore", 12 | ".eslintrc", 13 | ".babelrc", 14 | "*.json", 15 | "*.md", 16 | "*.yml" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "lib": [ 6 | "es5", 7 | "dom", 8 | "es2015.promise" 9 | ], 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noEmit": true 13 | }, 14 | "include": [ 15 | "*.d.ts", 16 | "../dist/logger.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /types/vue.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Extends interfaces in Vue.js 3 | */ 4 | 5 | import Vue = require("vue"); 6 | import { Store } from "./index"; 7 | 8 | declare module "vue/types/options" { 9 | interface ComponentOptions { 10 | store?: Store; 11 | } 12 | } 13 | 14 | declare module "vue/types/vue" { 15 | interface Vue { 16 | $store: Store; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/counter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vuex counter example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/counter-hot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vuex counter example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/shopping-cart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vuex shopping cart example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/index.esm.js: -------------------------------------------------------------------------------- 1 | import { Store, install } from './store' 2 | import { mapState, mapMutations, mapGetters, mapActions } from './helpers' 3 | 4 | export default { 5 | Store, 6 | install, 7 | version: '__VERSION__', 8 | mapState, 9 | mapMutations, 10 | mapGetters, 11 | mapActions 12 | } 13 | 14 | export { 15 | Store, 16 | mapState, 17 | mapMutations, 18 | mapGetters, 19 | mapActions 20 | } 21 | -------------------------------------------------------------------------------- /docs/old/README.md: -------------------------------------------------------------------------------- 1 | # Vuex 1.0 Docs 2 | 3 | - [English](https://github.com/vuejs/vuex/tree/1.0/docs/en) 4 | - [Chinese](https://github.com/vuejs/vuex/tree/1.0/docs/zh-cn) 5 | - [Japanese](https://github.com/vuejs/vuex/tree/1.0/docs/ja) 6 | - [Spanish](https://github.com/vuejs/vuex/tree/1.0/docs/es) 7 | - [Portuguese](https://github.com/vuejs/vuex/tree/1.0/docs/pt) 8 | - [Italian](https://github.com/vuejs/vuex/tree/1.0/docs/it) 9 | -------------------------------------------------------------------------------- /examples/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 3 | color: #2c3e50; 4 | } 5 | 6 | ul { 7 | line-height: 1.5em; 8 | padding-left: 1.5em; 9 | } 10 | 11 | a { 12 | color: #7f8c8d; 13 | text-decoration: none; 14 | } 15 | 16 | a:hover { 17 | color: #4fc08d; 18 | } 19 | -------------------------------------------------------------------------------- /examples/chat/store/getters.js: -------------------------------------------------------------------------------- 1 | export const threads = state => state.threads 2 | 3 | export const currentThread = state => { 4 | return state.currentThreadID 5 | ? state.threads[state.currentThreadID] 6 | : {} 7 | } 8 | 9 | export const currentMessages = state => { 10 | const thread = currentThread(state) 11 | return thread.messages 12 | ? thread.messages.map(id => state.messages[id]) 13 | : [] 14 | } 15 | -------------------------------------------------------------------------------- /types/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "lib": [ 6 | "es5", 7 | "dom", 8 | "es2015.promise", 9 | "es2015.core" 10 | ], 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noEmit": true 14 | }, 15 | "include": [ 16 | "*.ts", 17 | "../*.d.ts", 18 | "../../dist/logger.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/chat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vuex chat example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/counter-hot/store/actions.js: -------------------------------------------------------------------------------- 1 | export const increment = ({ commit }) => commit('increment') 2 | export const decrement = ({ commit }) => commit('decrement') 3 | 4 | export const incrementIfOdd = ({ commit, state }) => { 5 | if ((state.count + 1) % 2 === 0) { 6 | commit('increment') 7 | } 8 | } 9 | 10 | export const incrementAsync = ({ commit }) => { 11 | setTimeout(() => { 12 | commit('increment') 13 | }, 1000) 14 | } 15 | -------------------------------------------------------------------------------- /examples/shopping-cart/components/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /examples/chat/app.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue' 3 | import App from './components/App.vue' 4 | import store from './store' 5 | import { getAllMessages } from './store/actions' 6 | 7 | Vue.config.debug = true 8 | 9 | Vue.filter('time', timestamp => { 10 | return new Date(timestamp).toLocaleTimeString() 11 | }) 12 | 13 | new Vue({ 14 | el: '#app', 15 | store, 16 | render: h => h(App) 17 | }) 18 | 19 | getAllMessages(store) 20 | -------------------------------------------------------------------------------- /examples/todomvc/store/plugins.js: -------------------------------------------------------------------------------- 1 | import { STORAGE_KEY } from './mutations' 2 | import createLogger from '../../../src/plugins/logger' 3 | 4 | const localStoragePlugin = store => { 5 | store.subscribe((mutation, { todos }) => { 6 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)) 7 | }) 8 | } 9 | 10 | export default process.env.NODE_ENV !== 'production' 11 | ? [createLogger(), localStoragePlugin] 12 | : [localStoragePlugin] 13 | -------------------------------------------------------------------------------- /dist/logger.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types for the logger plugin. 3 | * This file must be put alongside the JavaScript file of the logger. 4 | */ 5 | 6 | import { Payload, Plugin } from "../types/index"; 7 | 8 | export interface LoggerOption { 9 | collapsed?: boolean; 10 | transformer?: (state: S) => any; 11 | mutationTransformer?:

(mutation: P) => any; 12 | } 13 | 14 | export default function createLogger(option: LoggerOption): Plugin; 15 | -------------------------------------------------------------------------------- /examples/chat/components/Message.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | -------------------------------------------------------------------------------- /docs/zh-cn/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "plugins": ["edit-link", "prism", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "编辑此页面" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/en/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "plugins": ["edit-link", "prism", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "Edit This Page" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/ja/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "plugins": ["edit-link", "prism", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "Edit This Page" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/kr/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "plugins": ["edit-link", "prism", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "Edit This Page" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/chat/components/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 22 | -------------------------------------------------------------------------------- /docs/zh-cn/strict.md: -------------------------------------------------------------------------------- 1 | # 严格模式 2 | 3 | 开启严格模式,仅需在创建 store 的时候传入 `strict: true`: 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | 在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。 13 | 14 | ### 开发环境与发布环境 15 | 16 | **不要在发布环境下启用严格模式!**严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。 17 | 18 | 类似于插件,我们可以让构建工具来处理这种情况: 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /src/plugins/devtool.js: -------------------------------------------------------------------------------- 1 | const devtoolHook = 2 | typeof window !== 'undefined' && 3 | window.__VUE_DEVTOOLS_GLOBAL_HOOK__ 4 | 5 | export default function devtoolPlugin (store) { 6 | if (!devtoolHook) return 7 | 8 | store._devtoolHook = devtoolHook 9 | 10 | devtoolHook.emit('vuex:init', store) 11 | 12 | devtoolHook.on('vuex:travel-to-state', targetState => { 13 | store.replaceState(targetState) 14 | }) 15 | 16 | store.subscribe((mutation, state) => { 17 | devtoolHook.emit('vuex:mutation', mutation, state) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /docs/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">3.0.0", 3 | "plugins": ["edit-link", "prism", "theme-vuejs@git+https://github.com/pearofducks/gitbook-plugin-theme-vuejs.git", "-fontsettings", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "Edit This Page" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/chat/api/index.js: -------------------------------------------------------------------------------- 1 | const data = require('./mock-data') 2 | const LATENCY = 16 3 | 4 | export function getAllMessages (cb) { 5 | setTimeout(() => { 6 | cb(data) 7 | }, LATENCY) 8 | } 9 | 10 | export function createMessage ({ text, thread }, cb) { 11 | const timestamp = Date.now() 12 | const id = 'm_' + timestamp 13 | const message = { 14 | id, 15 | text, 16 | timestamp, 17 | threadID: thread.id, 18 | threadName: thread.name, 19 | authorName: 'Evan' 20 | } 21 | setTimeout(function () { 22 | cb(message) 23 | }, LATENCY) 24 | } 25 | -------------------------------------------------------------------------------- /docs/ru/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">3.0.0", 3 | "plugins": ["edit-link", "prism", "theme-vuejs@git+https://github.com/pearofducks/gitbook-plugin-theme-vuejs.git", "-fontsettings", "-highlight", "github"], 4 | "pluginsConfig": { 5 | "edit-link": { 6 | "base": "https://github.com/vuejs/vuex/tree/dev/docs", 7 | "label": "Редактировать эту страницу" 8 | }, 9 | "github": { 10 | "url": "https://github.com/vuejs/vuex/" 11 | } 12 | }, 13 | "links": { 14 | "sharing": { 15 | "facebook": false, 16 | "twitter": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/chat/store/actions.js: -------------------------------------------------------------------------------- 1 | import * as api from '../api' 2 | import * as types from './mutation-types' 3 | 4 | export const getAllMessages = ({ commit }) => { 5 | api.getAllMessages(messages => { 6 | commit(types.RECEIVE_ALL, { 7 | messages 8 | }) 9 | }) 10 | } 11 | 12 | export const sendMessage = ({ commit }, payload) => { 13 | api.createMessage(payload, message => { 14 | commit(types.RECEIVE_MESSAGE, { 15 | message 16 | }) 17 | }) 18 | } 19 | 20 | export const switchThread = ({ commit }, payload) => { 21 | commit(types.SWITCH_THREAD, payload) 22 | } 23 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vuex Examples 6 | 7 | 8 | 9 |

Vuex Examples

10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import * as actions from './actions' 4 | import * as getters from './getters' 5 | import cart from './modules/cart' 6 | import products from './modules/products' 7 | import createLogger from '../../../src/plugins/logger' 8 | 9 | Vue.use(Vuex) 10 | 11 | const debug = process.env.NODE_ENV !== 'production' 12 | 13 | export default new Vuex.Store({ 14 | actions, 15 | getters, 16 | modules: { 17 | cart, 18 | products 19 | }, 20 | strict: debug, 21 | plugins: debug ? [createLogger()] : [] 22 | }) 23 | -------------------------------------------------------------------------------- /examples/chat/components/Thread.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 25 | -------------------------------------------------------------------------------- /docs/kr/strict.md: -------------------------------------------------------------------------------- 1 | # Strict 모드 2 | 3 | strict 모드를 사용하기 위해, `strict: true`를 Vuex 저장소를 만들 때 추가하면 됩니다. 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | 엄격 모드에서는 Vuex 상태가 변이 핸들러 외부에서 변이 될 때 마다 오류가 발생합니다. 이렇게하면 디버깅 도구로 모든 상태 변이를 명시적으로 추적 할 수 있습니다. 13 | 14 | ### 개발 vs. 배포 15 | 16 | **배포시 strict 모드를 켜지 마십시오!** Strict 모드는 부적절한 변이를 감지하기 위해 상태 트리를 자세히 관찰합니다. 성능 이슈를 피하기 위해 배포 환경에서 이를 해제 하십시오. 17 | 18 | 플러그인과 마찬가지로 빌드 도구가 다음을 처리하도록 할 수 있습니다. 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/ja/strict.md: -------------------------------------------------------------------------------- 1 | # 厳格モード 2 | 3 | 厳格(strict)モードを有効にするには Vuex store を作成するときに、ただ `strict: true` を指定するだけです: 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | 厳格モードでは Vuex の状態がミューテーションハンドラの外部で変更されたら、エラーを投げるようになります。これで全ての状態変更がデバッギングツールで明示的に追跡できることが保証されます。 13 | 14 | ### 開発環境 vs 本番環境 15 | 16 | **本番環境で厳格モードを有効にしてデプロイしてはいけません!**厳格モードでは不適切なミューテーションを検出するためにステートツリーに対して深い監視を実行します。パフォーマンスコストを回避するために本番環境では無効にしてください。 17 | 18 | プラグインと同様に、ビルドツールに処理させることができます: 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/zh-cn/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > 注意:本文档针对的是 vuex@2.x。 5 | 6 | 7 | - [寻找 1.0 文档?](https://github.com/vuejs/vuex/tree/1.0/docs) 8 | - [更新记录](https://github.com/vuejs/vuex/releases) 9 | - [安装](installation.md) 10 | - [Vuex 是什么?](intro.md) 11 | - [开始](getting-started.md) 12 | - 核心概念 13 | - [State](state.md) 14 | - [Getters](getters.md) 15 | - [Mutations](mutations.md) 16 | - [Actions](actions.md) 17 | - [Modules](modules.md) 18 | - [项目结构](structure.md) 19 | - [插件](plugins.md) 20 | - [严格模式](strict.md) 21 | - [表单处理](forms.md) 22 | - [测试](testing.md) 23 | - [热重载](hot-reload.md) 24 | - [API 文档](api.md) 25 | -------------------------------------------------------------------------------- /docs/kr/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > 참고: 이 문서는 vuex@2.x 을 기준으로 합니다. 5 | 6 | 7 | - [1.0 버전 문서를 보려면?](https://github.com/vuejs/vuex/tree/1.0/docs) 8 | - [릴리즈 노트](https://github.com/vuejs/vuex/releases) 9 | - [설치](installation.md) 10 | - [Vuex가 무엇인가요?](intro.md) 11 | - [시작하기](getting-started.md) 12 | - 핵심 컨셉 13 | - [상태](state.md) 14 | - [Getters](getters.md) 15 | - [변이](mutations.md) 16 | - [액션](actions.md) 17 | - [모듈](modules.md) 18 | - [애플리케이션 구조](structure.md) 19 | - [플러그인](plugins.md) 20 | - [Strict 모드](strict.md) 21 | - [폼 핸들링](forms.md) 22 | - [테스팅](testing.md) 23 | - [핫 리로딩](hot-reload.md) 24 | - [API 레퍼런스](api.md) 25 | -------------------------------------------------------------------------------- /examples/shopping-cart/components/ProductList.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | -------------------------------------------------------------------------------- /docs/ja/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > 注意: これは vuex@2.x のドキュメントです 5 | 6 | 7 | - [1.0のドキュメントをお探しですか?](https://github.com/vuejs/vuex/tree/1.0/docs/ja) 8 | - [リリースノート](https://github.com/vuejs/vuex/releases) 9 | - [インストール](installation.md) 10 | - [Vuex とは何か?](intro.md) 11 | - [Vuex 入門](getting-started.md) 12 | - コアコンセプト 13 | - [ステート](state.md) 14 | - [ゲッター](getters.md) 15 | - [ミューテーション](mutations.md) 16 | - [アクション](actions.md) 17 | - [モジュール](modules.md) 18 | - [アプリケーションの構造](structure.md) 19 | - [プラグイン](plugins.md) 20 | - [厳格モード](strict.md) 21 | - [フォームの扱い](forms.md) 22 | - [テスト](testing.md) 23 | - [ホットリローディング](hot-reload.md) 24 | - [API リファレンス](api.md) 25 | -------------------------------------------------------------------------------- /examples/counter/Counter.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /examples/shopping-cart/api/shop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mocking client-server processing 3 | */ 4 | const _products = [ 5 | {"id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2}, 6 | {"id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10}, 7 | {"id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5} 8 | ] 9 | 10 | export default { 11 | getProducts (cb) { 12 | setTimeout(() => cb(_products), 100) 13 | }, 14 | 15 | buyProducts (products, cb, errorCb) { 16 | setTimeout(() => { 17 | // simulate random checkout failure. 18 | (Math.random() > 0.5 || navigator.userAgent.indexOf('PhantomJS') > -1) 19 | ? cb() 20 | : errorCb() 21 | }, 100) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/counter-hot/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import * as getters from './getters' 4 | import * as actions from './actions' 5 | import * as mutations from './mutations' 6 | 7 | Vue.use(Vuex) 8 | 9 | const state = { 10 | count: 0, 11 | history: [] 12 | } 13 | 14 | const store = new Vuex.Store({ 15 | state, 16 | getters, 17 | actions, 18 | mutations 19 | }) 20 | 21 | if (module.hot) { 22 | module.hot.accept([ 23 | './getters', 24 | './actions', 25 | './mutations' 26 | ], () => { 27 | store.hotUpdate({ 28 | getters: require('./getters'), 29 | actions: require('./actions'), 30 | mutations: require('./mutations') 31 | }) 32 | }) 33 | } 34 | 35 | export default store 36 | -------------------------------------------------------------------------------- /docs/en/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > Note: This is docs for vuex@2.x. 5 | 6 | 7 | - [Looking for 1.0 Docs?](https://github.com/vuejs/vuex/tree/1.0/docs) 8 | - [Release Notes](https://github.com/vuejs/vuex/releases) 9 | - [Installation](installation.md) 10 | - [What is Vuex?](intro.md) 11 | - [Getting Started](getting-started.md) 12 | - Core Concepts 13 | - [State](state.md) 14 | - [Getters](getters.md) 15 | - [Mutations](mutations.md) 16 | - [Actions](actions.md) 17 | - [Modules](modules.md) 18 | - [Application Structure](structure.md) 19 | - [Plugins](plugins.md) 20 | - [Strict Mode](strict.md) 21 | - [Form Handling](forms.md) 22 | - [Testing](testing.md) 23 | - [Hot Reloading](hot-reload.md) 24 | - [API Reference](api.md) 25 | -------------------------------------------------------------------------------- /examples/counter-hot/Counter.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const webpack = require('webpack') 3 | const webpackDevMiddleware = require('webpack-dev-middleware') 4 | const webpackHotMiddleware = require('webpack-hot-middleware') 5 | const WebpackConfig = require('./webpack.config') 6 | 7 | const app = express() 8 | const compiler = webpack(WebpackConfig) 9 | 10 | app.use(webpackDevMiddleware(compiler, { 11 | publicPath: '/__build__/', 12 | stats: { 13 | colors: true, 14 | chunks: false 15 | } 16 | })) 17 | 18 | app.use(webpackHotMiddleware(compiler)) 19 | 20 | app.use(express.static(__dirname)) 21 | 22 | const port = process.env.PORT || 8080 23 | module.exports = app.listen(port, () => { 24 | console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`) 25 | }) 26 | -------------------------------------------------------------------------------- /docs/ru/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > Внимание: это — документация для версии vuex@2.x. 5 | 6 | 7 | - [Нужна документация для 1.0?](https://github.com/vuejs/vuex/tree/1.0/docs) 8 | - [История изменений](https://github.com/vuejs/vuex/releases) 9 | - [Установка](installation.md) 10 | - [Что такое Vuex?](intro.md) 11 | - [Начало работы](getting-started.md) 12 | - Основные концепции 13 | - [Состояние](state.md) 14 | - [Геттеры](getters.md) 15 | - [Мутации](mutations.md) 16 | - [Действия](actions.md) 17 | - [Модули](modules.md) 18 | - [Структура приложения](structure.md) 19 | - [Плагины](plugins.md) 20 | - [Strict Mode](strict.md) 21 | - [Обработка форм](forms.md) 22 | - [Тестирование](testing.md) 23 | - [Горячая замена](hot-reload.md) 24 | - [Справочник API](api.md) 25 | -------------------------------------------------------------------------------- /docs/fr/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | 4 | > Note : Ceci est la documentation pour vuex@2.x. 5 | 6 | 7 | - [Vous cherchez la documentation de la v1.0 ?](https://github.com/vuejs/vuex/tree/1.0/docs) 8 | - [Release Notes](https://github.com/vuejs/vuex/releases) 9 | - [Installation](installation.md) 10 | - [Qu'est-ce que Vuex ?](intro.md) 11 | - [Débuter](getting-started.md) 12 | - Concepts de base 13 | - [State](state.md) 14 | - [Getters](getters.md) 15 | - [Mutations](mutations.md) 16 | - [Actions](actions.md) 17 | - [Modules](modules.md) 18 | - [Structure d'une application](structure.md) 19 | - [Plugins](plugins.md) 20 | - [Strict Mode](strict.md) 21 | - [Formulaires](forms.md) 22 | - [Tests](testing.md) 23 | - [Hot Reloading](hot-reload.md) 24 | - [Documentation API](api.md) 25 | -------------------------------------------------------------------------------- /docs/zh-cn/structure.md: -------------------------------------------------------------------------------- 1 | # 项目结构 2 | 3 | Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则: 4 | 5 | 1. 应用层级的状态应该集中到单个 store 对象中。 6 | 7 | 2. 提交 **mutation** 是更改状态的唯一方法,并且这个过程是同步的。 8 | 9 | 3. 异步逻辑都应该封装到 **action** 里面。 10 | 11 | 只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation、和 getters 分割到单独的文件。 12 | 13 | 对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例: 14 | 15 | 16 | ``` bash 17 | ├── index.html 18 | ├── main.js 19 | ├── api 20 | │   └── ... # 抽取出API请求 21 | ├── components 22 | │   ├── App.vue 23 | │   └── ... 24 | └── store 25 | ├── index.js # 我们组装模块并导出 store 的地方 26 | ├── actions.js # 根级别的 action 27 | ├── mutations.js # 根级别的 mutation 28 | └── modules 29 |    ├── cart.js # 购物车模块 30 |    └── products.js # 产品模块 31 | ``` 32 | 33 | 请参考[购物车示例](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart) 34 | -------------------------------------------------------------------------------- /examples/shopping-cart/currency.js: -------------------------------------------------------------------------------- 1 | const digitsRE = /(\d{3})(?=\d)/g 2 | 3 | export function currency (value, currency, decimals) { 4 | value = parseFloat(value) 5 | if (!isFinite(value) || (!value && value !== 0)) return '' 6 | currency = currency != null ? currency : '$' 7 | decimals = decimals != null ? decimals : 2 8 | var stringified = Math.abs(value).toFixed(decimals) 9 | var _int = decimals 10 | ? stringified.slice(0, -1 - decimals) 11 | : stringified 12 | var i = _int.length % 3 13 | var head = i > 0 14 | ? (_int.slice(0, i) + (_int.length > 3 ? ',' : '')) 15 | : '' 16 | var _float = decimals 17 | ? stringified.slice(-1 - decimals) 18 | : '' 19 | var sign = value < 0 ? '-' : '' 20 | return sign + currency + head + 21 | _int.slice(i).replace(digitsRE, '$1,') + 22 | _float 23 | } 24 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/modules/products.js: -------------------------------------------------------------------------------- 1 | import shop from '../../api/shop' 2 | import * as types from '../mutation-types' 3 | 4 | // initial state 5 | const state = { 6 | all: [] 7 | } 8 | 9 | // getters 10 | const getters = { 11 | allProducts: state => state.all 12 | } 13 | 14 | // actions 15 | const actions = { 16 | getAllProducts ({ commit }) { 17 | shop.getProducts(products => { 18 | commit(types.RECEIVE_PRODUCTS, { products }) 19 | }) 20 | } 21 | } 22 | 23 | // mutations 24 | const mutations = { 25 | [types.RECEIVE_PRODUCTS] (state, { products }) { 26 | state.all = products 27 | }, 28 | 29 | [types.ADD_TO_CART] (state, { id }) { 30 | state.all.find(p => p.id === id).inventory-- 31 | } 32 | } 33 | 34 | export default { 35 | state, 36 | getters, 37 | actions, 38 | mutations 39 | } 40 | -------------------------------------------------------------------------------- /test/unit/module/module.spec.js: -------------------------------------------------------------------------------- 1 | import Module from '../../../src/module/module' 2 | 3 | describe('Module', () => { 4 | it('get state', () => { 5 | const module = new Module({ 6 | state: { 7 | value: true 8 | } 9 | }) 10 | expect(module.state).toEqual({ value: true }) 11 | }) 12 | 13 | it('get state: should return object if state option is empty', () => { 14 | const module = new Module({}) 15 | expect(module.state).toEqual({}) 16 | }) 17 | 18 | it('get namespacer: no namespace option', () => { 19 | const module = new Module({}) 20 | expect(module.namespaced).toBe(false) 21 | }) 22 | 23 | it('get namespacer: namespace option is true', () => { 24 | const module = new Module({ 25 | namespaced: true 26 | }) 27 | expect(module.namespaced).toBe(true) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /examples/chat/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import * as getters from './getters' 4 | import * as actions from './actions' 5 | import mutations from './mutations' 6 | import createLogger from '../../../src/plugins/logger' 7 | 8 | Vue.use(Vuex) 9 | 10 | const state = { 11 | currentThreadID: null, 12 | threads: { 13 | /* 14 | id: { 15 | id, 16 | name, 17 | messages: [...ids], 18 | lastMessage 19 | } 20 | */ 21 | }, 22 | messages: { 23 | /* 24 | id: { 25 | id, 26 | threadId, 27 | threadName, 28 | authorName, 29 | text, 30 | timestamp, 31 | isRead 32 | } 33 | */ 34 | } 35 | } 36 | 37 | export default new Vuex.Store({ 38 | state, 39 | getters, 40 | actions, 41 | mutations, 42 | plugins: process.env.NODE_ENV !== 'production' 43 | ? [createLogger()] 44 | : [] 45 | }) 46 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | var spawn = require('cross-spawn') 2 | var args = process.argv.slice(2) 3 | 4 | var server = args.indexOf('--dev') > -1 5 | ? null 6 | : require('../../examples/server') 7 | 8 | if (args.indexOf('--config') === -1) { 9 | args = args.concat(['--config', 'test/e2e/nightwatch.config.js']) 10 | } 11 | if (args.indexOf('--env') === -1) { 12 | args = args.concat(['--env', 'phantomjs']) 13 | } 14 | var i = args.indexOf('--test') 15 | if (i > -1) { 16 | args[i + 1] = 'test/e2e/specs/' + args[i + 1] 17 | } 18 | if (args.indexOf('phantomjs') > -1) { 19 | process.env.PHANTOMJS = true 20 | } 21 | 22 | var runner = spawn('./node_modules/.bin/nightwatch', args, { 23 | stdio: 'inherit' 24 | }) 25 | 26 | runner.on('exit', function (code) { 27 | server && server.close() 28 | process.exit(code) 29 | }) 30 | 31 | runner.on('error', function (err) { 32 | server && server.close() 33 | throw err 34 | }) 35 | -------------------------------------------------------------------------------- /test/unit/util.spec.js: -------------------------------------------------------------------------------- 1 | import { deepCopy } from '../../src/util' 2 | 3 | describe('util', () => { 4 | it('deepCopy: nornal structure', () => { 5 | const original = { 6 | a: 1, 7 | b: 'string', 8 | c: true, 9 | d: null, 10 | e: undefined 11 | } 12 | const copy = deepCopy(original) 13 | 14 | expect(copy).toEqual(original) 15 | }) 16 | 17 | it('deepCopy: nested structure', () => { 18 | const original = { 19 | a: { 20 | b: 1, 21 | c: [2, 3, { 22 | d: 4 23 | }] 24 | } 25 | } 26 | const copy = deepCopy(original) 27 | 28 | expect(copy).toEqual(original) 29 | }) 30 | 31 | it('deepCopy: circular structure', () => { 32 | const original = { 33 | a: 1 34 | } 35 | original.circular = original 36 | 37 | const copy = deepCopy(original) 38 | 39 | expect(copy).toEqual(original) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /docs/kr/structure.md: -------------------------------------------------------------------------------- 1 | # 애플리케이션 구조 2 | 3 | 실제로 Vuex는 코드 구조를 제한하지는 않습니다. 이보다 아래에 있는 상위 수준 원칙을 강요합니다. 4 | 5 | 1. 애플리케이션 레벨의 상태는 중앙 집중된 저장소 입니다. 6 | 7 | 2. 상태를 변경시키는 유일한 방법은 동기 트랜잭션인 **변이** 를 커밋하는 것입니다. 8 | 9 | 3. 비동기식 로직은 캡슐화되어야하며 **액션** 으로 구성 됩니다. 10 | 11 | 12 | 이 규칙을 따른다면 프로젝트를 구조화하는 것은 사용자에게 달려 있습니다. 저장소 파일이 너무 커지면 액션, 돌연변이 및 getter를 개별 파일로 분할하기만 하면됩니다. 13 | 14 | 중요한 앱의 경우 모듈을 활용해야 할 가능성이 높습니다. 다음은 프로젝트 구조의 예입니다. 15 | 16 | ``` bash 17 | ├── index.html 18 | ├── main.js 19 | ├── api 20 | │   └── ... # API 요청을 위한 추상화를 포함합니다. 21 | ├── components 22 | │   ├── App.vue 23 | │   └── ... 24 | └── store 25 | ├── index.js # 모듈을 조합하고 저장소를 내보내는 곳 입니다. 26 | ├── actions.js # 루트 액션 27 | ├── mutations.js # 루트 변이 28 | └── modules 29 |    ├── cart.js # cart 모듈 30 |    └── products.js # products 모듈 31 | ``` 32 | 33 | 참고 사항으로, [장바구니 예](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart)를 확인하십시오. 34 | -------------------------------------------------------------------------------- /test/e2e/specs/counter.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'counter': function (browser) { 3 | browser 4 | .url('http://localhost:8080/counter/') 5 | .waitForElementVisible('#app', 1000) 6 | .assert.containsText('div', 'Clicked: 0 times') 7 | .click('button:nth-child(1)') 8 | .assert.containsText('div', 'Clicked: 1 times') 9 | .click('button:nth-child(2)') 10 | .assert.containsText('div', 'Clicked: 0 times') 11 | .click('button:nth-child(3)') 12 | .assert.containsText('div', 'Clicked: 0 times') 13 | .click('button:nth-child(1)') 14 | .assert.containsText('div', 'Clicked: 1 times') 15 | .click('button:nth-child(3)') 16 | .assert.containsText('div', 'Clicked: 2 times') 17 | .click('button:nth-child(4)') 18 | .assert.containsText('div', 'Clicked: 2 times') 19 | .waitFor(1000) 20 | .assert.containsText('div', 'Clicked: 3 times') 21 | .end() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/en/strict.md: -------------------------------------------------------------------------------- 1 | # Strict Mode 2 | 3 | To enable strict mode, simply pass in `strict: true` when creating a Vuex store: 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. This ensures that all state mutations can be explicitly tracked by debugging tools. 13 | 14 | ### Development vs. Production 15 | 16 | **Do not enable strict mode when deploying for production!** Strict mode runs a synchronous deep watcher on the state tree for detecting inappropriate mutations, and it can be quite expensive when you make large amount of mutations to the state. Make sure to turn it off in production to avoid the performance cost. 17 | 18 | Similar to plugins, we can let the build tools handle that: 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/todomvc/store/mutations.js: -------------------------------------------------------------------------------- 1 | export const STORAGE_KEY = 'todos-vuejs' 2 | 3 | // for testing 4 | if (navigator.userAgent.indexOf('PhantomJS') > -1) { 5 | window.localStorage.clear() 6 | } 7 | 8 | export const state = { 9 | todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]') 10 | } 11 | 12 | export const mutations = { 13 | addTodo (state, { text }) { 14 | state.todos.push({ 15 | text, 16 | done: false 17 | }) 18 | }, 19 | 20 | deleteTodo (state, { todo }) { 21 | state.todos.splice(state.todos.indexOf(todo), 1) 22 | }, 23 | 24 | toggleTodo (state, { todo }) { 25 | todo.done = !todo.done 26 | }, 27 | 28 | editTodo (state, { todo, value }) { 29 | todo.text = value 30 | }, 31 | 32 | toggleAll (state, { done }) { 33 | state.todos.forEach((todo) => { 34 | todo.done = done 35 | }) 36 | }, 37 | 38 | clearCompleted (state) { 39 | state.todos = state.todos.filter(todo => !todo.done) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/fr/strict.md: -------------------------------------------------------------------------------- 1 | # Strict Mode 2 | 3 | Pour activer le mode strict, passez simplement l'option `strict: true` lorsque vous créez un store Vuex : 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | En mode strict, lorsque le state Vuex est modifié en dehors des handlers de mutation, une erreur sera lancée. Cela permet de s'assurer que toutes les mutations du state peuvent être explicitement tracées par les outils de debugging. 13 | 14 | ### Développement vs. Production 15 | 16 | **N'activez pas le mode strict lorsque vous déployez en production !** Le mode strict lance une observation récursive du state tree pour détecter des mutations inappropriées — assurrez-vous de l'avoir désactivé en production pour éviter un coût sur les performances. 17 | 18 | Tout comme les plugins, nous pouvons laisser nos outils de build gérer ça : 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/ja/structure.md: -------------------------------------------------------------------------------- 1 | # アプリケーションの構造 2 | 3 | Vuex は実際のところ、あなたがコードを構造化する方法を制限しません。もっと正確に言うと、それより高いレベルの原理原則を適用させます: 4 | 5 | 1. アプリケーションレベルの状態はストアに集約されます。 6 | 7 | 2. 状態を変更する唯一の方法は、同期的に処理を行う**ミューテーション**をコミットすることのみです。 8 | 9 | 3. 非同期的なロジックはカプセル化されるべきであり、それは**アクション**によって構成されます。 10 | 11 | これらのルールに従っている限り、プロジェクトをどのように構造化するかはあなた次第です。もしストアファイルが大きくなり過ぎたら、単純にアクションやミューテーション、ゲッターをそれぞれ別のファイルに切り出すことができます。 12 | 13 | それなりに手の込んだアプリケーションであれば、モジュールを活用する必要が出てきそうです。プロジェクトの構造の例は以下のようになります: 14 | 15 | ``` bash 16 | ├── index.html 17 | ├── main.js 18 | ├── api 19 | │   └── ... # API 呼び出しを抽象化する 20 | ├── components 21 | │   ├── App.vue 22 | │   └── ... 23 | └── store 24 | ├── index.js # モジュールを集めてストアをエクスポートする 25 | ├── actions.js # アクションのルートファイル 26 | ├── mutations.js # ミューテーションのルートファイル 27 | └── modules 28 |    ├── cart.js # cart モジュール 29 |    └── products.js # products モジュール 30 | ``` 31 | 32 | 参考として [Shopping Cart Example](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart) をみてみるのもよいでしょう。 33 | -------------------------------------------------------------------------------- /docs/zh-cn/installation.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | ### 直接下载 / CDN 引用 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) 提供了基于 NPM 的 CDN 链接。以上的链接会一直指向 NPM 上发布的最新版本。您也可以通过 `https://unpkg.com/vuex@2.0.0` 这样的方式指定特定的版本。 9 | 10 | 11 | 在 Vue 之后引入 `vuex` 会进行自动安装: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | 在一个模块化的打包系统中,您必须显式地通过 `Vue.use()` 来安装 Vuex: 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | 当使用全局 script 标签引用 Vuex 时,不需要以上安装过程。 40 | 41 | ### 自己构建 42 | 43 | 如果需要使用 dev 分支下的最新版本,您可以直接从 GitHub 上克隆代码并自己构建。 44 | 45 | ``` bash 46 | git clone https://github.com/vuejs/vuex.git node_modules/vuex 47 | cd node_modules/vuex 48 | npm install 49 | npm run build 50 | ``` 51 | -------------------------------------------------------------------------------- /src/mixin.js: -------------------------------------------------------------------------------- 1 | export default function (Vue) { 2 | const version = Number(Vue.version.split('.')[0]) 3 | 4 | if (version >= 2) { 5 | const usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1 6 | Vue.mixin(usesInit ? { init: vuexInit } : { beforeCreate: vuexInit }) 7 | } else { 8 | // override init and inject vuex init procedure 9 | // for 1.x backwards compatibility. 10 | const _init = Vue.prototype._init 11 | Vue.prototype._init = function (options = {}) { 12 | options.init = options.init 13 | ? [vuexInit].concat(options.init) 14 | : vuexInit 15 | _init.call(this, options) 16 | } 17 | } 18 | 19 | /** 20 | * Vuex init hook, injected into each instances init hooks list. 21 | */ 22 | 23 | function vuexInit () { 24 | const options = this.$options 25 | // store injection 26 | if (options.store) { 27 | this.$store = options.store 28 | } else if (options.parent && options.parent.$store) { 29 | this.$store = options.parent.$store 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vuex [![Build Status](https://circleci.com/gh/vuejs/vuex/tree/dev.png?style=shield)](https://circleci.com/gh/vuejs/vuex) 2 | 3 | > Centralized State Management for Vue.js. 4 | 5 |

6 | 7 |

8 | 9 | - [What is Vuex?](http://vuex.vuejs.org/en/intro.html) 10 | - [Full Documentation](http://vuex.vuejs.org/) 11 | 12 | ## Examples 13 | 14 | - [Counter](https://github.com/vuejs/vuex/tree/dev/examples/counter) 15 | - [Counter with Hot Reload](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) 16 | - [TodoMVC](https://github.com/vuejs/vuex/tree/dev/examples/todomvc) 17 | - [Flux Chat](https://github.com/vuejs/vuex/tree/dev/examples/chat) 18 | - [Shopping Cart](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart) 19 | 20 | Running the examples: 21 | 22 | ``` bash 23 | $ npm install 24 | $ npm run dev # serve examples at localhost:8080 25 | ``` 26 | 27 | ## License 28 | 29 | [MIT](http://opensource.org/licenses/MIT) 30 | -------------------------------------------------------------------------------- /docs/kr/installation.md: -------------------------------------------------------------------------------- 1 | # 설치 2 | 3 | ### 직접 다운로드 / CDN 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com)은 NPM 기반 CDN 링크를 제공합니다. 위의 링크는 항상 NPM의 최신 릴리스를 가리킵니다. `https://unpkg.com/vuex@2.0.0`과 같은 URL을 통해 특정 버전/태그를 사용할 수도 있습니다. 9 | 10 | 11 | Vue 뒤에 `vuex`를 추가하면 자동으로 설치됩니다: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | 모듈 시스템과 함께 사용하면 `Vue.use()`를 통해 Vuex를 명시적으로 추가해야 합니다. 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | 전역 스크립트 태그를 사용할 때는 이 작업을 할 필요가 없습니다. 40 | 41 | ### 개발용 빌드 42 | 43 | 최신 dev 빌드를 사용하고 싶은 경우 직접 GitHub에서 클론하고 `vuex`를 직접 빌드 해야합니다. 44 | 45 | 46 | ``` bash 47 | git clone https://github.com/vuejs/vuex.git node_modules/vuex 48 | cd node_modules/vuex 49 | npm install 50 | npm run build 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/ru/strict.md: -------------------------------------------------------------------------------- 1 | # Строгий режим (strict mode) 2 | 3 | Для включения строгого режима просто укажите `strict: true` при создании хранилища Vuex: 4 | 5 | ``` js 6 | const store = new Vuex.Store({ 7 | // ... 8 | strict: true 9 | }) 10 | ``` 11 | 12 | В строгом режиме любая попытка внесения изменений в состояние Vuex кроме мутаций будет выбрасывать ошибку. Это гарантирует, что все мутации состояния будут явно отслежены через инструменты отладки. 13 | 14 | ### Разработка vs. production 15 | 16 | **Не используйте строгий режим в production!** Строгий режим запускает глубокое отслеживание за деревом состояния приложения в синхронном режиме для обнаружения несоответствующих мутаций, и это может быть затратным для производительности когда совершается большое количество мутаций. Убедитесь, что выключили этот режим в production чтобы избежать ухудшения производительности. 17 | 18 | Аналогично плагинам, при использовании инструментов сборки это можно сделать так: 19 | 20 | ``` js 21 | const store = new Vuex.Store({ 22 | // ... 23 | strict: process.env.NODE_ENV !== 'production' 24 | }) 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/shopping-cart/components/Cart.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 38 | -------------------------------------------------------------------------------- /docs/ja/installation.md: -------------------------------------------------------------------------------- 1 | # インストール 2 | 3 | ### 直接ダウンロードする / CDN 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) で NPM ベースの CDN リンクが提供されています。上記リンクは常に NPM の最新のリリースを指します。`https://unpkg.com/vuex@2.0.0` のような URL によって特定のバージョン/タグを利用することもできます。 9 | 10 | 11 | Vue のあとで `vuex` を取り込むと自動的に Vuex が導入されます: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | モジュールシステムで利用される場合、 `Vue.use()` によって Vuex を明示的に導入する必要があります: 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | グローバルなスクリプトタグを利用する場合にはこのようにする必要はありません。 40 | 41 | ### 開発版ビルド 42 | 43 | 最新の開発版ビルドを利用したい場合には、 Github から直接クローンし `vuex` を自身でビルドする必要があります。 44 | 45 | ``` bash 46 | git clone https://github.com/vuejs/vuex.git node_modules/vuex 47 | cd node_modules/vuex 48 | npm install 49 | npm run build 50 | ``` 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Evan You 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/chat/components/ThreadSection.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 48 | -------------------------------------------------------------------------------- /docs/zh-cn/hot-reload.md: -------------------------------------------------------------------------------- 1 | # 热重载 2 | 3 | 使用 webpack 的 [Hot Module Replacement API](https://webpack.github.io/docs/hot-module-replacement.html),Vuex 支持在开发过程中热重载 mutation、modules、actions、和getters。你也可以在 Browserify 中使用 [browserify-hmr](https://github.com/AgentME/browserify-hmr/) 插件。 4 | 5 | 对于 mutation 和模块,你需要使用 `store.hotUpdate()` 方法: 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // 使 actions 和 mutations 成为可热重载模块 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // 获取更新后的模块 30 | // 因为 babel 6 的模块编译格式问题,这里需要加上 .default 31 | const newMutations = require('./mutations').default 32 | const newModuleA = require('./modules/a').default 33 | // 加载新模块 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | 参考热重载示例 [counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)。 45 | -------------------------------------------------------------------------------- /docs/kr/hot-reload.md: -------------------------------------------------------------------------------- 1 | # 핫 리로딩 2 | 3 | Vuex는 Webpack의 [핫 모듈 변경 API](https://webpack.github.io/docs/hot-module-replacement.html)를 사용하여 개발 중에 핫 리로드 변이, 모듈, 액션 및 getter를 지원합니다. [browserify-hmr](https://github.com/AgentME/browserify-hmr/) 플러그인으로 Browserify에서 사용할 수도 있습니다. 4 | 5 | 변이와 모듈의 경우, `store.hotUpdate()` API 메소드를 사용할 필요가 있습니다. 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // 액션과 변이를 핫 모듈로 받아 들인다. 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // 업데이트 된 모듈은 babel 6 모듈 출력으로 인해 30 | // .default를 여기에 추가해야합니다. 31 | const newMutations = require('./mutations').default 32 | const newModuleA = require('./modules/a').default 33 | // 새로운 액션과 변이로 바꿉니다. 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | [counter-hot 예제](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)로 핫 리로드를 확인하십시오. 45 | -------------------------------------------------------------------------------- /docs/en/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ### Direct Download / CDN 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like `https://unpkg.com/vuex@2.0.0`. 9 | 10 | 11 | Include `vuex` after Vue and it will install itself automatically: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | When used with a module system, you must explicitly install Vuex via `Vue.use()`: 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | You don't need to do this when using global script tags. 40 | 41 | ### Dev Build 42 | 43 | You will have to clone directly from GitHub and build `vuex` yourself if 44 | you want to use the latest dev build. 45 | 46 | ``` bash 47 | git clone https://github.com/vuejs/vuex.git node_modules/vuex 48 | cd node_modules/vuex 49 | npm install 50 | npm run build 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/zh-cn/forms.md: -------------------------------------------------------------------------------- 1 | # 表单处理 2 | 3 | 当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 `v-model` 会比较棘手: 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | 假设这里的 `obj` 是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时,`v-model` 会试图直接修改 `obj.message`。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。 10 | 11 | 用『Vuex 的思维』去解决这个问题的方法是:给 `` 中绑定 value,然后侦听 `input` 或者 `change` 事件,在事件回调中调用 action: 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage (e) { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | 下面是 mutation 函数: 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### 双向绑定的计算属性 42 | 43 | 必须承认,这样做比简单地使用“`v-model` + 局部状态”要啰嗦得多,并且也损失了一些 `v-model` 中很有用的特性。另一个方法是使用带有 setter 的双向绑定计算属性: 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | // ... 50 | computed: { 51 | message: { 52 | get () { 53 | return this.$store.state.obj.message 54 | }, 55 | set (value) { 56 | this.$store.commit('updateMessage', value) 57 | } 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /types/test/helpers.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("vue"); 2 | 3 | import { 4 | mapState, 5 | mapGetters, 6 | mapActions, 7 | mapMutations 8 | } from "../index"; 9 | 10 | new Vue({ 11 | computed: Object.assign({}, 12 | mapState(["a"]), 13 | mapState('foo', ["a"]), 14 | mapState({ 15 | b: "b" 16 | }), 17 | mapState('foo', { 18 | b: "b" 19 | }), 20 | mapState({ 21 | c: (state: any, getters: any) => state.c + getters.c 22 | }), 23 | mapState('foo', { 24 | c: (state: any, getters: any) => state.c + getters.c 25 | }), 26 | 27 | mapGetters(["d"]), 28 | mapGetters('foo', ["d"]), 29 | mapGetters({ 30 | e: "e" 31 | }), 32 | mapGetters('foo', { 33 | e: "e" 34 | }), 35 | 36 | { 37 | otherComputed () { 38 | return "f"; 39 | } 40 | } 41 | ), 42 | 43 | methods: Object.assign({}, 44 | mapActions(["g"]), 45 | mapActions({ 46 | h: "h" 47 | }), 48 | mapActions('foo', ["g"]), 49 | mapActions('foo', { 50 | h: "h" 51 | }), 52 | 53 | mapMutations(["i"]), 54 | mapMutations({ 55 | j: "j" 56 | }), 57 | mapMutations('foo', ["i"]), 58 | mapMutations('foo', { 59 | j: "j" 60 | }), 61 | 62 | { 63 | otherMethod () {} 64 | } 65 | ) 66 | }); 67 | -------------------------------------------------------------------------------- /docs/en/structure.md: -------------------------------------------------------------------------------- 1 | # Application Structure 2 | 3 | Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of high-level principles: 4 | 5 | 1. Application-level state is centralized in the store. 6 | 7 | 2. The only way to mutate the state is by committing **mutations**, which are synchronous transactions. 8 | 9 | 3. Asynchronous logic should be encapsulated in, and can be composed with **actions**. 10 | 11 | As long as you follow these rules, it's up to you how to structure your project. If your store file gets too big, simply start splitting the actions, mutations and getters into separate files. 12 | 13 | For any non-trivial app, we will likely need to leverage modules. Here's an example project structure: 14 | 15 | ``` bash 16 | ├── index.html 17 | ├── main.js 18 | ├── api 19 | │   └── ... # abstractions for making API requests 20 | ├── components 21 | │   ├── App.vue 22 | │   └── ... 23 | └── store 24 | ├── index.js # where we assemble modules and export the store 25 | ├── actions.js # root actions 26 | ├── mutations.js # root mutations 27 | └── modules 28 |    ├── cart.js # cart module 29 |    └── products.js # products module 30 | ``` 31 | 32 | As a reference, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart). 33 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.config.js: -------------------------------------------------------------------------------- 1 | // http://nightwatchjs.org/guide#settings-file 2 | module.exports = { 3 | 'src_folders': ['test/e2e/specs'], 4 | 'output_folder': 'test/e2e/reports', 5 | 'custom_commands_path': ['node_modules/nightwatch-helpers/commands'], 6 | 'custom_assertions_path': ['node_modules/nightwatch-helpers/assertions'], 7 | 8 | 'selenium': { 9 | 'start_process': true, 10 | 'server_path': require('selenium-server').path, 11 | 'host': '127.0.0.1', 12 | 'port': 4444, 13 | 'cli_args': { 14 | 'webdriver.chrome.driver': require('chromedriver').path 15 | } 16 | }, 17 | 18 | 'test_settings': { 19 | 'default': { 20 | 'selenium_port': 4444, 21 | 'selenium_host': 'localhost', 22 | 'silent': true, 23 | 'screenshots': { 24 | 'enabled': true, 25 | 'on_failure': true, 26 | 'on_error': false, 27 | 'path': 'test/e2e/screenshots' 28 | } 29 | }, 30 | 31 | 'chrome': { 32 | 'desiredCapabilities': { 33 | 'browserName': 'chrome', 34 | 'javascriptEnabled': true, 35 | 'acceptSslCerts': true 36 | } 37 | }, 38 | 39 | 'phantomjs': { 40 | 'desiredCapabilities': { 41 | 'browserName': 'phantomjs', 42 | 'javascriptEnabled': true, 43 | 'acceptSslCerts': true 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/kr/forms.md: -------------------------------------------------------------------------------- 1 | # 폼 핸들링 2 | 3 | strict 모드로 Vuex를 사용하는 경우 Vuex에 포함된 부분에 `v-model`을 사용하는 것은 약간 까다로울 수 있습니다. 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | `obj`가 저장소에서 객체를 반환하는 계산된 속성이라면, 여기에있는 `v-model`은 사용자가 입력 할 때 `obj.message`를 직접 변경하려고 합니다. strict 모드에서는 Vuex 변이 처리기 내부에서 변이가 수행되지 않으므로 오류가 발생합니다. 10 | 11 | 그것을 다루는 "Vuex 방식"은 ``의 값을 바인딩하고 `input` 또는 `change` 이벤트에 대한 액션을 호출하는 것 입니다. 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage (e) { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | 변이에 대한 핸들러 입니다. 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### 양방향 계산된 속성 42 | 43 | 틀림없이, 위의 내용은 `v-model` + 지역 상태보다 좀더 장황 해졌고, `v-model`의 유용한 기능 중 일부를 잃어 버렸습니다. 다른 방법은 setter를 사용하여 양방향 계산된 속성을 사용하는 것입니다. 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | // ... 50 | computed: { 51 | message: { 52 | get () { 53 | return this.$store.state.obj.message 54 | }, 55 | set (value) { 56 | this.$store.commit('updateMessage', value) 57 | } 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/ja/hot-reload.md: -------------------------------------------------------------------------------- 1 | # ホットリローディング 2 | 3 | Vuex は Webpack の [Hot Module Replacement API](https://webpack.github.io/docs/hot-module-replacement.html) を使用することで、アプリケーションの開発を行っている間のミューテーション、モジュール、アクション、ゲッターのホットリローディングをサポートします。Browserify では [browserify-hmr](https://github.com/AgentME/browserify-hmr/) プラグインを使用することができます。 4 | 5 | ミューテーションとモジュールのホットリローディングのために、`store.hotUpdate()` API メソッドを利用する必要があります: 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // ホットモジュールとしてアクションとモジュールを受け付けます 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // 更新されたモジュールをインポートする 30 | // babel 6 のモジュール出力のため、ここでは .default を追加しなければならない 31 | const newActions = require('./actions').default 32 | const newMutations = require('./mutations').default 33 | // 新しいアクションとミューテーションにスワップ 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | ホットリローディングを試したい場合は、[counter-hot example](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot)をチェックアウトしてください。 -------------------------------------------------------------------------------- /docs/ja/forms.md: -------------------------------------------------------------------------------- 1 | # フォームの扱い 2 | 3 | 厳格モードで Vuex を使用するとき、Vuex に属する状態の一部で `v-model` を使用するのは少しトリッキーです: 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | `obj` がストアからオブジェクトを返す算出プロパティ (computed property) と仮定すると、`v-model` は input でユーザーが入力するとき、直接 `obj.message` を変更します。厳格モードでは、この変更は明示的に Vuex のミューテーションハンドラ内部で処理されていないため、エラーを投げます。 10 | 11 | それに対処するための "Vuex way" は、`` の値をバインディングし、`input` または `change` イベントでアクションを呼び出すことです: 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage: ({ dispatch }, e) => { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | ミューテーションのハンドラは以下のようになります: 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### 双方向算出プロパティ 42 | 43 | 確かに、上記の例は単純な `v-model` と ローカルステートよりもかなり冗長で、`v-model` のいくつかの有用な機能が使えません。代わりに、セッターで双方向算出プロパティを使うアプローチがあります。 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | computed: { 50 | message: { 51 | get () { 52 | return this.$store.state.obj.message 53 | }, 54 | set (value) { 55 | this.$store.commit('updateMessage', value) 56 | } 57 | } 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/fr/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ### Téléchargement direct / CDN 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur NPM. Le lien ci-dessus pointera toujours vers la dernière release sur NPM. Vous pouvez aussi utiliser un tag ou une version spécifique comme `https://unpkg.com/vuex@2.0.0`. 9 | 10 | 11 | Incluez `vuex` après Vue et l'installation sera automatique : 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | Lorsque vous utilisez un système de modules, vous devez explicitement installer le router via `Vue.use()`: 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | Il n'est pas nécessaire de faire ceci lorsque vous utilisez des balises de script globales. 40 | 41 | ### Environnement de dev 42 | 43 | Vous devrez cloner directement depuis GitHub et compiler `vuex` vous-même si 44 | vous souhaitez utiliser la dernière version de dev. 45 | 46 | ``` bash 47 | git clone https://github.com/vuejs/vuex.git node_modules/vuex 48 | cd node_modules/vuex 49 | npm install 50 | npm run build 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/ru/installation.md: -------------------------------------------------------------------------------- 1 | # Установка 2 | 3 | ### Скачать напрямую или использовать CDN 4 | 5 | [https://unpkg.com/vuex](https://unpkg.com/vuex) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки на содержимое NPM-пакетов. Приведённая выше ссылка всегда будет указывать на самый свежий релиз Vuex, доступный в NPM. Кроме того, можно указать конкретную версию или тег, например `https://unpkg.com/vuex@2.0.0`. 9 | 10 | 11 | Подключите `vuex` после Vue, и установка произойдёт автоматически: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ### NPM 19 | 20 | ``` bash 21 | npm install vuex --save 22 | ``` 23 | 24 | ### Yarn 25 | 26 | ``` bash 27 | yarn add vuex 28 | ``` 29 | 30 | Если вы используете модули, установите Vuex явным образом командой `Vue.use()`: 31 | 32 | ``` js 33 | import Vue from 'vue' 34 | import Vuex from 'vuex' 35 | 36 | Vue.use(Vuex) 37 | ``` 38 | 39 | При использовании глобальных тегов ` 55 | -------------------------------------------------------------------------------- /docs/en/hot-reload.md: -------------------------------------------------------------------------------- 1 | # Hot Reloading 2 | 3 | Vuex supports hot-reloading mutations, modules, actions and getters during development, using Webpack's [Hot Module Replacement API](https://webpack.github.io/docs/hot-module-replacement.html). You can also use it in Browserify with the [browserify-hmr](https://github.com/AgentME/browserify-hmr/) plugin. 4 | 5 | For mutations and modules, you need to use the `store.hotUpdate()` API method: 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // accept actions and mutations as hot modules 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // require the updated modules 30 | // have to add .default here due to babel 6 module output 31 | const newMutations = require('./mutations').default 32 | const newModuleA = require('./modules/a').default 33 | // swap in the new actions and mutations 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | Checkout the [counter-hot example](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) to play with hot-reload. 45 | -------------------------------------------------------------------------------- /docs/ru/structure.md: -------------------------------------------------------------------------------- 1 | # Структура приложения 2 | 3 | В действительности Vuex не накладывает каких-то значительных ограничений на используемую структуру кода. Однако, он требует соблюдения нескольких высокоуровневых принципов: 4 | 5 | 1. Глобальное состояние приложения должно содержаться в централизованном хранилище; 6 | 7 | 2. Единственным механизмом изменения этого состояния являются **мутации**, являющиеся синхронными транзакциями; 8 | 9 | 3. Асинхронные операции инкапсулирутся в **действия**, или их комбинации. 10 | 11 | Покуда вы следуете этим правилам, можно использовать любую структуру проекта. Если ваш файл хранилища становится слишком большим, просто начните выносить действия, мутации и геттеры в отдельные файлы. 12 | 13 | Для любого нетривиального приложения скорее всего понадобится использование модулей. Вот пример возможной структуры проекта: 14 | 15 | ``` bash 16 | ├── index.html 17 | ├── main.js 18 | ├── api 19 | │   └── ... # абстракции для выполнения запросов к API 20 | ├── components 21 | │   ├── App.vue 22 | │   └── ... 23 | └── store 24 | ├── index.js # здесь мы собираем модули и экспортируем хранилище 25 | ├── actions.js # корневые действия 26 | ├── mutations.js # корневые мутации 27 | └── modules 28 |    ├── cart.js # модуль корзины 29 |    └── products.js # модуль товаров 30 | ``` 31 | 32 | Для справки можно использовать [Пример корзины покупок](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart). 33 | -------------------------------------------------------------------------------- /docs/kr/getting-started.md: -------------------------------------------------------------------------------- 1 | # 시작하기 2 | 3 | 모든 Vuex 애플리케이션의 중심에는 **store** 가 있습니다. "저장소"는 기본적으로 애플리케이션 **상태** 를 보유하고있는 컨테이너입니다. Vuex 저장소가 일반 전역 개체와 두 가지 다른 점이 있습니다. 4 | 5 | 1. Vuex store는 반응형 입니다. Vue 컴포넌트는 상태를 검색할 때 저장소의 상태가 변경되면 효율적으로 대응하고 업데이트합니다. 6 | 2. 저장소의 상태를 직접 변경할 수 없습니다. 저장소의 상태를 변경하는 유일한 방법은 명시적인 **커밋을 이용한 변이** 입니다. 이렇게하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있으며 툴를 사용하여 앱을 더 잘 이해할 수 있습니다. 7 | 8 | ### 가장 단순한 저장소 9 | 10 | > **참고:** 모든 예제는 ES2015 문법을 사용합니다. 사용하고 있지 않은 경우 [꼭 사용해야 합니다!](https://babeljs.io/docs/learn-es2015/) 11 | 12 | Vuex를 [설치](installation.md)한 후 저장소를 만들어 봅시다. 매우 간단합니다. 초기 상태 객체와 일부 변이를 제공하십시오. 13 | 14 | ``` js 15 | // 모듈 시스템을 사용하는 경우 Vue.use(Vuex)를 먼저 호출해야합니다. 16 | 17 | const store = new Vuex.Store({ 18 | state: { 19 | count: 0 20 | }, 21 | mutations: { 22 | increment (state) { 23 | state.count++ 24 | } 25 | } 26 | }) 27 | ``` 28 | 29 | 이제 state 객체에 `store.state`로 접근하여 `store.commit` 메소드로 상태 변경을 트리거 할 수 있습니다. 30 | 31 | ``` js 32 | store.commit('increment') 33 | 34 | console.log(store.state.count) // -> 1 35 | ``` 36 | 37 | 다시 말해, `store.state.count`를 직접 변경하는 대신 변이를 수행하는 이유는 명시적으로 추적을 하기 때문입니다. 이 간단한 규칙에 따라 의도를보다 명확하게 표현할 수 있으므로 코드를 읽을 때 상태 변화를 더 잘 지켜볼 수 있습니다. 또한 모든 변이를 기록하고 상태 스냅샷을 저장하거나 시간 흐름에 따라 디버깅을 할 수 있는 도구를 제공합니다. 38 | 39 | 컴포넌트 안에서 저장소 상태를 사용하는 것은 단순히 계산된 속성 내에서 상태를 반환하는 것입니다. 변경을 트리거하는 것은 컴포넌트 메소드에서 변경을 커밋하는 것을 의미합니다. 40 | 41 | 다음은 [가장 기본적인 Vuex 카운터 앱](https://jsfiddle.net/n9jmu5v7/341/)의 예입니다. 42 | 43 | 이제, 우리는 각 핵심 개념에 대해 더 자세히 설명 할 것입니다. [State](state.md)부터 시작해 보겠습니다. 44 | -------------------------------------------------------------------------------- /docs/ru/hot-reload.md: -------------------------------------------------------------------------------- 1 | # Горячая замена 2 | 3 | Vuex поддерживает горячую замену мутаций, модулей, действий и геттеров в момент разработки с помощью [Webpack Hot Module Replacement API](https://webpack.github.io/docs/hot-module-replacement.html). Аналогичный функционал в Browserify достижим при использовании плагина [browserify-hmr](https://github.com/AgentME/browserify-hmr/). 4 | 5 | Для мутаций и модулей необходимо использовать метод API `store.hotUpdate()`: 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // рассматриваем действия и мутации как модули для горячей замены 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // запрашиваем обновлённые модули 30 | // (нужно указать .default из-за формата вывода Babel 6) 31 | const newMutations = require('./mutations').default 32 | const newModuleA = require('./modules/a').default 33 | // заменяем старые действия и мутации новыми 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | [Пример counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) позволяет посмотреть на горячую замену в реальной жизни. 45 | -------------------------------------------------------------------------------- /test/e2e/specs/chat.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'chat': function (browser) { 3 | browser 4 | .url('http://localhost:8080/chat/') 5 | .waitForElementVisible('.chatapp', 1000) 6 | .assert.containsText('.thread-count', 'Unread threads: 2') 7 | .assert.count('.thread-list-item', 3) 8 | .assert.containsText('.thread-list-item.active', 'Functional Heads') 9 | .assert.containsText('.message-thread-heading', 'Functional Heads') 10 | .assert.count('.message-list-item', 2) 11 | .assert.containsText('.message-list-item:nth-child(1) .message-author-name', 'Bill') 12 | .assert.containsText('.message-list-item:nth-child(1) .message-text', 'Hey Brian') 13 | .enterValue('.message-composer', 'hi') 14 | .waitFor(50) // fake api 15 | .assert.count('.message-list-item', 3) 16 | .assert.containsText('.message-list-item:nth-child(3)', 'hi') 17 | .click('.thread-list-item:nth-child(2)') 18 | .assert.containsText('.thread-list-item.active', 'Dave and Bill') 19 | .assert.containsText('.message-thread-heading', 'Dave and Bill') 20 | .assert.count('.message-list-item', 2) 21 | .assert.containsText('.message-list-item:nth-child(1) .message-author-name', 'Bill') 22 | .assert.containsText('.message-list-item:nth-child(1) .message-text', 'Hey Dave') 23 | .enterValue('.message-composer', 'hi') 24 | .waitFor(50) // fake api 25 | .assert.count('.message-list-item', 3) 26 | .assert.containsText('.message-list-item:nth-child(3)', 'hi') 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/fr/hot-reload.md: -------------------------------------------------------------------------------- 1 | # Hot Reloading 2 | 3 | Vuex prend en charge le `hot-reloading` des mutations, modules, actions et getters durant le développement, en utilisant l'[API Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement.html) de Webpack. Vous pouvez également utiliser Browserify avec le plugin [browserify-hmr](https://github.com/AgentME/browserify-hmr/). 4 | 5 | Pour les mutations et les modules, vous aurez besoin d'utiliser la méthode d'API `store.hotUpdate()` : 6 | 7 | ``` js 8 | // store.js 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import mutations from './mutations' 12 | import moduleA from './modules/a' 13 | 14 | Vue.use(Vuex) 15 | 16 | const state = { ... } 17 | 18 | const store = new Vuex.Store({ 19 | state, 20 | mutations, 21 | modules: { 22 | a: moduleA 23 | } 24 | }) 25 | 26 | if (module.hot) { 27 | // accept actions and mutations as hot modules 28 | module.hot.accept(['./mutations', './modules/a'], () => { 29 | // require the updated modules 30 | // have to add .default here due to babel 6 module output 31 | const newMutations = require('./mutations').default 32 | const newModuleA = require('./modules/a').default 33 | // swap in the new actions and mutations 34 | store.hotUpdate({ 35 | mutations: newMutations, 36 | modules: { 37 | a: newModuleA 38 | } 39 | }) 40 | }) 41 | } 42 | ``` 43 | 44 | Jetez un œil à [l'exemple counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) pour jouer avec le hot-reload. 45 | -------------------------------------------------------------------------------- /src/module/module.js: -------------------------------------------------------------------------------- 1 | import { forEachValue } from '../util' 2 | 3 | export default class Module { 4 | constructor (rawModule, runtime) { 5 | this.runtime = runtime 6 | this._children = Object.create(null) 7 | this._rawModule = rawModule 8 | } 9 | 10 | get state () { 11 | return this._rawModule.state || {} 12 | } 13 | 14 | get namespaced () { 15 | return !!this._rawModule.namespaced 16 | } 17 | 18 | addChild (key, module) { 19 | this._children[key] = module 20 | } 21 | 22 | removeChild (key) { 23 | delete this._children[key] 24 | } 25 | 26 | getChild (key) { 27 | return this._children[key] 28 | } 29 | 30 | update (rawModule) { 31 | this._rawModule.namespaced = rawModule.namespaced 32 | if (rawModule.actions) { 33 | this._rawModule.actions = rawModule.actions 34 | } 35 | if (rawModule.mutations) { 36 | this._rawModule.mutations = rawModule.mutations 37 | } 38 | if (rawModule.getters) { 39 | this._rawModule.getters = rawModule.getters 40 | } 41 | } 42 | 43 | forEachChild (fn) { 44 | forEachValue(this._children, fn) 45 | } 46 | 47 | forEachGetter (fn) { 48 | if (this._rawModule.getters) { 49 | forEachValue(this._rawModule.getters, fn) 50 | } 51 | } 52 | 53 | forEachAction (fn) { 54 | if (this._rawModule.actions) { 55 | forEachValue(this._rawModule.actions, fn) 56 | } 57 | } 58 | 59 | forEachMutation (fn) { 60 | if (this._rawModule.mutations) { 61 | forEachValue(this._rawModule.mutations, fn) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/counter/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | // root state object. 7 | // each Vuex instance is just a single state tree. 8 | const state = { 9 | count: 0 10 | } 11 | 12 | // mutations are operations that actually mutates the state. 13 | // each mutation handler gets the entire state tree as the 14 | // first argument, followed by additional payload arguments. 15 | // mutations must be synchronous and can be recorded by plugins 16 | // for debugging purposes. 17 | const mutations = { 18 | increment (state) { 19 | state.count++ 20 | }, 21 | decrement (state) { 22 | state.count-- 23 | } 24 | } 25 | 26 | // actions are functions that causes side effects and can involve 27 | // asynchronous operations. 28 | const actions = { 29 | increment: ({ commit }) => commit('increment'), 30 | decrement: ({ commit }) => commit('decrement'), 31 | incrementIfOdd ({ commit, state }) { 32 | if ((state.count + 1) % 2 === 0) { 33 | commit('increment') 34 | } 35 | }, 36 | incrementAsync ({ commit }) { 37 | return new Promise((resolve, reject) => { 38 | setTimeout(() => { 39 | commit('increment') 40 | resolve() 41 | }, 1000) 42 | }) 43 | } 44 | } 45 | 46 | // getters are functions 47 | const getters = { 48 | evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd' 49 | } 50 | 51 | // A Vuex instance is created by combining the state, mutations, actions, 52 | // and getters. 53 | export default new Vuex.Store({ 54 | state, 55 | getters, 56 | actions, 57 | mutations 58 | }) 59 | -------------------------------------------------------------------------------- /docs/ja/getting-started.md: -------------------------------------------------------------------------------- 1 | # Vuex 入門 2 | 3 | Vuex アプリケーションの中心にあるものは**ストア**です。"ストア" は、基本的にアプリケーションの**状態(state)**を保持するコンテナです。単純なグローバルオブジェクトとの違いが 2つあります。 4 | 5 | 1. Vuex ストアはリアクティブです。Vue コンポーネントがストアから状態を取り出すとき、もしストアの状態が変化したら、ストアはリアクティブかつ効率的に更新を行います。 6 | 7 | 2. ストアの状態を直接変更することはできません。明示的に**ミューテーションをコミットする**ことによってのみ、ストアの状態を変更します。これによって、全ての状態の変更について追跡可能な記録を残すことが保証され、ツールでのアプリケーションの動作の理解を助けます。 8 | 9 | ### シンプルなストア 10 | 11 | > **注意:** 私たちは、このドキュメントのコード例に ES2015 のシンタックスを利用しています。 もし触れたことがなければ、[ぜひ触れてください](https://babeljs.io/docs/learn-es2015/)! 12 | 13 | Vuex を[インストール](installation.md) してから、ストアをつくってみましょう。Vuex ストアの作成は、とても簡単です。ストアオブジェクトの初期状態と、いくつかのミューテーションを準備するだけです。 14 | 15 | ``` js 16 | // モジュールシステムを利用しているときはあらかじめ Vue.use(Vuex) を呼び出していることを確認しておいてください 17 | 18 | const store = new Vuex.Store({ 19 | state: { 20 | count: 0 21 | }, 22 | mutations: { 23 | increment (state) { 24 | state.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | これで `store.state` でストアオブジェクトの状態を参照でき、また `store.commit` メソッドで状態の変更を行うことができます。 31 | 32 | ``` js 33 | store.commit('increment') 34 | 35 | console.log(store.state.count) // -> 1 36 | ``` 37 | 38 | そして `store.state.count` を直接変更する代わりにミューテーションをコミットする理由は、状態の変更を明確に追跡したいからです。このシンプルな規約は、あなたのコードの意図をさらに明確にし、コードを読んだ時にアプリケーションの状態の変更について、論理的に考えることができるようにします。加えて、私たちに全ての変更のログを取ったり、状態のスナップショットを取ったり、タイムトラベルデバッグを行うようなツールを実装する余地を与えてくれます。 39 | 40 | ストアオブジェクトの状態はリアクティブなので、ストアの状態をコンポーネント内で使うには算出プロパティ内でただ状態を返せば良いです。コンポーネントメソッドでミューテーションをコミットすることによって状態の変更を行います。 41 | 42 | こちらが [Vuex を使った最も基本的なカウンターアプリの例](https://jsfiddle.net/n9jmu5v7/341/)です。 43 | 44 | これから Vuex のコアコンセプトについて詳しく説明していきます。まずは[状態(state)](state.md)からはじめましょう。 45 | -------------------------------------------------------------------------------- /examples/shopping-cart/store/modules/cart.js: -------------------------------------------------------------------------------- 1 | import shop from '../../api/shop' 2 | import * as types from '../mutation-types' 3 | 4 | // initial state 5 | // shape: [{ id, quantity }] 6 | const state = { 7 | added: [], 8 | checkoutStatus: null 9 | } 10 | 11 | // getters 12 | const getters = { 13 | checkoutStatus: state => state.checkoutStatus 14 | } 15 | 16 | // actions 17 | const actions = { 18 | checkout ({ commit, state }, products) { 19 | const savedCartItems = [...state.added] 20 | commit(types.CHECKOUT_REQUEST) 21 | shop.buyProducts( 22 | products, 23 | () => commit(types.CHECKOUT_SUCCESS), 24 | () => commit(types.CHECKOUT_FAILURE, { savedCartItems }) 25 | ) 26 | } 27 | } 28 | 29 | // mutations 30 | const mutations = { 31 | [types.ADD_TO_CART] (state, { id }) { 32 | state.lastCheckout = null 33 | const record = state.added.find(p => p.id === id) 34 | if (!record) { 35 | state.added.push({ 36 | id, 37 | quantity: 1 38 | }) 39 | } else { 40 | record.quantity++ 41 | } 42 | }, 43 | 44 | [types.CHECKOUT_REQUEST] (state) { 45 | // clear cart 46 | state.added = [] 47 | state.checkoutStatus = null 48 | }, 49 | 50 | [types.CHECKOUT_SUCCESS] (state) { 51 | state.checkoutStatus = 'successful' 52 | }, 53 | 54 | [types.CHECKOUT_FAILURE] (state, { savedCartItems }) { 55 | // rollback to the cart saved before sending the request 56 | state.added = savedCartItems 57 | state.checkoutStatus = 'failed' 58 | } 59 | } 60 | 61 | export default { 62 | state, 63 | getters, 64 | actions, 65 | mutations 66 | } 67 | -------------------------------------------------------------------------------- /examples/chat/api/mock-data.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | id: 'm_1', 4 | threadID: 't_1', 5 | threadName: 'Jing and Bill', 6 | authorName: 'Bill', 7 | text: 'Hey Jing, want to give a Flux talk at ForwardJS?', 8 | timestamp: Date.now() - 99999 9 | }, 10 | { 11 | id: 'm_2', 12 | threadID: 't_1', 13 | threadName: 'Jing and Bill', 14 | authorName: 'Bill', 15 | text: 'Seems like a pretty cool conference.', 16 | timestamp: Date.now() - 89999 17 | }, 18 | { 19 | id: 'm_3', 20 | threadID: 't_1', 21 | threadName: 'Jing and Bill', 22 | authorName: 'Jing', 23 | text: 'Sounds good. Will they be serving dessert?', 24 | timestamp: Date.now() - 79999 25 | }, 26 | { 27 | id: 'm_4', 28 | threadID: 't_2', 29 | threadName: 'Dave and Bill', 30 | authorName: 'Bill', 31 | text: 'Hey Dave, want to get a beer after the conference?', 32 | timestamp: Date.now() - 69999 33 | }, 34 | { 35 | id: 'm_5', 36 | threadID: 't_2', 37 | threadName: 'Dave and Bill', 38 | authorName: 'Dave', 39 | text: 'Totally! Meet you at the hotel bar.', 40 | timestamp: Date.now() - 59999 41 | }, 42 | { 43 | id: 'm_6', 44 | threadID: 't_3', 45 | threadName: 'Functional Heads', 46 | authorName: 'Bill', 47 | text: 'Hey Brian, are you going to be talking about functional stuff?', 48 | timestamp: Date.now() - 49999 49 | }, 50 | { 51 | id: 'm_7', 52 | threadID: 't_3', 53 | threadName: 'Bill and Brian', 54 | authorName: 'Brian', 55 | text: 'At ForwardJS? Yeah, of course. See you there!', 56 | timestamp: Date.now() - 39999 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /docs/en/forms.md: -------------------------------------------------------------------------------- 1 | # Form Handling 2 | 3 | When using Vuex in strict mode, it could be a bit tricky to use `v-model` on a piece of state that belongs to Vuex: 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | Assuming `obj` is a computed property that returns an Object from the store, the `v-model` here will attempt to directly mutate `obj.message` when the user types in the input. In strict mode, this will result in an error because the mutation is not performed inside an explicit Vuex mutation handler. 10 | 11 | The "Vuex way" to deal with it is binding the ``'s value and call an action on the `input` or `change` event: 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage (e) { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | And here's the mutation handler: 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### Two-way Computed Property 42 | 43 | Admittedly, the above is quite a bit more verbose than `v-model` + local state, and we lose some of the useful features from `v-model` as well. An alternative approach is using a two-way computed property with a setter: 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | // ... 50 | computed: { 51 | message: { 52 | get () { 53 | return this.$store.state.obj.message 54 | }, 55 | set (value) { 56 | this.$store.commit('updateMessage', value) 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the first item that pass the test 3 | * by second argument function 4 | * 5 | * @param {Array} list 6 | * @param {Function} f 7 | * @return {*} 8 | */ 9 | function find (list, f) { 10 | return list.filter(f)[0] 11 | } 12 | 13 | /** 14 | * Deep copy the given object considering circular structure. 15 | * This function caches all nested objects and its copies. 16 | * If it detects circular structure, use cached copy to avoid infinite loop. 17 | * 18 | * @param {*} obj 19 | * @param {Array} cache 20 | * @return {*} 21 | */ 22 | export function deepCopy (obj, cache = []) { 23 | // just return if obj is immutable value 24 | if (obj === null || typeof obj !== 'object') { 25 | return obj 26 | } 27 | 28 | // if obj is hit, it is in circular structure 29 | const hit = find(cache, c => c.original === obj) 30 | if (hit) { 31 | return hit.copy 32 | } 33 | 34 | const copy = Array.isArray(obj) ? [] : {} 35 | // put the copy into cache at first 36 | // because we want to refer it in recursive deepCopy 37 | cache.push({ 38 | original: obj, 39 | copy 40 | }) 41 | 42 | Object.keys(obj).forEach(key => { 43 | copy[key] = deepCopy(obj[key], cache) 44 | }) 45 | 46 | return copy 47 | } 48 | 49 | /** 50 | * forEach for object 51 | */ 52 | export function forEachValue (obj, fn) { 53 | Object.keys(obj).forEach(key => fn(obj[key], key)) 54 | } 55 | 56 | export function isObject (obj) { 57 | return obj !== null && typeof obj === 'object' 58 | } 59 | 60 | export function isPromise (val) { 61 | return val && typeof val.then === 'function' 62 | } 63 | 64 | export function assert (condition, msg) { 65 | if (!condition) throw new Error(`[vuex] ${msg}`) 66 | } 67 | -------------------------------------------------------------------------------- /examples/todomvc/components/Todo.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 69 | -------------------------------------------------------------------------------- /src/plugins/logger.js: -------------------------------------------------------------------------------- 1 | // Credits: borrowed code from fcomb/redux-logger 2 | 3 | import { deepCopy } from '../util' 4 | 5 | export default function createLogger ({ 6 | collapsed = true, 7 | transformer = state => state, 8 | mutationTransformer = mut => mut 9 | } = {}) { 10 | return store => { 11 | let prevState = deepCopy(store.state) 12 | 13 | store.subscribe((mutation, state) => { 14 | if (typeof console === 'undefined') { 15 | return 16 | } 17 | const nextState = deepCopy(state) 18 | const time = new Date() 19 | const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}` 20 | const formattedMutation = mutationTransformer(mutation) 21 | const message = `mutation ${mutation.type}${formattedTime}` 22 | const startMessage = collapsed 23 | ? console.groupCollapsed 24 | : console.group 25 | 26 | // render 27 | try { 28 | startMessage.call(console, message) 29 | } catch (e) { 30 | console.log(message) 31 | } 32 | 33 | console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState)) 34 | console.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation) 35 | console.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState)) 36 | 37 | try { 38 | console.groupEnd() 39 | } catch (e) { 40 | console.log('—— log end ——') 41 | } 42 | 43 | prevState = nextState 44 | }) 45 | } 46 | } 47 | 48 | function repeat (str, times) { 49 | return (new Array(times + 1)).join(str) 50 | } 51 | 52 | function pad (num, maxLength) { 53 | return repeat('0', maxLength - num.toString().length) + num 54 | } 55 | -------------------------------------------------------------------------------- /docs/fr/forms.md: -------------------------------------------------------------------------------- 1 | # Formulaires 2 | 3 | Lorsque l'on utilise Vuex en mode strict, il peut être compliqué d'utiliser `v-model` sur une partie du state qui appartient à Vuex : 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | Supposons que `obj` est une computed property qui retourne un Object depuis le store, le `v-model` tentera de muter directement `obj.message` lorsque l'utilisateur saisit du texte dans le champ. En mode strict, cela produira une erreur car la mutation n'est pas effectuée dans un handler de mutation Vuex explicite. 10 | 11 | La "façon Vuex" de gérer ça est de binder la valeur de l'`input` est d'appeler une action sur l'event `input` ou `change` : 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage (e) { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | Et voici le handler de mutation : 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### Computed property bi-directionnelle 42 | 43 | Admettons tout de même que l'exemple ci-dessus est plus verbeux que le `v-model` couplé au state local, et on perd quelques fonctionnalités pratiques de `v-model` au passage. Une approche alternative consiste à utiliser une computed property bi-directionnelle avec un setter : 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | // ... 50 | computed: { 51 | message: { 52 | get () { 53 | return this.$store.state.obj.message 54 | }, 55 | set (value) { 56 | this.$store.commit('updateMessage', value) 57 | } 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/zh-cn/getters.md: -------------------------------------------------------------------------------- 1 | 2 | # Getters 3 | 4 | 有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数: 5 | 6 | ``` js 7 | computed: { 8 | doneTodosCount () { 9 | return this.$store.state.todos.filter(todo => todo.done).length 10 | } 11 | } 12 | ``` 13 | 14 | 如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它 —— 无论哪种方式都不是很理想。 15 | 16 | Vuex 允许我们在 store 中定义『getters』(可以认为是 store 的计算属性)。Getters 接受 state 作为其第一个参数: 17 | 18 | ``` js 19 | const store = new Vuex.Store({ 20 | state: { 21 | todos: [ 22 | { id: 1, text: '...', done: true }, 23 | { id: 2, text: '...', done: false } 24 | ] 25 | }, 26 | getters: { 27 | doneTodos: state => { 28 | return state.todos.filter(todo => todo.done) 29 | } 30 | } 31 | }) 32 | ``` 33 | 34 | Getters 会暴露为 `store.getters` 对象: 35 | 36 | ``` js 37 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 38 | ``` 39 | 40 | Getters 也可以接受其他 getters 作为第二个参数: 41 | 42 | ``` js 43 | getters: { 44 | // ... 45 | doneTodosCount: (state, getters) => { 46 | return getters.doneTodos.length 47 | } 48 | } 49 | ``` 50 | 51 | ``` js 52 | store.getters.doneTodosCount // -> 1 53 | ``` 54 | 55 | 我们可以很容易地在任何组件中使用它: 56 | 57 | ``` js 58 | computed: { 59 | doneTodosCount () { 60 | return this.$store.getters.doneTodosCount 61 | } 62 | } 63 | ``` 64 | 65 | ### `mapGetters` 辅助函数 66 | 67 | `mapGetters` 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性: 68 | 69 | ``` js 70 | import { mapGetters } from 'vuex' 71 | 72 | export default { 73 | // ... 74 | computed: { 75 | // 使用对象展开运算符将 getters 混入 computed 对象中 76 | ...mapGetters([ 77 | 'doneTodosCount', 78 | 'anotherGetter', 79 | // ... 80 | ]) 81 | } 82 | } 83 | ``` 84 | 85 | 如果你想将一个 getter 属性另取一个名字,使用对象形式: 86 | 87 | ``` js 88 | mapGetters({ 89 | // 映射 this.doneCount 为 store.getters.doneTodosCount 90 | doneCount: 'doneTodosCount' 91 | }) 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/ru/forms.md: -------------------------------------------------------------------------------- 1 | # Обработка форм 2 | 3 | Если вы используете Vuex в strict mode, привязать `v-model` к состоянию, хранимому во Vue, может быть немного непросто: 4 | 5 | ``` html 6 | 7 | ``` 8 | 9 | Предположим, что `obj` — это вычисляемое свойство, возвращающее ссылку на объект из хранилища. В таком случае, `v-model` будет пытаться напрямую изменить значение `obj.message` в ответ на действия пользователя. В strict mode это спровоцирует ошибку, поскольку это изменение происходит вне обработчика мутации Vuex. 10 | 11 | Чтобы подружить Vuex с такой ситуацией, следует однонаправленно связать атрибут `value` элемента `` с полем объекта, а для учёта изменений использовать событие `change`: 12 | 13 | ``` html 14 | 15 | ``` 16 | ``` js 17 | // ... 18 | computed: { 19 | ...mapState({ 20 | message: state => state.obj.message 21 | }) 22 | }, 23 | methods: { 24 | updateMessage (e) { 25 | this.$store.commit('updateMessage', e.target.value) 26 | } 27 | } 28 | ``` 29 | 30 | А вот и обработчик мутаций: 31 | 32 | ``` js 33 | // ... 34 | mutations: { 35 | updateMessage (state, message) { 36 | state.obj.message = message 37 | } 38 | } 39 | ``` 40 | 41 | ### Двухсторонние вычисляемые свойства 42 | 43 | Заметно, что получившаяся выше запись — куда многословнее, чем используемая в связке `v-model` с локальным состоянием, да и некоторые полезные возможности `v-model` мы таким образом упускаем. В качестве альтернативы можно предложить использование двунаправленного вычисляемого свойства с сеттером: 44 | 45 | ``` html 46 | 47 | ``` 48 | ``` js 49 | // ... 50 | computed: { 51 | message: { 52 | get () { 53 | return this.$store.state.obj.message 54 | }, 55 | set (value) { 56 | this.$store.commit('updateMessage', value) 57 | } 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /types/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("vue"); 2 | 3 | type Dictionary = { [key: string]: T }; 4 | 5 | export function mapState (map: string[]): Dictionary<() => any>; 6 | export function mapState (namespace: string, map: string[]): Dictionary<() => any>; 7 | export function mapState (map: Dictionary): Dictionary<() => any>; 8 | export function mapState (namespace: string, map: Dictionary): Dictionary<() => any>; 9 | export function mapState ( 10 | map: Dictionary<(this: typeof Vue, state: S, getters: any) => any> 11 | ): Dictionary<() => any>; 12 | export function mapState ( 13 | namespace: string, 14 | map: Dictionary<(this: typeof Vue, state: S, getters: any) => any> 15 | ): Dictionary<() => any>; 16 | 17 | type MutationMethod = (...args: any[]) => void; 18 | export function mapMutations (map: string[]): Dictionary; 19 | export function mapMutations (namespace: string, map: string[]): Dictionary; 20 | export function mapMutations (map: Dictionary): Dictionary; 21 | export function mapMutations (namespace: string, map: Dictionary): Dictionary; 22 | 23 | export function mapGetters (map: string[]): Dictionary<() => any>; 24 | export function mapGetters (namespace: string, map: string[]): Dictionary<() => any>; 25 | export function mapGetters (map: Dictionary): Dictionary<() => any>; 26 | export function mapGetters (namespace: string, map: Dictionary): Dictionary<() => any>; 27 | 28 | type ActionMethod = (...args: any[]) => Promise; 29 | export function mapActions (map: string[]): Dictionary; 30 | export function mapActions (namespace: string, map: string[]): Dictionary; 31 | export function mapActions (map: Dictionary): Dictionary; 32 | export function mapActions (namespace: string, map: Dictionary): Dictionary; 33 | -------------------------------------------------------------------------------- /examples/chat/store/mutations.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import * as types from './mutation-types' 3 | 4 | export default { 5 | [types.RECEIVE_ALL] (state, { messages }) { 6 | let latestMessage 7 | messages.forEach(message => { 8 | // create new thread if the thread doesn't exist 9 | if (!state.threads[message.threadID]) { 10 | createThread(state, message.threadID, message.threadName) 11 | } 12 | // mark the latest message 13 | if (!latestMessage || message.timestamp > latestMessage.timestamp) { 14 | latestMessage = message 15 | } 16 | // add message 17 | addMessage(state, message) 18 | }) 19 | // set initial thread to the one with the latest message 20 | setCurrentThread(state, latestMessage.threadID) 21 | }, 22 | 23 | [types.RECEIVE_MESSAGE] (state, { message }) { 24 | addMessage(state, message) 25 | }, 26 | 27 | [types.SWITCH_THREAD] (state, { id }) { 28 | setCurrentThread(state, id) 29 | } 30 | } 31 | 32 | function createThread (state, id, name) { 33 | Vue.set(state.threads, id, { 34 | id, 35 | name, 36 | messages: [], 37 | lastMessage: null 38 | }) 39 | } 40 | 41 | function addMessage (state, message) { 42 | // add a `isRead` field before adding the message 43 | message.isRead = message.threadID === state.currentThreadID 44 | // add it to the thread it belongs to 45 | const thread = state.threads[message.threadID] 46 | if (!thread.messages.some(id => id === message.id)) { 47 | thread.messages.push(message.id) 48 | thread.lastMessage = message 49 | } 50 | // add it to the messages map 51 | Vue.set(state.messages, message.id, message) 52 | } 53 | 54 | function setCurrentThread (state, id) { 55 | state.currentThreadID = id 56 | if (!state.threads[id]) { 57 | debugger 58 | } 59 | // mark thread as read 60 | state.threads[id].lastMessage.isRead = true 61 | } 62 | -------------------------------------------------------------------------------- /docs/kr/getters.md: -------------------------------------------------------------------------------- 1 | # Getters 2 | 3 | 때로는 저장소 상태를 기반하는 상태를 계산해야 할 수도 있습니다.(예: 아이템 리스트를 필터링하고 계산) 4 | 5 | ``` js 6 | computed: { 7 | doneTodosCount () { 8 | return this.$store.state.todos.filter(todo => todo.done).length 9 | } 10 | } 11 | ``` 12 | 13 | 둘 이상의 컴포넌트가 이를 사용 해야하는 경우 함수를 복제하거나 공유된 헬퍼를 추출하여 여러 위치에서 가져와야합니다. 둘 다 이상적이지 않습니다. 14 | 15 | Vuex를 사용하면 저장소에서 "getters"를 정의 할 수 있습니다(저장소의 계산된 속성으로 생각됩니다). Getters는 첫 번째 전달인자로 상태를 받습니다. 16 | 17 | ``` js 18 | const store = new Vuex.Store({ 19 | state: { 20 | todos: [ 21 | { id: 1, text: '...', done: true }, 22 | { id: 2, text: '...', done: false } 23 | ] 24 | }, 25 | getters: { 26 | doneTodos: state => { 27 | return state.todos.filter(todo => todo.done) 28 | } 29 | } 30 | }) 31 | ``` 32 | 33 | getters는 `store.getters` 객체에 노출 됩니다. 34 | 35 | ``` js 36 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 37 | ``` 38 | 39 | Getters는 두 번째 전달인자로 다른 getter도 받게됩니다. 40 | 41 | ``` js 42 | getters: { 43 | // ... 44 | doneTodosCount: (state, getters) => { 45 | return getters.doneTodos.length 46 | } 47 | } 48 | ``` 49 | 50 | ``` js 51 | store.getters.doneTodosCount // -> 1 52 | ``` 53 | 54 | 이제 모든 컴포넌트에서 쉽게 사용할 수 있습니다. 55 | 56 | ``` js 57 | computed: { 58 | doneTodosCount () { 59 | return this.$store.getters.doneTodosCount 60 | } 61 | } 62 | ``` 63 | 64 | ### `mapGetters` 헬퍼 65 | 66 | `mapGetters` 헬퍼는 저장소 getter를 로컬 계산된 속성에 매핑합니다. 67 | 68 | ``` js 69 | import { mapGetters } from 'vuex' 70 | 71 | export default { 72 | // ... 73 | computed: { 74 | // getter를 객체 전파 연산자로 계산하여 추가합니다. 75 | ...mapGetters([ 76 | 'doneTodosCount', 77 | 'anotherGetter', 78 | // ... 79 | ]) 80 | } 81 | } 82 | ``` 83 | 84 | getter를 다른 이름으로 매핑하려면 객체를 사용합니다. 85 | 86 | ``` js 87 | ...mapGetters({ 88 | // this.doneCount를 store.getters.doneTodosCount에 매핑하십시오. 89 | doneCount: 'doneTodosCount' 90 | }) 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/zh-cn/intro.md: -------------------------------------------------------------------------------- 1 | # Vuex 是什么? 2 | 3 | Vuex 是一个专为 Vue.js 应用程序开发的**状态管理模式**。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 [devtools extension](https://github.com/vuejs/vue-devtools),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。 4 | 5 | ### 什么是“状态管理模式”? 6 | 7 | 让我们从一个简单的 Vue 计数应用开始: 8 | 9 | ``` js 10 | new Vue({ 11 | // state 12 | data () { 13 | return { 14 | count: 0 15 | } 16 | }, 17 | // view 18 | template: ` 19 |
{{ count }}
20 | `, 21 | // actions 22 | methods: { 23 | increment () { 24 | this.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | 这个状态自管理应用包含以下几个部分: 31 | 32 | - **state**,驱动应用的数据源; 33 | - **view**,以声明方式将**state**映射到视图; 34 | - **actions**,响应在**view**上的用户输入导致的状态变化。 35 | 36 | 以下是一个表示“单向数据流”理念的极简示意: 37 | 38 |

39 | 40 |

41 | 42 | 但是,当我们的应用遇到**多个组件共享状态**时,单向数据流的简洁性很容易被破坏: 43 | 44 | - 多个视图依赖于同一状态。 45 | - 来自不同视图的行为需要变更同一状态。 46 | 47 | 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。 48 | 49 | 因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为! 50 | 51 | 另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。 52 | 53 | 这就是 Vuex 背后的基本思想,借鉴了 [Flux](https://facebook.github.io/flux/docs/overview.html)、[Redux](http://redux.js.org/)、和 [The Elm Architecture](https://guide.elm-lang.org/architecture/)。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。 54 | 55 | ![vuex](./images/vuex.png) 56 | 57 | ### 什么情况下我应该使用 Vuex? 58 | 59 | 虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。 60 | 61 | 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 [global event bus](http://vuejs.org/guide/components.html#Non-Parent-Child-Communication) 就足够您所需了。但是,如果您需要构建是一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是: 62 | 63 | > Flux 架构就像眼镜:您自会知道什么时候需要它。 64 | -------------------------------------------------------------------------------- /src/module/module-collection.js: -------------------------------------------------------------------------------- 1 | import Module from './module' 2 | import { forEachValue } from '../util' 3 | 4 | export default class ModuleCollection { 5 | constructor (rawRootModule) { 6 | // register root module (Vuex.Store options) 7 | this.root = new Module(rawRootModule, false) 8 | 9 | // register all nested modules 10 | if (rawRootModule.modules) { 11 | forEachValue(rawRootModule.modules, (rawModule, key) => { 12 | this.register([key], rawModule, false) 13 | }) 14 | } 15 | } 16 | 17 | get (path) { 18 | return path.reduce((module, key) => { 19 | return module.getChild(key) 20 | }, this.root) 21 | } 22 | 23 | getNamespace (path) { 24 | let module = this.root 25 | return path.reduce((namespace, key) => { 26 | module = module.getChild(key) 27 | return namespace + (module.namespaced ? key + '/' : '') 28 | }, '') 29 | } 30 | 31 | update (rawRootModule) { 32 | update(this.root, rawRootModule) 33 | } 34 | 35 | register (path, rawModule, runtime = true) { 36 | const parent = this.get(path.slice(0, -1)) 37 | const newModule = new Module(rawModule, runtime) 38 | parent.addChild(path[path.length - 1], newModule) 39 | 40 | // register nested modules 41 | if (rawModule.modules) { 42 | forEachValue(rawModule.modules, (rawChildModule, key) => { 43 | this.register(path.concat(key), rawChildModule, runtime) 44 | }) 45 | } 46 | } 47 | 48 | unregister (path) { 49 | const parent = this.get(path.slice(0, -1)) 50 | const key = path[path.length - 1] 51 | if (!parent.getChild(key).runtime) return 52 | 53 | parent.removeChild(key) 54 | } 55 | } 56 | 57 | function update (targetModule, newModule) { 58 | // update target module 59 | targetModule.update(newModule) 60 | 61 | // update nested modules 62 | if (newModule.modules) { 63 | for (const key in newModule.modules) { 64 | if (!targetModule.getChild(key)) { 65 | console.warn( 66 | `[vuex] trying to add a new module '${key}' on hot reloading, ` + 67 | 'manual reload is needed' 68 | ) 69 | return 70 | } 71 | update(targetModule.getChild(key), newModule.modules[key]) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /docs/kr/intro.md: -------------------------------------------------------------------------------- 1 | # Vuex가 무엇인가요? 2 | 3 | Vuex는 Vue.js 애플리케이션에 대한 **상태 관리 패턴 + 라이브러리** 입니다. 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경할 수 있습니다. 또한 Vue의 공식 [devtools 확장 프로그램](https://github.com/vuejs/vue-devtools)과 통합되어 설정 시간이 필요 없는 디버깅 및 상 태 스냅 샷 내보내기/가져오기와 같은 고급 기능을 제공합니다. 4 | 5 | ### "상태 관리 패턴"이란 무엇인가요? 6 | 7 | 간단한 Vue 카운터 앱부터 시작 해보겠습니다. 8 | 9 | ``` js 10 | new Vue({ 11 | // 상태 12 | data () { 13 | return { 14 | count: 0 15 | } 16 | }, 17 | // 뷰 18 | template: ` 19 |
{{ count }}
20 | `, 21 | // 액션 22 | methods: { 23 | increment () { 24 | this.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | 다음과 같은 기능을 가진 앱입니다. 31 | 32 | - **상태** 는 앱을 작동하는 원본 소스 입니다. 33 | - **뷰** 는 **상태의** 선언적 매핑입니다. 34 | - **액션** 은 **뷰** 에서 사용자 입력에 대해 반응적으로 상태를 바꾸는 방법입니다. 35 | 36 | 이것은 "단방향 데이터 흐름" 개념의 매우 단순한 도표입니다. 37 | 38 |

39 | 40 |

41 | 42 | 그러나 **공통의 상태를 공유하는 여러 컴포넌트** 가 있는 경우 단순함이 빠르게 저하됩니다. 43 | 44 | - 여러 뷰는 같은 상태에 의존합니다. 45 | - 서로 다른 뷰의 작업은 동일한 상태를 반영해야 할 수 있습니다. 46 | 47 | 첫번째 문제의 경우, 지나치게 중첩된 컴포넌트는 통과하는 prop는 장황할 수 있으며 형제 컴포넌트에서는 작동하지 않습니다. 두번째 문제의 경우 직접 부모/자식 인스턴스를 참조하거나 이벤트를 통해 상태의 여러 복사본을 변경 및 동기화 하려는 등의 해결 방법을 사용해야 합니다. 이러한 패턴은 모두 부서지기 쉽고 유지보수가 불가능한 코드로 빠르게 변경됩니다. 48 | 49 | 그렇다면 컴포넌트에서 공유된 상태를 추출하고 이를 전역 싱글톤으로 관리해야 합니다. 이를 통해 우리의 컴포넌트 트리는 커다란 "뷰"가 되며 모든 컴포넌트는 트리에 상관없이 상태에 액세스하거나 동작을 트리거 할 수 있습니다! 50 | 51 | 또한 상태 관리 및 특정 규칙 적용과 관련된 개념을 정의하고 분리함으로써 코드의 구조와 유지 관리 기능을 향상시킵니다. 52 | 53 | 이는 [Flux](https://facebook.github.io/flux/docs/overview.html), [Redux](http://redux.js.org/), [The Elm Architecture](https://guide.elm-lang.org/architecture/)에서 영감을 받은 Vuex의 기본 아이디어 입니다. 다른 패턴과 달리 Vuex는 Vue.js가 효율적인 업데이트를 위해 세분화된 반응 시스템을 활용하도록 특별히 고안된 라이브러리입니다. 54 | 55 | ![vuex](./images/vuex.png) 56 | 57 | ### 언제 사용해야 하나요? 58 | 59 | Vuex는 공유된 상태 관리를 처리하는 데 유용하지만, 개념에 대한 이해와 시작하는 비용도 함께 듭니다. 그것은 단기간과 장기간 생산성 간의 기회비용이 있습니다. 60 | 61 | 대규모 SPA를 구축하지 않고 Vuex로 바로 뛰어 들었다면, 시간이 오래 걸리고 힘든일일 것입니다. 이것은 일반 적인 일입니다. 앱이 단순하다면 Vuex없이는 괜찮을 것입니다. 간단한 [글로벌 이벤트 버스](https://kr.vuejs.org/v2/guide/components.html#비-부모-자식간-통신)만 있으면됩니다. 그러나 중대형 규모의 SPA를 구축하는 경우 Vue컴포넌트 외부의 상태를 보다 잘 처리할 수 있는 방법을 생각하게 될 가능성이 있으며 Vuex는 자연스럽게 선택할 수 있는 단계가 될 것입니다. Redux의 저자인 Dan Abramov의 좋은 인용이 있습니다. 62 | 63 | > Flux 라이브러리는 안경과 같습니다. 필요할 때 알아볼 수 있습니다. 64 | -------------------------------------------------------------------------------- /docs/en/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | At the center of every Vuex application is the **store**. A "store" is basically a container that holds your application **state**. There are two things that make a Vuex store different from a plain global object: 4 | 5 | 1. Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state changes. 6 | 7 | 2. You cannot directly mutate the store's state. The only way to change a store's state is by explicitly **committing mutations**. This ensures every state change leaves a track-able record, and enables tooling that helps us better understand our applications. 8 | 9 | ### The Simplest Store 10 | 11 | > **NOTE:** We will be using ES2015 syntax for code examples for the rest of the docs. If you haven't picked it up, [you should](https://babeljs.io/docs/learn-es2015/)! 12 | 13 | After [installing](installation.md) Vuex, let's create a store. It is pretty straightforward - just provide an initial state object, and some mutations: 14 | 15 | ``` js 16 | // Make sure to call Vue.use(Vuex) first if using a module system 17 | 18 | const store = new Vuex.Store({ 19 | state: { 20 | count: 0 21 | }, 22 | mutations: { 23 | increment (state) { 24 | state.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | Now, you can access the state object as `store.state`, and trigger a state change with the `store.commit` method: 31 | 32 | ``` js 33 | store.commit('increment') 34 | 35 | console.log(store.state.count) // -> 1 36 | ``` 37 | 38 | Again, the reason we are committing a mutation instead of changing `store.state.count` directly, is because we want to explicitly track it. This simple convention makes your intention more explicit, so that you can reason about state changes in your app better when reading the code. In addition, this gives us the opportunity to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging. 39 | 40 | Using store state in a component simply involves returning the state within a computed property, because the store state is reactive. Triggering changes simply means committing mutations in component methods. 41 | 42 | Here's an example of the [most basic Vuex counter app](https://jsfiddle.net/n9jmu5v7/341/). 43 | 44 | Next, we will discuss each core concept in much finer details, starting with [State](state.md). 45 | -------------------------------------------------------------------------------- /docs/ja/getters.md: -------------------------------------------------------------------------------- 1 | # ゲッター 2 | 3 | 例えば項目のリストをフィルタリングしたりカウントするときのように、ストアの状態を算出したいときがあります。 4 | 5 | ``` js 6 | computed: { 7 | doneTodosCount () { 8 | return this.$store.state.todos.filter(todo => todo.done).length 9 | } 10 | } 11 | ``` 12 | 13 | もしこの関数を複数のコンポーネントで利用したくなったら、関数をコピーするか、あるいは関数を共用のヘルパーに切り出して複数の場所でインポートする必要があります。しかし、どちらも理想的とはいえません。 14 | 15 | Vuex を利用するとストア内に "ゲッター" を定義することができます(ストアのための算出プロパティだと考えてください)。ゲッターはステート(状態)を第1引数として受け取ります: 16 | 17 | ``` js 18 | const store = new Vuex.Store({ 19 | state: { 20 | todos: [ 21 | { id: 1, text: '...', done: true }, 22 | { id: 2, text: '...', done: false } 23 | ] 24 | }, 25 | getters: { 26 | doneTodos: state => { 27 | return state.todos.filter(todo => todo.done) 28 | } 29 | } 30 | }) 31 | ``` 32 | 33 | ゲッターは `store.getters` オブジェクトから取り出されます: 34 | 35 | ``` js 36 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 37 | ``` 38 | 39 | ゲッターは第2引数として他のゲッターを受け取ります: 40 | 41 | ``` js 42 | getters: { 43 | // ... 44 | doneTodosCount: (state, getters) => { 45 | return getters.doneTodos.length 46 | } 47 | } 48 | ``` 49 | 50 | ``` js 51 | store.getters.doneTodosCount // -> 1 52 | ``` 53 | 54 | どのコンポーネントの内部でも簡単にゲッターを利用することができます: 55 | 56 | ``` js 57 | computed: { 58 | doneTodosCount () { 59 | return this.$store.getters.doneTodosCount 60 | } 61 | } 62 | ``` 63 | 64 | 関数を返り値にすることで、ゲッターに引数を渡すこともできます。これは特にストアの中の配列を検索する時に役立ちます: 65 | ```js 66 | getters: { 67 | // ... 68 | getTodoById: (state, getters) => (id) => { 69 | return state.todos.find(todo => todo.id === id) 70 | } 71 | } 72 | ``` 73 | 74 | ``` js 75 | store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } 76 | ``` 77 | 78 | 79 | ### `mapGetters` ヘルパー 80 | 81 | `mapGetters` ヘルパーはストアのゲッターをローカルの算出プロパティにマッピングさせます: 82 | 83 | ``` js 84 | import { mapGetters } from 'vuex' 85 | 86 | export default { 87 | // ... 88 | computed: { 89 | // ゲッターを、スプレッド演算子(object spread operator)を使って computed に組み込む 90 | ...mapGetters([ 91 | 'doneTodosCount', 92 | 'anotherGetter', 93 | // ... 94 | ]) 95 | } 96 | } 97 | ``` 98 | 99 | ゲッターを異なる名前でマッピングさせたいときはオブジェクトを使います: 100 | 101 | ``` js 102 | ...mapGetters({ 103 | // this.doneCount を store.getters.doneTodosCount にマッピングさせる 104 | doneCount: 'doneTodosCount' 105 | }) 106 | ``` 107 | -------------------------------------------------------------------------------- /examples/chat/css/chat.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | .chatapp { 14 | font-family: 'Muli', 'Helvetica Neue', helvetica, arial; 15 | max-width: 760px; 16 | margin: 20px auto; 17 | overflow: hidden; 18 | } 19 | 20 | .message-list, .thread-list { 21 | border: 1px solid #ccf; 22 | font-size: 16px; 23 | height: 400px; 24 | margin: 0; 25 | overflow-y: auto; 26 | padding: 0; 27 | } 28 | 29 | .message-section { 30 | float: right; 31 | width: 65%; 32 | } 33 | 34 | .thread-section { 35 | float: left; 36 | width: 32.5%; 37 | } 38 | 39 | .message-thread-heading, 40 | .thread-count { 41 | height: 40px; 42 | margin: 0; 43 | } 44 | 45 | .message-list-item, .thread-list-item { 46 | list-style: none; 47 | padding: 12px 14px 14px; 48 | } 49 | 50 | .thread-list-item { 51 | border-bottom: 1px solid #ccc; 52 | cursor: pointer; 53 | } 54 | 55 | .thread-list:hover .thread-list-item:hover { 56 | background-color: #f8f8ff; 57 | } 58 | 59 | .thread-list:hover .thread-list-item { 60 | background-color: #fff; 61 | } 62 | 63 | .thread-list-item.active, 64 | .thread-list:hover .thread-list-item.active, 65 | .thread-list:hover .thread-list-item.active:hover { 66 | background-color: #efefff; 67 | cursor: default; 68 | } 69 | 70 | .message-author-name, 71 | .thread-name { 72 | color: #66c; 73 | float: left; 74 | font-size: 13px; 75 | margin: 0; 76 | } 77 | 78 | .message-time, .thread-time { 79 | color: #aad; 80 | float: right; 81 | font-size: 12px; 82 | } 83 | 84 | .message-text, .thread-last-message { 85 | clear: both; 86 | font-size: 14px; 87 | padding-top: 10px; 88 | } 89 | 90 | .message-composer { 91 | box-sizing: border-box; 92 | font-family: inherit; 93 | font-size: 14px; 94 | height: 5em; 95 | width: 100%; 96 | margin: 20px 0 0; 97 | padding: 10px; 98 | } 99 | -------------------------------------------------------------------------------- /docs/fr/getters.md: -------------------------------------------------------------------------------- 1 | 2 | # Getters 3 | 4 | Parfois nous avons besoin de calculer des valeurs basées sur le state du store, par exemple pour filtrer une liste d'éléments et les compter : 5 | 6 | ``` js 7 | computed: { 8 | doneTodosCount () { 9 | return this.$store.state.todos.filter(todo => todo.done).length 10 | } 11 | } 12 | ``` 13 | 14 | Si plus d'un composant a besoin d'utiliser cela, il nous faut ou bien dupliquer cette fonction, ou bien l'extraire dans un helper séparé et l'importer aux endroits nécessaires — les deux idées sont loin d'être idéales. 15 | 16 | Vuex nous permet de définir des "getters" dans le store (voyez-les comme les computed properties des store). Les getters prennent le state en premier argument : 17 | 18 | ``` js 19 | const store = new Vuex.Store({ 20 | state: { 21 | todos: [ 22 | { id: 1, text: '...', done: true }, 23 | { id: 2, text: '...', done: false } 24 | ] 25 | }, 26 | getters: { 27 | doneTodos: state => { 28 | return state.todos.filter(todo => todo.done) 29 | } 30 | } 31 | }) 32 | ``` 33 | 34 | Les getters seront exposé sur l'objet `store.getters` : 35 | 36 | ``` js 37 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 38 | ``` 39 | 40 | Les getters recevront également les autres getters en second argument : 41 | 42 | ``` js 43 | getters: { 44 | // ... 45 | doneTodosCount: (state, getters) => { 46 | return getters.doneTodos.length 47 | } 48 | } 49 | ``` 50 | 51 | ``` js 52 | store.getters.doneTodosCount // -> 1 53 | ``` 54 | 55 | Nous pouvons maintenant facilement les utiliser dans n'importe quel composant : 56 | 57 | ``` js 58 | computed: { 59 | doneTodosCount () { 60 | return this.$store.getters.doneTodosCount 61 | } 62 | } 63 | ``` 64 | 65 | ### Le helper `mapGetters` 66 | 67 | Le helper `mapGetters` attache simplement vos getters du store aux computed properties locales : 68 | 69 | ``` js 70 | import { mapGetters } from 'vuex' 71 | 72 | export default { 73 | // ... 74 | computed: { 75 | // rajouter les getters dans computed avec l'object spread operator 76 | ...mapGetters([ 77 | 'doneTodosCount', 78 | 'anotherGetter', 79 | // ... 80 | ]) 81 | } 82 | } 83 | ``` 84 | 85 | Si vous voulez attacher un getter avec un nom différent, utilisez un objet : 86 | 87 | ``` js 88 | mapGetters({ 89 | // attacher this.doneCount à store.getters.doneTodosCount 90 | doneCount: 'doneTodosCount' 91 | }) 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/ja/intro.md: -------------------------------------------------------------------------------- 1 | # Vuex とは何か? 2 | 3 | Vuex は Vue.js アプリケーションのための **状態管理パターン + ライブラリ**です。 4 | これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。 5 | また Vue 公式の[開発ツール拡張](https://github.com/vuejs/vue-devtools)と連携し、設定なしでタイムトラベルデバッグやステートのスナップショットのエクスポートやインポートのような高度な機能を提供します。 6 | 7 | ### "状態管理パターン"とはなんですか? 8 | 9 | 単純な Vue で作られたカウンターアプリをみてみましょう: 10 | 11 | ``` js 12 | new Vue({ 13 | // state 14 | data () { 15 | return { 16 | count: 0 17 | } 18 | }, 19 | // view 20 | template: ` 21 |
{{ count }}
22 | `, 23 | // actions 24 | methods: { 25 | increment () { 26 | this.count++ 27 | } 28 | } 29 | }) 30 | ``` 31 | 32 | これはいくつかの要素をアプリ自身に含んでいます: 33 | 34 | - **状態**、これは私達のアプリを動かす信頼できる情報源(the source of truth)です。 35 | - **ビュー**、これは**状態**のただの宣言的なマッピングです。 36 | - **アクション**、これは**ビュー**からのユーザー入力に反応して、状態の変更を可能にする方法です。 37 | 38 | これらは"単方向データフロー"のコンセプトの極めてシンプルな責務です: 39 | 40 |

41 | 42 |

43 | 44 | しかし、単純さは、**共通の状態を共有する複数のコンポーネントを持ったときに**、すぐに破綻します: 45 | 46 | - 複数のビューが同じ状態に依存することがあります。 47 | - 異なるビューからのアクションで、同じ状態を変更する必要があります。 48 | 49 | 一つ目は、プロパティ (props) として深く入れ子になったコンポーネントに渡すのは面倒で、兄弟コンポーネントでは単純に機能しません。二つ目は、親子のインスタンスを直接参照したり、イベントを介して複数の状態のコピーを変更、同期することを試みるソリューションに頼っていることがよくあります。これらのパターンは、いずれも脆く、すぐにメンテナンスが困難なコードに繋がります。 50 | 51 | では、コンポーネントから共有している状態を抽出し、それをグローバルシングルトンで管理するのはどうでしょうか? これにより、コンポーネントツリーは大きな "ビュー" となり、どのコンポーネントもツリー内のどこにあっても状態にアクセスしたり、アクションをトリガーできます! 52 | 53 | さらに、状態管理に関わる概念を定義、分離し、特定のルールを敷くことで、コードの構造と保守性を向上させることができます。 54 | 55 | これが Vuex の背景にある基本的なアイディアであり、[Flux](https://facebook.github.io/flux/docs/overview.html)、 [Redux](http://redux.js.org/) そして [The Elm Architecture](https://guide.elm-lang.org/architecture/)から影響を受けています。 56 | 他のパターンと異なるのは、Vuex は効率的な更新のために、Vue.js の粒度の細かいリアクティビティシステムを利用するよう特別に調整して実装されたライブラリだということです。 57 | 58 | ![vuex](./images/vuex.png) 59 | 60 | ### いつ、Vuexを使うべきでしょうか? 61 | 62 | Vuex は、共有状態の管理に役立ちますが、さらに概念やボイラープレートのコストがかかります。これは、短期的生産性と長期的生産性のトレードオフです。 63 | 64 | もし、あなたが大規模な SPA を構築することなく、Vuex を導入した場合、冗長で恐ろしいと感じるかもしれません。そう感じることは全く普通です。あなたのアプリがシンプルであれば、Vuex なしで問題ないでしょう。単純な [グローバルイベントバス](http://jp.vuejs.org/v2/guide/components.html#%E8%A6%AA%E5%AD%90%E9%96%93%E4%BB%A5%E5%A4%96%E3%81%AE%E9%80%9A%E4%BF%A1) が必要なだけかもしれません。しかし、中規模から大規模の SPA を構築する場合は、Vue コンポーネントの外の状態をどうやってうまく扱うか考える絶好の機会です。Vuex は自然な次のステップとなるでしょう。これは Redux の作者、Dan Abramov からの良い引用です: 65 | 66 | > Flux ライブラリは眼鏡のようなものです: それらが必要になったときに知るのです。 67 | -------------------------------------------------------------------------------- /docs/ru/getting-started.md: -------------------------------------------------------------------------------- 1 | # Начало работы 2 | 3 | В центре любого Vuex-приложения находится **хранилище**. "Хранилище" — это, упрощённо говоря, контейнер, который хранит **состояние** вашего приложения. Два момента отличают хранилище Vuex от простого глобального объекта: 4 | 5 | 1. Хранилища Vuex реактивны. Если компоненты Vue зависят от их состояния, изменение состояния хранилища спровоцирует соответствующие изменения компонентов. 6 | 7 | 2. Непосредственное изменение состояния хранилища запрещено. Единственный способ внести изменения — явно **вызвать мутацию**. Этот подход позволяет быть уверенным, что каждое изменение оставляет в системе след, и даёт возможность использовать инструменты, позволяющие лучше понять работу приложения. 8 | 9 | ### Простейшее Хранилище 10 | 11 | > **ЗАМЕЧАНИЕ:** Мы будем использовать синтаксис ES2015 для примеров кода на всём протяжении этой документации. Если вы с ним ещё не разобрались, [сейчас самое время](https://babeljs.io/docs/learn-es2015/)! 12 | 13 | После [установки](installation.md) Vuex, давайте создадим хранилище. Всё довольно просто: нужно лишь указать исходное состояние и мутации: 14 | 15 | ``` js 16 | // Удостоверьтесь, что вызвали Vue.use(Vuex) в коде до этого, если используете модульный сборщик 17 | 18 | const store = new Vuex.Store({ 19 | state: { 20 | count: 0 21 | }, 22 | mutations: { 23 | increment (state) { 24 | state.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | Теперь мы можем получить доступ к объекту состояния `store.state`, или вызвать изменение состояния методом `store.commit`: 31 | 32 | ``` js 33 | store.commit('increment') 34 | 35 | console.log(store.state.count) // -> 1 36 | ``` 37 | 38 | Ещё раз заметим: мы вызываем мутацию, вместо того чтобы напрямую изменить `store.state.count`, потому что мы хотим явным образом отслеживать изменения. Это простое архитектурное соглашение делает намерения более очевидными и упрощает понимание изменений состояния приложения при чтении кода. Кроме того, этот подход позволяет реализовать инструменты для логирования каждой мутации, создания моментальных слепков состояния приложения и даже применения "машины времени" при отладке. 39 | 40 | Поскольку хранилище реактивно, для использования его состояния в компонентах достаточно просто создать вычисляемые свойства. Изменения состояния можно вызывать, инициализируя мутации в методах компонентов. 41 | 42 | Вот пример [простейшего приложения Vuex, реализующего счётчик](https://jsfiddle.net/n9jmu5v7/341/). 43 | 44 | Далее мы более подробно обсудим каждую из основных концепций, начиная с [Состояния](state.md) 45 | -------------------------------------------------------------------------------- /docs/fr/getting-started.md: -------------------------------------------------------------------------------- 1 | # Débuter 2 | 3 | Au cœur de chaque application Vuex, il y a le **store**. Un "store" est tout simplement un conteneur avec le **state** de votre application. Il y a deux choses qui différencient un store Vuex d'un simple objet global : 4 | 5 | 1. Les stores Vuex sont réactifs. Quand les composants Vue y récupèrent le state, ils modifieront efficacement et de façon réactive si le state du store change. 6 | 7 | 2. Vous ne pouvez pas muter directement le state du store. La seule façon de modifier le state d'un store est de **commiter** explicitement des **mutations**. Cela assure que chaque état laisse un enregistrement traçable, et permette à des outils de mieux nous aider à comprendre nos applications. 8 | 9 | ### Le store le plus simple 10 | 11 | > **NOTE:** Nous allons utiliser la syntaxe ES2015 dans les exemples de code pour le reste de la documentation. Si vous ne vous êtes pas encore penché dessus, [vous devriez](https://babeljs.io/docs/learn-es2015/) ! 12 | 13 | Après [avoir installé](installation.md) Vuex, nous allons créer un store. C'est assez simple — définissez juste un objet state initial et quelques mutations : 14 | 15 | ``` js 16 | // Make sure to call Vue.use(Vuex) first if using a module system 17 | 18 | const store = new Vuex.Store({ 19 | state: { 20 | count: 0 21 | }, 22 | mutations: { 23 | increment (state) { 24 | state.count++ 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | Maintenant, vous pouvez accéder à l'objet state avec `store.state`, et déclencher un changement de state avec la méthode `store.commit` : 31 | 32 | ``` js 33 | store.commit('increment') 34 | 35 | console.log(store.state.count) // -> 1 36 | ``` 37 | 38 | Encore une fois, la raison pour laquelle nous committons une mutation au lieu de modifier `store.state.count` directement, c'est parce que nous voulons le tracer explicitement. Cette simple convention rend votre intention plus explicite, ainsi vous pouvez raisonner plus facilement les changements de state en lisant votre code. De plus, cela nous donne l'opportunité d'implémenter des outils qui peuvent enregistrer chaque mutation, prendre des instantanés du state, ou même procéder à du debugging dans le temps. 39 | 40 | Utiliser le state du store dans un composant implique simplement de retourner le state dans une *computed property*, car le state du store est réactif. Déclencher des changements signifie simplement commiter des mutations dans les méthodes du composant. 41 | 42 | Voici un exemple de la [plus basique app Vuex de compteur](https://jsfiddle.net/n9jmu5v7/341/). 43 | 44 | Ensuite, nous allons examiner chaque concept de base plus en détails, et commençon avec le [State](state.md). 45 | -------------------------------------------------------------------------------- /examples/todomvc/components/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 45 | 46 | 97 | -------------------------------------------------------------------------------- /docs/en/getters.md: -------------------------------------------------------------------------------- 1 | # Getters 2 | 3 | Sometimes we may need to compute derived state based on store state, for example filtering through a list of items and counting them: 4 | 5 | ``` js 6 | computed: { 7 | doneTodosCount () { 8 | return this.$store.state.todos.filter(todo => todo.done).length 9 | } 10 | } 11 | ``` 12 | 13 | If more than one component needs to make use of this, we have to either duplicate the function, or extract it into a shared helper and import it in multiple places - both are less than ideal. 14 | 15 | Vuex allows us to define "getters" in the store (think of them as computed properties for stores). Getters will receive the state as their 1st argument: 16 | 17 | ``` js 18 | const store = new Vuex.Store({ 19 | state: { 20 | todos: [ 21 | { id: 1, text: '...', done: true }, 22 | { id: 2, text: '...', done: false } 23 | ] 24 | }, 25 | getters: { 26 | doneTodos: state => { 27 | return state.todos.filter(todo => todo.done) 28 | } 29 | } 30 | }) 31 | ``` 32 | 33 | The getters will be exposed on the `store.getters` object: 34 | 35 | ``` js 36 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 37 | ``` 38 | 39 | Getters will also receive other getters as the 2nd argument: 40 | 41 | ``` js 42 | getters: { 43 | // ... 44 | doneTodosCount: (state, getters) => { 45 | return getters.doneTodos.length 46 | } 47 | } 48 | ``` 49 | 50 | ``` js 51 | store.getters.doneTodosCount // -> 1 52 | ``` 53 | 54 | We can now easily make use of it inside any component: 55 | 56 | ``` js 57 | computed: { 58 | doneTodosCount () { 59 | return this.$store.getters.doneTodosCount 60 | } 61 | } 62 | ``` 63 | 64 | You can also pass arguments to getters by returning a function. This is particularly useful when you want to query an array in the store: 65 | ```js 66 | getters: { 67 | // ... 68 | getTodoById: (state, getters) => (id) => { 69 | return state.todos.find(todo => todo.id === id) 70 | } 71 | } 72 | ``` 73 | 74 | ``` js 75 | store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } 76 | ``` 77 | 78 | 79 | ### The `mapGetters` Helper 80 | 81 | The `mapGetters` helper simply maps store getters to local computed properties: 82 | 83 | ``` js 84 | import { mapGetters } from 'vuex' 85 | 86 | export default { 87 | // ... 88 | computed: { 89 | // mix the getters into computed with object spread operator 90 | ...mapGetters([ 91 | 'doneTodosCount', 92 | 'anotherGetter', 93 | // ... 94 | ]) 95 | } 96 | } 97 | ``` 98 | 99 | If you want to map a getter to a different name, use an object: 100 | 101 | ``` js 102 | ...mapGetters({ 103 | // map this.doneCount to store.getters.doneTodosCount 104 | doneCount: 'doneTodosCount' 105 | }) 106 | ``` 107 | -------------------------------------------------------------------------------- /test/unit/module/module-collection.spec.js: -------------------------------------------------------------------------------- 1 | import ModuleCollection from '../../../src/module/module-collection' 2 | 3 | describe('ModuleCollection', () => { 4 | it('get', () => { 5 | const collection = new ModuleCollection({ 6 | state: { value: 1 }, 7 | modules: { 8 | a: { 9 | state: { value: 2 } 10 | }, 11 | b: { 12 | state: { value: 3 }, 13 | modules: { 14 | c: { 15 | state: { value: 4 } 16 | } 17 | } 18 | } 19 | } 20 | }) 21 | expect(collection.get([]).state.value).toBe(1) 22 | expect(collection.get(['a']).state.value).toBe(2) 23 | expect(collection.get(['b']).state.value).toBe(3) 24 | expect(collection.get(['b', 'c']).state.value).toBe(4) 25 | }) 26 | 27 | it('getNamespace', () => { 28 | const module = (namespaced, children) => { 29 | return { 30 | namespaced, 31 | modules: children 32 | } 33 | } 34 | const collection = new ModuleCollection({ 35 | namespace: 'ignore/', // root module namespace should be ignored 36 | modules: { 37 | a: module(true, { 38 | b: module(false, { 39 | c: module(true) 40 | }), 41 | d: module(true) 42 | }) 43 | } 44 | }) 45 | const check = (path, expected) => { 46 | const type = 'test' 47 | const namespace = collection.getNamespace(path) 48 | expect(namespace + type).toBe(expected) 49 | } 50 | check(['a'], 'a/test') 51 | check(['a', 'b'], 'a/test') 52 | check(['a', 'b', 'c'], 'a/c/test') 53 | check(['a', 'd'], 'a/d/test') 54 | }) 55 | 56 | it('register', () => { 57 | const collection = new ModuleCollection({}) 58 | collection.register(['a'], { 59 | state: { value: 1 } 60 | }) 61 | collection.register(['b'], { 62 | state: { value: 2 } 63 | }) 64 | collection.register(['a', 'b'], { 65 | state: { value: 3 } 66 | }) 67 | 68 | expect(collection.get(['a']).state.value).toBe(1) 69 | expect(collection.get(['b']).state.value).toBe(2) 70 | expect(collection.get(['a', 'b']).state.value).toBe(3) 71 | }) 72 | 73 | it('unregister', () => { 74 | const collection = new ModuleCollection({}) 75 | collection.register(['a'], { 76 | state: { value: true } 77 | }) 78 | expect(collection.get(['a']).state.value).toBe(true) 79 | 80 | collection.unregister(['a']) 81 | expect(collection.get(['a'])).toBe(undefined) 82 | }) 83 | 84 | it('does not unregister initial modules', () => { 85 | const collection = new ModuleCollection({ 86 | modules: { 87 | a: { 88 | state: { value: true } 89 | } 90 | } 91 | }) 92 | collection.unregister(['a']) 93 | expect(collection.get(['a']).state.value).toBe(true) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex", 3 | "version": "2.2.1", 4 | "description": "state management for Vue.js", 5 | "main": "dist/vuex.js", 6 | "module": "dist/vuex.esm.js", 7 | "unpkg": "dist/vuex.js", 8 | "typings": "types/index.d.ts", 9 | "files": [ 10 | "dist", 11 | "src", 12 | "types/index.d.ts", 13 | "types/helpers.d.ts", 14 | "types/vue.d.ts" 15 | ], 16 | "scripts": { 17 | "dev": "node examples/server.js", 18 | "dev:dist": "rollup -wm -c build/rollup.config.js", 19 | "build": "npm run build:main && npm run build:esm && npm run build:logger", 20 | "build:main": "rollup -c build/rollup.config.js && uglifyjs dist/vuex.js -cm --comments -o dist/vuex.min.js", 21 | "build:esm": "rollup -c build/rollup.config.js --environment ESM", 22 | "build:logger": "rollup -c build/rollup.logger.config.js", 23 | "lint": "eslint src test", 24 | "test": "npm run lint && npm run test:types && npm run test:unit && npm run test:e2e", 25 | "test:unit": "rollup -c build/rollup.config.js && jasmine JASMINE_CONFIG_PATH=test/unit/jasmine.json", 26 | "test:e2e": "node test/e2e/runner.js", 27 | "test:types": "tsc -p types/test", 28 | "release": "bash build/release.sh", 29 | "docs": "cd docs && gitbook serve", 30 | "docs:deploy": "cd docs && ./deploy.sh" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/vuejs/vuex.git" 35 | }, 36 | "author": "Evan You", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/vuejs/vuex/issues" 40 | }, 41 | "homepage": "https://github.com/vuejs/vuex#readme", 42 | "devDependencies": { 43 | "babel-core": "^6.22.1", 44 | "babel-eslint": "^7.1.1", 45 | "babel-loader": "^6.2.10", 46 | "babel-plugin-transform-runtime": "^6.22.0", 47 | "babel-polyfill": "^6.22.0", 48 | "babel-preset-es2015": "^6.22.0", 49 | "babel-preset-es2015-rollup": "^3.0.0", 50 | "babel-preset-stage-2": "^6.22.0", 51 | "babel-runtime": "^6.22.0", 52 | "chromedriver": "^2.27.2", 53 | "cross-spawn": "^5.0.1", 54 | "css-loader": "^0.26.1", 55 | "eslint": "^3.15.0", 56 | "eslint-config-vue": "^2.0.2", 57 | "eslint-plugin-vue": "^2.0.1", 58 | "express": "^4.14.1", 59 | "jasmine": "2.5.3", 60 | "jasmine-core": "2.5.2", 61 | "nightwatch": "^0.9.12", 62 | "nightwatch-helpers": "^1.2.0", 63 | "phantomjs-prebuilt": "^2.1.14", 64 | "rollup": "^0.41.4", 65 | "rollup-plugin-buble": "^0.15.0", 66 | "rollup-plugin-replace": "^1.1.1", 67 | "rollup-watch": "^3.2.2", 68 | "selenium-server": "^2.53.1", 69 | "todomvc-app-css": "^2.0.6", 70 | "typescript": "^2.1.5", 71 | "uglify-js": "^2.7.5", 72 | "vue": "^2.1.10", 73 | "vue-loader": "^11.0.0", 74 | "vue-template-compiler": "^2.1.10", 75 | "webpack": "^2.2.1", 76 | "webpack-dev-middleware": "^1.10.0", 77 | "webpack-hot-middleware": "^2.16.1" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /docs/zh-cn/state.md: -------------------------------------------------------------------------------- 1 | # State 2 | 3 | ### 单一状态树 4 | 5 | Vuex 使用 **单一状态树** —— 是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源([SSOT](https://en.wikipedia.org/wiki/Single_source_of_truth))』而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。 6 | 7 | 单状态树和模块化并不冲突 —— 在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。 8 | 9 | ### 在 Vue 组件中获得 Vuex 状态 10 | 11 | 那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在[计算属性](http://cn.vuejs.org/guide/computed.html)中返回某个状态: 12 | 13 | ``` js 14 | // 创建一个 Counter 组件 15 | const Counter = { 16 | template: `
{{ count }}
`, 17 | computed: { 18 | count () { 19 | return store.state.count 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | 每当 `store.state.count` 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。 26 | 27 | 然而,这种模式导致组件依赖的全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。 28 | 29 | Vuex 通过 `store` 选项,提供了一种机制将状态从根组件『注入』到每一个子组件中(需调用 `Vue.use(Vuex)`): 30 | 31 | ``` js 32 | const app = new Vue({ 33 | el: '#app', 34 | // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件 35 | store, 36 | components: { Counter }, 37 | template: ` 38 |
39 | 40 |
41 | ` 42 | }) 43 | ``` 44 | 45 | 通过在根实例中注册 `store` 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 `this.$store` 访问到。让我们更新下 `Counter` 的实现: 46 | 47 | ``` js 48 | const Counter = { 49 | template: `
{{ count }}
`, 50 | computed: { 51 | count () { 52 | return this.$store.state.count 53 | } 54 | } 55 | } 56 | ``` 57 | 58 | ### `mapState` 辅助函数 59 | 60 | 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 `mapState` 辅助函数帮助我们生成计算属性,让你少按几次键: 61 | 62 | ``` js 63 | // 在单独构建的版本中辅助函数为 Vuex.mapState 64 | import { mapState } from 'vuex' 65 | 66 | export default { 67 | // ... 68 | computed: mapState({ 69 | // 箭头函数可使代码更简练 70 | count: state => state.count, 71 | 72 | // 传字符串参数 'count' 等同于 `state => state.count` 73 | countAlias: 'count', 74 | 75 | // 为了能够使用 `this` 获取局部状态,必须使用常规函数 76 | countPlusLocalState (state) { 77 | return state.count + this.localCount 78 | } 79 | }) 80 | } 81 | ``` 82 | 83 | 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 `mapState` 传一个字符串数组。 84 | 85 | ``` js 86 | computed: mapState([ 87 | // 映射 this.count 为 store.state.count 88 | 'count' 89 | ]) 90 | ``` 91 | 92 | ### 对象展开运算符 93 | 94 | `mapState` 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 `computed` 属性。但是自从有了[对象展开运算符](https://github.com/sebmarkbage/ecmascript-rest-spread)(现处于 ECMASCript 提案 stage-3 阶段),我们可以极大地简化写法: 95 | 96 | ``` js 97 | computed: { 98 | localComputed () { /* ... */ }, 99 | // 使用对象展开运算符将此对象混入到外部对象中 100 | ...mapState({ 101 | // ... 102 | }) 103 | } 104 | ``` 105 | 106 | ### 组件仍然保有局部状态 107 | 108 | 使用 Vuex 并不意味着你需要将**所有的**状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。 109 | -------------------------------------------------------------------------------- /docs/ru/getters.md: -------------------------------------------------------------------------------- 1 | # Геттеры 2 | 3 | Иногда может понадобится доступ к производным данным, основывающимся на состоянии хранилища: например, к отфильтрованной версии списка или количеству элементов в нём: 4 | 5 | ``` js 6 | computed: { 7 | doneTodosCount () { 8 | return this.$store.state.todos.filter(todo => todo.done).length 9 | } 10 | } 11 | ``` 12 | 13 | Если этот функционал требуется более чем одному компоненту, понадобится либо дублировать функцию, либо выносить её в совместно используемый хелпер и импортировать в нескольких местах. Оба эти подхода далеки от идеала. 14 | 15 | Vuex позволяет определять в хранилище "геттеры" (их можно считать вычисляемыми свойствами хранилища). Геттеры получают первым аргументом ссылку на состояние хранилища: 16 | 17 | ``` js 18 | const store = new Vuex.Store({ 19 | state: { 20 | todos: [ 21 | { id: 1, text: '...', done: true }, 22 | { id: 2, text: '...', done: false } 23 | ] 24 | }, 25 | getters: { 26 | doneTodos: state => { 27 | return state.todos.filter(todo => todo.done) 28 | } 29 | } 30 | }) 31 | ``` 32 | 33 | Геттеры доступны в `store.getters`: 34 | 35 | ``` js 36 | store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] 37 | ``` 38 | 39 | Вторым аргументом передаётся список всех геттеров: 40 | 41 | ``` js 42 | getters: { 43 | // ... 44 | doneTodosCount: (state, getters) => { 45 | return getters.doneTodos.length 46 | } 47 | } 48 | ``` 49 | 50 | ``` js 51 | store.getters.doneTodosCount // -> 1 52 | ``` 53 | 54 | В компонентах геттеры можно использовать например таким образом: 55 | 56 | ``` js 57 | computed: { 58 | doneTodosCount () { 59 | return this.$store.getters.doneTodosCount 60 | } 61 | } 62 | ``` 63 | 64 | Вы также можете передавать аргументы геттерам возвращая функцию. Это особенно полезно, если вы хотите вернуть элемент (или часть элементов) массива по переданному критерию: 65 | 66 | ```js 67 | getters: { 68 | // ... 69 | getTodoById: (state, getters) => (id) => { 70 | return state.todos.find(todo => todo.id === id) 71 | } 72 | } 73 | ``` 74 | 75 | ``` js 76 | store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } 77 | ``` 78 | 79 | ### Вспомогательная функция `mapGetters` 80 | 81 | Хелпер `mapGetters` попросту проксирует геттеры хранилища через локальные вычисляемые свойства компонента: 82 | 83 | ``` js 84 | import { mapGetters } from 'vuex' 85 | 86 | export default { 87 | // ... 88 | computed: { 89 | // примешиваем геттеры в вычисляемые свойства оператором расширения 90 | ...mapGetters([ 91 | 'doneTodosCount', 92 | 'anotherGetter', 93 | // ... 94 | ]) 95 | } 96 | } 97 | ``` 98 | 99 | Если вы хотите использовать при проксировании другое имя, примените объектный синтаксис: 100 | 101 | ``` js 102 | ...mapGetters({ 103 | // проксируем this.doneCount в store.getters.doneTodosCount 104 | doneCount: 'doneTodosCount' 105 | }) 106 | ``` 107 | -------------------------------------------------------------------------------- /docs/zh-cn/plugins.md: -------------------------------------------------------------------------------- 1 | # 插件 2 | 3 | Vuex 的 store 接受 `plugins` 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数: 4 | 5 | ``` js 6 | const myPlugin = store => { 7 | // 当 store 初始化后调用 8 | store.subscribe((mutation, state) => { 9 | // 每次 mutation 之后调用 10 | // mutation 的格式为 { type, payload } 11 | }) 12 | } 13 | ``` 14 | 15 | 然后像这样使用: 16 | 17 | ``` js 18 | const store = new Vuex.Store({ 19 | // ... 20 | plugins: [myPlugin] 21 | }) 22 | ``` 23 | 24 | ### 在插件内提交 Mutation 25 | 26 | 在插件中不允许直接修改状态——类似于组件,只能通过提交 mutation 来触发变化。 27 | 28 | 通过提交 mutation,插件可以用来同步数据源到 store。例如,同步 websocket 数据源到 store(下面是个大概例子,实际上 `createPlugin` 方法可以有更多选项来完成复杂任务): 29 | 30 | ``` js 31 | export default function createWebSocketPlugin (socket) { 32 | return store => { 33 | socket.on('data', data => { 34 | store.commit('receiveData', data) 35 | }) 36 | store.subscribe(mutation => { 37 | if (mutation.type === 'UPDATE_DATA') { 38 | socket.emit('update', mutation.payload) 39 | } 40 | }) 41 | } 42 | } 43 | ``` 44 | 45 | ``` js 46 | const plugin = createWebSocketPlugin(socket) 47 | 48 | const store = new Vuex.Store({ 49 | state, 50 | mutations, 51 | plugins: [plugin] 52 | }) 53 | ``` 54 | 55 | ### 生成 State 快照 56 | 57 | 有时候插件需要获得状态的『快照』,比较改变的前后状态。想要实现这项功能,你需要对状态对象进行深拷贝: 58 | 59 | ``` js 60 | const myPluginWithSnapshot = store => { 61 | let prevState = _.cloneDeep(store.state) 62 | store.subscribe((mutation, state) => { 63 | let nextState = _.cloneDeep(state) 64 | 65 | // 比较 prevState 和 nextState... 66 | 67 | // 保存状态,用于下一次 mutation 68 | prevState = nextState 69 | }) 70 | } 71 | ``` 72 | 73 | **生成状态快照的插件应该只在开发阶段使用**,使用 Webpack 或 Browserify,让构建工具帮我们处理: 74 | 75 | ``` js 76 | const store = new Vuex.Store({ 77 | // ... 78 | plugins: process.env.NODE_ENV !== 'production' 79 | ? [myPluginWithSnapshot] 80 | : [] 81 | }) 82 | ``` 83 | 84 | 上面插件会默认启用。在发布阶段,你需要使用 Webpack 的 [DefinePlugin](https://webpack.github.io/docs/list-of-plugins.html#defineplugin) 或者是 Browserify 的 [envify](https://github.com/hughsk/envify) 使 `process.env.NODE_ENV !== 'production'` 为 `false`。 85 | 86 | ### 内置 Logger 插件 87 | 88 | > 如果正在使用 [vue-devtools](https://github.com/vuejs/vue-devtools),你可能不需要此插件。 89 | 90 | Vuex 自带一个日志插件用于一般的调试: 91 | 92 | ``` js 93 | import createLogger from 'vuex/dist/logger' 94 | 95 | const store = new Vuex.Store({ 96 | plugins: [createLogger()] 97 | }) 98 | ``` 99 | 100 | `createLogger` 函数有几个配置项: 101 | 102 | ``` js 103 | const logger = createLogger({ 104 | collapsed: false, // 自动展开记录的 mutation 105 | transformer (state) { 106 | // 在开始记录之前转换状态 107 | // 例如,只返回指定的子树 108 | return state.subTree 109 | }, 110 | mutationTransformer (mutation) { 111 | // mutation 按照 { type, payload } 格式记录 112 | // 我们可以按任意方式格式化 113 | return mutation.type 114 | } 115 | }) 116 | ``` 117 | 118 | 日志插件还可以直接通过 `