溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于vue-ssr服務(wù)端渲染入門詳解

發(fā)布時間:2020-09-01 11:24:14 來源:腳本之家 閱讀:269 作者:wqzwh 欄目:web開發(fā)

第一部分 基本介紹

1、前言

服務(wù)端渲染實現(xiàn)原理機制:在服務(wù)端拿數(shù)據(jù)進行解析渲染,直接生成html片段返回給前端。然后前端可以通過解析后端返回的html片段到前端頁面,大致有以下兩種形式:

1、服務(wù)器通過模版引擎直接渲染整個頁面,例如java后端的vm模版引擎,php后端的smarty模版引擎。
2、服務(wù)渲染生成html代碼塊, 前端通過AJAX獲取然后使用js動態(tài)添加。

2、服務(wù)端渲染的優(yōu)劣

服務(wù)端渲染能夠解決兩大問題:

1、seo問題,有利于搜索引擎蜘蛛抓取網(wǎng)站內(nèi)容,利于網(wǎng)站的收錄和排名。
2、首屏加載過慢問題,例如現(xiàn)在成熟的SPA項目中,打開首頁需要加載很多資源,通過服務(wù)端渲染可以加速首屏渲染。
同樣服務(wù)端渲染也會有弊端,主要是根據(jù)自己的業(yè)務(wù)場景來選擇適合方式,由于服務(wù)端渲染前端頁面,必將會給服務(wù)器增加壓力。

3、SSR的實現(xiàn)原理

客戶端請求服務(wù)器,服務(wù)器根據(jù)請求地址獲得匹配的組件,在調(diào)用匹配到的組件返回 Promise (官方是preFetch方法)來將需要的數(shù)據(jù)拿到。最后再通過

<script>window.__initial_state=data</script>

將其寫入網(wǎng)頁,最后將服務(wù)端渲染好的網(wǎng)頁返回回去。

接下來客戶端會將vuex將寫入的 initial_state 替換為當前的全局狀態(tài)樹,再用這個狀態(tài)樹去檢查服務(wù)端渲染好的數(shù)據(jù)有沒有問題。遇到?jīng)]被服務(wù)端渲染的組件,再去發(fā)異步請求拿數(shù)據(jù)。說白了就是一個類似React的 shouldComponentUpdate 的Diff操作。

Vue2使用的是單向數(shù)據(jù)流,用了它,就可以通過 SSR 返回唯一一個全局狀態(tài), 并確認某個組件是否已經(jīng)SSR過了。

4、vue后端渲染主要插件:vue-server-renderer

由于virtual dom的引入,使得vue的服務(wù)端渲染成為了可能,下面是官方 vue-server-renderer提供的渲染流程圖:

基于vue-ssr服務(wù)端渲染入門詳解

可以看出vue的后端渲染分三個部分組成:頁面的源碼(source),node層的渲染部分和瀏覽器端的渲染部分。

source分為兩種entry point,一個是前端頁面的入口client entry,主要是實例化Vue對象,將其掛載到頁面中;另外一個是后端渲染服務(wù)入口server entry,主要是控服務(wù)端渲染模塊回調(diào),返回一個Promise對象,最終返回一個Vue對象(經(jīng)過測試,直接返回Vue對象也是可以的);

前面的source部分就是業(yè)務(wù)開發(fā)的代碼,開發(fā)完成之后通過 webpack 進行構(gòu)建,生成對應(yīng)的bundle,這里不再贅述client bundle,就是一個可在瀏覽器端執(zhí)行的打包文件;這里說下server bundle, vue2提供 vue-server-renderer模塊,模塊可以提供兩種render: rendererer/bundleRenderer ,下面分別介紹下這兩種render。

