您好,登錄后才能下訂單哦!
本來想前面寫點什么的, 還是算了, 直接說思路吧.
從 Vue2.3 版本后, SSR 的 cookies, 就變成一個無比麻煩的問題, 具體請訪問官網文檔: https://ssr.vuejs.org/zh/api.html#runinnewcontext
之前也說不少的思路, 可是都覺得不怎么好用, 雖然都能解決問題, 今天再說一種思路
因為 Vue2.3 以后, bundle 代碼將與服務器進程在同一個 global 上下文中運行, 所以不能再將 cookies 丟到 global 給 api 使用, 否則就會出現 cookies 污染
Vue2.3 以后, 我們需要為每個請求創建一個新的根 Vue 實例, 同樣的, router、store 也需要, 所以, 我們的思路也在此, 將封裝后的 api 注入到這 3 個實例當中去, 保證每個請求的 api 都是獨立, 那么就剩一個問題, 注入到哪個實例里面去!?
api 請求用到最多的兩個地方就是: 組件和 vuex 的 actions 里, 這兩個地方都有 store 的影子, 所以, 注入到 store 中, 毫無疑問是最好的
那么下面就來操作下:
1. 修改 api, 讓 api 文件導出一個工廠函數
import axios from 'axios' import qs from 'qs' import md5 from 'md5' import config from './config-server' const parseCookie = cookies => { let cookie = '' Object.keys(cookies).forEach(item => { cookie += item + '=' + cookies[item] + '; ' }) return cookie } export const api = cookies => { return { api: axios.create({ baseURL: config.api, headers: { 'X-Requested-With': 'XMLHttpRequest', cookie: parseCookie(cookies) }, timeout: config.timeout }), post(url, data) { return this.api({ method: 'post', url, data: qs.stringify(data), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } }) }, async get(url, params) { return this.api({ method: 'get', url, params }) } } }
把 cookies 當參數傳進工廠函數, 給 axios 使用
示例文件1: src/api/index-server.js
示例文件2: src/api/index-client.js
2. 修改 server.js 文件, 將 cookies 注入 renderer 的 上下文中
// 前后代碼略 const context = { title: 'M.M.F 小屋', url: req.url, cookies: req.cookies } renderer.renderToString(context, (err, html) => { if (err) { return handleError(err) } res.end(html) if (!isProd) { console.log(`whole request: ${Date.now() - s}ms`) } }) // 前后代碼略
示例文件: server.js
3. 修改服務端入口文件
import { createApp } from './app' import { api } from '~api' export default function(context) { return new Promise((resolve, reject) => { const s = Date.now() const { app, router, store } = createApp() const url = context.url const fullPath = router.resolve(url).route.fullPath if (fullPath !== url) { reject({ url: fullPath }) } router.push(url) router.onReady(() => { const matchedComponents = router.getMatchedComponents() if (!matchedComponents.length) { reject({ code: 404 }) } // 注意這里, 在步驟2中, context里已經帶有cookies了 // 創建一個新的api實例, 并把cookies傳進去 // 同時注入store和根狀態中 // 注入 store 中, 可以方便在組件中用 // 注入 根狀態中, 可以方便在 vuex 的 actions 中用 store.$api = store.state.$api = api(context.cookies) Promise.all( matchedComponents.map( ({ asyncData }) => asyncData && asyncData({ store, route: router.currentRoute, cookies: context.cookies, isServer: true, isClient: false }) ) ) .then(() => { console.log(`data pre-fetch: ${Date.now() - s}ms`) context.state = store.state context.isProd = process.env.NODE_ENV === 'production' resolve(app) }) .catch(reject) }, reject) }) }
示例文件: src/entry-server.js
4. 修改客戶端入口文件
import api from '~api' // 前后代碼略 const { app, router, store } = createApp() if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) // 客戶端就沒必要用工廠函數了, 用也可以, 但是需注意, api里的屬性必須和服務端的保持一致 store.$api = store.state.$api = api } // 前后代碼略
示例文件: src/entry-client.js
5. 在 vuex 的 actions 中使用
const actions = { async ['getArticleList']( { commit, state, rootState: { $api } // 這里就是前面注入的api }, config ) { const { data: { data, code } } = await $api.get('frontend/article/list', { ...config, cache: true }) if (data && code === 200) { commit('receiveArticleList', { ...config, ...data }) } } }
示例文件: src/store/modules/frontend-article.js
6. 在組件中使用
methods: { async recover(id) { const { data: { code, message } } = await this.$store.$api.get('frontend/comment/recover', { id }) if (code === 200) { this.$store.commit('global/comment/recoverComment', id) } } }
示例文件: src/components/frontend-comment.vue
至此, 全文結束, 完整代碼, 請參考: https://github.com/lincenying/mmf-blog-vue2-pwa-ssr
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。