您好,登錄后才能下訂單哦!
讀本文前,請先熟讀nuxt
官方文檔,并且具備一定的vue.js
相關開發經驗
中文文檔
英文文檔
vue SSR指南
一、CSR和SSR對比
在SPA
之前的時代,我們傳統的Web
架構大都是SSR
,如:Wordpress(PHP)
、JSP
技術、JavaWeb
等這些程序都是傳統典型的SSR
架構,即:服務端取出數據和模板組合生成 html
輸出給前端,前端發生請求時,重新向服務端請求html
資源。
SPA(CSR):
SPA
應用,到了Vue
、React
,單頁面應用優秀的用戶體驗,逐漸成為了主流,頁面整體是javaScript
渲染出來的,稱之為客戶端渲染CSR
。SPA
渲染過程。由客戶端訪問URL
發送請求到服務端,返回HTML
結構(但是SPA
的返回的HTML
結構是非常的小的,只有一個基本的結構)。客戶端接收到返回結果之后,在客戶端開始渲染HTML
,渲染時執行對應javaScript
,最后渲染template
,渲染完成之后,再次向服務端發送數據請求,注意這里時數據請求,服務端返回json
格式數據。客戶端接收數據,然后完成最終渲染。
CSR原理圖
CSR多數是基于webpack構建的項目,編譯出來的html文件,資源文件都被打包到js中,這樣的頁面是不利于搜索引擎優化(SEO, Search Engine Optimization)
,并且內容到達時間(time-to-content) (或稱之為首屏渲染時長)
也有很大的優化空間
簡單來講,SPA
雖然給服務器減輕了壓力,也存在比較明顯的兩個缺點:
JavaScript
加載完畢,并且執行完畢,才能渲染出首屏。SEO
不友好:爬蟲只能拿到一個div
元素,認為頁面是空的,不利于SEO
。什么是SEO
呢?SEO
即通過各種技術(手段)來確保,你的Web
內容被搜素引擎最大化收錄,最大化提高權重,帶來更多流量。大部分的搜索引擎僅能抓取URI
直接輸出的數據資源,對于 Ajax
類的異步請求的數據無法抓取
因此,對于那些展示宣傳型頁面,如官網,必須進行服務端渲染
SSR:
為了解決如上兩個問題,出現了SSR
解決方案,后端渲染出首屏的DOM
結構返回,前端拿到內容帶上首屏,后續的頁面操作,再用單頁面路由和渲染,稱之為服務端渲染(SSR)
。
SSR
渲染流程是這樣的,客戶端發送URL
請求到服務端,在服務端做出html
和數據
的渲染,渲染完成之后返回html
結構,客戶端拿到頁面的html
結構渲染首屏。所以用戶在瀏覽首屏的時候速度會很快,因為客戶端不需要再次發送ajax
請求。并不是做了SSR
我們的頁面就不屬于SPA
應用了,它仍然是一個獨立的spa
應用。
SSR原理圖
SSR
是處于CSR
與SPA
應用之間的一個折中的方案,在渲染首屏的時候在服務端做出了渲染,注意僅僅是首屏,其他頁面還是需要在客戶端渲染的,在服務端
接收到請求之后并且渲染出首屏頁面,會攜帶著剩余的路由信息預留給客戶端
去渲染其他路由的頁面。
vueSSR
將本來要放在瀏覽器執行創建的組件,放到服務端先創建好,然后生成對應的html將它們直接發送到瀏覽器,最后將這些靜態標記"激活"為客戶端上完全可交互的應用程序。
在瀏覽器第一次訪問某個URI
資源的時候(首屏),Web
服務器根據路由拿到對應數據渲染并輸出,且輸出的數據中包含兩部分:
在首屏渲染完成之后,此時我們看到的其實已經是一個和之前的SPA
相差無幾的應用程序了,接下來我們進行的任何操作都只是客戶端的應用進行交互,頁面/組件由Web
端渲染,路由也由瀏覽器控制,用戶只需要和當前瀏覽器內的應用打交道就可以了。
vueSSR原理圖
webpack
將 Source
打包出兩個bundle
文件:其中 Server Bundle
用于服務端渲染,服務端通過渲染器 bundleRenderer
將 bundle
生成首屏html
片段;而 Client Bundle
用于客戶端渲染,首屏外的交互和數據處理還是需要瀏覽器執行 Client Bundle
來完成
缺點:
(lifecycle hook)
中使用;一些外部擴展庫(external library)
可能需要特殊處理,才能在服務器渲染應用程序中運行。Node.js
中渲染完整的應用程序,顯然會比僅僅提供靜態文件的 server
更加大量占用 CPU
資源,因此如果你預料在高流量環境(high traffic)
下使用,請準備相應的服務器負載,并明智地采用緩存策略。二、nuxt.js介紹
1. nuxt.js是什么?
Nuxt.js
是vue
官方推薦的一個基于Vue.js
的做Vue SSR
的通用應用框架(開箱即用),集成了Vue,Vue-Router,Vuex,Vue-Meta
等組件/框架,內置了webpack
用于自動化構建,使我們可以更快速地搭建一個具有服務端渲染能力的應用。
2. nuxt.js的優勢?
作為框架,Nuxt.js
為 客戶端/服務端 這種典型的應用架構模式提供了許多有用的特性,例如異步數據加載、中間件支持、布局支持等。Nuxt.js
有以下比較明顯的特性
SASS,LESS
等等HTML
頭部標簽管理(依賴vue-meta
實現)webpack
配置,無需額外配置3. nuxt.js的使用
npm create nuxt-app <project-name>
4. nuxt.js目錄結構
(layouts、pages、static、store、nuxt.config.js、package.json)是Nuxt保留的,不可以更改
5. nuxt.js渲染流程
layout
組件)前執行某種操作,也可以在解析完 layout
之后,解析 page
組件前 執行某種操作。可以理解為是路由器的攔截器的作用Nuxt.js
將自動加載顯示 404
錯誤頁面或 500
錯誤頁面,或者進行重定向。vuex
的store
render
:最后進行渲染。將渲染后的頁面返回給瀏覽器,用戶在頁面進行操作,如果再次請求新的頁面,此時只會回到生命周期中的 middlerware 中,而非 nuxtServerInit。nuxt-link
,如果是發起一個新的路由,那么這個時候要從頭開始循環我們把服務器端創建的 .vue
文件全部理解成組件,在服務器端環境(node)
通過 beforeCreate
和 created
這倆個生命周期節點后服務器端 vue
組件生命周期結束。返回頁面給瀏覽器,在客戶端環境(v8)
中這個 vue
組件實例創建后會在客戶端再次擁有生命周期,此時生命周期中有 mounted
等鉤子函數。
需要特別注意的是 nuxt
中沒有 mounted
鉤子函數也沒有組件實例,只有 beforeCreate/created
鉤子與 context
對象。beforeCreated()
和created()
這兩個生命周期函數是同時運行在服務端&&客戶端,vue的其他鉤子則運行在客戶端,所以beforeCreated()
和created()
不存在window
對象
三、nuxt.js渲染過程部分詳解
1、nuxtServerInit
舉例:打開網頁要立即顯示的內容
// SSR方式: // 1、nuxtServerInit 方法 actions: { async nuxtServerInit({commit},{req,app}) { let {data: {province, city}} = await axios.get('/aa/bb') commit('home/setPosition',{province: '', city: ''}) } } // 2、middleware 屬性 middleware: async (ctx) => { let {data: {province, city}} = await axios.get('/aa/bb') } // NO-SSR vue 組件 mounted 函數發送請求
2、異步數據 asyncData
asyncData
方法會在組件(限于頁面組件)每次加載渲染之前,即在服務端或路由更新之前被調用。在asyncData()
中可以處理請求得來的數據,通過return
將處理后的數據返回給當前vue
組件的data
。再次強調這里不能使用this
,因為沒有組件實例,asyncData()
默認的參數是ctx
即content
對象。
該方法用來獲取數據,在服務器端把異步獲取到的數據扔給瀏覽器,那是如何拋給瀏覽器的呢?
通過下發一個`script`標簽,然后在`window`上掛了一個對象這個對象,第一個是告訴你用的是哪個模板,第二個給你的是數據
3、布局
Nuxt.js布局方式如下圖所示:
nuxt.js
實現了一個新的概念,layout
布局,我們可以通過layout
布 局方便的實現頁面的多個布局之間方便的切換。具體開發的頁面中,如果使用默認布局,則不需指定頁面的布局,nuxt
框架會自動對沒有指定布局的頁面和default
布局進行關聯。如果需要指定布局,則在layout
字段中對布局進行指定。
<script> export default { layout: 'plusBuy', ... } </script> // 如果layout文件中建立了一個單獨的文件,則在使用中也要指定 <script> export default { layout: 'plusBuy/plusBuy', ... } </script>
四、nuxt爬坑
1、localhost
訪問可以,換成真實的ip
地址后訪問不了
解決方案:
package.json
里做如下配置"config": { "nuxt": { "host": "0.0.0.0", "port": 3000 } }
2、接口跨域問題
解決方案
@nuxtjs/axios
、@nuxtjs/proxy
nuxt.config.js
做如下配置modules: ['@nuxtjs/axios'], // 不需要加入@nuxtjs/proxy axios: { proxy: true }, proxy: { '/wlfrontend': { // 請求到 /wlfrontend 代理到請求 http://10.102.140.38:7001/wlfrontend target: 'http://10.102.140.38:7001', changeOrigin: true // 如果接口跨域,需要進行這個參數配置 }, '/scenery': { // 將'localhost:8080/scenery/xxx'代理到'https://m.ly.com/scenery/xxx' target: 'https://m.ly.com', // 代理地址 changeOrigin: true, // 如果接口跨域,需要進行這個參數配置 secure: false // 默認情況下,不接受運行在 HTTPS 上,且使用了無效證書的后端服務器。如果你想要接受,只要設置 secure: false } }
3、asyncDate fetch created
因為服務端客戶端都會走,如果不想在客戶端執行?
async asyncData ({ query, store, req }) { if (!process.server) return } async fetch({ store, params }){ if (!process.server) return } created(){ if (!process.server) return },
4、頁面做緩存,也就是返回上一級保持數據不重新請求
解決方案:
在布局頁面處理,layout/default.vue
或者是自己建立的布局頁面
<template> <div class="plusBuy"> <nuxt keep-alive /> </div> </template>
5、nuxt
是把所有頁面的js都引入到主頁了?
在生產模式下,Nuxt.js
使用瀏覽器的預加載策略來預加載目標頁面的腳本資源。所以當用戶點擊某個鏈接時,會有一種秒開的感覺。預加載策略使得Nuxt.js
既可以保持代碼分離又能保證頁面訪問體驗。
<nuxt-link>
則是幫我們擴展了自動預獲取代碼分割頁面。可以使用no-prefetch
屬性 禁用
如果想要禁用,在nuxt.config.js
做如下配置
router: { prefetchLinks: false, // 全局禁用所有鏈接上的預取 } render: { resourceHints: false, // 添加prefetch和preload,以加快初始化頁面加載時間。如果有許多頁面和路由,可禁用此項 },
6、切換子路由的head
中外部引入腳本載入有延遲,所以在調用時報錯
注意:
1、引入腳本不要加async:true
,這樣的話腳本不會阻塞,在下面代碼有用到該腳本中的方式時,腳本可能還沒有加載完
2、需要每個小項目自己做個定制化頁面layout
,layout/我的目錄/我的頁面.vue 然后在定制化頁面中使用head()
加入腳本
export default { // 方式一: head: { script: [ { type: 'text/javascript', src: 'https://js.40017.cn/cn/min/??/touch/hb/c/bridge.2.1.4.js?v=2016053', defer: true } ] } // 方式二: head () { return { script: [ { type: 'text/javascript', src: 'https://js.40017.cn/cn/min/??/touch/hb/c/bridge.2.1.4.js?v=2016053', defer: true } ] } } }
7、滾動事件
如果html
和body
設置了100%,那么子頁面足夠長時滾動的話,滾動事件要綁定在子頁面上,因為body
的高度不是整個頁面的高度
// 1. 在子頁面父元素加 <template> <div class="plus" ref="mainPage"></div> </template> // 2. 樣式設置100%滾動 .plus { height: 100%; overflow-y: scroll; -webkit-overflow-scrolling : touch; } // 3. 再添加滾動事件 function scrollEvent() { var that = this; let dom = this.$refs.mainPage; dom.onscroll = function() { let wh = dom.scrollTop; // 頁面上滑,出現 wh > 100 ? (that.showBackTop = true) : (that.showBackTop = false); // 未開通,頁面滑動至不出現頂部的立即開通按鈕時,底部的立即開通固定展示 if(that.memberRightsInfo && !that.memberRightsInfo.IsPlusMember){ if(document.querySelector('.tab') && document.querySelector('.tab').offsetTop){ let distance = document.querySelector('.tab').offsetTop; wh > distance - 50 ? (that.isShowFixedBtn = true) : (that.isShowFixedBtn = false); } } }; }
8、文件下建立了其他文件,比如store/plusBuy/index.js
,并沒有在store
下直接建立index.js
,如何使用?
原理:Nuxt把store中的index.js文件中所有的state、mutations、actions、getters都作為其公共屬性掛載到了store實例上,然而其他的文件則是使用的是命名空間,其對應的命名空間的名字就是其文件名。
computed: { ...mapState('plusBuy', { nickName: state => state.nickName }) } ...mapMutations('plusBuy', { setCityId: 'setCityId' // 將 `this.setCityId()` 映射為 `this.$store.commit('setCityId')` }) ...mapActions('plusBuy', { login: 'login' // 將 `this.login()` 映射為 `this.$store.dispatch('login')` })
9、asyncData
不可以調用this
,如果有好多個異步或數據進行處理,如何優化asyncData()
// 可以使用類 class A { aatest(aa){ console.log(aa) } } // 調用方法 async asyncData ({ query, store, req }) { var test = new A(); test.aatest(123); }
10、如何獲取cookie
// 服務端獲取cookie b_getToken(req = {},c_name){ if (req.headers && req.headers.cookie) { var req_Cookies = req.headers.cookie.split("; ") let tokens = '' req_Cookies.forEach(v => { if (v.indexOf(c_name + "=")>=0) { tokens = v } }) return tokens.split('=')[1] } else { return '' } } // 客戶端獲取cookie getCookie: function(c_name) { if (document.cookie.length > 0) { //先查詢cookie是否為空,為空就return "" let c_start = document.cookie.indexOf(c_name + "=") || ''; //通過String對象的indexOf()來檢查這個cookie是否存在,不存在就為 -1 if (c_start != -1) { c_start = c_start + c_name.length + 1; //最后這個+1其實就是表示"="號啦,這樣就獲取到了cookie值的開始位置 let c_end = document.cookie.indexOf(";", c_start); // 為了得到值的結束位置。因為需要考慮是否是最后一項,所以通過";"號是否存在來判斷 if (c_end == -1) { c_end = document.cookie.length; } return unescape(document.cookie.substring(c_start, c_end)); } } return ""; }, // 調用 let token = ''; if(process.server){ token = serverUtilsFn.b_getToken(req,'17uCNRefId'); console.log('server:' + token) }else { token = utilsFn.getCookie('17uCNRefId'); console.log('client:' + token) }
11、axios
數據處理問題,重復問題
import axios from 'axios'; import requestCheck from './requestCheck'; // 確保使用 axios.create創建實例后再使用。否則多次刷新頁面請求服務器,服務端渲染會重復添加攔截器,導致數據處理錯誤 const myaxios = axios.create() // axios.defaults.baseURL = "http://localhost:3000/" myaxios.interceptors.request.use(config => { let req = {...config }; req.url = req.method.toLocaleLowerCase() == 'post' ? requestCheck(req.url, req.data) : requestCheck(req.url, req.params); return req; }, error => { return Promise.reject(error) }) myaxios.interceptors.response.use(response => { return response }, error => { return Promise.reject(error) }) export default myaxios;
12、跳轉路由傳遞參數并且取值
傳遞參數 -- this.$router.push({name: ' 路由的name ', params: {key: value}})
參數取值 -- this.$route.params.key
注: 使用這種方式,參數不會拼接在路由后面,地址欄上看不到參數
注意: 由于動態路由也是傳遞params的,所以在 this.$router.push() 方法中 path不能和params一起使用,否則params將無效。需要用name來指定頁面。
13、設置頁面動畫效果
/* 全局過渡動效設置 - 淡出 (fade) 效果*/ .page-enter-active, .page-leave-active { transition: opacity .5s; } .page-enter, .page-leave-active { opacity: 0; } /* 局部過渡動效設置 - 淡出 (fade) 效果*/ .test-enter-active, .test-leave-active { transition: opacity .5s; } .test-enter, .test-leave-active { opacity: 0; } // 在要使用的組件頁面中 export default { transition: 'test', }
14、如何使用插件
// 1. 安裝插件 yarn add swiper -D // 2. 引入 <script> import Swiper from 'swiper' </script> // 3. 引入樣式 <style lang="less" scoped> @import "../../node_modules/swiper/css/swiper.css"; </style>
15、如何在組件中使用異步數據
如果組件不是和路由綁定的頁面組件,原則上是不可以使用異步數據的。因為 Nuxt.js 僅僅擴展增強了頁面組件的data
方法,使得其可以支持異步數據處理。
對于非頁面組件,有兩種方式可以實現數據的異步獲取:
mounted
方法里面實現異步獲取數據的邏輯,之后設置組件的數據,限制是:不支持服務端渲染。asyncData
或fetch
方法中進行API調用,并將數據作為props
傳遞給子組件。服務器渲染工作正常。缺點:asyncData
或頁面提取可能不太可讀,因為它正在加載其他組件的數據。總之,使用哪種方法取決于你的應用是否需要支持子組件的服務端渲染。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。