renderer接收一個vue對象 ,然后進行渲染,這種對于簡單的vue對象,可以這么去做,但是對于復(fù)雜的項目,如果使用這種直接require一個vue對象,這個對于服務(wù)端代碼的結(jié)構(gòu)和邏輯都不太友好,首先模塊的狀態(tài)會一直延續(xù)在每個請求渲染請求,我們需要去管理和避免這次渲染請求的狀態(tài)影響到后面的請求,因此vue-server-renderer提供了另外一種渲染模式,通過一個 bundleRenderer去做渲染。

bundleRenderer是較為復(fù)雜項目進行服務(wù)端渲染官方推薦的方式,通過webpack以server entry按照一定的要求打包生成一個 server-bundle,它相當于一個可以給服務(wù)端用的app的打包壓縮文件,每一次調(diào)用都會重新初始化 vue對象,保證了每次請求都是獨立的,對于開發(fā)者來說,只需要專注于當前業(yè)務(wù)就可以,不用為服務(wù)端渲染開發(fā)更多的邏輯代碼。 renderer生成完成之后,都存在兩個接口,分別是renderToString和renderToStream,一個是一次性將頁面渲染成字符串文件,另外一個是流式渲染,適用于支持流的web服務(wù)器,可以是請求服務(wù)的速度更快。

第二部分 從零開始搭建

1、前言

上一節(jié)我們大致講了為什么需要使用vue后端渲染,以及vue后端渲染的基本原理,這節(jié)內(nèi)容我們將從零開始搭建屬于自己的vue后端渲染腳手架,當然不能不參考官方頁面響應(yīng)的實例vue-hackernews-2.0,從零開始搭建項目,源碼在將在下節(jié)與大家共享。

2、前期準備

基本環(huán)境要求:node版本6.10.1以上,npm版本3.10.10以上,本機環(huán)境是這樣的,建議升級到官方最新版本。

使用的技術(shù)棧:

1、vue 2.4.2
2、vuex 2.3.1
3、vue-router 2.7.0
4、vue-server-renderer 2.4.2
5、express 4.15.4
6、axios 0.16.2
7、qs 6.5.0
8、q https://github.com/kriskowal/q.git
9、webpack 3.5.0
10、mockjs 1.0.1-beta3
11、babel 相關(guān)插件

以上是主要是用的技術(shù)棧,在構(gòu)建過程中會是用相應(yīng)的插件依賴包來配合進行壓縮打包,以下是npm init后package.json文件所要添加的依賴包。

"dependencies": {
 "axios": "^0.16.2",
 "es6-promise": "^4.1.1",
 "express": "^4.15.4",
 "lodash": "^4.17.4",
 "q": "git+https://github.com/kriskowal/q.git",
 "qs": "^6.5.0",
 "vue": "^2.4.2",
 "vue-router": "^2.7.0",
 "vue-server-renderer": "^2.4.2",
 "vuex": "^2.3.1"
 },
 "devDependencies": {
 "autoprefixer": "^7.1.2",
 "babel-core": "^6.25.0",
 "babel-loader": "^7.1.1",
 "babel-plugin-syntax-dynamic-import": "^6.18.0",
 "babel-plugin-transform-runtime": "^6.22.0",
 "babel-preset-env": "^1.6.0",
 "babel-preset-stage-2": "^6.22.0",
 "compression": "^1.7.1",
 "cross-env": "^5.0.5",
 "css-loader": "^0.28.4",
 "extract-text-webpack-plugin": "^3.0.0",
 "file-loader": "^0.11.2",
 "friendly-errors-webpack-plugin": "^1.6.1",
 "glob": "^7.1.2",
 "less": "^2.7.2",
 "less-loader": "^2.2.3",
 "lru-cache": "^4.1.1",
 "mockjs": "^1.0.1-beta3",
 "style-loader": "^0.19.0",
 "sw-precache-webpack-plugin": "^0.11.4",
 "url-loader": "^0.5.9",
 "vue-loader": "^13.0.4",
 "vue-style-loader": "^3.0.3",
 "vue-template-compiler": "^2.4.2",
 "vuex-router-sync": "^4.2.0",
 "webpack": "^3.5.0",
 "webpack-dev-middleware": "^1.12.0",
 "webpack-hot-middleware": "^2.18.2",
 "webpack-merge": "^4.1.0",
 "webpack-node-externals": "^1.6.0"
 }

