国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

vue服務端渲染的實例代碼

2019-11-19 15:39:13
字體:
來源:轉載
供稿:網友

一、什么是服務端渲染

客戶端請求服務器,服務器根據請求地址獲得匹配的組件,在調用匹配到的組件返回Promise (官方是asyncData方法)來將需要的數據拿到。最后再通過window.__initial_state=data將其寫入網頁,最后將服務端渲染好的網頁返回回去。接下來客戶端將用新的store狀態把原來的store狀態替換掉,保證客戶端和服務端的數據同步。遇到沒被服務端渲染的組件,再去發異步請求拿數據。

服務端渲染的環境搭建

這是vue官網的服務端渲染的示意圖,ssr有兩個入口文件,分別是客戶端的入后文件和服務端的入口文件,webpack通過兩個入口文件分別打包成給服務端用的server bundle和給客戶端用的client bundle.當服務器接收到了來自客戶端的請求之后,會創建一個渲染器bundleRenderer,這個bundleRenderer會讀取上面生成的server bundle文件,并且執行它的代碼, 然后發送一個生成好的html到瀏覽器,等到客戶端加載了client bundle之后,會和服務端生成的DOM進行Hydration(判斷這個DOM和自己即將生成的DOM是否相同,如果相同就將客戶端的vue實例掛載到這個DOM上)

實現步驟:

1、創建vue實例(main.js)

importVuefrom'vue'importAppfrom'./App.vue'importiViewfrom'iview';import{createStore}from'./store'import{createRouter}from'./router'import{sync}from'vuex-router-sync'Vue.use(iView);export functioncreateApp() {conststore = createStore()constrouter = createRouter()sync(store,router)constapp =newVue({router,store,render: h => h(App)})return{app,router,store}}

因為要做服務端渲染,所以這里不需要再用el去掛載,現將app、router、store導出

2、服務端入口文件(entry-server.js)

import{ createApp }from'./main'constisDev = process.env.NODE_ENV !=='production'const{ app,router,store } = createApp()constgetAllAsyncData=function(component){letstores = []functionloopComponent(component) {if(typeofcomponent.asyncData !=='undefined') {for(letaofcomponent.asyncData({store,route: router.currentRoute})) {stores.push(a)}}if(typeofcomponent.components !=='undefined') {for(letcincomponent.components){loopComponent(component.components[c])}}}loopComponent(component)returnstores}export defaultcontext => {return newPromise((resolve,reject) => {consts = isDev && Date.now()const{url} = contextconstfullPath = router.resolve(url).route.fullPathif(fullPath !== url) {reject({url: fullPath })}router.push(url)router.onReady(() => {constmatchedComponents = router.getMatchedComponents()if(!matchedComponents.length) {reject({code:404})}letallAsyncData = getAllAsyncData(matchedComponents[0])Promise.all(allAsyncData).then(() => {isDev && console.log(`data pre-fetch:${Date.now() - s}ms`)context.state = store.stateresolve(app)}).catch(reject)},reject)})}

這個文件的主要工作是接受從服務端傳遞過來的context參數,context包含當前頁面的url,用getMatchedComponents方法獲取當前url下的組件,返回一個數組,遍歷這個數組中的組件,如果組件有asyncData鉤子函數,則傳遞store獲取數據,最后返回一個promise對象。

store.state的作用是將服務端獲取到的數據掛載到context對象上,后面在server.js文件里會把這些數據直接發送到瀏覽器端與客戶端的vue實例進行數據(狀態)同步。

3、客戶端入口文件(entry-client.js)

