1.專案準備
技術棧:
Vue
vue-router
vant
axios
,抽取apivue-cli
腳手架Vuex
狀態管理
1.1 rem適配
頭條專案使用rem適配
需求:瀏覽器尺寸改變之後,頁面元素自動適配
步驟:
下包導包
npm i amfe-flexible
main.js
匯入
通過modele的對應模組可以分隔螢幕
在對應檔案可以設定根文字的大小
1.2 通用樣式CSS
- 存放目錄在
/src/styles/base.less
- main.js中匯入樣式,順序應在元件庫之後,自己寫的要覆蓋其他的,在後面引入
1.3刪除測試程式碼
在建立專案的時候會自動建立一些測試程式碼
針對刪除
App.vue
內容幹掉- 保留掛載點 #app 和 router-view渲染結構
views
目錄內容,清空多餘的元件,建立需要的元件/router/index.js
- 自定義路由規則
components/HelloWorld.vue
刪掉
1.4Git託管
https 或 ssh託管
webstorm一鍵連結和管理
2.login頁面
2.1 頁面佈局和表單校驗
佈局:
1.導航欄
2.表單
3.提交按鈕
校驗:
1.新增rule物件規則
required必填項和提示資訊
正規表示式規定輸入資訊格式
van-form
- @submit:表單驗證成功之後觸發的回撥函式
- 引數是一個物件
- 內部的輸入元素的
name
屬性和value
值,拼接為一個物件
- @submit:表單驗證成功之後觸發的回撥函式
- 輸入元素
rules
:校驗規則- 陣列
- 每一條規則是一個
物件
- required:必填
- message:提示資訊
- pattern:正則的規則
<template>
<div class="login-container">
<!-- 導航欄 -->
<van-nav-bar title="登入" class="my-nav-bar" />
<!-- 表單 -->
// 內建的submit事件呼叫method的onSubmit自定義方法
<van-form @submit="onSubmit">
<van-field
v-model="username"
//name提交的引數名
//value在input框由使用者輸入
name="使用者名稱"
label="手機號"
//必填項
required
placeholder="請輸入手機號"
:rules="[
//校驗規則
{ required: true, message: '請輸入手機號' },
{
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
message: '手機號格式不對'
}
]"
/>
<van-field
v-model="password"
name="密碼"
label="驗證碼"
required
placeholder="請輸入驗證碼"
:rules="[
{ required: true, message: '請填寫驗證碼' },
{ pattern: /\d{6}/, message: '驗證碼格式不對' }
]"
/>
<div style="margin: 16px;">
<van-button round block type="info" native-type="submit"
>提交</van-button
>
</div>
</van-form>
</div>
</template>
<script>
export default {
//元件暴露的名稱
name: 'login',
//login元件的資料,以函式形式表達
data () {
return {
username: '',
password: ''
}
},
//對應表單的自定義方法
methods: {
//values是一個物件,name(程式碼定義)-value(使用者輸入)
onSubmit (values) {
console.log('submit', values)
}
}
}
</script>
<style lang="less">
//設定登入條的樣式
.login-container {
.my-nav-bar {
background-color: #3196fa;
.van-nav-bar__title {
color: white;
}
}
}
</style>
2.2login頁的介面抽取
下包導包
-
下包:
npm i axios
-
匯入在
/src/api/login.js
-
create
方法建立一個副本
4.可用資訊
- 手機號很多個
- mobile:
13912345678
- 驗證碼固定的
- code:
246810
2.5.loading效果
避免使用者頻繁提交,為按鈕增加
loading
效果,並且切換啟用
和禁用
狀態
需求:
- 資料提交時,為按鈕增加loading效果
- 切換
啟用
/禁用
狀態,避免重複點選 - 通過button按鈕的屬性實現
Example
<template>
<div>
<button @click="isLoading = !isLoading">切換loading</button>
<br />
<van-button :loading="isLoading" type="primary" />
<br />
<van-button :loading="isLoading" type="primary" loading-type="spinner" />
<br />
<van-button
//當載入中的時候,禁止提交按鈕的點選,其狀態為true是禁止
:disabled="isLoading"
//當載入中的時候,其狀態時true,顯示載入動畫
:loading="isLoading"
//type =info,是資訊按鈕,為藍色
type="info"
//載入中的文字
loading-text="載入中..."
>按鈕</van-button
>
</div>
</template>
<script>
export default {
data () {
return {
isLoading: false
}
}
}
</script>
<style></style>
2.6封裝token方法
實現功能,把
token
儲存到快取中
步驟:
sessionStorage
重新整理不在了localStorage
重新整理還在- .then
- 儲存起來
- 預設
無法直接儲存
複雜型別 - 除非轉為JSON格式的字串
JSON.stringify(複雜型別)
-->字串
- 預設
- 儲存起來
token
在多個地方都需要使用,比如登出
,介面
我們把它抽取一下,方便呼叫,同時避免出錯,為了方便操作快取,封裝工具函式
/src/utils/token.js
- 提供3個方法並暴露出來
saveToken
- 儲存
token
- 接收引數
- 儲存
removeToken
- 刪除
token
- 無引數,無返回至
- 刪除
getToken
- 返回
token
- 返回
實現token工具函式的封裝
// 定義key
const TOKENKEY = 'top-line-token'
// 儲存 token
const saveToken = tokenObj => {
window.localStorage.setItem(TOKENKEY, JSON.stringify(tokenObj))
}
// 刪除 token
const removeToken = () => {
window.localStorage.removeItem(TOKENKEY)
}
// 獲取 token
const getToken = () => {
//getToken需要return
// str-->obj
return JSON.parse(window.localStorage.getItem(TOKENKEY))
}
//將定義的方法暴露出去
export { saveToken, removeToken, getToken }
2.7輕提示toast
在呼叫onsumit
方法提交成功之後要列印數
據和彈出輕提示
1.匯入請求方法
2.在點選提交按鈕時呼叫onsumit
方法,提交請求之後通過then和catch判斷是否請求成功
3.如果請求成功就彈出輕提示--陳工
4.如果請求失敗就彈出輕提示--失敗
2.8整合三部分程式碼
loading-MV程式碼、token工具函式封裝、、toast-MV程式碼、
1.在請求成功時除了改變toast的輕提示,還有改變loading動畫的狀態
2.在請求時時除了改變toast的輕提示,還有改變loading動畫的狀態
3.為了防止loading響應太快,出現閃頓,設定一個定時器給關閉loading的語句
4.在Model結構中控制改變 過渡動畫的狀態
以及 禁用的狀態
實現程式碼如下
<template>
<div class = "login-container">
<!-- 導航欄 -->
<van-nav-bar title = "登入" class = "my-nav-bar"/>
<!-- 表單 -->
<van-form @submit = "onSubmit">
<van-field
//繫結data中的mobile資料
v-model = "mobile"
//設定請求的鍵名
name = "mobile"
//繫結要顯示的文字
label = "手機號"
//設定必選項
required
//設定佔位符placeholder,同時給佔位符文字內容
placeholder = "請輸入手機號"
//對文字框的內容通過正則進行限制
:rules = "[
{ required: true, message: '請輸入手機號' },
{
pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
message: '手機號格式不對'
}
]"
/>
<van-field
v-model = "code"
name = "code"
label = "驗證碼"
required
placeholder = "請輸入驗證碼"
//對文字框的內容通過正則進行限制
:rules = "[
{ required: true, message: '請填寫驗證碼' },
{ pattern: /\d{6}/, message: '驗證碼格式不對' }
]"
/>
<div style = "margin: 16px;">
<van-button
:loading = "isLoading"
:disabled = "isLoading"
loading-text = "登入ing"
round
block
type = "info"
native-type = "submit"
>提交
</van-button
>
</div>
</van-form>
</div>
</template>
<script>
// 匯入api的請求方法
import { userLogin } from '../../api/login'
// 匯入 token工具函式
import { saveToken } from '../../utils/token'
export default {
name: 'login',
data () {
return {
// 在頁面進入的時候載入一個手機號和驗證嗎,方便測試。
// 邏輯上說,應該由使用者輸入和驗證
mobile: '13912345678', // 手機號
code: '246810', // 驗證碼
isLoading: false// 是否正在載入中
}
},
methods: {
onSubmit (values) {
//鍵名是name,鍵值是使用者在表單輸入的,為了方便測試設定了一個預設的值
// values是一個物件{mobile:'xxx',code:'xxx'}
// 開啟loading
this.isLoading = true
//userlogin是封裝的請求方法,傳入請求的物件{ mobile: '13912345678', code: '246810' }
userLogin(values)
.then(res => {
console.log('res:', res)
// 儲存token
saveToken(res.data.data)
//設定定時器的目的是為了在呈現效果的時候不會閃頓,更加可控和順滑
//請求成功之後關閉輕提示,關閉動畫
setTimeout(() => {
this.$toast.success('登入成功!')
this.isLoading = false
}, 500)
})
.catch(errRes => {
console.log('errRes:', errRes)
//設定定時器的目的是為了在呈現效果的時候不會閃頓,更加可控和順滑
//請求失敗之後關閉輕提示,關閉動畫
setTimeout(() => {
this.$toast.fail('登入失敗!')
this.isLoading = false
}, 500)
})
}
}
}
</script>
//設定導航欄nav的樣式
<style lang = "less">
.login-container {
.my-nav-bar {
background-color: #3196fa;
.van-nav-bar__title {
color: white;
}
}
}
</style>
2.9使用token的儲存方法
2.10跳轉到home
userLogin(values).then(res => {
/* 進入到then就表示已經成功請求到了 */
console.log('res', res)
saveToken(res.data.data)
/* 防止太快看不到loading動畫,設一個定時器 */
setTimeout(() => {
this.$toast.success('登入成功!')
this.isLoading = false
this.$router.push({
path: '/home'
})
}, 300)
2.11 重定向
在沒有輸入
url
時,開啟專案看到的是白色介面,最後我們們通過重定向來解決這個問題 '/'跳轉到home
routes: [
{
// 預設地址
path: '/a',
// 重定向的地址 需要被註冊
redirect: '/b'
}
]
3.layout頁面
3.1整合底部layout結構
<template>
<div class = "layout-container">
layout
<van-tabbar v-model = "active" route>
//根據樣式找到對應的圖示類名
<van-tabbar-item icon = "home-o">首頁</van-tabbar-item>
<van-tabbar-item icon = "chat-o">問答</van-tabbar-item>
<van-tabbar-item icon = "video-o">視訊</van-tabbar-item>
<van-tabbar-item icon = "user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
name: 'layout',
data () {
return {
//高亮第一個圖示
active: 0
}
}
}
</script>
<style></style>
<van-tabbar v-model = "active" route>
route的作用是開啟路由模式,vant的Tabbar元件的引數
3.2layout巢狀路由
1.檔案結構
2.路由匯入
import home from '../views/layout/home'
import question from '../views/layout/question'
import movie from '../views/layout/movie'
import user from '../views/layout/user'
3.渲染結構的出口在layout的index.vue中的vue-router
<!--巢狀路由的出口-->
<router-view></router-view>
4.巢狀路由出口的渲染(router-view)
5.children在定義的時候也決定了它的路由出口在父元件上,所以元件的router-view最終渲染的位置在layout元件上,通過chrome外掛可以檢視 children: [{},{},{}]
4.user頁面
4.1.整合路由
SSR直連,解決timeout
4.2頂部區域
<template>
<div class = "user-container">
<div class = "info-box">
<van-image
class = "my-image"
round
src = "https://img01.yzcdn.cn/vant/cat.jpeg"
/>
<h2 class = "name">
起飛
<br>
<van-tag color = "#fff" text-color = "#3296fa" type = "primary">2021-6-10</van-tag>
</h2>
</div>
</div>
</template>
<script>
export default {
name: 'user'
}
</script>
<style lang = "less">
.user-container {
.info-box {
height: 100px;
background-color: #3296fa;
display: flex;
padding-left: 18px;
// 從 彈性佈局 左邊開始
justify-content: flex-start;
align-items: center;
.my-image {
width: 60px;
height: 60px;
margin-right: 5px;
}
.name {
margin-left: 5px;
color: white;
font-size: 15px;
font-weight: normal;
}
}
}
</style>
拓--justify-content:的樣式展示
4.3操作按鈕
less語法有&操作符 用法:&符號有2中用法,其一:父選擇符;其二:且的意思,在這裡使用的
設定操作連結的佈局和樣式
實現結構
<van-row class = "my-control-box">
<van-col class = "my-col" span = "8"
>
<van-icon class = "my-icon my" name = "newspaper-o"/>
我的作品
</van-col
>
<van-col class = "my-col " span = "8"
>
<van-icon class = "my-icon star" name = "star-o"/>
我的收藏
</van-col
>
<van-col class = "my-col " span = "8"
>
<van-icon class = "my-icon history" name = "tosend"/>
閱讀歷史
</van-col
>
</van-row>
實現樣式
//設定每一列的字型大小,並且居中
.my-col {
font-size: 12px;
text-align: center;
}
//單獨給icon字型圖示設定大小,並且轉為塊級元素
.my-icon {
font-size: 28px;
display: block;
//且的意思,my-icon且my
&.my {
color: #77aaff;
}
&.star {
color: #ff0000;
}
&.history {
color: #ffaa00;
}
}
}
4.4底部區域
1.icon
:左側圖示
2.title
:左側文字
3.is-link
:右側箭頭
實現結構
<template>
<div>
<van-cell-group>
<van-cell title="編輯資料" icon="edit" is-link />
<van-cell title="小智同學" icon="chat-o" is-link />
<van-cell title="系統設定" icon="setting-o" is-link />
<van-cell title="退出登入" icon="warning-o" is-link />
</van-cell-group>
</div>
</template>
<script>
export default {}
</script>
<style></style>
實現樣式
.my-control-box {
//設定上下內邊距,把三個小圖示擠進去
padding-top: 20px;
padding-bottom: 20px;
//設定每一列的字型大小,並且居中
.my-col {
font-size: 12px;
text-align: center;
}
//單獨給icon字型圖示設定大小,並且轉為塊級元素
.my-icon {
font-size: 28px;
display: block;
//且的意思,my-icon且my
&.my {
color: #77aaff;
}
&.star {
color: #ff0000;
}
&.history {
color: #ffaa00;
}
}
}
4.5使用者資訊
1.測試介面
2.api抽取
在src/api資料夾封裝一個user.js用來儲存user頁面用到的api
//匯入請求元件axios
import axios from 'axios'
// 匯入獲取token的工具函式
import { getToken } from '@/utils/token'
//設定基地址
const request = axios.create({
baseURL: 'http://toutiao-app.itheima.net'
})
//封裝獲取使用者資訊的方法
const getUserInfo = () => {
return request({
//剩餘的請求地址
url: '/v1_0/user/profile',
//請求的方式
method: 'get',
//請求頭的設定
headers: {
Authorization: `Bearer ${getToken().token}`
}
})
}
//暴露獲取使用者資訊的方法
export { getUserInfo }
//儲存的token
//{token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2M…HNlfQ.4XdqWBCJ-Q_IGxNY5jEekiqrmzOg6zZIYLQkbK5WIWE", refresh_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2M…ydWV9.UycupnAiENN4AZROq7n8LbxkNEAuUYZHcXe52SFfVgs"}
////獲得請求頭的授權token,通過呼叫token工具函式,因為在saveToken的時候是一個物件,所以呼叫函式得到的是一個物件 setItem(TOKENKEY, JSON.stringify(tokenObj))
3.使用新增資料
實現邏輯
//匯入預設的圖片
import defaultImg from '../../../assets/logo.png'
// 匯入 請求的 api方法
import { getUserInfo } from '../../../../src/api/user'
//暴露介面
export default {
name: 'user',
data () {
return {
defaultImg,
// 設定一個空物件儲存使用者資訊
userInfo: {}
}
},
created () {
// 在鉤子新增請求到的資訊
getUserInfo().then(res => {
console.log('res:', res)
//如果獲取成功使用者資訊,新增到 userInfo: {}
this.userInfo = res.data.data
})
}
}
實現結構
<div class = "info-box">
<van-image
class = "my-image"
round
//設定返回資料的頭像
:src = "userInfo.photo"
/>
<h2 class = "name">
//設定返回資料的名稱
{{ userInfo.name }}
<br>
//設定返回資料的生日
<van-tag color = "#fff" text-color = "#3296fa" type = "primary"> {{ userInfo.birthday }}</van-tag>
</h2>
</div>
4.6登出功能
通過dialog模態框實現彈出
清空資訊的語句在後面有改善,通過Vuex在登出方法中做出優化
this.$store.commit('setUserInfo', {})
實現邏輯
methods: {
logout () {
//
this.$dialog
.confirm({})
.then(() => {
// 登出後刪除token資料
removeToken()
// 登出後將使用者資訊清空,之前儲存的使用者資訊是一個物件
this.userInfo = {}
// 登出後通過router元件跳轉頁面到home,因為不是後臺管理系統所以不需要跳轉到login登入頁面
this.$router.push({
path: '/home'
})
// 登出失敗返回一個catch
}).catch(() => {
console.log('catch')
})
}
}
實現功能
在登出的標籤註冊一個點選事件
<van-cell title = "退出登入" icon = "warning-o" is-link @click = "logout"/>
4.7登入判斷token--前置守衛
限制在user頁面進行判斷,其他頁面不登陸也可以訪問
檔案目錄 --src/router/index.js
實現邏輯
導航守衛的三個引數必須按順序書寫
//routes引數的設定
{
path: 'user',
name: 'user',
component: user,
meta: {
//自定義的規則欄位
needLogin: true
}
}
// 1.在導航守衛中先獲取meta是否需要登入
router.beforeEach((to, from, next) => {
//判斷是否需要
if (to.meta.needLogin !== true) {
//如果不需要元資訊,直接返回next()
return next()
}
// 拿到令牌
const tokenObj = getToken()
//2.判斷token令牌是否存在
if (tokenObj === null) {
//如果令牌為空,彈出輕提示
Toast.fail('請先登入')
//如果令牌為空,跳轉到login頁面
return next({ path: '/login' })
}
//token既不是空,而且需要元資訊
next()
//3.判斷token令牌是否正確
// 拿到個人的資訊
//getUserInfo根據已有的token發起請求,並且返回使用者的資料
getUserInfo()
// 請求成功之後攜帶資料直接到下一站
.then(res => {
console.log(res)
next()
})
// 請求失敗,根據返回的狀態碼401判斷token不正確(後端完成)
.catch(errRes => {
if (errRes.response.status === 401) {
removeToken()
// 如果token給出提示請先登入
Toast.fail('請先登入')
// 如果token不對跳轉登入頁面
next({ path: '/login' })
}
})
})
console.dir()可以解析屬性的值
解決Vuerouter的Promise報錯(吞掉)
// 吞掉 沒有處理的 promise的錯誤
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location, onResolve, onReject) {
if (onResolve || onReject) {
// 執行之後 阻斷後續程式碼
return originalPush.call(this, location, onResolve, onReject)
}
return originalPush.call(this, location).catch(err => err)
}
4.8 共享使用者資訊-vuex
1.新增vuex元件
vue add vuex
在mian.js檔案上
// 將Vuex倉庫註冊Vue建構函式上
store,
2.修改使用者資訊
src/router/index.js
// 將獲取的使用者資訊共享到Vuex的倉庫的state中,通過定義的 mutations的setUserInfo方法修改預設定的資訊資訊
//第一個引數是呼叫的修改資料的方法,第二個資料是修改後的資料
store.commit('setUserInfo', res.data.data)
3.設定路由元
資訊
export default new Vuex.Store({
//設定原始資料
state: {
userInfo: {}
},
mutations: {
//定義修改資料的方法
//這個方法接收一個原始的資料,第二個引數是傳入的修改後的資料
setUserInfo (state, newUserInfo) {
state.userInfo = newUserInfo
}
},
4.檢視共享的資料
4.9使用者頁面調整
1.導航守衛中請求了一次
導航守衛屬於router,在router資料夾的index.js當中
目的有三
為了判斷是否需要判斷needLogin
為了判斷請求的資料是否存在token
為了判斷token的值是否正確,401?
這裡拿到了使用者的資料只進行了判斷,沒有進行使用
2.載入user頁面請求一次
這裡通過呼叫 getUserInfo()方法獲取,用來渲染頁面。
請求使用者資訊的方法getUserInfo()定義在src/api/user中,因為也算是請求,放在api檔案當中
3.解決多次請求
Vuex本質是共享資料,統一放在新建的src/store的檔案中的index.js
使用者資訊儲存到Vuex中,調整使用者頁面資訊的資料來源(目的是為了只請求一次放在倉庫中,減少請求次數),之前是兩次裡請求
在登出方法logout(),修改清空資料的語句
// 登出後將使用者資訊清空,之前儲存的使用者資訊是一個物件
// this.userInfo = {}
// 優化登出資訊清空,現在拿到的資訊是從Vuex中來的
this.$store.commit('setUserInfo', {})
4.10登陸成功返回訪問頁
1.正常訪問login頁面之後區home頁面
2.因為沒有登入被轉到login,在登入成功之後要返回
1.login/index.vue登陸成功之後進行判斷
1.有redirect引數
2.沒有redirect引數
實現邏輯
在router檔案的index.js中實現redirect引數的攜帶
1.沒有token攜帶redirect引數
if (tokenObj === null) {
Toast.fail('請先登入')
// 如果沒有token令牌跳轉到登陸頁面,並且攜帶redirect引數
return next({
path: '/login',
// 定義重新定向的到哪去的路徑引數,這個路徑引數指向當前沒登陸的頁面
query: { redirect: to.path }
})
}
2.token令牌錯誤攜帶redirect引數
.catch(errRes => {
if (errRes.response.status === 401) {
removeToken()
// 如果token給出提示請先登入
Toast.fail('請先登入')
// 如果token不對跳轉登入頁面
next({
path: '/login',
// 如果請求的token令牌是錯誤的也要重新定向
query: { redirect: to.path }
})
}
})
3.在login檔案中的index.vue對redirect引數進行判斷
該邏輯寫在表單提交成功的then之中
const redirect = this.$route.query.redirect
if (redirect) {
return this.$router.push({
path: redirect
})
}
// 沒有redirect引數
this.$router.push({
path: '/home'
})
5.edit頁面
5.1整合路由
1.建立edit頁面在src/views/layout/edit/index.vue
<template>
<div class = "edit-container">
<!-- 導航條 -->
<van-nav-bar left-arrow title = "編輯資料"></van-nav-bar>
<!-- 頭像部分 -->
<div class = "avatar">
<van-image fit = "cover" round src = "https://img.yzcdn.cn/vant/cat.jpeg"/>
</div>
<!-- 資訊展示 -->
<van-cell-group>
<van-cell is-link title = "名稱" value = "暱稱"/>
<van-cell is-link title = "性別" value = "男"/>
<van-cell is-link title = "生日" value = "2020-1-1"/>
</van-cell-group>
</div>
</template>
<script>
export default {
name: 'editUser'
}
</script>
<style lang = "less">
.edit-container {
// 導航條部分
.van-nav-bar {
background-color: #3196fa;
.van-nav-bar__title {
color: #fff;
}
.van-icon-arrow-left {
color: #fff;
}
}
// 頭像部分
.avatar {
padding: 20px 0;
text-align: center;
.van-image {
width: 120px;
height: 120px;
}
}
}
</style>
2.在src/router/index.js
建立對應的edit路由
路由安放的位置在layout路由的children序列當中
layout是所有chidren路由渲染的出口<router-view></router-view>
{
path: 'edit',
name: 'edit',
component: edit
},
5.2user跳轉edit
1.新增跳轉屬性
操作目錄:src/views/layout/user/index.vue
<van-cell title = "編輯資料" icon = "edit" is-link to = "/edit"/>
2.從edit返回user的兩種方法
操作目錄src/views/layout/edit/index.vue
go方法
onClickleft () {
this.$router.go(-1)
}
back方法
onClickleft () {
this.$router.back()
}
5.3 edit登入判斷
需求:
- 編輯頁面需要登入才可以訪問
步驟:
- 給任意希望登入才可以訪問的路由(頁面)
- 新增
元資訊
即可meta:{needLogin:true}
5.4編輯隱藏Tabbar
需求:
- 訪問
/edit
頁面時隱藏tabbar
- 結合
路由元資訊
實現
步驟:
- 配置路由元資訊 ,
src/router/index.js
{
path: 'edit',
name: 'edit',
component: edit,
meta: {
needLogin: true,
// 給他設定單獨的路由元,為false,其他沒有true預設顯示,在V-show取反時直接v-show:false隱藏
hideTabbar: true
}
}
2.在layout頁面隱藏底部導航條,src/views/layout/index.vue
獲取元路由資訊
對預設的hideTabbar取反
<van-tabbar v-model = "active" route v-show = "!$route.meta.showTabbar">
3.不隱藏的效果:
4.隱藏的實現效果:
5.5渲染edit頁面
需求:
- 進入編輯頁面,把使用者資料渲染到頁面上
步驟:
-
路由中對token進行了判斷,並且將資料儲存到了Vuex的倉庫當中
store.commit('setUserInfo', res.data.data)
-
在edit頁面獲取倉庫中的資料
在計算屬性中定義一個使用者資訊的方法,並且返回使用者的資訊
computed: { // 在計算屬性內部定義一個方法,呼叫時不需要加括號 userInfo () { return this.$store.state.userInfo }
3.將獲得資料新增到頁面, src/views/layout/edit/index.vue
<div class = "avatar">
<van-image fit = "cover" round :src = "userInfo.photo"/>
</div>
<!-- 資訊展示 -->
<van-cell-group>
<van-cell is-link title = "名稱" :value = "userInfo.name"/>
<van-cell is-link title = "性別" :value = "userInfo.gender === 0?'男':'女'"/>
<van-cell is-link title = "生日" :value = "userInfo.birthday"/>
</van-cell-group>
在渲染性別的時候要對繫結的資料的值進行判斷
因為在行內可以通過三元表示式
<van-cell is-link title = "性別" :value = "userInfo.gender === 0?'男':'女'"/>
5.6 mapState整合
1.匯入mapstate函式
import { mapState } from 'vuex'
2.整合computed
computed: mapState(['userInfo']),
//因為只有一個資料所以不用使用物件和擴充運算子直接進行復制操作
// 之前的定義
//computed: {
// // 在計算屬性內部定義一個方法,呼叫時不需要加括號
// userInfo () {
// return this.$store.state.userInfo
// }
// },
5.7使用者資訊請求請求優化
(2到1)-- 登陸判斷
在router的index.js對使用者資訊進行判斷
一次是在user頁面,一次是在編輯頁面
1.user頁面的請求
computed: {
userInfo () {
return this.$store.state.userInfo
}
},
2.編輯頁面的請求
computed: {
// 在計算屬性內部定義一個方法,呼叫時不需要加括號
userInfo () {
return this.$store.state.userInfo
}
},
3.優化之後
// 為了防止重複的對使用者資訊發起請求,
if (store.state.userInfo.name) {
return next
}
// 不能通過 store.state.userInfo 來判斷,因為是個空物件,空物件判斷之後也是true
//return可以打斷程式碼不再往下執行,而且如果有使用者資訊證明一定有正確的token
//console.log({}===true)//true
5.8 編輯使用者名稱
1.dialog彈出模態框
實現結構
1.繫結點選事件
<van-cell is-link title = "名稱" :value = "userInfo.name" @click = "showEditName"/>
2.整合使用者名稱編輯框
<!--姓名編輯框-->
<van-dialog v-model = "showName" title = "修改姓名" show-cancel-button>
<van-field ref = "nameField" v-model = "name" placeholder = "請輸入使用者名稱"/>
</van-dialog>
2.cell繫結點選事件
1.彈框
2.使用者的資訊填入輸入框
3.輸入框獲取焦點
設定filed框設定ref
this.$refs.屬性名獲取標籤新增點選事件
實現邏輯
data () {
return {
showName: false,
name: ''
}
},
methods : {
showEditName () {
//點選模態框,修改showName的屬性為true
this.showName = true
//獲取使用者資訊的name賦值給定義的name資料
this.name = this.userInfo.name
this.$nextTick(() => {
this.$refs.nameField.focus()
})
}
}
4.修改模態框的樣式
邊框
父盒子設定內邊距
.van-dialog__content {
padding: 10px;
}
.van-field {
border: 1px solid #ccc;
}
3.注意事項
vue更新資料和更新dom是非同步的
vue資料更新--->dom更新是非同步的
可以通過$nextTick註冊一個回撥函式
dom更新之後執行
5.9 抽取edit的api
方便發起請求和維護
1.設定請求攔截器的資訊
2.封裝編輯使用者資訊的請求方法
// 匯入
import axios from 'axios'
// 匯入token工具函式
import { getToken } from '../utils/token'
// create方法設定基地址的請求request
const request = axios.create({
baseURL: 'http://toutiao-app.itheima.net'
})
// 註冊攔截器
// 新增請求攔截器
request.interceptors.request.use(
function (config) {
// 在傳送請求之前做些什麼
console.log('請求攔截器執行啦')
//設定請求攔截器的資訊token令牌!!!
config.headers.Authorization = `Bearer ${getToken().token}`
return config
},
function (error) {
// 對請求錯誤做些什麼
return Promise.reject(error)
}
)
// 抽取方法 - 編輯使用者資訊
const editUserInfo = data => {
return request({
url: '/v1_0/user/profile',
//後臺文件定義的請求方式
method: 'patch',
// data:{...data}不縮寫的引數
data:data
})
}
// 暴露出去
export { editUserInfo }
5.10 儲存使用者名稱資料
實現結構
新增修改暱稱的事件
//顯示編輯的使用者名稱
<van-cell is-link title = "名稱" :value = "userInfo.name" @click = "showEditName"/>
//儲存修改後的使用者名稱
<van-dialog v-model = "showName" title = "修改姓名" show-cancel-button @confirm = "saveNameEdit">
實現邏輯
//顯示編輯的使用者名稱
showEditName () {
this.showName = true
this.name = this.userInfo.name
this.$nextTick(() => {
this.$refs.nameField.focus()
})
}
// 定義一個儲存使用者名稱的方法
saveNameEdit () {
// 發起修改資料的請求,傳入已經存在的值
editUserInfo({ name: this.name })
// 請求成功之後返回資料,在then回撥中處理
.then(res => {
// 在倉庫中更新使用者的資訊
this.$store.commit('setUserInfo', {
// 展開所有的使用者資訊
...this.userInfo,
// 覆蓋使用者的暱稱資訊
name: this.name
})
})
},
5.11 編輯性別資訊
實現結構
<van-cell is-link title = "性別" :value = "userInfo.gender === 0?'男':'女'" @click = "showGender = true"/>
//popup編輯框
<van-popup v-model = "showGender" position = "bottom">
<van-nav-bar title = "修改性別" left-text = "取消"/>
<van-cell-group>
<van-cell title = "男" is-link/>
<van-cell title = "女" is-link/>
</van-cell-group>
</van-popup>
實現邏輯
//在data中定義
showGender: false,
點選切換cell元件,修改popub的布林值,切換彈出的狀態
跳轉不了看一下路由router
5.12儲存性別資訊
1.修改資料庫中的資料
2.關閉顯示的模態框(點選取消,男,女)
實現結構
1.<!--在渲染性別的時候要對繫結的資料的值進行判斷 因為在行內可以通過三元表示式,在點選性別的時候顯示性別框-->
<van-cell is-link title = "性別" :value = "userInfo.gender === 0?'男':'女'" @click = "showGender = true"/>
//2.
<van-nav-bar title = "修改性別" left-text = "取消" @click-left = "showGender = false"/>
//3.
<van-cell-group>
<van-cell title = "男" is-link @click = "saveGenderEdit(0)"/>
<van-cell title = "女" is-link @click = "saveGenderEdit(1)"/>
</van-cell-group>
實現邏輯
saveGenderEdit (gender) {
editUserInfo({ gender }).then(res => {
this.showGender = false
this.$store.commit('setUserInfo', {
// 展開所有的使用者資訊
...this.userInfo,
// 覆蓋使用者的性別資訊
gender
})
})
},
5.13編輯日期
data(){
birthday: '',
minDate: new Date(1970, 0, 1)//設定最小值,月份從0開始,看文件
}
showBirthdayPop () {
this.showBirthday = true
this.birthday = this.userInfo.birthday
}
<van-cell is-link title = "生日" :value = "userInfo.birthday" @click="showBirthdayPop"/>//為生日模態框繫結點選事件
<!--生日編輯框-->
<van-popup v-model = "birthday" position = "bottom">
<van-datetime-picker
v-model = "birthday"
type = "date"
title = "選擇年月日"
:min-date = "minDate"
/>
</van-popup>
5.14儲存編輯日期
實現結構
<van-datetime-picker
v-model = "birthday"
type = "date"
title = "選擇年月日"
:min-date = "minDate"
@cancel = "showBirthday = false"
@confirm = "saveBirthday"
/>
實現邏輯
saveBirthday () {
// 處理生日的格式,符合要求
const birthday = moment(this.birthday).format('YYYY-MM-DD')
editUserInfo({ birthday }).then(res => {
this.showBirthday = false
this.$store.commit('setUserInfo', {
...this.userInfo,
birthday
})
})
}
6.xxx
7.知識點的補充
1.具名插槽
2.axios-create建立例項
通過axios-create
建立例項來抽取api
this.$axios
的方式呼叫介面會有兩個問題
1.沒有辦法在審查程式碼時立即定位
2.修改引數、地址比較麻煩,不利於後期的維護
抽取案例
3.輕提示toast
4.vuex的基本使用
Vuex是什麼:
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。Vuex 和單純的全域性物件有以下兩點不同:
- Vuex 的狀態儲存是響應式的。當 Vue 元件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的元件也會相應地得到高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用
基礎使用
1.在main.js中宣告以store倉庫,通過Vue的store方法,傳入引數物件count,這樣就可以在main.js之外的頁面使用count
<div class = "info-box">
<van-image
class = "my-image"
round
:src = "userInfo.photo"
/>
<h2 class = "name">
{{ userInfo.name }}
<br>
<van-tag color = "#fff" text-color = "#3296fa" type = "primary"> {{ userInfo.birthday }}</van-tag>
</h2>
</div>
在其他頁面通過語句
this.$store.state.xxx
修改和獲取資料
5.巢狀路由
目的:為了搭建更為複雜的專案
語法:
/login
登入頁/home
首頁/home/index
/home/news
/home/vip
/home/hots
router-view
// 巢狀路由的規則
import index from '../views/home/index/index.vue'
import news from '../views/home/news'
import vip from '../views/home/vip'
import hots from '../views/home/hots'
Vue.use(VueRouter)
const routes = [
{
path: '/login',
name: 'login',
component: login
},
{
path: '/home',
name: 'home',
component: home,
//通過children物件來設定巢狀路由的地址
children: [
{
path: 'index', // /home/index
component: index
},
{
path: 'news', // /home/news
component: news
},
{
path: 'vip', // /home/vip
component: vip
},
{
path: 'hots', // /home/hots
component: hots
}
]
}
]
const router = new VueRouter({
routes
})
export default router
6.路由元資訊
Router中的mata,
是一個物件
與path、name、component同級
7.導航守衛
Router中的,
不止一個導航守衛
三個引數:
1.to去的路由資訊
2.from來的路由資訊
3.next()是否繼續執行
語法
const router = new VueRouter({
routes
})
// 新增全域性前置守衛
router.beforeEach((to, from, next) => {
// to 即將到達的路由資訊 this.$route獲取到的是一致的
console.log('to:', to)
// from 離開的路由資訊 this.$route獲取到的是一致的
console.log('from:', from)
// console.log('next:', next)
// 不執行next 卡在這個地方 路由的切換就有問題啦!
// next(),類似於node中的中介軟體,如果沒有next到這就停了
// 如果你要去的就是 404 直接放走
if (to.path === '/404') {
// 直接放走
next()
} else {
// 除他之外 去404
// 直接跳轉到指定頁面
next({ path: '/404' })
}
})
導航守衛有多個,不止是beforeEach()
8.vuex的mutations
主要作用是修改資料
this.$store.state.xxx
取值/賦值
這種方法可以修改,但是外掛無法捕捉,不方便除錯和修改
2.元件中呼叫這個方法
this.$store.commit('mutations',引數)
取值/賦值
實現原理
setFood要和mutation中一樣,state是被修改的資料,setFood方法內將傳進的資料引數對原始state資料進行賦值修改
類似於
this.$emit()
9.Vuex的mapState輔助函式
意義:方便計算屬性的定義取值
作用:
1.自動生成計算屬性:this.$store.state.userInfo
2.直接通過mapState({'userInfo'})獲取倉庫的資料
返回值:
{userIfo:function(){return this.$store.state.userInfo}}
展開了物件{food:f}
food:function(){}
輔助函式的兩種方法
1.有自己的資料要繫結,通過展開運算子
computed: {
//本地的計算屬性
localComputed () { /* ... */ },
// 使用物件展開運算子將此物件混入到外部物件中
...mapState({
// ...
})
}
2.沒有自己的資料直接使用
computed: mapState([
// 對映 this.count 為 store.state.count
'state中儲存的名字'
])
10.axios的攔截器
邏輯關係
實現程式碼
// 新增請求攔截器
axios.interceptors.request.use(function (config) {
// 在傳送請求之前做些什麼
return config;
}, function (error) {
// 對請求錯誤做些什麼
return Promise.reject(error);
});
// 新增響應攔截器
axios.interceptors.response.use(function (response) {
// 對響應資料做點什麼
return response;
}, function (error) {
// 對響應錯誤做點什麼
return Promise.reject(error);
});
11.點選user無效
在晚上點選user按鈕之後沒有任何的反應(一段時間之後會報錯),首先是伺服器的響應失效了。
沒有任何反應的原因是,在單擊我的之後。user的vue頁面一直在傳送請求但是沒有請求到資料,所以一直在等待載入被
if (store.state.userInfo.name) {
return next()
}
卡住了,請求不到使用者的名字,就不能next,所以一直在等待請求
頁面有四種方法到指定頁面但是都不符合
1.不要登陸(不符合)
2.有請求的使用者資訊名稱(不符合)
3.沒有token next跳轉到login(不符合)
4.定義的方法沒有呼叫所以不餓能next(不符合)