浏览代码

docs(ja): setup japanese translation

kazuya kawaguchi 9 年之前
父节点
当前提交
2cd55b735a
共有 19 个文件被更改,包括 972 次插入0 次删除
  1. 1 0
      docs/LANGS.md
  2. 1 0
      docs/ja/README.md
  3. 15 0
      docs/ja/SUMMARY.md
  4. 136 0
      docs/ja/actions.md
  5. 93 0
      docs/ja/api.md
  6. 1 0
      docs/ja/book.json
  7. 31 0
      docs/ja/concepts.md
  8. 92 0
      docs/ja/data-flow.md
  9. 36 0
      docs/ja/forms.md
  10. 29 0
      docs/ja/hot-reload.md
  11. 13 0
      docs/ja/intro.md
  12. 81 0
      docs/ja/middlewares.md
  13. 96 0
      docs/ja/mutations.md
  14. 2 0
      docs/ja/quickstart.md
  15. 52 0
      docs/ja/state.md
  16. 25 0
      docs/ja/strict.md
  17. 133 0
      docs/ja/structure.md
  18. 135 0
      docs/ja/testing.md
  19. 二进制
      docs/ja/vuex.png

+ 1 - 0
docs/LANGS.md

@@ -1,2 +1,3 @@
 * [English](en/)
 * [简体中文](zh-cn/)
+* [日本語](ja/)

+ 1 - 0
docs/ja/README.md

@@ -0,0 +1 @@
+SUMMARY.md

+ 15 - 0
docs/ja/SUMMARY.md

@@ -0,0 +1,15 @@
+# Table of Contents
+
+- [Vuex は何ですか?](intro.md)
+- [コアコンセプト](concepts.md)
+  - [ステート](state.md)
+  - [ミューテーション](mutations.md)
+  - [アクション](actions.md)
+- [データフロー](data-flow.md)
+- [アプリケーションの構造](structure.md)
+- [ミドルウェア](middlewares.md)
+- [Strict Mode](strict.md)
+- [フォームのハンドリング](forms.md)
+- [テスト](testing.md)
+- [ホットリローディング](hot-reload.md)
+- [API リファレンス](api.md)

+ 136 - 0
docs/ja/actions.md