importVuefrom'vue'import'es6-promise/auto'import{ createApp }from'./main'importProgressBarfrom'./components/ProgressBar.vue'// global progress barconstbar = Vue.prototype.$bar =newVue(ProgressBar).$mount()document.body.appendChild(bar.$el)Vue.mixin({beforeRouteUpdate(to,from,next) {const{ asyncData } =this.$optionsif(asyncData) {Promise.all(asyncData({store:this.$store,route: to})).then(next).catch(next)}else{next()}}})const{ app,router,store } = createApp()if(window.__INITIAL_STATE__) {store.replaceState(window.__INITIAL_STATE__)}router.onReady(() => {router.beforeResolve((to,from,next) => {constmatched = router.getMatchedComponents(to)constprevMatched = router.getMatchedComponents(from)letdiffed =falseconstactivated = matched.filter((c,i) => {returndiffed || (diffed = (prevMatched[i] !== c))})constasyncDataHooks = activated.map(c => c.asyncData).filter(_ => _)if(!asyncDataHooks.length) {returnnext()}bar.start()Promise.all(asyncDataHooks.map(hook => hook({ store,route: to }))).then(() => {bar.finish()next()}).catch(next)})app.$mount('#app')})if('https:'=== location.protocol && navigator.serviceWorker) {navigator.serviceWorker.register('/service-worker.js')}
if(window.INITIAL_STATE) {store.replaceState(window.INITIAL_STATE)}

這句的作用是如果服務端的vuex數據發生改變,就將客戶端的數據替換掉,保證客戶端和服務端的數據同步

Service Worker主要用于攔截并修改訪問和資源請求,細粒度地緩存資源。它運行瀏覽器在后臺,運行環境與普通頁面腳本不同,所以不能直接參與頁面交互。出于安全考慮,service worker只能運行在HTTPS上,防止被人從中攻擊。

4、創建服務端渲染器(server.js)

constfs = require('fs')constpath = require('path')constLRU = require('lru-cache')constexpress = require('express')constcompression = require('compression')constresolve= file => path.resolve(__dirname,file)const{ createBundleRenderer } = require('vue-server-renderer')constisProd = process.env.NODE_ENV ==='production'|| process.env.NODE_ENV ==='beta'constuseMicroCache = process.env.MICRO_CACHE !=='false'constserverInfo =`express/${require('express/package.json').version}`+`vue-server-renderer/${require('vue-server-renderer/package.json').version}`constapp = express()consttemplate = fs.readFileSync(resolve('./src/index.template.html'),'utf-8')functioncreateRenderer(bundle,options) {returncreateBundleRenderer(bundle,Object.assign(options,{template,cache: LRU({max:1000,maxAge:1000*60*15}),basedir: resolve('./dist'),runInNewContext:false}))}letrendererletreadyPromiseif(isProd) {constbundle = require('./dist/vue-ssr-server-bundle.json')constclientManifest = require('./dist/vue-ssr-client-manifest.json')renderer = createRenderer(bundle,{clientManifest})}else{readyPromise = require('./build/setup-dev-server')(app,(bundle,options) => {renderer = createRenderer(bundle,options)})}constserve= (path,cache) => express.static(resolve(path),{maxAge: cache && isProd ?1000*60*60*24*30:0})app.use(compression({threshold:0}))app.use('/dist',serve('./dist',true))app.use('/static',serve('./static',true))app.use('/service-worker.js',serve('./dist/service-worker.js'))constmicroCache = LRU({max:100,maxAge:1000})constisCacheable= req => useMicroCachefunctionrender(req,res) {consts = Date.now()res.setHeader("Content-Type","text/html")res.setHeader("Server",serverInfo)consthandleError= err => {if(err.url) {res.redirect(err.url)}else if(err.code ===404) {res.status(404).end('404 | Page Not Found')}else{// Render Error Page or Redirectres.status(500).end('500 | Internal Server Error')console.error(`error during render :${req.url}`)console.error(err.stack)}}constcacheable = isCacheable(req)if(cacheable) {consthit = microCache.get(req.url)if(hit) {if(!isProd) {console.log(`cache hit!`)}returnres.end(hit)}}constcontext = {title:'Vue DB',// default titleurl: req.url}renderer.renderToString(context,(err,html) => {if(err) {returnhandleError(err)}res.end(html)if(cacheable) {microCache.set(req.url,html)}if(!isProd) {console.log(`whole request:${Date.now() - s}ms`)}})}app.get('*',isProd ? render : (req,res) => {readyPromise.then(() => render(req,res))})constport = process.env.PORT ||8888app.listen(port,() => {console.log(`server started at localhost:${port}`)})

5、客戶端api文件create-api-client.js

/** * Created by lin on 2017/8/25. */import axios from 'axios';let api;axios.defaults.baseURL = process.env.API_URL;axios.defaults.timeout = 10000;axios.interceptors.response.use((res) => { if (res.status >= 200 && res.status < 300) {  return res; } return Promise.reject(res);}, (error) => { return Promise.reject({message: '網絡異常,請刷新重試', err: error});});if (process.__API__) { api = process.__API__;} else { api = {  get: function(url) {   return new Promise((resolve, reject) => {    axios.get(url).then(res => {     resolve(res);    }).catch((error) => {     reject(error);    });   });  },  post: function(target, options = {}) {   return new Promise((resolve, reject) => {    axios.post(target, options).then(res => {     resolve(res);    }).catch((error) => {     reject(error);    });   });  } };}export default api;

6、服務端api文件create-api-server.js

/** * Created by lin on 2017/8/25. */import axios from 'axios';let cook = process.__COOKIE__ || '';let api;axios.defaults.baseURL = 'https://api.douban.com/v2/';axios.defaults.timeout = 10000;axios.interceptors.response.use((res) => { if (res.status >= 200 && res.status < 300) {  return Promise.resolve(res); } return Promise.reject(res);}, (error) => { // 網絡異常 return Promise.reject({message: '網絡異常,請刷新重試', err: error, type: 1});});if (process.__API__) { api = process.__API__;} else { api = {  get: function(target) {   return new Promise((resolve, reject) => {    axios.request({     url: encodeURI(target),     method: 'get',     headers: {      'Cookie': cook     }    }).then(res => {     resolve(res);    }).catch((error) => {     reject(error);    });   });  },  post: function(target, options = {}) {   return new Promise((resolve, reject) => {    axios.request({     url: target,     method: 'post',     headers: {      'Cookie': cook     },     params: options    }).then(res => {     resolve(res);    }).catch((error) => {     reject(error);    });   });  } };}export default api;

六、那些年遇到的那些坑

問題1、window is not defined

答案1:給用到瀏覽器對象的地方加if (typeof window !== 'undefined') {},有一些插件里也用到了瀏覽器對象,在使用的地方也加一個條件判斷:

if (typeofwindow !== 'undefined') {Vue.use(VueAnalytics, {id: process.env.UA_TRACKING_ID,router})}

問題2:用到非Vue系列的插件,如hello.all.js(三方登錄的插件),需要用的地方才引用,報的錯和問題1一樣。

答案2:這個時候不能再用import導入,需要使用require,

let hello

if (typeof window !== 'undefined') {hello = require('hello')}

問題3:引用bootstrap

答案3:將bootstrap.css和bootstrap.js加入webpack.base.config.js的entry中的vendor中

問題6:bootstap需要jquery,此時把jQuery加在vendor中沒用。

答案6:給webpack.base.config.js的plugins添加一個插件,如:

newwebpack.ProvidePlugin({$ : "jquery",jQuery : "jquery","window.jQuery" :"jquery"})

七、例子

https://github.com/linmoer/ssr-vue這是一個服務端渲的例子

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 平南县| 托克逊县| 泰兴市| 宁远县| 建宁县| 靖远县| 晋城| 如东县| 晋宁县| 元谋县| 香格里拉县| 金塔县| 镇坪县| 兴宁市| 扶余县| 乌兰县| 肇庆市| 阿拉善盟| 六盘水市| 香河县| 郴州市| 云林县| 西城区| 虹口区| 阳西县| 洪江市| 治县。| 靖远县| 鹤山市| 高安市| 长宁县| 墨脱县| 镇远县| 奉节县| 海晏县| 噶尔县| 海伦市| 禄丰县| 勃利县| 华坪县| 鲜城|