服務(wù)端渲染實(shí)現(xiàn)原理機(jī)制:在服務(wù)端拿數(shù)據(jù)進(jìn)行解析渲染,直接生成html片段返回給前端。然后前端可以通過解析后端返回的html片段到前端頁面,大致有以下兩種形式:
創(chuàng)新互聯(lián)公司專注于鐵力企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),電子商務(wù)商城網(wǎng)站建設(shè)。鐵力網(wǎng)站建設(shè)公司,為鐵力等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
1、服務(wù)器通過模版引擎直接渲染整個(gè)頁面,例如java后端的vm模版引擎,php后端的smarty模版引擎。
2、服務(wù)渲染生成html代碼塊, 前端通過AJAX獲取然后使用js動(dòng)態(tài)添加。
服務(wù)端渲染能夠解決兩大問題:
1、seo問題,有利于搜索引擎蜘蛛抓取網(wǎng)站內(nèi)容,利于網(wǎng)站的收錄和排名。
2、首屏加載過慢問題,例如現(xiàn)在成熟的SPA項(xiàng)目中,打開首頁需要加載很多資源,通過服務(wù)端渲染可以加速首屏渲染。
同樣服務(wù)端渲染也會(huì)有弊端,主要是根據(jù)自己的業(yè)務(wù)場景來選擇適合方式,由于服務(wù)端渲染前端頁面,必將會(huì)給服務(wù)器增加壓力。
客戶端請求服務(wù)器,服務(wù)器根據(jù)請求地址獲得匹配的組件,在調(diào)用匹配到的組件返回 Promise (官方是preFetch方法)來將需要的數(shù)據(jù)拿到。最后再通過
<script>window.__initial_state=data</script>
將其寫入網(wǎng)頁,最后將服務(wù)端渲染好的網(wǎng)頁返回回去。
接下來客戶端會(huì)將vuex將寫入的 initial_state 替換為當(dāng)前的全局狀態(tài)樹,再用這個(gè)狀態(tài)樹去檢查服務(wù)端渲染好的數(shù)據(jù)有沒有問題。遇到?jīng)]被服務(wù)端渲染的組件,再去發(fā)異步請求拿數(shù)據(jù)。說白了就是一個(gè)類似React的 shouldComponentUpdate 的Diff操作。
Vue2使用的是單向數(shù)據(jù)流,用了它,就可以通過 SSR 返回唯一一個(gè)全局狀態(tài), 并確認(rèn)某個(gè)組件是否已經(jīng)SSR過了。
由于virtual dom的引入,使得vue的服務(wù)端渲染成為了可能,下面是官方 vue-server-renderer提供的渲染流程圖:
可以看出vue的后端渲染分三個(gè)部分組成:頁面的源碼(source),node層的渲染部分和瀏覽器端的渲染部分。
source分為兩種entry point,一個(gè)是前端頁面的入口client entry,主要是實(shí)例化Vue對象,將其掛載到頁面中;另外一個(gè)是后端渲染服務(wù)入口server entry,主要是控服務(wù)端渲染模塊回調(diào),返回一個(gè)Promise對象,最終返回一個(gè)Vue對象(經(jīng)過測試,直接返回Vue對象也是可以的);
前面的source部分就是業(yè)務(wù)開發(fā)的代碼,開發(fā)完成之后通過 webpack 進(jìn)行構(gòu)建,生成對應(yīng)的bundle,這里不再贅述client bundle,就是一個(gè)可在瀏覽器端執(zhí)行的打包文件;這里說下server bundle, vue2提供 vue-server-renderer模塊,模塊可以提供兩種render: rendererer/bundleRenderer ,下面分別介紹下這兩種render。
renderer接收一個(gè)vue對象 ,然后進(jìn)行渲染,這種對于簡單的vue對象,可以這么去做,但是對于復(fù)雜的項(xiàng)目,如果使用這種直接require一個(gè)vue對象,這個(gè)對于服務(wù)端代碼的結(jié)構(gòu)和邏輯都不太友好,首先模塊的狀態(tài)會(huì)一直延續(xù)在每個(gè)請求渲染請求,我們需要去管理和避免這次渲染請求的狀態(tài)影響到后面的請求,因此vue-server-renderer提供了另外一種渲染模式,通過一個(gè) bundleRenderer去做渲染。
bundleRenderer是較為復(fù)雜項(xiàng)目進(jìn)行服務(wù)端渲染官方推薦的方式,通過webpack以server entry按照一定的要求打包生成一個(gè) server-bundle,它相當(dāng)于一個(gè)可以給服務(wù)端用的app的打包壓縮文件,每一次調(diào)用都會(huì)重新初始化 vue對象,保證了每次請求都是獨(dú)立的,對于開發(fā)者來說,只需要專注于當(dāng)前業(yè)務(wù)就可以,不用為服務(wù)端渲染開發(fā)更多的邏輯代碼。 renderer生成完成之后,都存在兩個(gè)接口,分別是renderToString和renderToStream,一個(gè)是一次性將頁面渲染成字符串文件,另外一個(gè)是流式渲染,適用于支持流的web服務(wù)器,可以是請求服務(wù)的速度更快。
上一節(jié)我們大致講了為什么需要使用vue后端渲染,以及vue后端渲染的基本原理,這節(jié)內(nèi)容我們將從零開始搭建屬于自己的vue后端渲染腳手架,當(dāng)然不能不參考官方頁面響應(yīng)的實(shí)例vue-hackernews-2.0,從零開始搭建項(xiàng)目,源碼在將在下節(jié)與大家共享。
基本環(huán)境要求:node版本6.10.1以上,npm版本3.10.10以上,本機(jī)環(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)建過程中會(huì)是用相應(yīng)的插件依賴包來配合進(jìn)行壓縮打包,以下是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" }
基本目錄結(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
文件目錄基本介紹:
開始編寫app.js項(xiàng)目入口代碼
使用vue開發(fā)項(xiàng)目入口文件一般都會(huì)如下寫法:
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) });
這種寫法是程序共享一個(gè)vue實(shí)例,但是在后端渲染中很容易導(dǎo)致交叉請求狀態(tài)污染,導(dǎo)致數(shù)據(jù)流被污染了。
所以,避免狀態(tài)單例,我們不應(yīng)該直接創(chuàng)建一個(gè)應(yīng)用程序?qū)嵗?,而是?yīng)該暴露一個(gè)可以重復(fù)執(zhí)行的工廠函數(shù),為每個(gè)請求創(chuàng)建新的應(yīng)用程序?qū)嵗?,同樣router和store入口文件也需要重新創(chuàng)建一個(gè)實(shí)例。
為了配合webpack動(dòng)態(tài)加載路由配置,這里會(huì)改寫常規(guī)路由引入寫法,這樣可以根據(jù)路由路徑來判斷加載相應(yīng)的組件代碼:
import Home from '../views/index/index.vue' // 改寫成 component: () => ('../views/index/index.vue')
以下是路由的基本寫法router,對外會(huì)拋出一個(gè)createRouter方法來創(chuàng)建一個(gè)新的路由實(shí)例:
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)管理的基本寫法,對外暴露了一個(gè)createStore方法,方便每次訪問創(chuàng)建一個(gè)新的實(shí)例:
// 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入口文件代碼來編寫整個(gè)項(xiàng)目的入口文件app.js代碼內(nèi)容,同樣最終也會(huì)對外暴露一個(gè)createApp方法,在每次創(chuàng)建app的時(shí)候保證router,store,app都是新創(chuàng)建的實(shí)例,這里還引入了一個(gè)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 實(shí)例 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)用客戶端的時(shí)候,重新創(chuàng)建一個(gè)新的app,router,store,部分代碼如下:
import { createApp } from './app' const { app, router, store } = createApp()
這里我們會(huì)使用到onReady方法,此方法通常用于等待異步的導(dǎo)航鉤子完成,比如在進(jìn)行服務(wù)端渲染的時(shí)候,例子代碼如下:
import { createApp } from './app' const { app, router, store } = createApp() router.onReady(() => { app.$mount('#app') })
我們會(huì)調(diào)用一個(gè)新方法beforeResolve,只有在router2.5.0以上的版本才會(huì)有的方法,注冊一個(gè)類似于全局路由保護(hù)router.beforeEach(),除了在導(dǎo)航確認(rèn)之后,在所有其他保護(hù)和異步組件已解決之后調(diào)用?;緦懛ㄈ缦拢?/p>
router.beforeResolve((to, from, next) => { // to 和 from 都是 路由信息對象 // 返回目標(biāo)位置或是當(dāng)前路由匹配的組件數(shù)組(是數(shù)組的定義/構(gòu)造類,不是實(shí)例)。通常在服務(wù)端渲染的數(shù)據(jù)預(yù)加載時(shí)時(shí)候。 const matched = router.getMatchedComponents(to) const prevMatched = router.getMatchedComponents(from) })
服務(wù)端把要給客戶端的 state 放在了 window. INITIAL_STATE 這個(gè)全局變量上面。前后端的 HTML 結(jié)構(gòu)應(yīng)該是一致的。然后要把 store 的狀態(tài)樹寫入一個(gè)全局變量( INITIAL_STATE ),這樣客戶端初始化 render 的時(shí)候能夠校驗(yàn)服務(wù)器生成的 HTML 結(jié)構(gòu),并且同步到初始化狀態(tài),然后整個(gè)頁面被客戶端接管?;敬a如下:
// 將服務(wù)端渲染時(shí)候的狀態(tài)寫入vuex中 if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) }
接下來貼出來完整的客戶端代碼,這里的Q也可以不用引入,直接使用babel就能編譯es6自帶的Promise,因?yàn)楸救耸褂昧?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ù)端渲染時(shí)候的狀態(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)心之前沒有渲染的組件 // 所以我們對比它們,找出兩個(gè)匹配列表的差異組件 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代碼編寫:
基本編寫和客戶端的差不多,因?yàn)檫@是服務(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)。 // 當(dāng)我們將狀態(tài)附加到上下文, // 并且 `template` 選項(xiàng)用于 renderer 時(shí), // 狀態(tài)將自動(dòng)序列化為 `window.__INITIAL_STATE__`,并注入 HTML。 context.state = store.state resolve(app) }).catch(reject) }, reject) }) }
到這里src下面主要的幾個(gè)文件代碼已經(jīng)編寫完成,接下里介紹下整個(gè)項(xiàng)目的目錄結(jié)構(gòu)如下:
主要幾個(gè)文件介紹如下:
server.js入口文件編寫
我們還需要編寫在服務(wù)端啟動(dòng)服務(wù)的代碼server.js,我們會(huì)使用到部分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啟動(dòng)服務(wù),引入webpack打包項(xiàng)目代碼的dist文件,引入緩存模塊(這里不做深入介紹,后期會(huì)單獨(dú)詳細(xì)介紹),判斷端口是否被占用,自動(dòng)啟動(dòng)其他接口服務(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è)置緩存的時(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啟動(dòng)服務(wù),代碼片段如下:
const server = express(); //定義在啟動(dòng)服務(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('啟動(dòng)服務(wù)路徑'+uri) server.listen(data); });
到這里,基本的代碼已經(jīng)編寫完成,webpack打包配置文件基本和官方保持不變,接下來可以嘗試啟動(dòng)本地的項(xiàng)目服務(wù),這里簡要的使用網(wǎng)易嚴(yán)選首頁作為demo示例,結(jié)果如下:
上一節(jié)大致介紹了服務(wù)端和客戶端入口文件代碼內(nèi)容,現(xiàn)在已經(jīng)可以正常運(yùn)行你的后端渲染腳手架了,這一節(jié),跟大家分享下如何使用axios做ajax請求,如何使用mockjs做本地假數(shù)據(jù),跑通本地基本邏輯,為以后前后端連調(diào)做準(zhǔn)備。
需要用npm安裝axios,mockjs依賴包,由于mockjs只是代碼開發(fā)的輔助工具,所以安裝的時(shí)候我會(huì)加--save-dev來區(qū)分,具體可以根據(jù)自己的需求來定,當(dāng)然,如果有mock服務(wù)平臺的話,可以直接走mock平臺造假數(shù)據(jù),本地直接訪問mock平臺的接口,例如可以使用阿里的Rap平臺管理工具生成。
npm install axios --save npm install mockjs --save-dev
其他請求方式,代碼示例如下:
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]])
具體詳細(xì)可以點(diǎn)擊查看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項(xiàng)目基本配置如下:
1、在public下新建conf.js全局定義請求url地址,代碼如下:
module.exports = { HOST: "http://www.xxx.com", DEBUGMOCK: true };
2、在views/index根目錄下新建conf.js,定義組件mock的請求路徑,并且定義是否開始單個(gè)組件使用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": '王奇' } } } } }
以上就是基本的流程,如果有更好更靈活的使用方案,希望能夠參與溝通并且分享,項(xiàng)目工作流已經(jīng)在github上分享,并且會(huì)繼續(xù)維護(hù)更新, 點(diǎn)擊查看詳情,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
文章標(biāo)題:基于vue-ssr服務(wù)端渲染入門詳解
標(biāo)題URL:http://www.rwnh.cn/article20/pgscjo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、關(guān)鍵詞優(yōu)化、手機(jī)網(wǎng)站建設(shè)、網(wǎng)站策劃、Google、用戶體驗(yàn)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)