@@ -0,0 +1,136 @@
+# Actions
+
+Actions are functions that dispatch mutations. Actions can be asynchronous and a single action can dispatch multiple mutations.
+
+An action expresses the intention for something to happen, and abstracts the details away from the component calling it. When a component wants to do something, it just calls an action - there's no need to worry about a callback or a return value, because actions result in state changes, and state changes will trigger the component's DOM to update - the component is completely decoupled from how that action is actually performed.
+
+Therefore, we usually perform API calls to data endpoints inside actions, and hide the asynchronous details from both the Components calling the actions, and the mutations triggered by the actions.
+
+> Vuex actions are in fact "action creators" in vanilla flux definitions, but I find that term more confusing than useful.
+
+### Simple Actions
+
+It is common that an action simply triggers a single mutation. Vuex provides a shorthand for defining such actions:
+
+``` js
+const store = new Vuex.Store({
+  state: {
+    count: 1
+  },
+  mutations: {
+    INCREMENT (state, x) {
+      state.count += x
+    }
+  },
+  actions: {
+    // shorthand
+    // just provide the mutation name.
+    increment: 'INCREMENT'
+  }
+})
+```
+
+Now when we call the action:
+
+``` js
+store.actions.increment(1)
+```
+
+It simply calls the following for us:
+
+``` js
+store.dispatch('INCREMENT', 1)
+```
+
+Note any arguments passed to the action is also passed along to the mutation handler.
+
+### Normal Actions
+
+For actions that involve logic depending on current state, or that need async operations, we define them as functions. Action functions always get the store calling it as the first argument:
+
+``` js
+const vuex = new Vuex({
+  state: {
+    count: 1
+  },
+  mutations: {
+    INCREMENT (state, x) {
+      state += x
+    }
+  },
+  actions: {
+    incrementIfOdd: (store, x) => {
+      if ((store.state.count + 1) % 2 === 0) {
+        store.dispatch('INCREMENT', x)
+      }
+    }
+  }
+})
+```
+
+It is common to use ES6 argument destructuring to make the function body less verbose (here the `dispatch` function is pre-bound to the store instance so we don't have to call it as a method):
+
+``` js
+// ...
+actions: {
+  incrementIfOdd: ({ dispatch, state }, x) => {
+    if ((state.count + 1) % 2 === 0) {
+      dispatch('INCREMENT', x)
+    }
+  }
+}
+```
+
+The string shorthand is essentially syntax sugar for the following:
+
+``` js
+actions: {
+  increment: 'INCREMENT'
+}
+// ... equivalent to:
+actions: {
+  increment: ({ dispatch }, ...payload) => {
+    dispatch('INCREMENT', ...payload)
+  }
+}
+```
+
+### Async Actions
+
+We can use the same syntax for defining async actions:
+
+``` js
+// ...
+actions: {
+  incrementAsync: ({ dispatch }, x) => {
+    setTimeout(() => {
+      dispatch('INCREMENT', x)
+    }, 1000)
+  }
+}
+```
+
+A more practical example is when checking out a shopping cart - we may need to trigger multiple mutations: one that signifies the checkout has started, one for success, and one for failure:
+
+``` js
+// ...
+actions: {
+  checkout: ({ dispatch, state }, products) => {
+    // save the current in cart items
+    const savedCartItems = [...state.cart.added]
+    // send out checkout request, and optimistically
+    // clear the cart
+    dispatch(types.CHECKOUT_REQUEST)
+    // the shop API accepts a success callback and a failure callback
+    shop.buyProducts(
+      products,
+      // handle success
+      () => dispatch(types.CHECKOUT_SUCCESS),
+      // handle failure
+      () => dispatch(types.CHECKOUT_FAILURE, savedCartItems)
+    )
+  }
+}
+```
+
+Again, all the component needs to do to perform the entire checkout is just calling `vuex.actions.checkout(products)`.

+ 93 - 0
docs/ja/api.md

@@ -0,0 +1,93 @@
+# API Reference
+
+### Vuex.Store
+
+``` js
+import Vuex from 'vuex'
+
+const store = new Vuex.Store({ ...options })
+```
+
+### Vuex.Store Constructor Options
+
+- **state**
+  
+  - type: `Object`
+
+    The root state object for the Vuex store.
+
+    [Details](state.md)
+
+- **mutations**
+
+  - type: `Object | Array<Object>`
+
+    An object where each entry's key is the mutation name and the value is a mutation handler function. The handler function always receives `state` as the first argument, and receives all arguments passed to the dispatch call following that.
+
+    If passing in an Array of Objects, these objects will be automatically merged together into one final object.
+
+    [Details](mutations.md)
+
+- **actions**
+
+  - type: `Object | Array<Object>`
+
+    An object where each entry's key is the action name and the value is either
+
+    1. A mutation name string; or
+    2. A function which will receive the store as the first argument, followed by additional payload arguments.
+
+    Vuex will process these entries and create the actual callable action functions and expose them on the `actions` property of the store.
+
+    If passing in an Array of Objects, these objects will be automatically merged together into one final object.
+
+    [Details](actions.md)
+
+- **middlewares**
+
+  - type: `Array<Object>`
+
+    An array of middleware objects that are in the shape of:
+
+    ``` js
+    {
+      snapshot: Boolean, // default: false
+      onInit: Function,
+      onMutation: Function
+    }
+    ```
+
+    All fields are optional. [Details](middlewares.md)
+
+- **strict**
+
+  - type: `Boolean`
+  - default: `false`
+
+    Force the Vuex store into strict mode. In strict mode any mutations to Vuex state outside of mutation handlers will throw en Error.
+
+    [Details](strict.md)
+
+### Vuex.Store Instance Properties
+
+- **state**
+
+  - type: `Object`
+
+    The root state. Read only.
+
+- **actions**
+
+  - type: `Object`
+
+    The callable action functions.
+
+### Vuex.Store Instance Methods
+
+- **dispatch(mutationName: String, ...args)**
+
+  Directly dispatch a mutation. This is useful in certain situations are in general you should prefer using actions in application code.
+
+- **hotUpdate(newOptions: Object)**
+
+  Hot swap new actions and mutations. [Details](hot-reload.md)

+ 1 - 0
docs/ja/book.json

@@ -0,0 +1 @@
+../book.json

+ 31 - 0
docs/ja/concepts.md

@@ -0,0 +1,31 @@
+# Core Concepts
+
+You can use the `Vuex.Store` constructor to create Vuex stores. In most cases, you only need a single store each application. Each Vuex store consists of three types of "ingredients":
+
+- **State**: A plain object representing the application state.
+
+- **Mutations**: Functions that mutates the state. Mutations **must be synchronous**.
+
+- **Actions**: Functions that dispatch mutations. An action can contain asynchronous operations and can dispatch multiple mutations.
+
+Why do we differentiate between *mutations* and *actions*, rather then just simple functions that manipulate the state however we want? The reason is because we want to **separate mutation and asynchronicity**. A lot of application complexity roots from the combination of the two. When separated, they both become easier to reason about and write tests for.
+
+> If you are familiar with Flux, note there's a term/concept difference here: Vuex mutations are the equivalent of Flux **actions**, while Vuex actions are equivalent to Flux **action creators**.
+
+### Creating a Vuex Store
+
+> **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/)! The doc also assumes you are already familiar with the concepts discussed in [Building Large-Scale Apps with Vue.js](http://vuejs.org/guide/application.html).
+
+Creating a Vuex store is pretty straightforward - just put the aforementioned ingredients together:
+
+``` js
+import Vuex from 'vuex'
+
+const store = new Vuex.Store({
+  state: { ... },
+  actions: { ... },
+  mutations: { ... }
+})
+```
+
+Once created, you can access the state via `store.state`, and the actions via `store.actions`. You cannot directly access the mutation functions - they can only be triggered by actions or calling `store.dispatch()`. We will discuss each concept in more details next.

+ 92 - 0
docs/ja/data-flow.md

@@ -0,0 +1,92 @@
+# Data Flow
+
+Let's build a simple counter app with Vuex to get a better understanding of the data flow inside Vuex apps. Note this is a trivial example solely for the purpose of explaining the concepts - in practice you don't need Vuex for such simple tasks.
+
+### Setup
+
+``` js
+// store.js
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+```
+
+### Define App State
+
+``` js
+const state = {
+  count: 0
+}
+```
+
+### Define Possible State Mutations
+
+``` js
+const mutations = {
+  INCREMENT (state) {
+    state.count++
+  },
+  DECREMENT (state) {
+    state.count--
+  }
+}
+```
+
+### Define Callable Actions
+
+``` js
+const actions = {
+  increment: 'INCREMENT',
+  decrement: 'DECREMENT'
+}
+```
+
+### Create a Vuex Store
+
+``` js
+export default new Vuex.Store({
+  state,
+  mutations,
+  actions
+})
+```
+
+### Use It in a Vue Component
+
+**Template**
+
+``` html
+<div>
+  Clicked: {{ count }} times
+  <button v-on:click="increment">+</button>
+  <button v-on:click="decrement">-</button>
+</div>
+```
+
+**Script**
+
+``` js
+import store from './store.js'
+
+export default {
+  computed: {
+    // bind to state using computed properties
+    count () {
+      return store.state.count
+    }
+  },
+  methods: {
+    increment: store.actions.increment,
+    decrement: store.actions.decrement
+  }
+}
+```
+
+Here you will notice the component itself is extremely simple: it simply displays some state from the Vuex store (not even owning its own data), and calls some store actions on user input events.
+
+You will also notice the data flow is unidirectional, as it should be in Flux:
+
+<p align="center">
+  <img width="700px" src="vuex.png">
+</p>

+ 36 - 0
docs/ja/forms.md

@@ -0,0 +1,36 @@
+# Form Handling
+
+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:
+
+``` html
+<input v-model="obj.message">
+```
+
+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 en error because the mutation is not performed inside an explicit Vuex mutation handler.
+
+The "Vuex way" to deal with it is binding the `<input>`'s value and call an action on the `input` or `change` event:
+
+``` html
+<input :value="obj.message" @input="updateMessage">
+```
+``` js
+// ...
+methods: {
+  updateMessage: function (e) {
+    store.actions.updateMessage(e.target.value)
+  }
+}
+```
+
+Assuming the `updateMessage` action simply dispatches `'UPDATE_MESSAGE'`, here's the mutation handler:
+
+``` js
+// ...
+mutations: {
+  UPDATE_MESSAGE (state, message) {
+    state.obj.message = message
+  }
+}
+```
+
+Admittedly, this is quite a bit more verbose than a simple `v-model`, but such is the cost of making state changes explicit and track-able. At the same time, do note that Vuex doesn't demand putting all your state inside a Vuex store - if you do not wish to track the mutations for form interactions at all, you can simply keep the form state outside of Vuex as component local state, which allows you to freely leverage `v-model`.

+ 29 - 0
docs/ja/hot-reload.md

@@ -0,0 +1,29 @@
+# Hot Reloading
+
+Vuex supports hot-reloading actions and mutations 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.
+
+It's as simple as calling `store.hotUpdate()` with the new actions and mutations:
+
+``` js
+// ...
+const store = new Vuex.Store({
+  state,
+  actions,
+  mutations
+})
+
+if (module.hot) {
+  // accept actions and mutations as hot modules
+  module.hot.accept(['./actions', './mutations'], () => {
+    // require the updated modules
+    // have to add .default here due to babel 6 module output
+    const newActions = require('./actions').default
+    const newMutations = require('./mutations').default
+    // swap in the new actions and mutations
+    store.hotUpdate({
+      actions: newActions,
+      mutations: newMutations
+    })
+  })
+}
+```

+ 13 - 0
docs/ja/intro.md

@@ -0,0 +1,13 @@
+## What is Vuex?
+
+Vuex is an application architecture for centralized state management in Vue.js applications. It is inspired by [Flux](https://facebook.github.io/flux/) and [Redux](https://github.com/rackt/redux), but with simplified concepts and an implementation that is designed specifically to take advantage of Vue.js' reactivity system.
+
+## Why do I need this?
+
+If your app is simple enough, you probably don't need Vuex. Don't apply it prematurely. But if you are building a medium-to-large-scale SPA, chances are you have run into situations that make you think about how to better structure things outside of your Vue components. This is where Vuex comes into play.
+
+When using Vue.js alone, we often tend to store the state "inside" our components. That is, each component owns a piece of our application state, and as a result the state is scattered all over the place. However, sometimes a piece of state needs to be shared by multiple components. A commonly-seen practice is letting one component "send" some state to other components using the custom event system. The problem with this pattern is that the event flow inside large component trees can quickly become complex, and it's often difficult to reason about when something goes wrong.
+
+To better deal with shared state in large applications, we need to differentiate between **component local state** and **application level state**. Application state does not belong to a specific component, but our components can still observe it for reactive DOM updates. By centralizing its management in a single place, we no longer need to pass events around, because everything that affects more than one component should belong there. In addition, this allows us to record and inspect every mutation for easier understanding of state changes, and even implement fancy stuff like time-travel debugging.
+
+Vuex also enforces some opinions on how to split state management logic into different places, but still allows enough flexibility for the actual code structure.

+ 81 - 0
docs/ja/middlewares.md

@@ -0,0 +1,81 @@
+# Middlewares
+
+Vuex stores accept the `middlewares` option that exposes hooks for each mutation (Note this is completely unrelated to Redux middlewares). A Vuex middleware is simply an object that implements some hook functions:
+
+``` js
+const myMiddleware = {
+  onInit (state) {
+    // record initial state
+  },
+  onMutation (mutation, state) {
+    // called after every mutation.
+    // The mutation comes in the format of { type, payload }
+  }
+}
+```
+
+And can be used like this:
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  middlewares: [myMiddleware]
+})
+```
+
+By default, a middleware receives the actual `state` object. Since middlewares are primarily used for debugging purposes or data persistence, they are **not allowed to mutate the state**.
+
+Sometimes a middleware may want to receive "snapshots" of the state, and also compare the post-mutation state with pre-mutation state. Such middlewares must declare the `snapshot: true` option:
+
+``` js
+const myMiddlewareWithSnapshot = {
+  snapshot: true,
+  onMutation (mutation, nextState, prevState) {
+    // nextState and prevState are deep-cloned snapshots
+    // of the state before and after the mutation.
+  }
+}
+```
+
+**Middlewares that take state snapshots should be used only during development.** When using Webpack or Browserify, we can let our build tools handle that for us:
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  middlewares: process.env.NODE_ENV !== 'production'
+    ? [myMiddlewareWithSnapshot]
+    : []
+})
+```
+
+The middleware will be used by default. For production, use the build setup described [here](http://vuejs.org/guide/application.html#Deploying_for_Production) to convert the value of `process.env.NODE_ENV !== 'production'` to `false` for the final build.
+
+### Built-in Logger Middleware
+
+Vuex comes with a logger middleware for common debugging usage:
+
+``` js
+const store = new Vuex.Store({
+  middlewares: [Vuex.createLogger()]
+})
+```
+
+The `createLogger` function takes a few options:
+
+``` js
+const logger = Vuex.createLogger({
+  collapsed: false, // auto-expand logged mutations
+  transformer (state) {
+    // transform the state before logging it.
+    // for example return only a specific sub-tree
+    return state.subTree
+  },
+  mutationTransformer (mutation) {
+    // mutations are logged in the format of { type, payload }
+    // we can format it anyway we want.
+    return mutation.type
+  }
+})
+```
+
+Note the logger middleware takes state snapshots, so use it only during development.

+ 96 - 0
docs/ja/mutations.md

@@ -0,0 +1,96 @@
+# Mutations
+
+Vuex mutations are essentially events: each mutation has a **name** and a **handler**. The handler function always gets the entire state tree as the first argument:
+
+``` js
+import Vuex from 'vuex'
+
+const store = new Vuex.Store({
+  state: {
+    count: 1
+  },
+  mutations: {
+    INCREMENT (state) {
+      // mutate state
+      state.count++
+    }
+  }
+})
+```
+
+Using all caps for mutation names is just a convention to make it easier to differentiate them from actions.
+
+You cannot directly call a mutation handler. The options here is more like event registration: "When an `INCREMENT` event is dispatched, call this handler." To invoke a mutation handler, you need to dispatch a mutation event:
+
+``` js
+store.dispatch('INCREMENT')
+```
+
+### Dispatch with Arguments
+
+It is also possible to pass along arguments:
+
+``` js
+// ...
+mutations: {
+  INCREMENT (state, n) {
+    state.count += n
+  }
+}
+```
+``` js
+store.dispatch('INCREMENT', 10)
+```
+
+Here `10` will be passed to the mutation handler as the second argument following `state`. Same for any additional arguments. These arguments are called the **payload** for the given mutation.
+
+### Mutations Follow Vue's Reactivity Rules
+
+Since a Vuex store's state is made reactive by Vue, when we mutate the state, Vue components observing the state will update automatically. This also means Vuex mutations are subject to the same reactivity caveats when working with plain Vue:
+
+1. Prefer initializing your store's initial state with all desired fields upfront.
+
+2. When adding new properties to an Object, you should either:
+
+  - Use `Vue.set(obj, 'newProp', 123)`, or -
+
+  - Replace that Object with a fresh one. For example, using the stage-2 [object spread syntax](https://github.com/sebmarkbage/ecmascript-rest-spread) we can write it like this:
+
+  ``` js
+  state.obj = { ...state.obj, newProp: 123 }
+  ```
+
+### Using Constants for Mutation Names
+
+It is also common to use constants for mutation names - they allow the code to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application:
+
+``` js
+// mutation-types.js
+export const SOME_MUTATION = 'SOME_MUTATION'
+```
+
+``` js
+// store.js
+import Vuex from 'vuex'
+import { SOME_MUTATION } from './mutation-types'
+
+const store = new Vuex.Store({
+  state: { ... },
+  actions: { ... },
+  mutations: {
+    // we can use the ES2015 computed property name feature
+    // to use a constant as the function name
+    [SOME_MUTATION] (state) {
+      // mutate state
+    }
+  }
+})
+```
+
+Whether to use constants is largely a preference - it can be helpful in large projects with many developers, but it's totally optional if you don't like them.
+
+### On to Actions
+
+So far, we've triggering mutations by manually calling `store.dispatch`. This is a viable approach, but in practice, we will rarely do this in our component code. Most of the time we will be calling [actions](actions.md), which can encapsulate more complex logic such as async data fetching.
+
+Also, one important rule to remember: all mutation handlers must be **synchronous**. Any async operations belong in actions.

+ 2 - 0
docs/ja/quickstart.md

@@ -0,0 +1,2 @@
+# Quickstart
+

+ 52 - 0
docs/ja/state.md

@@ -0,0 +1,52 @@
+# State
+
+### Single State Tree
+
+Vuex uses a **single state tree** - that is, this single object contains all your application level state and serves as the "single source of truth". This makes it straightforward to locate a specific piece of state, and allows us to easily take snapshots of the current app state for debugging purposes.
+
+The single state tree does not conflict with modularity - in later chapters we will discuss how to split your state managing logic into sub modules.
+
+### Getting Vuex State into Vue Components
+
+Similar to `data` objects passed to Vue instances, the `state` object, once passed into a Vuex store, becomes reactive powered by [Vue's reactivity system](http://vuejs.org/guide/reactivity.html). This means binding Vuex state to Vue components is as simple as returning it from within a computed property:
+
+``` js
+// inside a Vue component module
+
+// import a vuex store
+import store from './store'
+
+export default {
+  computed: {
+    message () {
+      return store.state.message
+    }
+  }
+}
+```
+
+There's no need to worry about setting up and tearing down listeners, or "connecting" the component to a store. The only thing to remember is that you should **always reference state via `store.state.xxx` inside your computed properties**. Do not cache the reference to a piece of state outside computed properties.
+
+> Flux reference: this can be roughly compared to [`mapStateToProps`](https://github.com/rackt/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) in Redux and [getters](https://optimizely.github.io/nuclear-js/docs/04-getters.html) in NuclearJS.
+
+Why don't we just use `data` to bind to the state? Consider the following example:
+
+``` js
+export default {
+  data () {
+    return {
+      message: store.state.message
+    }
+  }
+}
+```
+
+Because the `data` function does not track any reactive dependencies, we are only getting a static reference to `store.state.message`. When the state is mutated later, the component has no idea that something has changed. In comparison, computed properties track all reactive dependencies when they are evaluated, so they reactively re-evaluate when the related state is mutated.
+
+### Components Are Not Allowed to Directly Mutate State
+
+Using read-only computed properties has another benefit in that it helps emphasizing the rule that **components should never directly mutate Vuex store state**. Because we want every state mutation to be explicit and trackable, all vuex store state mutations must be conducted inside the store's mutation handlers.
+
+To help enforce this rule, when in [Strict Mode](strict.md), if a store's state is mutated outside of its mutation handlers, Vuex will throw an error.
+
+With this rule in place, our Vue components now hold a lot less responsibility: they are bound to Vuex store state via read-only computed properties, and the only way for them to affect the state is by calling **actions**, which in turn trigger **mutations**. They can still possess and operate on their local state if necessary, but we no longer put any data-fetching or global-state-mutating logic inside individual components.

+ 25 - 0
docs/ja/strict.md

@@ -0,0 +1,25 @@
+# Strict Mode
+
+To enable strict mode, simply pass in `strict: true` when creating a Vuex store:
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  strict: true
+})
+```
+
+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.
+
+### Development vs. Production
+
+**Do not enable strict mode when deploying for production!** Strict mode runs a deep watch on the state tree for detecting inappropriate mutations - make sure to turn it off in production to avoid the performance cost.
+
+Similar to middlewares, we can let the build tools handle that:
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  strict: process.env.NODE_ENV !== 'production'
+})
+```

+ 133 - 0
docs/ja/structure.md

@@ -0,0 +1,133 @@
+# Application Structure
+
+Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of opinions:
+
+1. Application state lives in a single object.
+2. Only mutation handlers can mutate the state.
+3. Mutations must be synchronous, and the only side effects they produce should be state mutation.
+4. All asynchronous logic such as data fetching should be performed in actions.
+
+The nice thing about Vuex actions and mutations is that **they are just functions**. As long as you follow these rules, it's up to you how to structure your project. The simplest Vuex instance can even be declared [in a single file](https://github.com/vuejs/vuex/blob/master/examples/counter/vuex.js)! However, this is unlikely to suffice for any serious project, so here are some recommended structures depending on the scale of your app.
+
+### Simple Project
+
+For a simple project, we can simply separate **actions** and **mutations** into respective files:
+
+``` bash
+.
+├── index.html
+├── main.js
+├── components
+│   ├── App.vue
+│   └── ...
+└── store
+    ├── index.js     # exports the vuex store
+    ├── actions.js   # exports all actions
+    └── mutations.js # exports all mutations
+```
+
+For an actual example, check out the [TodoMVC example](https://github.com/vuejs/vuex/tree/master/examples/todomvc).
+
+### Medium to Large Project
+
+For any non-trivial app, we probably want to further split Vuex-related code into multiple "modules" (roughly comparable to "stores" in vanilla Flux), each dealing with a specific domain of our app. Each module would be managing a sub-tree of the state, exporting the initial state for that sub-tree and all mutations that operate on that sub-tree:
+
+``` bash
+├── index.html
+├── main.js
+├── api
+│   └── ... # abstractions for making API requests
+├── components
+│   ├── App.vue
+│   └── ...
+└── store
+    ├── actions.js # exports all actions
+    ├── index.js
+    ├── modules
+    │   ├── cart.js       # state and mutations for cart
+    │   └── products.js   # state and mutations for products
+    └── mutation-types.js # constants
+```
+
+A typical module looks like this:
+
+``` js
+// vuex/modules/products.js
+import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../mutation-types'
+
+// initial state
+export const productsInitialState = []
+
+// mutations
+export const productsMutations = {
+  [RECEIVE_PRODUCTS] (state, products) {
+    state.products = products
+  },
+
+  [ADD_TO_CART] ({ products }, productId) {
+    const product = products.find(p => p.id === productId)
+    if (product.inventory > 0) {
+      product.inventory--
+    }
+  }
+}
+```
+
+And in `store/index.js`, we "assemble" multiple modules together to create the Vuex instance:
+
+``` js
+import Vue from 'vue'
+import Vuex from '../../../src'
+import * as actions from './actions'
+// import parts from modules
+import { cartInitialState, cartMutations } from './modules/cart'
+import { productsInitialState, productsMutations } from './modules/products'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  // ...
+  // combine sub-trees into root state
+  state: {
+    cart: cartInitialState,
+    products: productsInitialState
+  },
+  // mutations can be an array of mutation definition objects
+  // from multiple modules
+  mutations: [cartMutations, productsMutations]
+})
+```
+
+Since all modules simply export objects and functions, they are quite easy to test and maintain. You are also free to alter the patterns used here to find a structure that fits your preference.
+
+Note that we do not put actions into modules, because a single action may dispatch mutations that affect multiple modules. It's also a good idea to decouple actions from the state shape and the implementation details of mutations for better separation of concerns. If the actions file gets too large, we can turn it into a folder and split out the implementations of long async actions into individual files.
+
+For an actual example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
+
+### Extracting Shared Computed Getters
+
+In large projects, it's possible that multiple components will need the same computed property based on Vuex state. Since computed getters are just functions, you can split them out into a separate file so that they can be shared in any component:
+
+``` js
+// getters.js
+import store from './store'
+
+export function filteredTodos () {
+  return store.state.messages.filter(message => {
+    return message.threadID === store.state.currentThreadID
+  })
+}
+```
+
+``` js
+// in a component...
+import { filteredTodos } from './getters'
+
+export default {
+  computed: {
+    filteredTodos
+  }
+}
+```
+
+This is very similar to [Getters in NuclearJS](https://optimizely.github.io/nuclear-js/docs/04-getters.html).

+ 135 - 0
docs/ja/testing.md

@@ -0,0 +1,135 @@
+# Testing
+
+Mutations are very straightforward to test, because they are just functions that completely rely on their arguments. Actions can be a bit more tricky because they may call out to external APIs. When testing actions, we usually need to do some level of mocking - for example, we can abstract the API calls into a service and mock that service inside our tests. In order to easily mock dependencies, we can use Webpack and [inject-loader](https://github.com/plasticine/inject-loader) to bundle our test files.
+
+If your mutations and actions are written properly, the tests should have no direct dependency on Browser APIs after proper mocking. Thus you can simply bundle the tests with Webpack and run it directly in Node. Alternatively, you can use `mocha-loader` or Karma + `karma-webpack` to run the tests in real browsers.
+
+Example testing a mutation using Mocha + Chai (you can use any framework/assertion libraries you like):
+
+``` js
+// mutations.js
+export const INCREMENT = state => state.count++
+```
+
+``` js
+// mutations.spec.js
+import { expect } from 'chai'
+import { INCREMENT } from './mutations'
+
+describe('mutations', () => {
+  it('INCREMENT', () => {
+    // mock state
+    const state = { count: 0 }
+    // apply mutation
+    INCREMENT(state)
+    // assert result
+    expect(state.count).to.equal(1)
+  })
+})
+```
+
+Example testing an async action:
+
+``` js
+// actions.js
+import shop from '../api/shop'
+
+export const getAllProducts = ({ dispatch }) => {
+  dispatch('REQUEST_PRODUCTS')
+  shop.getProducts(products => {
+    dispatch('RECEIVE_PRODUCTS', products)
+  })
+}
+```
+
+``` js
+// actions.spec.js
+
+// use require syntax for inline loaders.
+// with inject-loader, this returns a module factory
+// that allows us to inject mocked dependencies.
+import { expect } from 'chai'
+const actionsInjector = require('inject!./actions')
+
+// create the module with our mocks
+const actions = actionsInjector({
+  '../api/shop': {
+    getProducts (cb) {
+      setTimeout(() => {
+        cb([ /* mocked response */ ])
+      }, 100)
+    }
+  }
+})
+
+// helper for testing action with expected mutations
+const testAction = (action, state, expectedMutations, done) => {
+  let count = 0
+  // mock dispatch
+  const dispatch = (name, payload) => {
+    const mutation = expectedMutations[count]
+    expect(mutation.name).to.equal(name)
+    if (payload) {
+      expect(mutation.payload).to.deep.equal(payload)
+    }
+    count++
+    if (count >= expectedMutations.length) {
+      done()
+    }
+  }
+  // call the action with mocked store
+  action({
+    dispatch,
+    state
+  })
+}
+
+describe('actions', () => {
+  it('getAllProducts', done => {
+    testAction(actions.getAllProducts, {}, [
+      { name: 'REQUEST_PRODUCTS' },
+      { name: 'RECEIVE_PRODUCTS', payload: [ /* mocked response */ ] }
+    ], done)
+  })
+})
+```
+
+### Running in Node
+
+Create the following webpack config:
+
+``` js
+module.exports = {
+  entry: './test.js',
+  output: {
+    path: __dirname,
+    filename: 'test-bundle.js'
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        loader: 'babel',
+        exclude: /node_modules/
+      }
+    ]
+  },
+  babel: {
+    presets: ['es2015']
+  }
+}
+```
+
+Then:
+
+``` bash
+webpack
+mocha test-bundle.js
+```
+
+### Running in Browser
+
+1. Install `mocha-loader`
+2. Change the `entry` from the Webpack config above to `'mocha!babel!./test.js'`. 
+3. Start `webpack-dev-server` using the config
+4. Go to `localhost:8080/webpack-dev-server/test-bundle`.

二进制
docs/ja/vuex.png