3、項目主目錄搭建

基本目錄結(jié)構(gòu)如下:

├── LICENSE
├── README.md
├── build
│ ├── setup-dev-server.js
│ ├── vue-loader.config.js
│ ├── webpack.base.config.js
│ ├── webpack.client.config.js
│ └── webpack.server.config.js
├── log
│ ├── err.log
│ └── out.log
├── package.json
├── pmlog.json
├── server.js
└── src
 ├── App.vue
 ├── app.js
 ├── assets
 │ ├── images
 │ ├── style
 │ │ └── css.less
 │ └── views
 │  └── index.css
 ├── components
 │ ├── Banner.vue
 │ ├── BottomNav.vue
 │ ├── FloorOne.vue
 │ └── Header.vue
 ├── entry-client.js
 ├── entry-server.js
 ├── index.template.html
 ├── public
 │ ├── conf.js
 │ └── utils
 │  ├── api.js
 │  └── confUtils.js
 ├── router
 │ └── index.js
 ├── static
 │ ├── img
 │ │ └── favicon.ico
 │ └── js
 │  └── flexible.js
 ├── store
 │ ├── actions.js
 │ ├── getters.js
 │ ├── index.js
 │ ├── modules
 │ │ └── Home.js
 │ ├── mutationtypes.js
 │ └── state.js
 └── views
  └── index
   ├── conf.js
   ├── index.vue
   ├── mock.js
   └── service.js

文件目錄基本介紹:

  1. views文件夾下分模塊文件,模塊文件下下又分模塊本身的.vue文件(模版文件),index.js文件(后臺數(shù)據(jù)交互文件),mock.js(本模塊的mock假數(shù)據(jù)),conf.js(配置本模塊一些參數(shù),請求路徑,模塊名稱等信息)
  2. components 公共組件文件夾
  3. router 主要存放前端路由配置文件,寫法規(guī)范按照vue-router官方例子即可。
  4. store 主要是存放共享狀態(tài)文件,里面包含action.js,getter.js,mutationtype.js等,后期會根據(jù)模塊再細分這些。
  5. public 主要存放公共組件代碼和項目使用的公共文件代碼,例如后期我們將axios封裝成公共的api庫文件等等
  6. static文件夾代表靜態(tài)文件,不會被webpack打包的
  7. app.js 是項目入口文件
  8. App.vue 是項目入口文件
  9. entry-client和entry-server分別是客戶端入口文件和服務(wù)端的入口文件
  10. index.template.html是整個項目的模版文件

開始編寫app.js項目入口代碼

使用vue開發(fā)項目入口文件一般都會如下寫法:

import Vue from 'vue';
import App from './index.vue';
import router from './router'
import store from './store';

new Vue({
 el: '#app',
 store,
 router,
 render: (h) => h(App)
});

這種寫法是程序共享一個vue實例,但是在后端渲染中很容易導(dǎo)致交叉請求狀態(tài)污染,導(dǎo)致數(shù)據(jù)流被污染了。

所以,避免狀態(tài)單例,我們不應(yīng)該直接創(chuàng)建一個應(yīng)用程序?qū)嵗菓?yīng)該暴露一個可以重復(fù)執(zhí)行的工廠函數(shù),為每個請求創(chuàng)建新的應(yīng)用程序?qū)嵗瑯觬outer和store入口文件也需要重新創(chuàng)建一個實例。

為了配合webpack動態(tài)加載路由配置,這里會改寫常規(guī)路由引入寫法,這樣可以根據(jù)路由路徑來判斷加載相應(yīng)的組件代碼:

