From d7c5babf1f66309cedfa4dc3537df82f84d01618 Mon Sep 17 00:00:00 2001 From: Brendan Date: Sun, 25 Dec 2016 21:16:30 +0100 Subject: [PATCH 1/6] better vuex store --- examples/vuex-store/pages/index.vue | 5 +++- examples/vuex-store/pages/todos.vue | 41 +++++++++++++++++++++++++++++ examples/vuex-store/store/index.js | 20 ++++---------- examples/vuex-store/store/todos.js | 20 ++++++++++++++ lib/app/index.js | 2 +- lib/app/store.js | 18 +++++++++++++ lib/build/index.js | 4 +++ lib/nuxt.js | 2 +- 8 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 examples/vuex-store/pages/todos.vue create mode 100644 examples/vuex-store/store/todos.js create mode 100644 lib/app/store.js diff --git a/examples/vuex-store/pages/index.vue b/examples/vuex-store/pages/index.vue index 3ebbfdcac6..55131f0fee 100644 --- a/examples/vuex-store/pages/index.vue +++ b/examples/vuex-store/pages/index.vue @@ -1,8 +1,11 @@ diff --git a/examples/vuex-store/pages/todos.vue b/examples/vuex-store/pages/todos.vue new file mode 100644 index 0000000000..7b5d30dfb3 --- /dev/null +++ b/examples/vuex-store/pages/todos.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/examples/vuex-store/store/index.js b/examples/vuex-store/store/index.js index e5ad937c65..a4521c2fc2 100644 --- a/examples/vuex-store/store/index.js +++ b/examples/vuex-store/store/index.js @@ -1,17 +1,7 @@ -import Vue from 'vue' -import Vuex from 'vuex' +export const state = { counter: 0 } -Vue.use(Vuex) - -const store = new Vuex.Store({ - state: { - counter: 0 - }, - mutations: { - increment (state) { - state.counter++ - } +export const mutations = { + increment (state) { + state.counter++ } -}) - -export default store +} diff --git a/examples/vuex-store/store/todos.js b/examples/vuex-store/store/todos.js new file mode 100644 index 0000000000..ca87fe5a3f --- /dev/null +++ b/examples/vuex-store/store/todos.js @@ -0,0 +1,20 @@ +export const state = { + list: [] +} + +export const mutations = { + add (state, { text }) { + state.list.push({ + text, + done: false + }) + }, + + delete (state, { todo }) { + state.list.splice(state.list.indexOf(todo), 1) + }, + + toggle (state, { todo }) { + todo.done = !todo.done + } +} diff --git a/lib/app/index.js b/lib/app/index.js index 64eba583f1..e851d1dc67 100644 --- a/lib/app/index.js +++ b/lib/app/index.js @@ -3,7 +3,7 @@ import Vue from 'vue' import Meta from 'vue-meta' import router from './router.js' -<% if (store) { %>import store from '~store/index.js'<% } %> +<% if (store) { %>import store from './store.js'<% } %> import NuxtChild from './components/nuxt-child.js' import NuxtLink from './components/nuxt-link.js' import Nuxt from './components/nuxt.vue' diff --git a/lib/app/store.js b/lib/app/store.js new file mode 100644 index 0000000000..dc8e030b0f --- /dev/null +++ b/lib/app/store.js @@ -0,0 +1,18 @@ +import Vue from 'vue' +import Vuex from 'vuex' +Vue.use(Vuex) + +const files = require.context('~store', false, /^\.\/.*\.js$/) + +const storeData = { modules: {} } +for (let filename of files.keys()) { + let name = filename.replace(/^\.\//, '').replace(/\.js$/, '') + if (name === 'index') { + Object.assign(storeData, files(filename)) + } else { + storeData.modules[name] = files(filename) + storeData.modules[name].namespaced = true + } +} + +export default new Vuex.Store(storeData) diff --git a/lib/build/index.js b/lib/build/index.js index 4e7a8f15dc..e6adad90c8 100644 --- a/lib/build/index.js +++ b/lib/build/index.js @@ -191,6 +191,10 @@ function * generateRoutesAndFiles () { templatesFiles.push('layouts/default.vue') layouts.default = r(__dirname, 'app', 'layouts', 'default.vue') } + // Add store if needed + if (this.options.store) { + templatesFiles.push('store.js') + } let moveTemplates = templatesFiles.map((file) => { return readFile(r(__dirname, 'app', file), 'utf8') .then((fileContent) => { diff --git a/lib/nuxt.js b/lib/nuxt.js index a3ca5b06c8..cdfa088c92 100644 --- a/lib/nuxt.js +++ b/lib/nuxt.js @@ -51,7 +51,7 @@ class Nuxt { this.dir = (typeof options.rootDir === 'string' && options.rootDir ? options.rootDir : process.cwd()) this.srcDir = (typeof options.srcDir === 'string' && options.srcDir ? resolve(this.dir, options.srcDir) : this.dir) // If store defined, update store options to true - if (fs.existsSync(join(this.srcDir, 'store', 'index.js'))) { + if (fs.existsSync(join(this.srcDir, 'store'))) { this.options.store = true } // Template From c2d8329bc0a88ce4864e8599d724b006898a23e4 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 26 Dec 2016 15:55:00 +0100 Subject: [PATCH 2/6] duplicate examples --- examples/vuex-store-2/README.md | 83 +++++++++++++++++++ examples/vuex-store-2/package.json | 11 +++ examples/vuex-store-2/pages/about.vue | 8 ++ examples/vuex-store-2/pages/index.vue | 29 +++++++ .../pages/todos.vue | 0 examples/vuex-store-2/store/index.js | 7 ++ .../store/todos.js | 0 examples/vuex-store/pages/index.vue | 5 +- examples/vuex-store/store/index.js | 20 +++-- 9 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 examples/vuex-store-2/README.md create mode 100644 examples/vuex-store-2/package.json create mode 100644 examples/vuex-store-2/pages/about.vue create mode 100644 examples/vuex-store-2/pages/index.vue rename examples/{vuex-store => vuex-store-2}/pages/todos.vue (100%) create mode 100644 examples/vuex-store-2/store/index.js rename examples/{vuex-store => vuex-store-2}/store/todos.js (100%) diff --git a/examples/vuex-store-2/README.md b/examples/vuex-store-2/README.md new file mode 100644 index 0000000000..4bf9cbec9e --- /dev/null +++ b/examples/vuex-store-2/README.md @@ -0,0 +1,83 @@ +# Nuxt.js with Vuex + +> Using a store to manage the state is important to every big application, that's why nuxt.js implement Vuex in its core. + +## Activating the store + +Nuxt.js will try to `require('./store/index.js')`, if exists, it will import `Vuex`, add it to the vendors and add the `store` option to the root `Vue` instance. + +## Create the store folder + +Let's create a file `store/index.js`: + +```js +import Vue from 'vue' +import Vuex from 'vuex' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + state: { + counter: 0 + }, + mutations: { + increment (state) { + state.counter++ + } + } +}) + +export default store +``` + +> We don't need to install `Vuex` since it's shipped with nuxt.js + +## VoilĂ  ! + +We can now use `this.$store` inside our `.vue` files. + +```html + +``` + +## fetch (context) + +> Used to fill the store before rendering the page + +The `fetch` method, *if set*, is called every time before loading the component (*only if attached to a route*). It can be called from the server-side or before navigating to the corresponding route. + +The `fetch` method receives the context as the first argument, we can use it to fetch some data and fill the store. To make the fetch method asynchronous, **return a Promise**, nuxt.js will wait for the promise to be resolved before rendering the Component. + +For example: +```js +export default { + fetch ({ store, params }) { + return axios.get('http://my-url') + .then((res) => { + store.commit('setUser', res.data) + }) + } +} +``` + +## Context + +To see the list of available keys in `context`, take a look at [this documentation](https://github.com/nuxt/nuxt.js/tree/master/examples/async-data#context). + +## Action `nuxtServerInit` + +If we define the action `nuxtServerInit` in our store, Nuxt.js will call it with the context. It can be useful when having some data on the server we want to give directly to the client-side, for example, the authenticated user: +```js +// store/index.js +actions: { + nuxtServerInit ({ commit }, { req }) { + if (req.authUser) { + commit('user', req.authUser) + } + } +} +``` + +The context given to `nuxtServerInit` is the same as the `data` of `fetch` method except `context.redirect()` and `context.error()` are omitted. diff --git a/examples/vuex-store-2/package.json b/examples/vuex-store-2/package.json new file mode 100644 index 0000000000..3bd4273fa4 --- /dev/null +++ b/examples/vuex-store-2/package.json @@ -0,0 +1,11 @@ +{ + "name": "nuxt-vuex-store", + "dependencies": { + "nuxt": "latest" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/vuex-store-2/pages/about.vue b/examples/vuex-store-2/pages/about.vue new file mode 100644 index 0000000000..f489bfe089 --- /dev/null +++ b/examples/vuex-store-2/pages/about.vue @@ -0,0 +1,8 @@ + diff --git a/examples/vuex-store-2/pages/index.vue b/examples/vuex-store-2/pages/index.vue new file mode 100644 index 0000000000..55131f0fee --- /dev/null +++ b/examples/vuex-store-2/pages/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/examples/vuex-store/pages/todos.vue b/examples/vuex-store-2/pages/todos.vue similarity index 100% rename from examples/vuex-store/pages/todos.vue rename to examples/vuex-store-2/pages/todos.vue diff --git a/examples/vuex-store-2/store/index.js b/examples/vuex-store-2/store/index.js new file mode 100644 index 0000000000..a4521c2fc2 --- /dev/null +++ b/examples/vuex-store-2/store/index.js @@ -0,0 +1,7 @@ +export const state = { counter: 0 } + +export const mutations = { + increment (state) { + state.counter++ + } +} diff --git a/examples/vuex-store/store/todos.js b/examples/vuex-store-2/store/todos.js similarity index 100% rename from examples/vuex-store/store/todos.js rename to examples/vuex-store-2/store/todos.js diff --git a/examples/vuex-store/pages/index.vue b/examples/vuex-store/pages/index.vue index 55131f0fee..3ebbfdcac6 100644 --- a/examples/vuex-store/pages/index.vue +++ b/examples/vuex-store/pages/index.vue @@ -1,11 +1,8 @@ diff --git a/examples/vuex-store/store/index.js b/examples/vuex-store/store/index.js index a4521c2fc2..e5ad937c65 100644 --- a/examples/vuex-store/store/index.js +++ b/examples/vuex-store/store/index.js @@ -1,7 +1,17 @@ -export const state = { counter: 0 } +import Vue from 'vue' +import Vuex from 'vuex' -export const mutations = { - increment (state) { - state.counter++ +Vue.use(Vuex) + +const store = new Vuex.Store({ + state: { + counter: 0 + }, + mutations: { + increment (state) { + state.counter++ + } } -} +}) + +export default store From e2d5d2fa617b5ed807bb387e0236ff9a09aadde7 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 26 Dec 2016 17:19:10 +0100 Subject: [PATCH 3/6] Accept both ways of creating the store --- lib/app/store.js | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/app/store.js b/lib/app/store.js index dc8e030b0f..3cdf3ccc84 100644 --- a/lib/app/store.js +++ b/lib/app/store.js @@ -3,16 +3,36 @@ import Vuex from 'vuex' Vue.use(Vuex) const files = require.context('~store', false, /^\.\/.*\.js$/) +function getModule (filename) { + let file = files(filename) + return file.default + ? file.default + : file +} -const storeData = { modules: {} } -for (let filename of files.keys()) { - let name = filename.replace(/^\.\//, '').replace(/\.js$/, '') - if (name === 'index') { - Object.assign(storeData, files(filename)) +let store +let storeData = {} + +// Check if store/index.js returns a vuex store +if (files.keys().includes('./index.js')) { + let mainModule = getModule('./index.js') + if (mainModule.commit) { + store = mainModule } else { - storeData.modules[name] = files(filename) - storeData.modules[name].namespaced = true + storeData = mainModule } } -export default new Vuex.Store(storeData) +// Generate the store if there is no store yet +if (store == null) { + storeData.modules = storeData.modules || {} + for (let filename of files.keys()) { + let name = filename.replace(/^\.\//, '').replace(/\.js$/, '') + if (name === 'index') continue + storeData.modules[name] = getModule(filename) + storeData.modules[name].namespaced = true + } + store = new Vuex.Store(storeData) +} + +export default store From 09d0d03198fa7b3097b71655e14a1a61807b678b Mon Sep 17 00:00:00 2001 From: Brendan Date: Fri, 30 Dec 2016 10:11:55 +0100 Subject: [PATCH 4/6] documenting store generation --- examples/vuex-store-2/README.md | 113 ++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/examples/vuex-store-2/README.md b/examples/vuex-store-2/README.md index 4bf9cbec9e..8146795f6c 100644 --- a/examples/vuex-store-2/README.md +++ b/examples/vuex-store-2/README.md @@ -1,33 +1,51 @@ -# Nuxt.js with Vuex +# Nuxt.js with Vuex 2 > Using a store to manage the state is important to every big application, that's why nuxt.js implement Vuex in its core. +> Alternative way of creating a store modularly. + ## Activating the store -Nuxt.js will try to `require('./store/index.js')`, if exists, it will import `Vuex`, add it to the vendors and add the `store` option to the root `Vue` instance. +Nuxt.js will look for the `./store/` directory, if it exists, its will import and use Vuex. If there is no `./store/index.js` file that returns a store, Nuxt.js will go through all files of the `./store/` directory and create a store with a module for each file (`./store/index.js` being "root" module). ## Create the store folder Let's create a file `store/index.js`: ```js -import Vue from 'vue' -import Vuex from 'vuex' +export const state = { counter: 0 } -Vue.use(Vuex) - -const store = new Vuex.Store({ - state: { - counter: 0 - }, - mutations: { - increment (state) { - state.counter++ - } +export const mutations = { + increment (state) { + state.counter++ } -}) +} +``` -export default store +and +`store/todos.js`: + +```js +export const state = { + list: [] +} + +export const mutations = { + add (state, { text }) { + state.list.push({ + text, + done: false + }) + }, + + delete (state, { todo }) { + state.list.splice(state.list.indexOf(todo), 1) + }, + + toggle (state, { todo }) { + todo.done = !todo.done + } +} ``` > We don't need to install `Vuex` since it's shipped with nuxt.js @@ -42,42 +60,35 @@ We can now use `this.$store` inside our `.vue` files. ``` -## fetch (context) - -> Used to fill the store before rendering the page - -The `fetch` method, *if set*, is called every time before loading the component (*only if attached to a route*). It can be called from the server-side or before navigating to the corresponding route. - -The `fetch` method receives the context as the first argument, we can use it to fetch some data and fill the store. To make the fetch method asynchronous, **return a Promise**, nuxt.js will wait for the promise to be resolved before rendering the Component. - -For example: +The store will be as such: ```js -export default { - fetch ({ store, params }) { - return axios.get('http://my-url') - .then((res) => { - store.commit('setUser', res.data) - }) - } -} -``` - -## Context - -To see the list of available keys in `context`, take a look at [this documentation](https://github.com/nuxt/nuxt.js/tree/master/examples/async-data#context). - -## Action `nuxtServerInit` - -If we define the action `nuxtServerInit` in our store, Nuxt.js will call it with the context. It can be useful when having some data on the server we want to give directly to the client-side, for example, the authenticated user: -```js -// store/index.js -actions: { - nuxtServerInit ({ commit }, { req }) { - if (req.authUser) { - commit('user', req.authUser) +new Vuex.Store({ + state: { counter: 0 }, + mutations: { + increment (state) { + state.counter++ + } + }, + modules: { + todos: { + state: { + list: [] + }, + mutations: { + add (state, { text }) { + state.list.push({ + text, + done: false + }) + }, + delete (state, { todo }) { + state.list.splice(state.list.indexOf(todo), 1) + }, + toggle (state, { todo }) { + todo.done = !todo.done + } + } } } -} +}) ``` - -The context given to `nuxtServerInit` is the same as the `data` of `fetch` method except `context.redirect()` and `context.error()` are omitted. From 9fd04a6ec846f6893b6fc05b06a49196f47f4d9b Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 2 Jan 2017 10:13:53 +0100 Subject: [PATCH 5/6] safer store with try catch --- lib/app/store.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/app/store.js b/lib/app/store.js index 3cdf3ccc84..7722a7fd15 100644 --- a/lib/app/store.js +++ b/lib/app/store.js @@ -2,7 +2,16 @@ import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) -const files = require.context('~store', false, /^\.\/.*\.js$/) +let files +let filenames = [] + +try { + files = require.context('~store', false, /^\.\/.*\.js$/) + filenames = files.keys() +} catch (e) { + console.warn('Nuxt.js store:', e.message) +} + function getModule (filename) { let file = files(filename) return file.default @@ -14,7 +23,7 @@ let store let storeData = {} // Check if store/index.js returns a vuex store -if (files.keys().includes('./index.js')) { +if (filenames.includes('./index.js')) { let mainModule = getModule('./index.js') if (mainModule.commit) { store = mainModule @@ -26,7 +35,7 @@ if (files.keys().includes('./index.js')) { // Generate the store if there is no store yet if (store == null) { storeData.modules = storeData.modules || {} - for (let filename of files.keys()) { + for (let filename of filenames) { let name = filename.replace(/^\.\//, '').replace(/\.js$/, '') if (name === 'index') continue storeData.modules[name] = getModule(filename) From 13aff87b9badc96b1df4096c7f95c581f017a818 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 2 Jan 2017 10:50:18 +0100 Subject: [PATCH 6/6] no more includes --- lib/app/store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app/store.js b/lib/app/store.js index 7722a7fd15..adbc0ec4d2 100644 --- a/lib/app/store.js +++ b/lib/app/store.js @@ -23,7 +23,7 @@ let store let storeData = {} // Check if store/index.js returns a vuex store -if (filenames.includes('./index.js')) { +if (filenames.indexOf('./index.js') !== -1) { let mainModule = getModule('./index.js') if (mainModule.commit) { store = mainModule