Vue進階 生命周期 組件運行的過程 組件的生命周期是:組件從創建->運行(渲染)->銷毀的整個過程,是一個時間段 如何監聽組件的不同時刻 vue框架為組件內置了不同時刻的生命周期函數,是他在關鍵時刻幫我們調用的一些特殊名稱的函數,生命周期函數會伴隨著組件的運行而自動調用。 created函數 組件 ...
Vue進階
生命周期
組件運行的過程
組件的生命周期是:組件從創建->運行(渲染)->銷毀的整個過程,是一個時間段
如何監聽組件的不同時刻
vue框架為組件內置了不同時刻的生命周期函數,是他在關鍵時刻幫我們調用的一些特殊名稱的函數,生命周期函數會伴隨著組件的運行而自動調用。
created
函數 組件在記憶體中被創建完畢mounted
函數 組件第一次被渲染到頁面上unmounted
函數 組件被銷毀完畢了(隱藏)updated
函數 當組件被重新渲染完畢後會自動調用
當組件被重新渲染完畢後,會自動調用updated生命周期函數
組件中全部的生命周期函數
問:為什麼不在beforeCreate中發ajax請求?
此階段無法訪問data,請求到的數據無法掛載到data中供組件渲染去使用
完整的生命周期函數
Vue2.x
Vue3.x
組件化編程
模塊:向外提供特定功能的Js程式,一般是一個Js文件。
組件:實現應用中局部功能代碼和資源的代碼集合。
模塊化:當應用中的Js都以模塊化來編寫,那這個應用就是一個模塊化應用。
組件化:當應用中的功能都是多組件的方式來編寫,那這個應用就是一個組件化的應用。
單文件組件:一個文件中只有一個組件
vue單文件組件的構成
template 組件的模板結構(必須)
script 組件的js行為
style 組件的樣式
template節點
規定:每個組件對應的模板結構,都需要定義<template>
節點中,是vue提供的容器標簽,<template>
只起到包裹性質的作用,不會被渲染成真正的DOM元素。
在<template>
節點中支持指令語法
在vue2.x的版本中,DOM結構僅支持單個根節點
在vue3.x的版本中,DOM結構僅支持多個根節點
script節點
<script>
export default{} //data數據和methods方法
</script>
name
節點:為組件定義一個名稱
data
節點:vue組件渲染期間需要用到的數據(data需指向一個函數,函數中再return對象)
methods
節點:組件中的事件處理函數節點
style節點
style標簽上的lang=‘css’屬性是可選的,它表示所使用的樣式語言,預設css,還有less和scss語法
組件的基本使用
1、組件的命名
kebab-case
命名法(短橫線命名法)、Camelcase
命名法(大駝峰命名法)(後者可轉化為短橫線使用,只能在腳手架中使用)
也可以將name
屬性作為註冊後組件的名稱,儘可能迴避HTML已有的元素名稱。
2、組件的註冊
組件之間可以相互的引用,但必須先註冊後使用
全局和局部註冊
被全局註冊的組件可以在全局如何一個組件內使用,被局部註冊只能在當前註冊範圍內使用
.component(’ 名稱‘,組件名)
方法全局註冊
component:{鍵值對}
局部註冊
使用頻率高的進行全局註冊,使用頻率低的局部註冊
3、組件之間的樣式衝突問題
父組件的樣式會影響子組件
根本原因:SPA中,所有組件的DOM結構,都基於唯一的index.html頁面進行呈現,每個組件的樣式都會影響到整個index.html頁面中的DOM元素
解決:
方法一:為每個組件分配唯一的自定義屬性,在編寫組件樣式時,通過屬性選擇器來控制樣式的作用域
<template>
<div data-v-001>
<h1 data-v-001> 這是App.vue組件</h1>
<p data-v-001>這是App中的p標簽</p>
</div>
</template>
<script>
</script>
<style lang="less">
p[data-v-001]{
color: red;
}
</style>
方法二:vue為style節點提供了scoped屬性,從而防止組件之間的樣式衝突問題
但如果給當前組件的style的節點添加了scoped屬性,則當前組件的樣式對其子組件是不生效的,如果想生效,可以使用/deep/深度選擇器,vue3.x中推薦 :deep()
/deep/.title{ } 等價於 [data-v-xxx].title 等價於 :deep(title){ }
4、組件的props
封裝vue組件時的基本原則:
- 組件的DOM結構、Style樣式儘量復用
- 組件中要展示的數據,儘量由使用者提供
為了方便使用者為組件提供要展示的數據,vue提供了props的概念
什麼是組件的props?props是組件的自定義屬性,組件的使用者可以通過props把數據傳遞到子組件內部,供子組件內部使用
作用:父組件通過props向子組件傳遞要展示的數據
聲明:在封裝vue組件時,可以把動態的數據項聲明為props自定義屬性
(無法使用未聲明的props)
<template>
<div data-v-001>
<h1 data-v-001> 書名:{{title}}</h1>
<p data-v-001>作者:{{author}}</p>
</div>
</template>
<script>
export default{
name:'MyArticle',
props:['title','author']
}
</script>
動態綁定props的值:可以使用v-bind屬性綁定的形式,為組件動態綁定props的值
props的大小寫命名:如果使用camelCase聲明props屬性的名稱,則可以使用:駝峰/短橫線的形式為組件綁定屬性的值
5、Class和Style綁定
vue允許開發者通過v-bind屬性綁定指令,為元素動態綁定class屬性的值和行內的style樣式,來動態操作元素樣式
- 動態綁定HTML的class:可以通過三元表達式,動態的為元素綁定class的類名
<template>
<div>
<h3 class="thin" :class="isItalic ? 'italic': ''">MyStyle組件</h3>
<button @click="isItalic=!isItalic">Toggle Italic</button>
</div>
</template>
<script>
export default{
name:'MyArticle',
data(){//防止一個組件被多次調用時,存在數據的引用關係
return{
isItalic:true,
}
}
}
</script>
<style lang="less">
.thin{
font-weight: 200;
}
.italic{
font-style: italic;
}
</style>
- 以數組語法綁定HTML的class
如果元素需要動態綁定多個class類名,可以使用數組的語法格式
<h3 class="thin" :class="[isItalic ? 'italic': '',isDelate ? 'delate': '']">MyStyle組件</h3>
- 以對象語法綁定HTML的class
採用數組語法會導致模板結構臃腫,故使用對象語法進行簡化
<h3 class="thin" :class="classObj">MyStyle組件</h3>
<script>
export default{
name:'MyArticle',
data(){
return{
isItalic:true,
classObj:{
isItalic:true,
}
}
}
}
</script>
- 以對象語法綁定內聯的style樣式
<template>
<div data-v-001>
<div :style="{color:active,fontsize:fsize+'px','background-color':bgcolor}"></div>
//不寫成駝峰就得用單引號包括,表示是一個字元串的屬性
<button @click="isItalic=!isItalic">Toggle Italic</button>
</div>
</template>
<script>
export default{
name:'MyArticle',
data(){
return{
isItalic:true,
active:'red',
fsize:30,
bgcolor:'pink',
}
}
}
</script>
VueComponent構造函數
- 組件本質是一個名為
VueComponent
的構造函數,且不是程式員定義的,由Vue.extend生成的。 - 調用組件時,Vue解析時會幫我們創建組件的實例對象,即幫我們執行:
new VueComponent(options)
- 註意:每次調用
Vue.extend
,返回的都是一個個全新的VueComponent
關於this指向:
組件配置中:
data函數、methods中的函數、watch中的函數、computed中的函數,this均是【VueComponent
實例對象(vc)】
new Vue()
配置中:
data函數、methods中的函數、watch中的函數、computed中的函數,this均是【Vue
實例對象(vm)】
一個重要的內置關係:VueComponent.prototype.__proto__==Vue.prototype
讓組件實例對象可以訪問到Vue原型上的屬性、方法。
單頁面應用程式(SPA)
一個Web網站中只有唯一的一個HTML頁面(所有的功能與交互)
特點:僅在該web頁面初始化時載入相應的資源,不會因為用戶操作來進行頁面的重新載入或跳轉
有點:良好的交互體驗(不會出現白屏現象)
良好的前後端工作分離模式(後端專註於提供API介面,前端專註於頁面的渲染)
減輕伺服器的壓力(伺服器只提供數據,不負責頁面合成和邏輯處理)
缺點:首屏載入慢(解決:路由懶載入、代碼壓縮、CDN加速、網路傳輸壓縮)
不利於SEO(引擎優化) (解決:SSR伺服器端渲染)
腳手架—快速創建SPA項目
1、基於vite創建SPA項目
2、基於vue-cli創建SPA項目
vite的基本使用
1、創建vite項目
npm init vite-app 項目名稱
cd 項目名稱
npm install
npm run dev
2、梳理項目的結構
3、vite項目的運行流程
在工程化項目中,vue要做的事情很單純,通過main.js把App.vue渲染到index.html的指定區域中
App.vue用來編寫待渲染的模板結構
index.html中需要預留一個el區域
main.js把App.vue渲染到index.html所預留的區域中
Vue-cli的基本使用
1、創建cli項目
npm i -g @vue/cli
vue create 項目名稱
cd 項目名稱
npm run serve
2、查看具體的webpack配置
vue inspect > output.js
組件通訊
組件之間的數據共用的6種方案
組件之間的關係
- 父子關係
- 兄弟關係
- 後代關係
父子組件之間的數據共用
1、父->子共用數據
父組件通過v-bind屬性綁定向子組件共用數據。同時,子組件需要使用props接受數據
2、子->父共用數據
子組件通過自定義事件的方式向父組件共用數據,emit傳參
3、父<->子雙向數據同步
使用v-model指令維護組件內外數據的雙向同步
兄弟組件之間的數據共用
4、兄<->弟共用數據
共用方案:EventBus,可以藉助第三方包mitt來創建eventBus對象
- 安裝mitt依賴包
npm install [email protected]
- 創建公共的EventBus模塊
import mitt from 'mitt'
//創建Eventbus的實例對象
const bus=mitt()
//將Eventbus的是實例對象共用出去
export default bus
- 在數據接收方聲明自定義事件
調用bus.on('事件名稱',事件處理函數)方法註冊一個自定義事件
<script>
import bus from 'eventBus.js'
export default{
data(){return { count 0}},
created(){
bus.on('',(count)=>{
this.count=count
})
}
}
</script>
- 在數據發送方觸發事件
調用bus.emit('事件名稱',要發送的數據)方法觸發自定義事件
<script>
import bus from 'eventBus.js'
export default{
data(){return { count 0}},
methods:{
add(){
this.count++
bus.on('',this.count)
}
}
}
</script>
後代組件之間的數據共用
5、父<->子孫共用數據
指的是父節點組件向其子孫組件共用數據。此時組件之間的嵌套關係比較複雜,可以使用provide和inject實現後代關係組件之間的數據共用
- 父組件通過provide共用數據
<script>
export default{
data(){return { count 0}},
provide(){
return {
color:this.color
}
}
}
</script>
- 子孫組件通過inject接收數據
<script>
export default{
data(){return { count 0}},
inject:['color'],
}
</script>
- 父節點對外共用響應式數據(provide非響應式)
<script>
import {computed} from 'vue'//從vue中導入computed函數
export default{
data(){return {color:'red'}},
provide(){
return {
//使用computed函數,可以把要共用的數據包裝為響應式數據
color:computed(()=>this.color)
}
}
}
</script>
- 子孫節點使用響應式數據
子孫節點必須使用.value的形式進行使用
6、Vuex全局數據共用
總結
ref引用
ref用來輔助開發者在不依賴jQuery的情況下,獲取DOM元素或組件的引用。
每個vue組件實例上,都包含一個$refs對象,裡面存儲這對應DOM元素或組件的引用,預設情況下,組件里的$refs指向一個空對象
使用ref引用DOM元素
<h1 ref="myh111"> nihao</h1>
<button @click="getRefs">
獲取“$refs”引用
</button>
<script>
export default {
name:'MyApp',
methods:{
getRefs(){
this.$refs.myh111.style.color='red'
}
}
}
</script>
使用ref引用組件
<h1 ref="myh111"> nihao</h1>
<button @click="getRefs">
獲取“$refs”引用
</button>
<my-counter ref="countRef"></my-counter>
<script>
export default {
name:'MyApp',
methods:{
getRefs(){
this.$refs.countRef.reset()//調用組件內的reset()方法
}
}
}
</script>
控制文本框的按鈕的按需切換(文本框自動獲得焦點.focus()方法)
組件是非同步執行DOM更新的
解決:this.$nextTick(cb) cb回調被延遲到DOM重新渲染完成之後執行,保證cd回調函數可以操作最新的DOM元素
<script>
export default {
name:'MyApp',
methods:{
getRefs(){
this.$nextTick(()=>{
this.$refs.ipt.focus()
})
}
}
}
</script>
mixin混入
在components
編寫mixin.js
文件,可以把多個組件共用的配置提取成一個混入對象。
export const mixin={
methods:{
showNmae(){
alert(this.name)
}
}
}
//局部混入
<script>
import {hunhe} from '../mixin'
export default {
name:'MyApp',
data(){
return {
name:''
}
},
mixin:[hunhe],
}
</script>
//全局混入
Vue.mixin(xxx)
動態組件
指的是動態切換組件的顯示與隱藏。vue提供了一個內置的
實現動態組件渲染
<component :is="組件名稱"></component>
使用keep-alive保持狀態(緩存)
隱藏組件時會將組件在記憶體中銷毀,不會保持最後的狀態,可以使用
<keep-alive>
<component :is="組件名稱"></component>
</keep-alive>
scoped樣式
為什麼會產生樣式類名衝突?
Vue解析組件時,會將所有的style合併,當類名相同時會以後引入的樣式為主。
使用scoped
解決css樣式類名衝突問題,但App組件不適用。
插槽
什麼是插槽?
插槽(slot)是vue為組件封裝者提供的能力。允許開發者在封裝組件時,把不確定的,希望用戶指定的部分定義為插槽,(為用戶預留的內容的占位符)。
在封裝組件時,可以通過<slot></slot>
元素定義插槽,從而為用戶預留內容占位符
沒有預留插槽的自定義內容會被丟棄(Vue不知道將該DOM渲染到何處)
<--header組件!-->
<template>
<div>
<slot></slot>
</div>
</template>
<--引用!-->
<header>
<ul>
<li></li>
<li></li>
</ul>
<--將ul插入header組件!-->
</header>
插槽的預設內容(後備內容)
可以為預留的<slot>
插槽提供後備內容(預設內容),如果使用者沒有提供內容,則後備內容將會生效
具名插槽
上述定義的是預設插槽,如果在封裝組件時需要預留多個插槽節點,則需要為每個<slot>
插槽指定具體的name名稱,這種帶有具體名稱的插槽叫做:具名插槽
<slot name=" "></slot>
<slot ></slot> //預設名:default
<--header組件!-->
<template>
<div>
<slot name="center"></slot>
<slot name="footer"></slot>
</div>
</template>
<--引用!-->
<header>
<ul slot="center">
<li></li>
<li></li>
</ul>
<a slot="footer"></a>
</header>
<--另一種寫法!-->
<header>
<ul slot="center">
<li></li>
<li></li>
</ul>
<template v-slot:header>
<h1>
滕王閣序
</h1>
</template>
</header>
<header>
<ul slot="center">
<li></li>
<li></li>
</ul>
<template #header>
<h1>
滕王閣序
</h1>
</template>
</header>
作用域插槽
在封裝組件的過程中,可以為預留的<slot>
插槽綁定props數據,使得子組件可以向引用傳參(數據逆流),這種帶有props數據的<slot>
叫做:作用域插槽。
<--header組件!-->
<template>
<div>
<slot :games="games"></slot>
</div>
</template>
......
data(){
return{
games:['first','second','third']
}
}
<header>
<template scope="games">
<ul>
<li></li>
<li></li>
</ul>
<h1>
滕王閣序
</h1>
</template>
</header>
解構作用域插槽的prop
scope
是一個對象,那麼能否直接使用對象的解構賦值呢?
<header>
<template scope="{games}">
<ul>
<li></li>
<li></li>
</ul>
<h1>
滕王閣序
</h1>
</template>
</header>
<header>
<template slot-scope="{games}">
<ul>
<li></li>
<li></li>
</ul>
<h1>
滕王閣序
</h1>
</template>
</header>