import Home from '../views/index/index.vue'
// 改寫成
component: () => ('../views/index/index.vue')

以下是路由的基本寫法router,對外會拋出一個createRouter方法來創(chuàng)建一個新的路由實例:

import Vue from 'vue'
import Router from 'vue-router';
Vue.use(Router)
export function createRouter() {
 return new Router({
  mode: 'history',
  routes: [{
   name:'Home',
   path: '/',
   component: () =>
    import ('../views/index/index.vue')
  }]
 })
}

以下是store狀態(tài)管理的基本寫法,對外暴露了一個createStore方法,方便每次訪問創(chuàng)建一個新的實例:

// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import getters from './getters'
import modules from './modules/index'
Vue.use(Vuex)
export function createStore() {
 return new Vuex.Store({
 actions,
 getters,
 modules,
 strict: false
 })
}

結(jié)合寫好的router和store入口文件代碼來編寫整個項目的入口文件app.js代碼內(nèi)容,同樣最終也會對外暴露一個createApp方法,在每次創(chuàng)建app的時候保證router,store,app都是新創(chuàng)建的實例,這里還引入了一個vue路由插件vuex-router-sync,主要作用是同步路由狀態(tài)(route state)到 store,以下是app.js完整代碼:

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
import { createStore } from './store'
import { sync } from 'vuex-router-sync'
require('./assets/style/css.less');
export function createApp () {
 // 創(chuàng)建 router 和 store 實例
 const router = createRouter()
 const store = createStore()
 // 同步路由狀態(tài)(route state)到 store
 sync(store, router)
 // 創(chuàng)建應(yīng)用程序?qū)嵗?,?router 和 store 注入
 const app = new Vue({
 router,
 store,
 render: h => h(App)
 })
 // 暴露 app, router 和 store。
 return { app, router, store }
}

entry-client.js代碼編寫:

首頁引入從app文件中暴露出來的createApp方法,在每次調(diào)用客戶端的時候,重新創(chuàng)建一個新的app,router,store,部分代碼如下:

import { createApp } from './app'
const { app, router, store } = createApp()

這里我們會使用到onReady方法,此方法通常用于等待異步的導(dǎo)航鉤子完成,比如在進行服務(wù)端渲染的時候,例子代碼如下:

import { createApp } from './app'
const { app, router, store } = createApp()
router.onReady(() => {
 app.$mount('#app')
})

我們會調(diào)用一個新方法beforeResolve,只有在router2.5.0以上的版本才會有的方法,注冊一個類似于全局路由保護router.beforeEach(),除了在導(dǎo)航確認之后,在所有其他保護和異步組件已解決之后調(diào)用?;緦懛ㄈ缦拢?/p>

router.beforeResolve((to, from, next) => {
 // to 和 from 都是 路由信息對象
 // 返回目標位置或是當前路由匹配的組件數(shù)組(是數(shù)組的定義/構(gòu)造類,不是實例)。通常在服務(wù)端渲染的數(shù)據(jù)預(yù)加載時時候。
 const matched = router.getMatchedComponents(to)
 const prevMatched = router.getMatchedComponents(from)
})

服務(wù)端把要給客戶端的 state 放在了 window. INITIAL_STATE 這個全局變量上面。前后端的 HTML 結(jié)構(gòu)應(yīng)該是一致的。然后要把 store 的狀態(tài)樹寫入一個全局變量( INITIAL_STATE ),這樣客戶端初始化 render 的時候能夠校驗服務(wù)器生成的 HTML 結(jié)構(gòu),并且同步到初始化狀態(tài),然后整個頁面被客戶端接管?;敬a如下:

// 將服務(wù)端渲染時候的狀態(tài)寫入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

接下來貼出來完整的客戶端代碼,這里的Q也可以不用引入,直接使用babel就能編譯es6自帶的Promise,因為本人使用習(xí)慣了,這里可以根據(jù)自身的需求是否安裝:

import { createApp } from './app'
import Q from 'q'
import Vue from 'vue'

Vue.mixin({
 beforeRouteUpdate (to, from, next) {
 const { asyncData } = this.$options
 if (asyncData) {
  asyncData({
  store: this.$store,
  route: to
  }).then(next).catch(next)
 } else {
  next()
 }
 }
})
const { app, router, store } = createApp()

// 將服務(wù)端渲染時候的狀態(tài)寫入vuex中
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
 router.beforeResolve((to, from, next) => {
  const matched = router.getMatchedComponents(to)
  const prevMatched = router.getMatchedComponents(from)
  // 我們只關(guān)心之前沒有渲染的組件
  // 所以我們對比它們,找出兩個匹配列表的差異組件
  let diffed = false
  const activated = matched.filter((c, i) => {
  return diffed || (diffed = (prevMatched[i] !== c))
  })
  if (!activated.length) {
  return next()
  }
  // 這里如果有加載指示器(loading indicator),就觸發(fā)
  Q.all(activated.map(c => {
  if (c.asyncData) {
   return c.asyncData({ store, route: to })
  }
  })).then(() => {
  // 停止加載指示器(loading indicator)
  next()
  }).catch(next)
 })
 app.$mount('#app')
})

entry-server.js代碼編寫:

基本編寫和客戶端的差不多,因為這是服務(wù)端渲染,涉及到與后端數(shù)據(jù)交互定義的問題,我們需要在這里定義好各組件與后端交互使用的方法名稱,這樣方便在組件內(nèi)部直接使用,這里根我們常規(guī)在組件直接使用ajax獲取數(shù)據(jù)有些不一樣,代碼片段如下:

//直接定義組件內(nèi)部asyncData方法來觸發(fā)相應(yīng)的ajax獲取數(shù)據(jù)
if (Component.asyncData) {
 return Component.asyncData({
 store,
 route: router.currentRoute
 })
}

以下是完整的服務(wù)端代碼:

import { createApp } from './app'
import Q from 'q'
export default context => {
 return new Q.Promise((resolve, reject) => {
 const { app, router, store } = createApp()
 router.push(context.url)
 router.onReady(() => {
  const matchedComponents = router.getMatchedComponents()
  if (!matchedComponents.length) {
  return reject({ code: 404 })
  }
  // 對所有匹配的路由組件調(diào)用 `asyncData()`
  Q.all(matchedComponents.map(Component => {
  if (Component.asyncData) {
   return Component.asyncData({
   store,
   route: router.currentRoute
   })
  }
  })).then(() => {
  // 在所有預(yù)取鉤子(preFetch hook) resolve 后,
  // 我們的 store 現(xiàn)在已經(jīng)填充入渲染應(yīng)用程序所需的狀態(tài)。
  // 當我們將狀態(tài)附加到上下文,
  // 并且 `template` 選項用于 renderer 時,
  // 狀態(tài)將自動序列化為 `window.__INITIAL_STATE__`,并注入 HTML。
  context.state = store.state
  resolve(app)
  }).catch(reject)
 }, reject)
 })
}

4、腳手架其他目錄介紹:

到這里src下面主要的幾個文件代碼已經(jīng)編寫完成,接下里介紹下整個項目的目錄結(jié)構(gòu)如下:

基于vue-ssr服務(wù)端渲染入門詳解

主要幾個文件介紹如下:

  1. build 主要存放webpack打包配置文件
  2. dist webpack打包后生成的目錄
  3. log 使用pm2監(jiān)控進程存放的日志文件目錄
  4. server.js node服務(wù)器啟動文件
  5. pmlog.json pm2配置文件

server.js入口文件編寫

我們還需要編寫在服務(wù)端啟動服務(wù)的代碼server.js,我們會使用到部分node原生提供的api,片段代碼如下:

