Vue SSR, 在服务端请求数据时怎么带 cookies?

原创
2018/03/20 22:42
阅读数 4.3K

通过 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来获取数据的

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部