##SPA实例
项目中常会包含多个页面及组件,需要用到 vue-router 来进行路由控制,服务端渲染组件时,都需要从数据库中获取真实数据。
一下强调几个重难点
入口文件分离
服 务端不同于客户端,需要整个应用的 Vue 实例,但可以通过不同的入口文件来进行不同组件的操作,各取所需。
其中client-entry.js和server-entry.js分别是两端的入口文件,
client-entry.js
require('es6-promise').polyfill() // 引入 ES6 语法
import { app, store } from './app' store.replaceState(window.__INITIAL_STATE__) // 获取当前应用状态
app.$mount('#app')
server-entry.js
import { app, router, store } from './app' export default context => {
router.push(context.url) // 将 router 设置成当前环境下的 url
// 手动匹配符合当前路由的组件
return Promise.all(router.getMatchedComponents().map(component => {
// 组件中需要定义 preFetch 函数,用于调用数据接口获取真实数据
if (component.preFetch) {
return component.preFetch(store)
} })).then(() => {
context.initialState = store.state // 在上下文中保存应用状态
// 获取完数据后,返回 app 实例
return app
})
}
##数据接口
前端获取数据利用的是 XMLHttpRequest 对象,而后端 NodeJS 若要发起 HTTP 请求则需要利用自身的 HTTP 模块,需要使用一个第三 方库将两者的调用方式封装起来,使得在浏览器端的时候能使用 XMLHttpRequest,而在 NodeJS 环境中使用 HTTP 模块,避免重复编写两套不同的数据请求接口,
数据接口如下:
// store/api.js
import axios from 'axios'
const defaults = { baseURL: '/api/' }
Object.assign(axios.defaults, defaults)
export const fetchList = () =>
{
return axios.get('/items') }
export const fetchDetail = (id) => {
return axios.get('/items/' + id)
}
##前后端状态统一
服务端完成渲染后,需要将应用的状态(即获取的数据结 构)传递给前端的 Vue.js 实例,使得能够进行数据绑定等初始化行为。由于用户在浏览器端是 可以直接访问任意有效 url 的,也就是说服务端需要处理所有有效请求对应的组件,引入 Vuex 来管理整体的组件状态。
import Vue from 'vue'
import Vuex from 'vuex'
import * as api from './api'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
list: [],
detail: {}
},
actions: {
FETCH_LIST ({commit, state}) {
return api.fetchList()
.then(({data}) => {
commit('SET_LIST', [ data ])
})
},
FETCH_DETAIL ({ commit, state }, id) {
return api.fetchDetail(id)
.then(({data}) => {
commit('SET_DETAIL', data)
})
} },
mutations: {
SET_LIST (state, data) {
state.list = data
},
SET_DETAIL (state, data) {
state.detail = data
}
}
})
export default store