const Vue = require('vue')
const express = require('express')
const path = require('path')
const LRU = require('lru-cache')
const { createBundleRenderer } = require('vue-server-renderer')
const fs = require('fs')
const net = require('net')

大致思路是,引入前端模版頁面index.template.html,使用express啟動服務(wù),引入webpack打包項目代碼的dist文件,引入緩存模塊(這里不做深入介紹,后期會單獨詳細介紹),判斷端口是否被占用,自動啟動其他接口服務(wù)。

引入前端模版文件并且設(shè)置環(huán)境變量為production,片段代碼如下:

const template = fs.readFileSync('./src/index.template.html', 'utf-8')
const isProd = process.env.NODE_ENV === 'production'

vue-server-renderer插件的具體使用,通過讀取dist文件夾下的目錄文件,來創(chuàng)建createBundleRenderer函數(shù),并且使用LRU來設(shè)置緩存的時間,通過判斷是生產(chǎn)環(huán)境還是開發(fā)環(huán)境,調(diào)用不同的方法,代碼片段如下:

const resolve = file => path.resolve(__dirname, file)
function createRenderer (bundle, options) {
 return createBundleRenderer(bundle, Object.assign(options, {
 template,
 cache: LRU({
  max: 1000,
  maxAge: 1000 * 60 * 15
 }),
 basedir: resolve('./dist'),
 runInNewContext: false
 }))
}
let renderer;
let readyPromise
if (isProd) {
 const bundle = require('./dist/vue-ssr-server-bundle.json')
 const clientManifest = require('./dist/vue-ssr-client-manifest.json')
 renderer = createRenderer(bundle, {
 clientManifest
 })
} else {
 readyPromise = require('./build/setup-dev-server')(server, (bundle, options) => {
 renderer = createRenderer(bundle, options)
 })
}

使用express啟動服務(wù),代碼片段如下:

const server = express();

//定義在啟動服務(wù)錢先判斷中間件中的緩存是否過期,是否直接調(diào)用dist文件。
const serve = (path, cache) => express.static(resolve(path), {
 maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0
})
server.use('/dist', serve('./dist', true))
server.get('*', (req, res) => {
 const context = {
 title: 'hello',
 url: req.url
 }
 renderer.renderToString(context, (err, html) => {
 if (err) {
  res.status(500).end('Internal Server Error')
  return
 }
 res.end(html)
 })
})

判斷端口是否被占用,片段代碼如下:

function probe(port, callback) {
 let servers = net.createServer().listen(port)
 let calledOnce = false
 let timeoutRef = setTimeout(function() {
  calledOnce = true
  callback(false, port)
 }, 2000)
 timeoutRef.unref()
 let connected = false
 servers.on('listening', function() {
  clearTimeout(timeoutRef)

  if (servers)
   servers.close()

  if (!calledOnce) {
   calledOnce = true
   callback(true, port)
  }
 })
 servers.on('error', function(err) {
  clearTimeout(timeoutRef)

  let result = true
  if (err.code === 'EADDRINUSE')
   result = false

  if (!calledOnce) {
   calledOnce = true
   callback(result, port)
  }
 })
}
const checkPortPromise = new Promise((resolve) => {
 (function serverport(_port) {
  let pt = _port || 8080;
  probe(pt, function(bl, _pt) {
   // 端口被占用 bl 返回false
   // _pt:傳入的端口號
   if (bl === true) {
    // console.log("\n Static file server running at" + "\n\n=> http://localhost:" + _pt + '\n');
    resolve(_pt);
   } else {
    serverport(_pt + 1)
   }
  })
 })()

})
checkPortPromise.then(data => {
 uri = 'http://localhost:' + data;
 console.log('啟動服務(wù)路徑'+uri)
 server.listen(data);
});

到這里,基本的代碼已經(jīng)編寫完成,webpack打包配置文件基本和官方保持不變,接下來可以嘗試啟動本地的項目服務(wù),這里簡要的使用網(wǎng)易嚴選首頁作為demo示例,結(jié)果如下:

基于vue-ssr服務(wù)端渲染入門詳解

第三部分 mockjs和axios配合使用

1、前言

上一節(jié)大致介紹了服務(wù)端和客戶端入口文件代碼內(nèi)容,現(xiàn)在已經(jīng)可以正常運行你的后端渲染腳手架了,這一節(jié),跟大家分享下如何使用axios做ajax請求,如何使用mockjs做本地假數(shù)據(jù),跑通本地基本邏輯,為以后前后端連調(diào)做準備。

2、前期準備

需要用npm安裝axios,mockjs依賴包,由于mockjs只是代碼開發(fā)的輔助工具,所以安裝的時候我會加--save-dev來區(qū)分,具體可以根據(jù)自己的需求來定,當然,如果有mock服務(wù)平臺的話,可以直接走mock平臺造假數(shù)據(jù),本地直接訪問mock平臺的接口,例如可以使用阿里的Rap平臺管理工具生成。

npm install axios --save
npm install mockjs --save-dev

3、簡要介紹axios

其他請求方式,代碼示例如下:

axios.request(config);
axios.get(url[,config]);
axios.delete(url[,config]);
axios.head(url[,config]);
axios.post(url[,data[,config]]);
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])

具體詳細可以點擊查看axios基本使用介紹

api.js完整代碼如下:

import axios from 'axios'
import qs from 'qs'
import Q from 'q'
/**
 * 兼容 不支持promise 的低版本瀏覽器
 */
require('es6-promise').polyfill();
import C from '../conf'

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.withCredentials = true

function ajax(url, type, options) {

 return Q.Promise((resolve, reject) => {
 axios({
  method: type,
  url: C.HOST + url,
  params: type === 'get' ? options : null,
  data: type !== 'get' ? qs.stringify(options) : null
  })
  .then((result) => {
  if (result && result.status === 401) {
   // location.href = '/views/401.html'
  }
  if (result && result.status === 200) {
   if (result.data.code === 200) {
   resolve(result.data.data);
   } else if (result.data.code === 401) {
   reject({
    nopms: true,
    msg: result.data.msg
   });
   } else {
   reject({
    error: true,
    msg: result.data.msg
   });
   }
  } else {
   reject({
   errno: result.errno,
   msg: result.msg
   });
  }
  })
  .catch(function(error) {
  console.log(error, url);
  });
 })
}

const config = {
 get(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'get', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 post(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'post', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 put(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'put', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 delete(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'delete', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 },

 jsonp(url, options) {
 const _self = this;
 return Q.Promise((resolve, reject) => {
  ajax(url, 'jsonp', options)
  .then((data) => {
   resolve(data);
  }, (error) => {
   reject(error);
  });
 })
 }
};

export default config;

mockjs項目基本配置如下:

1、在public下新建conf.js全局定義請求url地址,代碼如下:

module.exports = {
 HOST: "http://www.xxx.com",
 DEBUGMOCK: true
};

2、在views/index根目錄下新建conf.js,定義組件mock的請求路徑,并且定義是否開始單個組件使用mock數(shù)據(jù)還是線上接口數(shù)據(jù),代碼如下:

const PAGEMOCK = true;
const MODULECONF = {
 index: {
 NAME: '首頁',
 MOCK: true,
 API: {
  GET: '/api/home',
 }
 }
};

3、在組件內(nèi)部定義mockjs來編寫mock假數(shù)據(jù),代碼如下:

import Mock from 'mockjs';
const mData = {
 index: {
 API: {
  GET: {
  "code": 200,
  "data": {
   "pin": 'wangqi',
   "name": '王奇'
  }
  }
 }
 }
}

以上就是基本的流程,如果有更好更靈活的使用方案,希望能夠參與溝通并且分享,項目工作流已經(jīng)在github上分享,并且會繼續(xù)維護更新, 點擊查看詳情,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI