經過 express 取得 cookies
在 server.js 上引入 cookies 解析中間件
const cookieParser = require('cookie-parser')
而且在路由前面 use
app.use(cookieParser())
部份代碼
const fs = require('fs')
const path = require('path')
const LRU = require('lru-cache')
const express = require('express')
const favicon = require('serve-favicon')
const compression = require('compression')
const microcache = require('route-cache')
const resolve = file => path.resolve(__dirname, file)
const { createBundleRenderer } = require('vue-server-renderer')
const isProd = process.env.NODE_ENV === 'production'
const useMicroCache = process.env.MICRO_CACHE !== 'false'
const serverInfo =
`express/${require('express/package.json').version} ` +
`vue-server-renderer/${require('vue-server-renderer/package.json').version}`
const app = express()
// cookie
const cookieParser = require('cookie-parser')
// 而且在路由前面 use
app.use(cookieParser())
將 cookies 注入 render 的上下文中
app.get('*', (req, res) => {
// 其餘代碼省略
const context = {
url: req.url,
cookies: req.cookies
}
const renderStream = renderer.renderToStream(context)
// 其餘代碼省略
})
vue 2.0的則改爲這樣
function render (req, res) {
const s = Date.now()
res.setHeader("Content-Type", "text/html")
// res.setHeader("Server", serverInfo)
// 改header
res.setHeader("Server", 'zibuzhai.com')
res.setHeader("X-Powered-By", 'zibuzhai.com')
const handleError = err => {
if (err.url) {
res.redirect(err.url)
} else if(err.code === 404) {
res.status(404).send('404 | Page Not Found')
} else {
// Render Error Page or Redirect
res.status(500).send('500 | Internal Server Error')
console.error(`error during render : ${req.url}`)
console.error(err.stack)
}
}
const context = {
title: 'title HN 2.0', // default title
keywords: 'keywords HN 2.0', // default keywords
description: 'description HN 2.0', // default description
url: req.url,
cookies: req.cookies
}
renderer.renderToString(context, (err, html) => {
if (err) {
return handleError(err)
}
res.send(html)
if (!isProd) {
console.log(`whole request: ${Date.now() - s}ms`)
}
})
}
app.get('*', isProd ? render : (req, res) => {
readyPromise.then(() => render(req, res))
})
將 cookies 注入 vuex 的 store 中
在server-entry.js文件中, 將 cookies 注入到 store 中
export default context => {
// 其餘代碼省略
if (context.cookies) {
store.state.cookies = context.cookies
}
// 其餘代碼省略
}
vue2.0後代碼
export default context => {
return new Promise((resolve, reject) => {
const s = isDev && Date.now()
const { app, router, store } = createApp()
if (context.cookies) {
store.state.cookies = context.cookies
}
const { url } = context
const { fullPath } = router.resolve(url).route
if (fullPath !== url) {
return reject({ url: fullPath })
}
// set router's location
router.push(url)
// wait until router has resolved possible async hooks
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// no matched routes
if (!matchedComponents.length) {
return reject({ code: 404 })
}
// Call fetchData hooks on components matched by the route.
// A preFetch hook dispatches a store action and returns a Promise,
// which is resolved when the action is complete and store state has been
// updated.
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute,
cookies: context.cookies
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
// After all preFetch hooks are resolved, our store is now
// filled with the state needed to render the app.
// Expose the state on the render context, and let the request handler
// inline the state in the HTML response. This allows the client-side
// store to pick-up the server-side state without having to duplicate
// the initial data fetching on the client.
context.state = store.state
resolve(app)
}).catch(reject)
}, reject)
})
}
這樣就能夠在組件中取到 cookies 了.
封裝請求
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import settings from '../config'
// import store from '../store/index'
// import router from '../router/index'
import createRouter from '../router/index'
import createStore from '../store/index'
import Cookies from 'js-cookie';
Vue.use(VueAxios, axios)
const router = createRouter()
// const store = createStore()
// 動態設置本地和線上接口域名
Vue.axios.defaults.baseURL = settings.host
// 初始化
var configDefault = Vue.axios.defaults.headers
const parseCookie = cookies => {
let cookie = ''
if (cookies) {
Object.keys(cookies).forEach(item => {
cookie+= item + '=' + cookies[item] + '; '
})
}
return cookie
}
export const request = ({type = 'post', url, data = {}, config = {}, globalLoading = false} = {},cookies) => {
let requestCookies = ''
if (JSON.stringify(cookies) && JSON.stringify(cookies) != "{}") {
Object.keys(cookies).forEach(item => {
requestCookies += item + '=' + cookies[item] + '; '
})
}
if (requestCookies) {
let cookie = requestCookies
configDefault = {headers: {
'X-Requested-With': 'XMLHttpRequest',
cookie
}
}
// config = configDefault
}
config = configDefault
let datas = type === 'get' ? {params: data} : data
return Vue.axios[type](url, datas, config)
.catch(response => {
/* eslint-disable prefer-promise-reject-errors */
return Promise.reject({code: 500, message: '服務器繁忙!'})
})
.then((response) => {
// 統一loading
// store.commit('showAjaxLoading',false)
let {data} = response
if (typeof data === 'string') { // 轉換返回json
data = JSON.parse(data)
}
console.log(data)
if (data && data.code == 200) {
// Promise.reject(data)
// 爲何不是router.query??
var redirect = router.currentRoute.query.redirect
if (redirect) {
console.log(redirect)
// 用location 不會報promise錯,用router的跳轉會報錯
location.href = redirect
Promise.reject(data)
}
// 請求成功
return data
}
if (data.code === 400) {
// 統一報錯彈窗
// store.commit('showAjaxMessage',true)
return data
}
if (data && data.code === 401) { // 沒有登陸權限
// router.replace({
// name: 'not',
// query: {redirect: router.currentRoute.path}
// })
// return data;
// location.href = '/#login'
// router.push('login?redirect='+router.currentRoute.path)
// router.push({ path: 'login', query: { redirect: router.currentRoute.path }})
// 用location 不會報promise錯,用router的跳轉會報錯
// location.href = '/login?redirect='+router.currentRoute.path
// Promise.reject(data)
// console.log(router.currentRoute)
return data.data
} else {
return data;
}
return Promise.reject(data)
})
.catch(err => {
return Promise.reject(err)
})
}
發起帶 cookies 參數的請求
export default {
name: 'Test',
data () {
return {
title:'服務端渲染test',
data: this.$store.state.count
}
},
mounted () {
},
created () {
// 檢查cookie,ssr與spa不同的地方
makelaravelcookie(this.$store.state.cookies)
console.log('this.$store.state.count')
console.log(this.$store.state.count)
},
asyncData ({ store, route }) {
console.log('asyncData')
// 觸發 action 後,會返回 Promise
return store.dispatch('increment',store)
},
computed: {
user () {
// return 31
return this.$store.state.count
}
},
// seo
title () {
return this.user
},
}
vue 服務端渲染是經過asyncData來獲取數據的