Commit 530a486c authored by Evan You's avatar Evan You

restructure

parent 6482f808
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
import { createStore } from './store'
import { createRouter } from './router'
import { sync } from 'vuex-router-sync'
import * as filters from './filters'
// sync the router with the vuex store.
// this registers `store.state.route`
sync(store, router)
// register global utility filters.
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
// create the app instance.
// here we inject the router and store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = new Vue({
// Expose a factory function that creates a fresh set of store, router,
// app instances on each call (which is called for each SSR request)
export function createApp () {
// create store and router instances
const store = createStore()
const router = createRouter()
// sync the router with the vuex store.
// this registers `store.state.route`
sync(store, router)
// create the app instance.
// here we inject the router and store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = new Vue({
router,
store,
render: h => h(App)
})
})
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
export { app, router, store }
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return { app, router, store }
}
import 'es6-promise/auto'
import { app, store, router } from './app'
import { createApp } from './app'
const { app, router, store } = createApp()
// prime the store with server-initialized state.
// the state is determined during SSR and inlined in the page markup.
......
import { app, router, store } from './app'
import { createApp } from './app'
const isDev = process.env.NODE_ENV !== 'production'
......@@ -10,6 +10,8 @@ const isDev = process.env.NODE_ENV !== 'production'
export default context => {
const s = isDev && Date.now()
const { app, router, store } = createApp()
return new Promise((resolve, reject) => {
// set router's location
router.push(context.url)
......
......@@ -3,24 +3,12 @@ import Router from 'vue-router'
Vue.use(Router)
// We are using Webpack code splitting here so that each route's associated
// component code is loaded on-demand only when the route is visited.
// It's actually not really necessary for a small project of this size but
// the goal is to demonstrate how to do it.
//
// Note that the dynamic import syntax should actually be just `import()`
// but buble/acorn doesn't support parsing that syntax until it's stage 4
// so we use the old System.import here instead.
//
// If using Babel, `import()` can be supported via
// babel-plugin-syntax-dynamic-import.
import createListView from '../views/CreateListView'
import ItemView from '../views/ItemView.vue'
import UserView from '../views/UserView.vue'
const createListView = name => () =>
System.import('../views/CreateListView').then(m => m.createListView(name))
const ItemView = () => System.import('../views/ItemView.vue')
const UserView = () => System.import('../views/UserView.vue')
export default new Router({
export function createRouter () {
return new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: [
......@@ -33,4 +21,5 @@ export default new Router({
{ path: '/user/:id', component: UserView },
{ path: '/', redirect: '/top' }
]
})
})
}
import {
fetchUser,
fetchItems,
fetchIdsByType
} from './api'
export default {
// ensure data for rendering given list type
FETCH_LIST_DATA: ({ commit, dispatch, state }, { type }) => {
commit('SET_ACTIVE_TYPE', { type })
return fetchIdsByType(type)
.then(ids => commit('SET_LIST', { type, ids }))
.then(() => dispatch('ENSURE_ACTIVE_ITEMS'))
},
// ensure all active items are fetched
ENSURE_ACTIVE_ITEMS: ({ dispatch, getters }) => {
return dispatch('FETCH_ITEMS', {
ids: getters.activeIds
})
},
FETCH_ITEMS: ({ commit, state }, { ids }) => {
// on the client, the store itself serves as a cache.
// only fetch items that we do not already have, or has expired (3 minutes)
const now = Date.now()
ids = ids.filter(id => {
const item = state.items[id]
if (!item) {
return true
}
if (now - item.__lastUpdated > 1000 * 60 * 3) {
return true
}
return false
})
if (ids.length) {
return fetchItems(ids).then(items => commit('SET_ITEMS', { items }))
} else {
return Promise.resolve()
}
},
FETCH_USER: ({ commit, state }, { id }) => {
return state.users[id]
? Promise.resolve(state.users[id])
: fetchUser(id).then(user => commit('SET_USER', { user }))
}
}
export default {
// ids of the items that should be currently displayed based on
// current list type and current pagination
activeIds (state) {
const { activeType, itemsPerPage, lists } = state
const page = Number(state.route.params.page) || 1
if (activeType) {
const start = (page - 1) * itemsPerPage
const end = page * itemsPerPage
return lists[activeType].slice(start, end)
} else {
return []
}
},
// items that should be currently displayed.
// this Array may not be fully fetched.
activeItems (state, getters) {
return getters.activeIds.map(id => state.items[id]).filter(_ => _)
}
}
import Vue from 'vue'
import Vuex from 'vuex'
import { fetchItems, fetchIdsByType, fetchUser } from './api'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
export function createStore () {
return new Vuex.Store({
state: {
activeType: null,
itemsPerPage: 20,
......@@ -18,94 +21,8 @@ const store = new Vuex.Store({
job: []
}
},
actions: {
// ensure data for rendering given list type
FETCH_LIST_DATA: ({ commit, dispatch, state }, { type }) => {
commit('SET_ACTIVE_TYPE', { type })
return fetchIdsByType(type)
.then(ids => commit('SET_LIST', { type, ids }))
.then(() => dispatch('ENSURE_ACTIVE_ITEMS'))
},
// ensure all active items are fetched
ENSURE_ACTIVE_ITEMS: ({ dispatch, getters }) => {
return dispatch('FETCH_ITEMS', {
ids: getters.activeIds
})
},
FETCH_ITEMS: ({ commit, state }, { ids }) => {
// on the client, the store itself serves as a cache.
// only fetch items that we do not already have, or has expired (3 minutes)
const now = Date.now()
ids = ids.filter(id => {
const item = state.items[id]
if (!item) {
return true
}
if (now - item.__lastUpdated > 1000 * 60 * 3) {
return true
}
return false
actions,
mutations,
getters
})
if (ids.length) {
return fetchItems(ids).then(items => commit('SET_ITEMS', { items }))
} else {
return Promise.resolve()
}
},
FETCH_USER: ({ commit, state }, { id }) => {
return state.users[id]
? Promise.resolve(state.users[id])
: fetchUser(id).then(user => commit('SET_USER', { user }))
}
},
mutations: {
SET_ACTIVE_TYPE: (state, { type }) => {
state.activeType = type
},
SET_LIST: (state, { type, ids }) => {
state.lists[type] = ids
},
SET_ITEMS: (state, { items }) => {
items.forEach(item => {
if (item) {
Vue.set(state.items, item.id, item)
}
})
},
SET_USER: (state, { user }) => {
Vue.set(state.users, user.id, user)
}
},
getters: {
// ids of the items that should be currently displayed based on
// current list type and current pagination
activeIds (state) {
const { activeType, itemsPerPage, lists } = state
const page = Number(state.route.params.page) || 1
if (activeType) {
const start = (page - 1) * itemsPerPage
const end = page * itemsPerPage
return lists[activeType].slice(start, end)
} else {
return []
}
},
// items that should be currently displayed.
// this Array may not be fully fetched.
activeItems (state, getters) {
return getters.activeIds.map(id => state.items[id]).filter(_ => _)
}
}
})
export default store
}
import Vue from 'vue'
export default {
SET_ACTIVE_TYPE: (state, { type }) => {
state.activeType = type
},
SET_LIST: (state, { type, ids }) => {
state.lists[type] = ids
},
SET_ITEMS: (state, { items }) => {
items.forEach(item => {
if (item) {
Vue.set(state.items, item.id, item)
}
})
},
SET_USER: (state, { user }) => {
Vue.set(state.users, user.id, user)
}
}
......@@ -3,7 +3,7 @@ import ItemList from '../components/ItemList.vue'
// This is a factory function for dynamically creating root-level list views,
// since they share most of the logic except for the type of items to display.
// They are essentially higher order components wrapping ItemList.vue.
export function createListView (type) {
export default function createListView (type) {
return {
name: `${type}-stories-view`,
// this will be called during SSR to pre-fetch data into the store!
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment