No5.生命周期、axios、購物車案例
知識點自測
知道window.onload的作用。 ajax是什么, XMLHttpRequest的使用步驟。 jQ的 $.ajax底層是什么? Promise的作用。 async和await的作用-如何和Promise配合。 同步異步的概念, 代碼執行順序。 請求和響應, 以及JSON解析能力。 Vue基礎, 組件使用, props傳值, 組件通信, 計算屬性使用, 對象引用類型使用。 axios掛載到Vue原型上 。
學習目標
1.能夠說出 vue 組件的生命周期。 2.能夠掌握 axios 的使用。 4.能夠了解 $refs, $nextTick 使用和 name 使用。 4.能夠完成購物車案例開發。
1. vue生命周期
一組件從 創建 到 銷毀 的整個過程就是生命周期。
1-1.鉤子函數
含義: 鉤子函數是 Vue 框架的內置函數,隨著組件的生命周期階段,自動執行。
作用: 特定的時間點,執行特定的操作。 場景: 組件創建完畢后,可以在created 生命周期函數中發起Ajax 請求,從而初始化 data 數據。 分類 : 4大階段8個方法
階段 方法名 方法名 初始化 beforeCreate created 掛載 beforeMount mounted 更新 beforeUpdate updated 銷毀 beforeDestroy destroyed
官網文檔-生命周期圖示
1-2.初始化階段
含義講解 :
1.new Vue() 表示 Vue實例化(組件也是一個小的Vue實例)
2.Init Events & Lifecycle – 初始化事件和生命周期函數
3.beforeCreate – 生命周期鉤子函數被執行,此時還無法獲取到data/methods
4.Init injections&reactivity – Vue內部添加data和methods等
5.created – 生命周期鉤子函數被執行, 實例創建完畢(可以得到data和methods 等等)
6.接下來是編譯模板階段 –開始分析
7.Has el option? – 是否有 el 選項 – 檢查要掛到哪里 ? 沒有. 調用 $mount() 方法 ? 有, 繼續檢查 template 選項
components/Life.vue - 創建一個文件
< script>
export default { data ( ) { return { msg : "hello, Vue" } } , beforeCreate ( ) { console. log ( "beforeCreate -- 執行" ) ; console. log ( this . msg) ; } , created ( ) { console. log ( "created -- 執行" ) ; console. log ( this . msg) ; this . timer = setInterval ( ( ) => { console. log ( "哈哈哈" ) ; } , 1000 ) }
}
</ script>
< template
> < div
> < h1
> 1. 生命周期
< / h1
> < Life
> < / Life
> < / div
>
< / template
> < script
>
import Life
from './components/Life'
export default { components : { Life
}
}
< / script
>
1-3.掛載階段
含義講解 : 1.template選項檢查 ? 有 - 編譯 template 返回 render 渲染函數 ? 無 – 編譯 el 選項對應標簽作為template(要渲染的模板)
2.虛擬DOM掛載成真實DOM之前
3.beforeMount – 生命周期鉤子函數被執行,此時還無法獲取“真實”DOM元素
4.Create vm.$el and replace “el” with it – 把虛擬DOM和渲染的數據一并掛到真實的DOM上
5.真實DOM掛載完畢
6.mounted – 生命周期鉤子函數被執行,組件掛載完畢,可以獲取到真實的“DOM”
components/Life.vue - 創建一個文件
< template> < div> < p> 學習生命周期 - 看控制臺打印
</ p> < p id = " myP" > {{ msg }}
</ p> </ div>
</ template> < script>
export default { beforeMount ( ) { console. log ( "beforeMount -- 執行" ) ; console. log ( document. getElementById ( "myP" ) ) ; this . msg = "重新值" } , mounted ( ) { console. log ( "mounted -- 執行" ) ; console. log ( document. getElementById ( "myP" ) ) ; }
}
</ script>
1-4.更新階段
含義講解: 1.當data里數據改變, 更新DOM之前
2.beforeUpdate – 生命周期鉤子函數被執行。掛載以后data更新,將要更新DOM之前 。
3.Virtual DOM…… – 虛擬DOM重新渲染, 打補丁到真實DOM
4.updated – 生命周期鉤子函數被執行,獲取更新的“真實”DOM
5.當有data數據改變 – 重復這個循環
components/Life.vue - 創建一個文件 準備ul+li循環, 按鈕添加元素, 觸發data改變->導致更新周期開始:
< template> < div> < p> 學習生命周期 - 看控制臺打印
</ p> < p id = " myP" > {{ msg }}
</ p> < ul id = " myUL" > < li v-for = " (val, index) in arr" :key = " index" > {{ val }}
</ li> </ ul> < button @click = " arr.push(1000)" > 點擊末尾加值
</ button> </ div>
</ template> < script>
export default { data ( ) { return { msg : "hello, Vue" , arr : [ 5 , 8 , 2 , 1 ] } } , beforeUpdate ( ) { console. log ( "beforeUpdate -- 執行" ) ; console. log ( document. querySelectorAll ( "#myUL>li" ) [ 4 ] ) ; } , updated ( ) { console. log ( "updated -- 執行" ) ; console. log ( document. querySelectorAll ( "#myUL>li" ) [ 4 ] ) ; }
}
</ script>
1-5.銷毀階段
含義講解: 銷毀:組件從真實的DOM上被移除。
1.當 $destroy() 被調用 – 比如組件DOM被移除(例v-if)
2.beforeDestroy – 生命周期鉤子函數被執行,手動清除:定時器/計時器/eventBus事件($off)
3.拆卸數據監視器、子組件和事件偵聽器
4.實例銷毀后, 最后觸發一個鉤子函數
5.destroyed – 生命周期鉤子函數被執行,已經銷毀。
components/Life.vue - 準備生命周期方法(Life組件即將要被刪除)
< script>
export default { beforeDestroy ( ) { clearInterval ( this . timer) } , destroyed ( ) { }
}
</ script>
App.vue - 點擊按鈕讓Life組件從DOM上移除 -> 導致Life組件進入銷毀階段
< Life v
- if = "show" > < / Life
>
< button @click
= "show = false" > 銷毀組件
< / button
> < script
> data ( ) { return { show : true } } ,
< / script
>
2. axios
2-1.axios基本使用
定義 :axios是一個專門用于發送ajax請求的庫。
axios官方文檔
什么是ajax? 一種前端異步請求后端的技術。 ajax原理 : 瀏覽器window接口的XMLHttpRequest。 axios是什么 ? 基于原生ajax+Promise技術封裝的用于前后端的請求庫。
特點:
支持客戶端發送Ajax請求。 支持服務端Node.js發送請求。 支持Promise相關用法。 支持請求和響應的攔截器功能。 自動轉換JSON數據。 axios 底層還是原生js實現, 內部 通過Promise封裝 的。
axios ( { method : '請求方式' , url : '請求地址' , data : { xxx : xxx
, } , params : { xxx : xxx
}
} ) . then ( res => { console
. log ( res
. data
)
} ) . catch ( err => { console
. log ( err
)
} )
get請求獲取數據
功能: 點擊調用后臺接口, 拿到所有數據 – 打印到控制臺。 接口: 參考:8.接口文檔 引入: 下載axios, 引入后才能使用。yarn add axios 或者 npm i axios 效果: 例子如下:
< template
> < div
> < p
> 1. 獲取所有圖書信息
< / p
> < button @click
= "getAllFn" > 點擊
- 查看控制臺
< / button
> < / div
>
< / template
> < script
>
import axios
from "axios" ;
export default { methods : { getAllFn ( ) { axios ( { url : "http://123.57.109.30:3006/api/getbooks" , method : "GET" , } ) . then ( ( res ) => { console
. log ( res
) ; } ) ; } , }
}
< / script
>
get傳參
功能: 點擊調用后臺接口, 查詢用戶想要的書籍信息 – 打印到控制臺上。 接口: 參考:8.接口文檔 效果:
例子如下:
components/UseAxios.vue,拿到用戶輸入框表單中的值,最簡單的方式就是使用雙向綁定:v-model
< template
> < div
> < p
> 2. 查詢某本書籍信息
< / p
> < input type
= "text" placeholder
= "請輸入要查詢的書名" v
- model
= "bName" / > < button @click
= "findFn" > 查詢
< / button
> < / div
>
< / template
> < script
>
import axios
from "axios" ;
export default { data ( ) { return { bName : "" } } , methods : { findFn ( ) { axios ( { url : "http://123.57.109.30:3006/api/getbooks" , method : "GET" , params : { bookname : this . bName
} } ) . then ( res => { console
. log ( res
) } ) } } ,
}
< / script
>
post傳參(發布書籍)
功能: 點擊新增按鈕, 把用戶輸入的書籍信息, 傳遞給后臺 – 把結果打印在控制臺上。 接口: 參考:8.接口文檔 效果:
ES6中的擴展運算符(…)
例子如下:
components/UseAxios.vue ES6中的擴展運算符(…)
< template
> < div
> < p
> 3. 新增圖書信息
< / p
> < div
> < input type
= "text" placeholder
= "書名" v
- model
= "bookObj.bookname" > < / div
> < div
> < input type
= "text" placeholder
= "作者" v
- model
= "bookObj.author" > < / div
> < div
> < input type
= "text" placeholder
= "出版社" v
- model
= "bookObj.publisher" > < / div
> < button @click
= "sendFn" > 發布
< / button
> < / div
>
< / template
> < script
>
import axios
from "axios"
export default { data ( ) { return { bName : "" , bookObj : { bookname : "" , author : "" , publisher : "" } } } , methods : { sendFn ( ) { axios ( { url : "http://123.57.109.30:3006/api/addbook" , method : "POST" , data : { appkey : "7250d3eb-18e1-41bc-8bb2-11483665535a" , ... this . bookObj
} } ) } } ,
}
< / script
>
全局配置
目標: 避免前綴基地址, 暴露在邏輯頁面里, 統一設置。
axios
. defaults
. baseURL
= "http://123.57.109.30:3006"
getAllFn ( ) { axios ( { url : "/api/getbooks" , method : "GET" , } ) . then ( ( res ) => { console
. log ( res
) ; } ) ;
} ,
3. $nextTick和$refs
問題 :Vue中如何獲取真實的DOM元素呢?
3-1.$refs-獲取原生DOM
在 mounted 生命周期,有2種方式獲取原生DOM標簽。
< template
> < div
> < p
> 1 . ref
/ id獲取原生
DOM 元素
< / p
> < h1 id
= "h" ref
= "myH" > 通過id或者ref屬性獲取原生
DOM < / h1
> < / div
>
< / template
> < script
>
export default { mounted ( ) { console
. log ( document
. getElementById ( "h" ) ) ; console
. log ( this . $refs
. myH
) ; }
}
< / script
>
總結: 通過id / ref, 都可以獲取原生DOM標簽。
3-2.$refs-獲取組件對象
目標: 通過ref屬性獲取組件對象, 調用組件里的屬性或者方法。
1.創建Demo.vue組件,寫一個方法。 2.App.vue 使用Demo組件,給ref屬性(名字隨意 ) 3.通過ref屬性獲取組件對象,可調用組件對象里面的方法。
components/Child/Demo.vue
< template
> < div
> < p
> 我是Demo組件
< / p
> < / div
>
< / template
> < script
>
export default { methods : { fn ( ) { console
. log ( "demo組件內的方法被調用了" ) ; } }
}
< / script
>
components/More.vue - 獲取組件對象 - 調用組件方法
< template
> < div
> < p
> 1 . ref
/ id獲取原生
DOM 元素
< / p
> < h1 id
= "h" ref
= "myH" > 通過id或者ref屬性獲取原生
DOM < / h1
> < p
> 2. 獲取組件對象
- 可調用組件內的一切
< / p
> < Demo ref
= "demo" > < / Demo
> < / div
>
< / template
> < script
>
import Demo
from './Child/Demo'
export default { mounted ( ) { console
. log ( document
. getElementById ( "h" ) ) ; console
. log ( this . $refs
. myH
) ; let demoObj
= this . $refs
. demo
; demoObj
. fn ( ) } , components : { Demo
}
}
< / script
>
總結: ref定義值, 通過 $refs.值 來獲取組件對象, 就能繼續調用組件內的變量。
3-3.$nextTick使用
等DOM更新后,觸發此方法里的函數體執行。this.$nextTick(函數體)
Vue更新DOM是異步的
點擊count++, 馬上通過"原生DOM"拿標簽內容, 無法拿到新值。
components/Move.vue - 繼續新增第三套代碼
< template
> < div
> < p
> 1 . ref
/ id獲取原生
DOM 元素
< / p
> < h1 id
= "h" ref
= "myH" > 通過id或者ref屬性獲取原生
DOM < / h1
> < p
> 2. 獲取組件對象
- 可調用組件內的一切
< / p
> < Demo ref
= "demo" > < / Demo
> < p
> 3 . vue更新
DOM 是異步的
< / p
> < p ref
= "myP" > { { count
} } < / p
> < button @click
= "btn" > 點擊count
+ 1 , 馬上提取p標簽內容
< / button
> < / div
>
< / template
> < script
>
import Demo
from './Child/Demo'
export default { mounted ( ) { console
. log ( document
. getElementById ( "h" ) ) ; console
. log ( this . $refs
. myH
) ; let demoObj
= this . $refs
. demo
; demoObj
. fn ( ) } , components : { Demo
} , data ( ) { return { count : 0 } } , methods : { btn ( ) { this . count
++ ; console
. log ( this . $refs
. myP
. innerHTML
) ; this . $nextTick ( ( ) => { console
. log ( this . $refs
. myP
. innerHTML
) ; } ) } }
}
< / script
>
總結: 我們可以在哪里訪問到更新后的DOM呢?
this.$nextTick里的函數體。 updated生命周期鉤子函數。
3-4.$nextTick使用場景-輸入框聚焦
目標: 點擊搜索按鈕, 彈出聚焦的輸入框, 按鈕消失;出現輸入框并馬上處于激活狀態。
< template
> < div
> < input ref
= "myInp" type
= "text" placeholder
= "這是一個輸入框" v
- if = "isShow" > < button v
- else @click
= "btn" > 點擊我進行搜索
< / button
> < / div
>
< / template
> < script
>
export default { data ( ) { return { isShow : false } } , methods : { async btn ( ) { this . isShow
= true ; await this . $nextTick ( ) this . $refs
. myInp
. focus ( ) } }
}
< / script
>
優化 :$nextTick() 這個函數在原地返回的是一個Promise對象,所以可以用$nextTick() 返回Promise配合await使用。await只能用在被async修飾的函數內 。
3-5.組件name屬性使用
可以用組件的name屬性值, 來注冊組件名字。
問題: 組件名不是可以隨便寫的? 答案: 我們封裝的組件-可以自己定義name屬性組件名-讓使用者有個統一的前綴風格。
< template
> < div
> < p
> 我是一個Com組件
< / p
> < / div
>
< / template
> < script
>
export default { name : "ComNameHaHa"
}
< / script
>
< template
> < div
> < h1
> 1. 生命周期
< / h1
> < Life v
- if = "show" > < / Life
> < button @click
= "show = false" > 銷毀組件
< / button
> < hr
> < h1
> 2 . axios使用
< / h1
> < UseAxios
> < / UseAxios
> < hr
> < h1
> 3 . $refs的使用
< / h1
> < More
> < / More
> < hr
> < h1
> 4 . $nextTick使用場景
< / h1
> < Tick
> < / Tick
> < hr
> < h1
> 5. 組件對象里name屬性
< / h1
> < ComNameHaHa
> < / ComNameHaHa
> < / div
>
< / template
> < script
>
import Life
from './components/Life'
import UseAxios
from './components/UseAxios'
import More
from './components/More'
import Tick
from './components/Tick'
import Com
from './components/Com'
export default { data ( ) { return { show : true } } , components : { Life
, UseAxios
, More
, Tick
, [ Com
. name
] : Com
}
}
< / script
>
4. 案例 - 購物車
4.0 項目初始化
目標: 初始化新項目, 清空不要的東西, 下載bootstrap庫, 下載less模塊
vue create shopcar-demo
yarn add bootstrap 或者npm i bootstrap
yarn add less less-loader@5.0.0 -D
按照需求, 把項目頁面拆分成幾個組件, 在components下創建:
MyHeader組件、MyFooter組件、MyGoods組件 - 商品、MyCount組件 然后新建 vue.config.js ,暫時關閉 eslint 檢查:
module
. exports
= { lintOnSave : false
}
import "bootstrap/dist/css/bootstrap.css"
< template
> < div
class = "my-header" > 購物車案例
< / div
>
< / template
> < script
>
export default {
}
< / script
> < style lang
= "less" scoped
> . my
- header
{ height : 45px
; line
- height
: 45px
; text
- align
: center
; background
- color
: #1d7bff
; color : #fff
; position : fixed
; top : 0 ; left : 0 ; width : 100 % ; z
- index
: 2 ; }
< / style
>
< template
> < div
class = "my-goods-item" > < div
class = "left" > < div
class = "custom-control custom-checkbox" > < input type
= "checkbox" class = "custom-control-input" id
= "input" > < label
class = "custom-control-label" for = "input" > < img src
= "http://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" alt
= "" > < / label
> < / div
> < / div
> < div
class = "right" > < div
class = "top" > 商品名字
< / div
> < div
class = "bottom" > < span
class = "price" > ¥
100 < / span
> < span
> 數量組件
< / span
> < / div
> < / div
> < / div
>
< / template
> < script
>
export default {
}
< / script
> < style lang
= "less" scoped
>
. my
- goods
- item
{ display : flex
; padding : 10px
; border
- bottom
: 1px solid #ccc
; . left
{ img
{ width : 120px
; height : 120px
; margin
- right
: 8px
; border
- radius
: 10px
; } . custom
- control
- label
: : before
, . custom
- control
- label
: : after
{ top : 50px
; } } . right
{ flex : 1 ; display : flex
; flex
- direction
: column
; justify
- content
: space
- between
; . top
{ font
- size
: 14px
; font
- weight
: 700 ; } . bottom
{ display : flex
; justify
- content
: space
- between
; padding : 5px
0 ; align
- items
: center
; . price
{ color : red
; font
- weight
: bold
; } } }
}
< / style
>
目標: 完成商品組件右下角商品組件的開發
< template
> < div
class = "my-counter" > < button type
= "button" class = "btn btn-light" > - < / button
> < input type
= "number" class = "form-control inp" > < button type
= "button" class = "btn btn-light" > + < / button
> < / div
>
< / template
> < script
>
export default {
}
< / script
> < style lang
= "less" scoped
>
. my
- counter
{ display : flex
; . inp
{ width : 45px
; text
- align
: center
; margin : 0 10px
; } . btn
, . inp
{ transform : scale ( 0.9 ) ; }
}
< / style
>
在MyGoods.vue組件上引入MyCount.vue組件:
< template
> < div
class = "my-goods-item" > < div
class = "left" > < / div
> < div
class = "right" > < div
class = "top" > 商品名字
< / div
> < div
class = "bottom" > < span
class = "price" > ¥
100 < / span
> < span
> < MyCount
> < / MyCount
> < / span
> < / div
> < / div
> < / div
>
< / template
>
< script
>
import Mycount
from './MyCount'
export default { components : { MyCount
}
}
< / script
>
< template
> < ! -- 底部
-- > < div
class = "my-footer" > < ! -- 全選
-- > < div
class = "custom-control custom-checkbox" > < input type
= "checkbox" class = "custom-control-input" id
= "footerCheck" > < label
class = "custom-control-label" for = "footerCheck" > 全選
< / label
> < / div
> < ! -- 合計
-- > < div
> < span
> 合計
: < / span
> < span
class = "price" > ¥
0 < / span
> < / div
> < ! -- 按鈕
-- > < button type
= "button" class = "footer-btn btn btn-primary" > 結算 ( 0 ) < / button
> < / div
>
< / template
> < script
>
export default { }
< / script
> < style lang
= "less" scoped
>
. my
- footer
{ position : fixed
; z
- index
: 2 ; bottom : 0 ; width : 100 % ; height : 50px
; border
- top
: 1px solid #ccc
; display : flex
; justify
- content
: space
- between
; align
- items
: center
; padding : 0 10px
; background : #fff
; . price
{ color : red
; font
- weight
: bold
; font
- size
: 15px
; } . footer
- btn
{ min
- width
: 80px
; height : 30px
; line
- height
: 30px
; border
- radius
: 25px
; padding : 0 ; }
}
< / style
>
< template
> < div
> < MyHeader
> < / MyHeader
> < div
class = "main" > < MyGoods
> < / MyGoods
> < / div
> < MyFooter
> < / MyFooter
> < / div
>
< / template
> < script
>
import MyHeader
from "./components/MyHeader"
import MyGoods
from "./components/MyGoods"
import MyFooter
from "./components/MyFooter" export default { components : { MyHeader
, MyGoods
, MyFooter
, }
}
< / script
>
< style scoped
> . main
{ padding
- top
: 45px
; padding
- bottom
: 50px
; }
< / style
>
啟動項目 :yarn serve 或者 npm run serve
4.1 頭部自定義組件
目的: 頭部的標題, 顏色, 背景色可以隨便修改, props類型的校驗
思路: 1.在MyHeader.vue中準備props里變量, 然后使用。 2.在使用MyHeader.vue組件時, 傳入相應的值 (color和backgroundColor) key值color和變量名重名了,所以可以簡寫成color 。 動態style ::style="{css屬性名:值}"
< template
> < div
class = "my-header" : style
= "{backgroundColor: background, color}" > { { title
} } < / div
>
< / template
> < script
>
export default { props : { background : String
, color : { type : String
, default : "#fff" } , title : { type : String
, required : true } }
}
< / script
> < style lang
= "less" scoped
> . my
- header
{ height : 45px
; line
- height
: 45px
; text
- align
: center
; background
- color
: #1d7bff
; color : #fff
; position : fixed
; top : 0 ; left : 0 ; width : 100 % ; z
- index
: 2 ; }
< / style
>
< MyHeader backgrounnd
= "pink" title
= "購物車案例" > < / MyHeader
>
總結:
<MyHeader title=“購物車案例”></MyHeader>中title前面沒有加冒號表示是固定的值,如果加了冒號就代表是變量了。
【問題1】封裝的組件-如何支持不同的項目需求?
可以自定義props變量 使用,使用組件者,傳入具體數據 。
【問題2】props有哪2種定義方式:
props: [] - 只能聲明變量和接收, 不能類型校驗。 props: {} - 聲明變量和校驗類型規則 - 外部傳入值不對則報錯。
4.2 請求數據(axios掛載到Vue原型上)
配置基礎地址: https://www.escook.cn/api/cart (get方式)
1.下載axios:yarn add axios 或者 npm i axios 【問題】如何定義全局變量 ? 在Vue的原型上添加,組件對象通過原型鏈可以訪問。 2.main.js 中: axios掛載到Vue原型上,作為全局屬性。 3.但是,把 axios 掛載到 Vue 原型上,有一個缺點:不利于 API 接口的復用!!!
import axios
from 'axios'
axios
. defaults
. baseURL
= "https://www.escook.cn"
Vue . prototype
. $axios
= axios
new Vue ( { render : h => h ( App
) ,
} ) . $mount ( '#app' )
App.vue請求使用
< script
>
export default { created ( ) { this . $axios ( { url : "/api/cart" } ) . then ( res => { console
. log ( res
) ; } ) }
}
< / script
>
總結: 利用axios, 調用接口, 把數據請求回來。
4.3 數據渲染
需求: 把上面請求的數據, 使用MyGoods組件展示。 1.App.vue把數據保存到data()定義的變量上。 2.使用v-for循環數組使用組件。 3.分別給商品組件傳入數據對象。
數據格式:
< MyGoods v
- for = "obj in list" : key
= "obj.id" : gObj
= "obj"
> < / MyGoods
> < script
>
export default { data ( ) { return { list : [ ] } } , created ( ) { this . $axios ( { url : "/api/cart" } ) . then ( res => { console
. log ( res
) ; this . list
= res
. data
. list
} ) }
< / script
>
< template
> < div
class = "my-goods-item" > < div
class = "left" > < div
class = "custom-control custom-checkbox" > < ! -- * 重要
: 每個對象和組件都是獨立的對象里的goods_state關聯自己對應商品的復選框
-- > < ! -- bug
: 循環的所有label的
for 都是input
, id也都是input
- 默認只有第一個生效
解決 : 每次對象里的id值 ( 1 , 2 ) , 分別給id和
for 使用即可區分
-- > < input type
= "checkbox" class = "custom-control-input" : id
= "gObj.id" v
- model
= "gObj.goods_state" > < label
class = "custom-control-label" : for = "gObj.id" > < img
: src
= "gObj.goods_img" alt
= "" > < / label
> < / div
> < / div
> < div
class = "right" > < div
class = "top" > { { gObj
. goods_name
} } < / div
> < div
class = "bottom" > < span
class = "price" > ¥
{ { gObj
. goods_price
} } < / span
> < span
> < MyCount
: obj
= "gObj" > < / MyCount
> < / span
> < / div
> < / div
> < / div
>
< / template
> < script
>
import MyCount
from './MyCount'
export default { props : { gObj : Object
} , components : { MyCount
}
}
< / script
>
< template
> < div
class = "my-counter" > < button type
= "button" class = "btn btn-light" > - < / button
> < input type
= "number" class = "form-control inp" v
- model
. number
= "obj.goods_count" > < button type
= "button" class = "btn btn-light" > + < / button
> < / div
>
< / template
> < script
>
export default { props : { obj : Object
}
}
< / script
>
總結: 把各個組件關聯起來, 把數據都鋪設到頁面上。
4.4 商品選中
Bug :循環的所有label的for(要和關聯的input標簽的id屬性值一樣)值都是input,input標簽的id值也是input,這樣就導致默認只有第一個生效(點擊圖片不能使復選框選中)。 解決 :每次對象里的id值,分別給id和for使用即可區分。因為id是唯一的, 如果用id=“input”,那么無論點擊哪一張圖片,都是第一個選框選中效果生效。所以用數據的id來作為標簽的id, 分別獨立, 為了兼容label點擊圖片也能選中的效果。
< input type
= "checkbox" class = "custom-control-input" : id
= "gObj.id" v
- model
= "gObj.goods_state" >
< label
class = "custom-control-label" : for = "gObj.id" > < img
: src
= "gObj.goods_img" alt
= "" >
< / label
>
總結: lable標簽的for值對應input標簽的id值, 點擊label標簽就能讓對應input處于激活。 label標簽有什么用 ?關聯表單,點擊label標簽上,相當于點擊表單。
4.5 數量控制
需求: 點擊 + 和 - 或者直接修改輸入框的值影響商品購買的數量。
MyCount.vue中的 obj.goods_count > 1 && 可以省略。
< template
> < div
class = "my-counter" > < button type
= "button" class = "btn btn-light" : disabled
= "obj.goods_count === 1" @click
= "obj.goods_count > 1 && obj.goods_count--" > - < / button
> < input type
= "number" class = "form-control inp" v
- model
. number
= "obj.goods_count" > < button type
= "button" class = "btn btn-light" @click
= "obj.goods_count++" > + < / button
> < / div
>
< / template
> < script
>
export default { props : { obj : Object
} , watch : { obj : { deep : true , handler ( ) { if ( this . obj
. goods_count
< 1 ) { this . obj
. goods_count
= 1 } } } }
}
< / script
>
4.6 全選功能
全選和小選框,互相影響的思路是什么? 全選 =》關聯計算屬性的set方法 =》 同步所有小選。 小選 =》 觸發計算屬性的get方法 =》 統計后返回全選狀態。
需求1:點擊全選影響所有單選。 需求2:點擊單選影響全選。 分析 : 1.v-model用計算屬性,關聯全選框。 2.在set方法里,獲取全選狀態。 3.回傳到App.vue再同步給所有單選框。子MyFooter傳父App.vue。 4.在get方法里,統計單選框給全選賦值。父App.vue傳子MyFooter.vue 思路: 1.點擊獲取它的選中狀態。 2.同步給上面每個單選框 - 而單選框的選中狀態又在數組里。 3.把數組傳給MyFooter, 然后更新即可 - 因為對象都是引用關系的。
every()檢測數組所有元素是否都符合指定條件
MyFooter.vue
涉及到的知識點:every() 檢測數組所有元素是否都符合指定條件
< template
> < ! -- 底部
-- > < div
class = "my-footer" > < ! -- 全選
-- > < div
class = "custom-control custom-checkbox" > < input type
= "checkbox" class = "custom-control-input" id
= "footerCheck" v
- model
= "isAll" > < label
class = "custom-control-label" for = "footerCheck" > 全選
< / label
> < / div
> < ! -- 合計
-- > < div
> < span
> 合計
: < / span
> < span
class = "price" > ¥
{ { allPrice
} } < / span
> < / div
> < ! -- 按鈕
-- > < button type
= "button" class = "footer-btn btn btn-primary" > 結算 ( { { allCount
} } ) < / button
> < / div
>
< / template
> < script
>
export default { props : { arr : Array
} , computed : { isAll : { set ( val
) { this . $emit ( 'changeAll' , val
) } , get ( ) { return this . arr
. every ( obj => obj
. goods_state
=== true ) } } }
}
< / script
>
JS中 map 和 forEach 的區別
涉及到的知識點:JS中的 forEach() 方法 JS中 map 和 forEach 的區別 App.vue
< MyFooter @changeAll
= "allFn" : arr
= "list" > < / MyFooter
> < script
>
methods : { allFn ( bool ) { this . list
. forEach ( obj => obj
. goods_state
= bool
) } }
< / script
>
總結: 全選的v-model的值, 使用計算屬性完整寫法
4.7 總數量
需求: 完成底部組件, 顯示選中的商品的總數量。 1.計算屬性allCount變量 2.在統計數組里的數據時,要判斷勾選狀態才累加數量。 3.把累加好的數量返回給allCount變量使用顯示。
JS數組reduce()方法詳解及高級技巧
MyFooter.vue
知識點:JS數組reduce()方法詳解及高級技巧
ES6中的數組reduce()方法詳解
arr.reduce(callback(accumulator, currentValue, [index], [array])[, initialValue])
回調函數第一次執行時,accumulator 和currentValue的取值有兩種情況:如果調用reduce()時提供了initialValue,accumulator取值為initialValue,currentValue取數組中的第一個值;如果沒有提供 initialValue,那么accumulator取數組中的第一個值,currentValue取數組中的第二個值。
如果沒有提供initialValue,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供initialValue,從索引0開始 。
< ! -- 按鈕
-- >
< button type
= "button" class = "footer-btn btn btn-primary" > 結算 ( { { allCount
} } ) < / button
> computed : { allCount ( ) { return this . arr
. reduce ( ( sum, obj ) => { if ( obj
. goods_state
=== true ) { sum
+= obj
. goods_count
; } return sum
; } , 0 ) } ,
}
總結: 對象之間是引用關系, 對象值改變, 所有用到的地方都跟著改變。
4.8 總價
需求:統計已選中商品的總價格。
< span
> 合計
: < / span
>
< span
class = "price" > ¥
{ { allPrice
} } < / span
> allPrice ( ) { return this . arr
. reduce ( ( sum, obj ) => { if ( obj
. goods_state
) { sum
+= obj
. goods_count
* obj
. goods_price
} return sum
; } , 0 )
}
總結: 把數組傳給了MyFooter組件, 統計總價。
5.總結
vue的生命周期哪4個階段, 哪8個方法? axios是什么, 底層是什么, 具體如何使用? axios返回的是什么, 如何接收結果? 知道 ref 和 $refs 使用和作用以及場景? 知道 $nextTick 的作用。 完成購物車案例。
6.面試題
6-1.Vue 的 nextTick 的原理是什么? (高薪常問)
1》為什么需要 nextTick ,Vue 是異步修改 DOM 的并且不鼓勵開發者直接接觸 DOM,但有時候業務需要必須對數據更改–刷新后的 DOM 做相應的處理,這時候就可以使用 Vue.nextTick(callback)這個 api 了。
?2》理解原理前的準備 首先需要知道事件循環中宏任務和微任務這兩個概念,常見的宏任務有 script, setTimeout, setInterval, setImmediate, I/O, UI rendering 常見的微任務有 process.nextTick(Nodejs),Promise.then(), MutationObserver;
3》理解 nextTick 的原理正是 vue 通過異步隊列控制 DOM 更新和 nextTick 回調函數先后執行的方式。如果大家看過這部分的源碼,會發現其中做了很多 isNative()的判斷,因為這里還存在兼容性優雅降級的問題。可見 Vue 開發團隊的深思熟慮,對性能的良苦用心。
6-2.vue生命周期總共分為幾個階段?(必會)
Vue 實例從創建到銷毀的過程,就是生命周期。也就是從開始創建、初始化數據、編譯模板、掛載Dom→渲染、更新→渲染、卸載等一系列過程,我們稱這是 Vue 的生命周期。
1)beforeCreate ? 在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用。
2)created ? 在實例創建完成后被立即調用。在這一步,實例已完成以下的配置:數據觀測 (data observer), 屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見。
3)beforeMount ? 在掛載開始之前被調用:相關的 render 函數首次被調用。
4)mounted ? el 被新創建的 vm.$el 替換,并掛載到實例上去之后調用該鉤子。如果 root 實例掛載了一個文檔內元素,當 mounted 被調用時 vm.$el 也在文檔內。
5)beforeUpdate ? 數據更新時調用,發生在虛擬 DOM 打補丁之前。這里適合在更新之前訪問現有的 DOM,比如手動移除已添加的事件監聽器。該鉤子在服務器端渲染期間不被調用,因為只有初次渲染會在服務端進行。
6)updated ? 由于數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之后會調用該鉤子。
7)activated ? keep-alive 組件激活時調用。該鉤子在服務器端渲染期間不被調用。
8)deactivated ? keep-alive 組件停用時調用。該鉤子在服務器端渲染期間不被調用。
9)beforeDestroy ? 實例銷毀之前調用。在這一步,實例仍然完全可用。該鉤子在服務器端渲染期間不被調用。
10)destroyed ? Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。該鉤子在服務器端渲染期間不被調用。
11)errorCaptured(2.5.0+ 新增) ? 當捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。此鉤子可以返回 false 以阻止該錯誤繼續向上傳播。
6-3.第一次加載頁面會觸發哪幾個鉤子函數?(必會)
當頁面第一次頁面加載時會觸發 beforeCreate, created, beforeMount, mounted 這幾個鉤子函數
7.接口文檔
axios請求接口使用,根域名: http://123.57.109.30:3006
案例 - 圖書相關
獲取
請求方式: GET 請求地址: 根域名/api/getbooks 請求參數:
不傳參獲取所有默認書籍, 也可以選擇傳遞下面任意1-多個參數, 獲取指定的相關書籍信息
參數名稱參數類型是否必選參數說明 id Number 否 圖書Id bookname String 否 圖書名稱 author String 否 作者 publisher String 否 出版社 appkey String 否 個人ID
返回示例:
{ "status" : 200 , "msg" : "獲取圖書列表成功" , "data" : [ { "id" : 1 , "bookname" : "西游記" , "author" : "吳承恩" , "publisher" : "北京圖書出版社" } , { "id" : 2 , "bookname" : "紅樓夢" , "author" : "曹雪芹" , "publisher" : "上海圖書出版社" } , { "id" : 3 , "bookname" : "三國演義" , "author" : "羅貫中" , "publisher" : "北京圖書出版社" } ]
}
添加
請求方式: POST 請求地址: 根域名/api/addbook 請求參數:
參數名稱參數類型是否必選參數說明 bookname String 是 圖書名稱 author String 是 作者 publisher String 是 出版社 appkey String 是 個人ID - 用’7250d3eb-18e1-41bc-8bb2-11483665535a’
返回示例:
{ "status" : 201 , "data" : { "author" : "施大神" "bookname" : "水滸傳2" "id" : 41 "publisher" : "未來出版社" } "msg" : "添加圖書成功"
}
總結
以上是生活随笔 為你收集整理的【Vue知识点- No5.】生命周期、axios、购物车案例 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。