1. Nuxt.js 概述
1.1 我們一起做過的SPA
-
SPA(single page web application)單頁 Web 應用,Web 不再是一張張頁面,而是一個整體的應用,一個由路由系統、數據系統、頁面(組件)系統等等,組成的應用程序。
-
我們之前學習的Vue就是SPA中的佼佼者。
-
SPA 應用廣泛用于對SEO要求不高的場景中
1.2 什么是SEO
-
SEO:搜索引擎優化(Search Engine Optimization), 通過各種技術(手段)來確保,我們的Web內容被搜索引擎最大化收錄,最大化提高權重,最終帶來更多流量。
-
非常明顯,SPA程序不利于SEO
-
SEO解決方案:提前將
頁面和數據
進行整合- 前端:采用SSR
-
后端:頁面靜態化 (freemarker 、thymeleaf、velocity)
1.3 什么是SSR技術
-
服務端渲染(Server Side Render),即:網頁是通過服務端渲染生成后輸出給客戶端。
- 在SSR中,前端分成2部分:前端客戶端、前端服務端
- 前端服務端,用于發送ajax,獲得數據
- 前端客戶端,用于將ajax數據和頁面進行渲染,渲染成html頁面,并響應給調用程序(瀏覽器、爬蟲)
-
如果爬蟲獲得html頁面,就可以啟動處理程序,處理頁面內容,最終完成SEO操作。
1.4 SPA和SSR對比
SPA單頁應用程序 | SSR服務器端渲染 | |
---|---|---|
優勢 | 1.頁面內容在客戶端渲染 2. 只關注View層,與后臺耦合度低,前后端分離 3.減輕后臺渲染畫面的壓力 | 1.更好的SEO,搜索引擎工具可以直接查看完全渲染的畫面 2.更快的內容到達時間 (time-to-content),用戶能更快的看到完整渲染的畫面 |
劣勢 | 1.首屏加載緩慢 2.SEO(搜索引擎優化)不友好 | 1.更多的服務器端負載 2.涉及構建設置和部署的更多要求,需要用Node.js渲染 3.開發條件有限制,一些生命周期將失效 4.一些常用的瀏覽器API無法使用 |
1.5 什么是Nuxt.js
-
Nuxt.js 是一個基于 Vue.js 的通用應用框架。
- Nuxt支持vue的所有功能,此類內容為
前端客戶端
內容。 - Nuxt特有的內容,都是
前端服務端
內容。
- Nuxt支持vue的所有功能,此類內容為
-
通過對客戶端/服務端基礎架構的抽象組織,Nuxt.js 主要關注的是應用的 UI 渲染。
-
Nuxt.js 預設了利用 Vue.js 開發服務端渲染的應用所需要的各種配置。
2 入門案例
2.1 create-nuxt-app 介紹
-
Nuxt.js 提供了腳手架工具
create-nuxt-app
-
create-nuxt-app
需要使用npx
-
npx 命令為 NPM版本5.2.0默認安裝組件
2.2 安裝
npx create-nuxt-app <project-name>
- 例如
npx create-nuxt-app demo_nuxt02
2.3 啟動
npm run dev
-
nuxtjs改善
2.4 訪問
http://localhost:3000
3. 目錄結構
3.1 目錄
目錄名稱 | 描述 |
---|---|
assets | 資源目錄,用于存放需要編譯的靜態資源。例如:LESS、SASS等 默認情況下,Nuxt使用Webpack若干加載器處理目錄中的文件 |
components | vue組件目錄,Nuxt.js 不會增強該目錄,及不支持SSR |
layouts | 布局組件目錄 |
pages | 頁面目錄,所有的vue視圖,nuxt根據目錄結構自動生成對應的路由。 |
plugins | 插件目錄 |
static | 靜態文件目錄,不需要編譯的文件 |
store | vuex目錄 |
nuxt.config.js | nuxt個性化配置文件,內容將覆蓋默認 |
package.json | 項目配置文件 |
3.2 別名
-
assets 資源的引用:~ 或 @
// HTML 標簽 <img src="~assets/13.jpg" style="height:100px;width:100px;" alt=""> <img src="~/assets/13.jpg" style="height:100px;width:100px;" alt=""> <img src="@/assets/13.jpg" style="height:100px;width:100px;" alt="">// CSS background-image: url(~assets/13.jpg); background-image: url(~/assets/13.jpg); background-image: url(@/assets/13.jpg);
-
static 目錄資源的引用:/ 直接引用
//html標簽 <img src="/12.jpg" style="height:100px;width:100px;" alt="">//css background-image: url(/12.jpg);
-
實例
<template><div><!-- 引用 assets 目錄下經過 webpack 構建處理后的圖片 --><img src="~assets/13.jpg" style="height:100px;width:100px;" alt=""><!-- 引用 static 目錄下的圖片 --><img src="/12.jpg" style="height:100px;width:100px;" alt=""><!-- css --><div class="img1"></div><div class="img2"></div></div> </template><script> export default {} </script><style>.img1 {height: 100px;width: 100px;background-image: url(~assets/13.jpg);background-size: 100px 100px;display: inline-block;}.img2 {height: 100px;width: 100px;background-image: url(/12.jpg);background-size: 100px 100px;display: inline-block;} </style>
4 路由
4.1 路由概述
- Nuxt.js 依據 pages 目錄結構自動生成 vue-router 模塊的路由配置。
- 要在頁面之間切換路由,我們建議使用
<nuxt-link>
標簽。
標簽名 | 描述 |
---|---|
<nuxt-link> | nuxt.js中切換路由 |
<Nuxt /> | nuxt.js的路由視圖 |
<router-link> | vue默認切換路由 |
<router-view/> | vue默認路由視圖 |
4.2 基礎路由
- 自動生成基礎路由規則
路徑 | 組件位置及其名稱 | 規則 |
---|---|---|
/ | pages/index.vue | 默認文件 index.vue |
/user | pages/user/index.vue | 默認文件 index.vue |
/user/one | pages/user/one.vue | 指定文件 |
-
實例
情況1:訪問路徑,由pages目錄資源的名稱組成(目錄名稱、文件的名稱)- 資源位置: ~/pages/user/one.vue- 訪問路徑:http://localhost:3000/user/one情況2:每一個目錄下,都有一個默認文件 index.vue- 資源位置: ~/pages/user/index.vue- 訪問路徑:http://localhost:3000/user
-
思考:
/user
可以匹配幾種文件?pages/user.vue
文件 【優先級高】pages/user/index.vue
文件
4.3 動態路由
- 在 Nuxt.js 里面定義帶參數的動態路由,需要創建對應的以下劃線作為前綴的 Vue 文件 或 目錄。
路由中路徑匹配 | 組件位置及其名稱 |
---|---|
/ | pages/index.vue |
/user/:id | pages/user/_id.vue |
/:slug | pages/_slug/index.vue |
/:slug/comments | pages/_slug/comments.vue |
- 實例1:獲得id值,創建資源
user/_id.vue
<template><div>查詢詳情 {{this.$route.params.id}}</div>
</template><script>
export default {transition: 'test',mounted() {console.info(this.$route)},
}
</script><style></style>
4.4 動態命名路由
-
路徑
/news/123
匹配_id.vue
還是_name.vue
? -
我們可以使用
<nuxt-link>
解決以上問題- 通過name 確定組件名稱:“xxx-yyy”
- 通過params 給對應的參數傳遞值
<nuxt-link :to="{name:'news-id',params:{id:1002}}">第2新聞</nuxt-link>
<nuxt-link :to="{name:'news-name',params:{name:1003}}">第3新聞</nuxt-link>
4.5 默認路由
路徑 | 組件位置及其名稱 |
---|---|
不匹配的路徑 | pages/_.vue |
- 404頁面,可以采用 _.vue進行處理
4.6 嵌套路由(知道)
-
創建嵌套子路由,你需要添加一個 父組件Vue 文件,同時添加一個與該文件同名的目錄用來存放子視圖組件。
- 要求:父組件 使用
<nuxt-child/>
顯示子視圖內容
pages/ --| book/ //同名文件夾 -----| _id.vue -----| index.vue --| book.vue //父組件
- 要求:父組件 使用
-
步驟1:編寫父組件
pages/child/book.vue
<template><div><nuxt-link to="/child/book/list">書籍列表</nuxt-link> |<nuxt-link to="/child/book/123">書籍詳情</nuxt-link> |<hr><nuxt-child /></div> </template><script> export default {} </script><style></style>
-
步驟2:編寫子組件
pages/child/book/list.vue
<template><div>書籍列表</div> </template><script> export default {} </script><style></style>
-
步驟3:編寫子組件
pages/child/book/_id.vue
<template><div>書籍詳情{{$route.params.id}} </div> </template><script> export default {} </script><style></style>
4.7 過渡動效(了解)
4.7.1 全局過渡動效設置
-
Nuxt.js 默認使用的過渡效果名稱為 page
.page-enter-active
樣式表示進入的過渡效果。.page-leave-active
樣式表示離開的過渡效果。
-
步驟1:創建
assets/main.css
,編寫如下內容:.page-enter-active, .page-leave-active {transition: opacity .5s; } .page-enter, .page-leave-active {opacity: 0; }
-
步驟2:nuxt.config.js 引入main.css文件
module.exports = {css: ['assets/main.css'] }
4.7.1 自定義動畫
-
如果想給某個頁面自定義過渡特效的話,只要在該頁面組件中配置
transition
字段即可: -
步驟1:在全局樣式
assets/main.css
中添加名稱為test
的過渡效果.test-enter-active, .test-leave-active {transition: all 2s;font-size:12px; }.test-enter, .test-leave-active {opacity: 0;font-size:40px; }
-
步驟2:需要使用特效的vue頁面編寫如下:
export default {transition: 'test' }
4.8 案例:學生管理
-
需求1:首頁
-
需求2:點擊,學生管理
-
需求3:點擊“添加”按鈕
-
需求4:點擊修改按鈕
-
需求5:詳情
5. 視圖
5.1 默認模板(了解)
-
定制化默認的 html 模板,只需要在應用根目錄下創建一個 app.html 的文件。
-
默認模板:
<!DOCTYPE html> <html {{ HTML_ATTRS }}><head {{ HEAD_ATTRS }}>{{ HEAD }}</head><body {{ BODY_ATTRS }}>{{ APP }}</body> </html>
-
修改模板,對低版本IE瀏覽器進行支持(兼容IE瀏覽器)
<!DOCTYPE html> <!--[if IE 9]><html lang="en-US" class="lt-ie9 ie9" {{ HTML_ATTRS }}><![endif]--> <!--[if (gt IE 9)|!(IE)]><!--><html {{ HTML_ATTRS }}><!--<![endif]--><head {{ HEAD_ATTRS }}>{{ HEAD }}</head><body {{ BODY_ATTRS }}>{{ APP }}</body> </html>
5.2 默認布局【掌握】
5.2.1 布局概述
-
布局:Nuxt.js根據布局,將不同的組件進行組合。
-
模板:html頁面,是布局后所有組件掛載的基礎。
5.2.2 布局分析
- layouts/default.vue 默認布局組件
- 訪問路徑根據路由,確定執行組件
- 組件具體顯示的位置,有布局來確定
5.2.3 公共導航
- 修改
layouts/default.vue
<template><div><nuxt-link to="/">首頁</nuxt-link> |<nuxt-link to="/user/login">登錄</nuxt-link> |<nuxt-link to="/user/123">詳情</nuxt-link> |<nuxt-link to="/about">默認頁</nuxt-link> |<nuxt-link to="/nuxt/async">async</nuxt-link> |<hr/><Nuxt /></div>
</template>
5.3 自定義布局
-
在layouts目錄下創建組件:layouts/blog.vue
<template><div>開頭<br/><nuxt/>結束<br/></div></template><script> export default {} </script><style></style>
-
在需要的視圖中使用 blog布局
<script> export default {layout: 'blog'//... } </script>
5.4 錯誤頁面(了解)
- 編寫
layouts/error.vue
頁面,實現個性化錯誤頁面
<template><div><div v-if="error.statusCode == 404">404 頁面不存在 {{error.message}}</div><div v-else>應用程序錯誤</div><nuxt-link to="/">首 頁</nuxt-link></div>
</template><script>
export default {props: ['error']
}
</script><style></style>
- 解決問題: 404 、500、連接超時(服務器關閉)
- 總結:所學習的技術中,有2種方式處理錯誤頁面
- 方式1:默認路徑,
_.vue
(先執行) - 方式2:錯誤頁面,
~/layouts/error.vue
- 方式1:默認路徑,
6 Nuxt組件特殊配置
6.1 概述
- Nuxt頁面組件實際上是 Vue 組件,只不過 Nuxt.js 為這些組件添加了一些特殊的配置項(在Vue組件的基礎上,添加了額外功能)
特殊配置項 | 描述 |
---|---|
asyncData | SSR進行異步數據處理,也就是服務器端ajax操作區域。 |
fetch | 在渲染頁面之前獲取數據填充應用的狀態樹(store) |
head | 配置當前頁面的head標簽,整合第三方css、js等。 |
layout | 指定當前頁面使用的布局 |
transition | 指定頁面切換的過渡動效 |
scrollToTop | 布爾值,默認: false。 用于判定渲染頁面前是否需要將當前頁面滾動至頂部。 |
6.2 模板代碼
<template><h1 class="red">Hello {{ name }}!</h1>
</template><script>
export default {asyncData (context) { //異步處理數據, 每次加載之前被調用return { name: 'World' }}, fetch () { //用于在渲染頁面之前獲取數據填充應用的狀態樹(store)},head: { //配置當前頁面的 Meta 標簽},layout: '自定義布局名' //自定義布局...
}
</script><style>
.red {color: red;
}
</style>
6.3 head - 入門
-
html模板代碼
<html> <head><meta charset="UTF-8" /><title>我是標題</title><link rel="stylesheet" type="text/css" href="css外部文件"/><script src="js外部文件" type="text/javascript" charset="utf-8"></script> </head> <body> </body> </html>
-
通過nuxt提供head屬性,可以給單個也是設置:標題、外部css、外部js 等內容。
-
基本模板
<script> export default {head: {link: [], //導入第三方css文件,可以導入多個script: [] //導入第三方js文件,可以導入多個} } </script>
-
完整代碼
<template><div>詳情頁 {{$route.params.id}} <br/><div class="bg2"></div><div class="bg3"></div></div>
</template><script>
export default {head: {title: '詳情頁',link: [{rel:'stylesheet',href:'/style/img.css'},....],script: [{ type: 'text/javascript', src: '/js/news.js' }]}
}
</script><style>.bg2 {background-image: url('~static/img/2.jpg');width: 300px;height: 300px;background-size: 300px;}
</style>
7. ajax操作
7.1 整合 axios
7.1.1 默認整合
-
在構建項目時,如果選擇axios組件,nuxt.js將自動與axios進行整合
7.1.2 手動整合(可選)
-
步驟1:package.json有axios的版本
"dependencies": {"@nuxtjs/axios": "^5.13.1",},
-
步驟2:安裝
npm install
-
步驟3:nuxt.config.js 以模塊的方式添加axios
modules: [// https://go.nuxtjs.dev/axios'@nuxtjs/axios',],
7.1.3 常見配置
-
修改 nuxt.config.js 進行baseURL的配置
// Axios module configuration: https://go.nuxtjs.dev/config-axiosaxios: {baseURL:'http://localhost:10010/'},
7.2 使用axios發送ajax
-
在vue頁面中,通過 this. a x i o s . x x x ( ) 操作 a j a x 。 t h i s . axios.xxx() 操作ajax。this. axios.xxx()操作ajax。this.axios 與之前 axios等效。
this.$axios.post("/search-service/search",this.searchMap).then( res => {//獲得查詢結果this.searchResult = res.data.data; });
7.3 使用asyncData發送 ajax
-
asyncData中的ajax將在“前端服務端執行”,在瀏覽器看到是數據,而不是ajax程序。
7.3.1 發送一次請求
- 語法:
export default {async asyncData( context ) { //context就相當于其他地方的this//發送ajaxlet { data } = await context.$axios.get('路徑','參數')// 返回結果return {變量: 查詢結果從data獲取 }},
}
-
實例
<template><div>{{echo}}</div> </template><script> export default {async asyncData(context) {// 發送ajaxlet {data} = await context.$axios.get('/service-consumer/feign/echo/abc')// 返回數據return {echo: data}}, } </script><style></style>
7.3.2 發送多次請求
-
語法1:
export default {async asyncData( content ) {let [結果1,結果2] = await Promise.all([ ajax請求1, ajax請求2])return {變量1: 結果1,變量2: 結果2}}, }
-
語法2:
export default {async asyncData( content ) {let [{數據:別名1},{數據:別名2}] = await Promise.all([ ajax請求1, ajax請求2])return {變量1: 別名1,變量2: 別名2}}, }
//演化過程 let response = ajax請求 let [response,response] = await Promise.all([ajax1,ajax2]) let [{data},{data}] = await Promise.all([ajax1,ajax2]) let [{data:別名1},{data:別名2}] = await Promise.all([ajax1,ajax2])
-
實例
<template><div>{{echo}} {{echo2}}</div>
</template><script>
export default {async asyncData(context) {// 發送ajaxlet [{data:echo}, {data:echo2}] = await Promise.all([context.$axios.get('/service-consumer/feign/echo/abc'),context.$axios.get('/service-consumer/client/echo/abc')])// 返回數據return {echo,echo2}},
}
</script><style></style>
7.4 使用fetch發送 ajax
- fetch 方法用于在渲染頁面前填充應用的狀態樹(store)數據, 與 asyncData 方法類似,不同的是它不會設置組件的數據。
-
步驟1:創建
store/index.js
export const state = () => ({str: 0 })export const mutations = {setData (state, value) {state.str = value} }
-
步驟2:測試頁面
<template><div><!-- 顯示數據 -->{{$store.state.str}}</div> </template><script>export default {async fetch( {store, $axios} ) {// 發送ajaxlet { data } = await $axios.get('/service-consumer/feign/echo/abc')// 設置數據store.commit('setData' , data )} } </script><style></style>
7.5 插件:自定義axios
7.5.0 分析
7.5.1 客戶端
-
步驟一:在
nuxt.conf.js
中配置客戶端插件,設置 mode 為 clientplugins: [{ src: '~plugins/api_client.js', mode: 'client' }//{ src: '~plugins/api.js', ssr: false }],
-
步驟二:編寫
plugins/api_client.js
對 內置的 $axios進行增強//自定義函數 const request = {test : (params) => {return axios.get('/service-consumer/feign/echo/abc',{params})}, }var axios = null export default ({ $axios }, inject) => {//3) 保存內置的axiosaxios = $axios//4) 將自定義函數交于nuxt// 使用方式1:在vue中,this.$request.xxx()// 使用方式2:在nuxt的asyncData中,content.app.$request.xxx()inject('request', request) }
7.5.2 服務端
-
步驟一:配置服務端插件,設置 mode 為 server
plugins: [{ src: '~plugins/api_client.js', mode: 'client' },{ src: '~plugins/api_server.js', mode: 'server' },//{ src: '~plugins/api.js', ssr: false },//{ src: '~plugins/api.server.js', ssr: true }],
-
步驟二:編寫
plugins/api.server.js
對 內置的 $axios進行增強const request = {test : (params) => {return axios.get('/service-consumer/feign/echo/abc',{params})},}var axios = null export default ({ $axios, redirect, process }, inject) => {//賦值axios = $axios//4) 將自定義函數交于nuxt// 使用方式1:在vue中,this.$requestServer.xxx()// 使用方式2:在nuxt的asyncData中,content.app.$requestServer.xxx()inject('requestServer', request) }
-
注意:前端服務端插件,不支持切換路由。也就是說刷新可以訪問,使用
<nuxt-link>
切換不能訪問。解決方案:- 方案1:修改mode,支持client和service。
- 方案2:使用
location.href = ‘路徑’
進行跳轉
7.5.3 插件配置總結
//方式1:通過src設置文件,通過mode設置模式
plugins: [{ src: '~/plugins/apiclient.js', mode: 'client' }, //前端客戶端{ src: '~/plugins/apiserver.js', mode: 'server' }, //前端服務端{ src: '~/plugins/api.js' } //前端客戶端 + 前端服務端
]//方式2:通過命名來確定模式
plugins: ['~/plugins/api.client.js', //前端客戶端'~/plugins/api.server.js', //前端服務端'~/plugins/api.js', //前端客戶端 + 前端服務端
]
8. Vuex 狀態樹
8.1 根模塊數據操作
-
步驟一:創建
store/index.js
添加一個 counter變量,并可以繼續累加操作export const state = () => ({counter: 0 })export const mutations = {increment (state) {state.counter++} }
-
步驟二:在頁面中,使用
<template><div>首頁 {{counter}}<input type="button" value="+" @click="increment"/></div> </template><script> import { mapState,mapMutations } from 'vuex' export default {computed: {...mapState(['counter'])},methods: {...mapMutations(['increment'])}, } </script><style></style>
8.2 其他模塊數據操作
-
步驟一:創建其他模塊
store/book.js
export const state = () => ({money: 0 })export const mutations = {addmoney (state) {state.money += 5} }
-
步驟二:使用指定模塊中的數據
<template><div>金額:{{money}} <br><input type="button" value="累加" @click="addMoney(5)"></div> </template><script> import {mapState, mapMutations} from 'vuex' export default {methods: {// ...mapMutations({'方法名':'模塊/action名稱'})...mapMutations({'addMoney':'book/addMoney'})},computed: {//...mapState('模塊名稱',['變量'])...mapState('book',['money'])} } </script><style></style>
8.3 完整vuex模板
// state為一個函數, 注意箭頭函數寫法
const state = () => ({user: 'jack'
})// mutations為一個對象
const mutations = {setUser(state, value) {state.counter = value}
}
// action執行mutation
const actions = {userAction (context,value){// 可以發送ajaxcontext.commit('setUser',value)}}// 獲取數據
const getters = {getUser (state) {return state.user}
}
export default {namespace: true, // 命名空間,強制要求,在使用時,加上所屬的模塊名,例如:book/addmoneystate,mutations,actions,getters
}
9. nuxt流程總結
10. Nuxt整合Element UI
11 綜合練習
10.1 練習1:學生列表
-
表結構
#班級表 create table tab_class(cid int primary key auto_increment,cname varchar(50) ); insert into tab_class(cid, cname) values(1,'Java56'); insert into tab_class(cid, cname) values(2,'Java78');#學生表 create table tab_student(sid int primary key auto_increment,sname varchar(50),cid int );insert into tab_student(sname,cid) values('張三',1); insert into tab_student(sname,cid) values('李四',1); insert into tab_student(sname,cid) values('王五',2); insert into tab_student(sname,cid) values('趙六',2);
-
需求:查詢學生列表信息
- 要求1:可以進行“班級”條件查詢
- 要求2:對“班級”數據進行SEO
- 要求3:學生數據不進行SEO
10.2 練習2:
10.2.1 表結構:
CREATE TABLE tb_teacher(tid INT PRIMARY KEY AUTO_INCREMENT,tname VARCHAR(50) COMMENT '老師姓名',TYPE INT COMMENT '老師類型:1.授課老師、2.助理老師、3.輔導員老師'
);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(1,'梁桐老師',1);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(2,'馬坤老師',2);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(3,'仲燕老師',3);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(4,'袁新奇老師',1);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(5,'任林達老師',2);
INSERT INTO tb_teacher(tid,tname,TYPE) VALUES(6,'王珊珊老師',3);CREATE TABLE tb_class(cid INT PRIMARY KEY AUTO_INCREMENT,cname VARCHAR(50) COMMENT '班級名稱',teacher1_id INT COMMENT '授課老師',teacher2_id INT COMMENT '助理老師',teacher3_id INT COMMENT '輔導員老師'
);INSERT INTO tb_class(cid,cname,teacher1_id,teacher2_id,teacher3_id) VALUES(1,'Java56',1,2,3);
INSERT INTO tb_class(cid,cname,teacher1_id,teacher2_id,teacher3_id) VALUES(2,'Java78',1,2,3);
INSERT INTO tb_class(cid,cname,teacher1_id,teacher2_id,teacher3_id) VALUES(3,'Java12',4,5,6);
INSERT INTO tb_class(cid,cname,teacher1_id,teacher2_id,teacher3_id) VALUES(4,'Java34',4,5,6);
10.2.2 需求:查詢
- 需求:使用“自定義axios”完成
- 查詢班級詳情
- 通過班級名稱模糊查詢
- 查詢班級的同時,查詢老師信息