Vue講解
Vue 是一套用于構建用戶界面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,Vue 也完全能夠為復雜的單頁應用提供驅動。有些知識點Vue官網講解的不是很清晰,所以我就寫了一些小的demo;但是官網上有些知識點這里是沒有講解到的,大神請坐高鐵走!小白每天抽空看一遍,不會也會了!
案例地址: https://github.com/pengjunshan/WebPJS/Vue
其它Web文章
CSS浮動的使用和解決浮動的五種方法
CSS定位relative、absolute、fixed使用總結
原生開發WebApi知識點總結
開發中常用jQuery知識點總結
C3動畫+H5+Flex布局使用總結
ES6常用知識總結
開發環境到生產環境配置webpack
待續......
本編文章會講到的知識點
- Vue基礎使用
- vue的使用
- Mustache表達式使用
- 指令v-使用
- 動態添加數據到data、異步更新DOM
- filter過濾器
- watch監聽配置項
- computed計算屬性配置項
- 事件修飾符
- 鍵值修飾符
- vue聲明周期鉤子函數
- 自定義指令
- 小案例
- vue組件
- 全局組件
- 局部組件
- 父組件傳遞子組件數據
- 子組件傳遞父組件數據
- 非父子組件傳遞數據
- 組件中插槽使用
- vue中ref使用
- vue-router路由
- 路由基本使用
- 重定向、高亮
- 路由傳參方式
- 路由嵌套-子路由
- vuex
- State
- Mutation
- Action
- Getter
Vue基礎使用
1.vue的使用
下載Vue:npm i vue
- 首先下載vue,引入vue.js
- js中創建一個Vue對象實例
- 通過el指定vue管理頁面的邊界
<!-- 引入vue.js文件 -->
<script src="lib/vue.js"></script>
<div id="app">
<input v-model="message" type="text"><br>
<h1>{{message}}</h1>
</div>
<!-- 下載Vue:npm i vue -->
<script>
var vm = new Vue({
/**
* el是element的簡寫,用來指定vue管理頁面的邊界,
* 也就是說只有包裹在 #app內部的元素,才會收到Vue的管理!!!
*/
el: '#app',
/**
* 頁面中用的到數據都放到data對象中
*/
data: {
message: '鄧紫棋喜歡你'
}
// 寫Vue可能會遇到的錯誤:
// 1 注意:Vue 是以大寫字母開頭的,它是一個構造函數!!!
// 2 注意:在 Vue 中,HTML屬性值無法使用 {{}}!!!
// 3 開發期間一定要使用未壓縮版的Vue(開發版)
})
/**
* vm是Vue的對象實例,可以通過vm.$data.屬性名獲取data中的屬性值,可以省略$data
*/
console.log(vm.message)
console.log(vm.$data.message)
console.log(vm.message === vm.$data.message)
Mustache表達式使用
雙花括號{{}} 就是 mustache語法,用于展示data中的內容,mustache 中可以出現任意的 JS表達式;
- 表達式{{}}只能從數據對象data中獲取數據;
- mustache中不能出現語句,比如:if () {} / for(var i =0 ...) {} / var num = 1;
- Mustache 語法不能作用在 HTML 元素的屬性上;
<div id="app">
<h1>{{ msg }}</h1>
<p>{{ 1 + 2 }}</p>
<p>{{ ['a', 'c', 'b'] }}</p>
<p>{{ ['a', 'c', 'b'].join('-') }}</p>
<p>{{ msg + ' -- 拼接內容' }}</p>
<p>{{ age > 18 ? '成年了' : '未成年' }}</p>
<p>{{ Math.random() }}</p>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:'鄧紫棋金魚嘴',
age:19
}
})
</script>
指令v-使用
指令 (Directives) 是帶有 v- 前綴的特殊屬性,當表達式的值改變時,將其產生的連帶影響,響應式地作用于 DOM。
1.v-text
用來設置當前元素的文本內容,相當于DOM對象的 innerText或textContent
<div v-text='msg'></div>
2.v-html
更新DOM對象的 innerHTML
<div v-html='htmlMsg'></div>
3.v-bind
通過v-bind為HTML元素綁定屬性,使用data中提供的數據;
因為 v-bind:title 這種使用方式很繁瑣,所以,vue提供了一個簡化語法 :title
<img v-bind:title='msg' v-bind:src='imgPath' v-bind:name='name'>
<img :title='msg' :src='imgPath' :name='name'>
4.v-on
綁定事件,支持js所有的事件類型, v-on綁定的事件方法都要寫在Vue實例中的methods對象中;
v-on:省略寫 @
<button v-on:click='getData'>點我</button><input v-on:onfocus='getFocus'>
<button @click='getData'>點我</button><input @onfocus='getFocus'>
5.v-model
在表單元素上創建雙向數據綁定;
只能用在表單元素中,注意:不同的表達元素,v-model的表現可能會有所不同。
比如:v-model操作文本框的value屬性,而復選框 v-model 就是操作其選中狀態;
<!-- 綁定的是文本框輸入的內容 -->
<input type="text" v-model='msg'>
<!-- 綁定的是復選框是否選中 -->
<input type="checkbox" v-model='isCheck'>
6.v-for
基于源數據多次渲染元素或模板塊,不僅可以渲染集合List也可以遍歷對象Obj;
<!-- v-for 遍歷list集合-->
<ul>
<!--1.item是每一項對象
使用 v-for 的時候提供 key 屬性,以獲得性能提升。
-->
<li v-for='item in list' :key='item.key'>
姓名:{{item.name}} -- 年齡:{{item.age}}
</li>
<!--2.item 為當前項,index 為索引 -->
<li v-for='(item,index) in list'>
姓名:{{item.name}} -- 年齡:{{item.age}} -- 下標:{{index}}
</li>
</ul>
<!-- v-for Obj對象 value,key,index順序不能變 -->
<ul>
<li v-for='(value,key,index) in csObj'>
key={{key}} -- value={{value}} -- index={{index}}
</li>
</ul>
7.v-bind:class和v-bind:style
表達式的類型:字符串、數組、對象(重點)
<!-- 可以是對象,key是類名 value是布爾值,如果是true就添加這個類,否則就不添加-->
<h2 :class='{pink:true,green:true}'>中國驚奇先生</h2>
<!-- 可以是數組,-->
<h2 :class='['pink','fz','green']'>斗羅大陸</h2>
<h2 :style="{ color: activeColor, 'font-size': fontSize + 'px' }">不良人</h2>
8.v-if
根據表達式布爾值的真假條件是否加載這段代碼, true:DOM中會加載這段代碼,false:DOM中不會加載這段代碼;
<h3 v-if='isIF'>我是v-if,是否會加載我</h3>
9.v-show
根據表達式之真假值,切換元素的 display CSS 屬性,無論true還是false DOM中都會加載這段代碼;
<h3 v-show='isShow'>我是v-show,是否會顯示出來
</h3>
10.v-pre
跳過這個元素和它的子元素的編譯過程。可以用來顯示原始 Mustache 標簽。跳過大量沒有指令的節點會加快編譯。
<!--測試:頁面中的msg不會顯示data中的內容,因為跳過了表達式編譯-->
<div v-pre>v-pre跳過編譯過程 {{msg}}</div>
11.v-once
只渲染元素和組件一次。隨后的重新渲染,元素/組件及其所有的子節點將被視為靜態內容并跳過。
<!--測試:在控制臺通過vm對象修改msg,頁面顯示中的msg內容不會有變化-->
<div v-once>v-once跳過編譯過程 {{msg}}</div>
12.v-cloak
頁面中使用 {{}} 的時候,經歷了由 {{}} -> 具體內同,這么一個過程,所以頁面會造成“閃爍”
解決:通過添加 v-cloak 指令,配合 [v-cloak] { display: none; } 避免了頁面閃爍
<div v-cloak>{{msg}}</div>
動態添加數據到data、異步更新DOM
1.動態添加數據到data
只有data中的數據才是響應式的,動態添加進來的數據默認為非響應式
可以通過以下方式實現動態添加數據的響應式:
- 1 Vue.set(object, key, value) - 適用于添加單個屬性
- 2 Object.assign() - 適用于添加多個屬性
2.異步更新DOM
當綁定的數據發生變動時,Vue 異步執行 DOM 更新,監視所有數據改變,一次性更新DOM;
解決方法:
- Vue.nextTick
- this.$nextTick
<div id="app">
<!-- 點擊按鈕之前data中還沒有age屬性 -->
<button @click='addAge'>給data添加age</button>
<div>名字:{{stu.name}}</div>
<!--
這里使用的表達式中的屬性必須是響應式的
只有data中的數據才是響應式的,動態添加進來的數據默認為非響應式
-->
<div>年齡:{{stu.age}}</div>
<div>性別:{{stu.sex}}</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'hello vue',
stu: {
name: 'jack',
}
},
methods: {
addAge: function () {
//可以通過以下方式實現動態添加數據的響應式
//1.添加單個屬性
// 第一個參數:表示要給哪個對象添加響應式屬性 $data可以省略
// 第二個參數:表示要添加的屬性名稱
// 第三個參數:表示屬性的值
Vue.set(this.stu, "age", 18)
//2.添加多個屬性
//第一個參數:是一個空對象
//第二個參數:添加到哪個對象
//第三個參數:添加屬性的對象
this.stu = Object.assign({},this.stu,{"name":"鄧紫棋","age":18,"sex":"man"})
//此時打印的內容為 名字:jeck
//
//為什么呢? Vue 異步執行 DOM 更新,監視所有數據改變,一次性更新DOM
console.log(this.$el.children[1].innerText)
//解決方法 Vue.nextTick 和 this.$nextTick 是相同的
//在DOM更新后,回調執行某個操作(DOM操作
this.$nextTick(function(){
console.log(this.$el.children[1].innerText)
})
}
}
})
</script>
filter過濾器
- 作用:文本數據格式化 , 也就是: 將數據按照我們指定的一種格式輸出
- 過濾器可以用在兩個地方:{{}}表達式 和 v-bind 指令中
- 兩種過濾器:1 全局過濾器 2 局部過濾器
1.全局過濾器
- 說明:通過全局方式創建的過濾器,在任何一個vue實例中都可以使用
- 注意:使用全局過濾器的時候,需要先創建全局過濾器,再創建Vue實例
<div>{{ dateStr | date }}</div>
<div>{{ dateStr | date('YYYY-MM-DD hh:mm:ss') }}</div>
<script>
Vue.filter('date', function(value, format) {
// value 要過濾的字符串內容,比如:dateStr
// format 過濾器的參數,比如:'YYYY-MM-DD hh:mm:ss'
})
var vm = new Vue({
})
</script>
2.局部過濾器
- 說明:局部過濾器是在某一個vue實例的內容創建的,只在當前實例中起作用
<div>{{msg | fi("九")}}</div>
var vm = new Vue({
el:'#app',
data:{
msg:'八百個標兵奔北坡,八百個標兵奔北坡'
},
//2. 局部過濾器 只有在當前Vue實例中才起作用
// 通過 filters 配置項, 來創建過濾器
filters:{
// content是內容,format是過濾的規則可以多個參數
fi:function(content,format){
return content.replace(/八/g,format);
}
}
})
watch監聽配置項
- 概述:watch是一個對象,鍵是需要觀察的表達式,值是對應回調函數
- 作用:當表達式的值發生變化后,會調用對應的回調函數完成響應的監視操作
<div id="app">
<input type="text" v-model='userName'>
<p v-show='isError'>請輸入4-8位字符</p>
<input type="text" v-model='stu.age'>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
userName: '',
isError: false,
stu: {
age: 10,
}
},
// 通過 watch 配置項,來監視數據變化
// 只能監視 data 中的數據,要監視的數據,作為watch的屬性
watch: {
// 監視userName值的變化,方法名要用要監視的值的名字
userName:function(curVal, oldVal){
console.log('當前值為:', curVal, '上一次值為:', oldVal);
if(curVal.length>=4 && curVal.length<=8){
this.isError = false;
}else{
this.isError = true;
}
},
// 監聽對象,加上deep:true
// 注意:如果監視對象的變化,那么,curVal 和 oldVal 是相同的,指向同一個對象
stu:{
handler:function(curVal, oldVal){
console.log('當前值為:', curVal, '上一次值為:', oldVal);
},
deep: true
},
// 一般都是監聽對象中的屬性
// 只需要監視某個屬性的變化,而不是整個對象中所有的屬性的變化
'stu.age':function(curVal, oldVal){
console.log('當前值為:', curVal, '上一次值為:', oldVal);
}
}
})
</script>
computed計算屬性配置項
- 說明:計算屬性是基于它們的依賴進行緩存的,只有在它的依賴發生改變時才會重新求值
- 注意:Mustache語法({{}})中不要放入太多的邏輯,否則會讓模板過重、難以理解和維護
- 注意:computed中的屬性不能與data中的屬性同名,否則會報錯
<div id="app">
<input type="text" v-model='num1'>+
<input type="text" v-model='num2'>=
<input type="text" v-model='result'>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
num1: 0,
num2: 0,
// result: 0 計算屬性名不能和data中的屬性相同
},
// 計算屬性,通過 computed 配置項來指定
// 注意:計算屬性不能與data中的屬性相同!!!否則會報錯
// 特點:計算屬性依賴的屬性(比如:num1 和 num2)發生改變,那么計算屬性就會被重新計算
// 優勢:內部使用緩存機制,如果頁面中多個地方都用到了計算屬性,那么計算屬性只會被重新計算一次!!!
computed: {
result:function(){
return (this.num1-0)+(this.num2-0);
}
}
})
</script>
事件修飾符
- .stop 阻止向上冒泡 不會調用父的事件
- .prevent 阻止默認行為,調用 event.preventDefault()
- .capture捕獲冒泡
- .self 只當事件在該元素本身觸發時,才會觸發事件
- .once 事件只觸發一次
<!-- .stop 阻止向上冒泡 不會調用父的事件 -->
<div @click='cathFather'>我是父事件修飾符
<!-- .stop 阻止冒泡,調用 event.stopPropagation() -->
<div @click.stop='catchSon'>我是子事件修飾符</div>
</div>
<!-- .prevent 阻止默認行為,調用 event.preventDefault() -->
<a @click.prevent='onPrevent'>我是prevent事件</a>
<!-- .capture捕獲冒泡,
即有冒泡發生時,有該修飾符的dom元素會先執行,如果有多個,從外到內依次執行,然后再按自然順序執行觸發的事件。
-->
<!-- 如果不給爺爺添加capture 點擊兒子觸發的順序是 兒子、爸爸、爺爺 -->
<!-- 給爺爺添加了capture事件后 點擊兒子觸發的順序是 爺爺、兒子、爸爸 -->
<div @click.capture='grandpa'>我是爺爺
<div @click='father'>我是爸爸
<div @click='son'>
我是兒子
</div>
</div>
</div>
<!-- .self 只當事件在該元素本身觸發時,才會觸發事件 -->
<div @click.self='onSelfFather'>self事件爸爸
<div @click='onSelfSon'>self事件兒子</div>
</div>
<!-- .once 事件只觸發一次 -->
<div @click.once='onOnce'>再點我一次試試</div>
鍵值修飾符
- 說明:在監聽鍵盤事件時,Vue 允許為 v-on 在監聽鍵盤事件時添加關鍵修飾符
<div id="app">
<!-- 鍵值修飾符 包括鍵盤、鼠標 -->
<!-- 13是Enter鍵的code值 -->
<input type="text" v-model='msg' @keyup.13='submit'>
<input type="text" v-model='msg2' @keyup.enter='submit2'><br>
<!-- 使用自定義鍵值修飾符 -->
<input type="text" v-model='msg' @keyup.f2='submit'>
</div>
<script>
// 自定義鍵值修飾符 有時候寫code值是數字的時候并沒有語義,所有我們給它定義一下
Vue.config.keyCodes.f2 = 113;
var vm = new Vue({
el:'#app',
data:{
msg:'',
msg2:''
},
methods:{
submit:function(){
console.log('提交數據='+this.msg)
},
submit2:function(){
console.log('提交數據='+this.msg2)
},
}
})
</script>
vue聲明周期鉤子函數
beforeCreate()
- 說明:在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用
- 注意:此時,無法獲取 data中的數據、methods中的方法
- 使用場景:可以在這個鉤子函數中開啟頁面加載的 loading 效果
created()
- 注意:這是一個常用的生命周期,可以調用methods中的方法、改變data中的數據
- 使用場景:發送請求獲取數據
beforeMounted()
- 說明:組件將要掛載到頁面中,也就是說:組件的內容還沒有被掛載到頁面中
- 注意:此時,獲取不到頁面中DOM元素
mounted()
- 說明:組件已經被掛載到頁面中,此時,可以進行DOM操作了
beforeUpdate()
- 說明:數據更新時調用,發生在虛擬 DOM 重新渲染和打補丁之前。你可以在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。
updated()
- 說明:組件 DOM 已經更新完成,所以你現在可以執行依賴于 DOM 的操作。
beforeDestroy()
- 說明:實例銷毀之前調用。在這一步,實例仍然完全可用。
- 使用:實例銷毀之前,執行清理任務,比如:清除定時器等
destroyed()
- 說明:Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。
自定義指令
Vue這種MVVM模式的框架不推薦開發人員直接手動操作DOM有些情況, 還是需要操作DOM的, 如果需要操作DOM, 就通過 Vue中的自定義指令來操作!!!
通過Vue.directive()方法自定義指令:
- 第一個參數: 表示自定義指令的名稱;
- 第二個參數 1.表示自定義指令運行的時候, 需要執行的邏輯操作;
Vue.directive('ff1', function (el) {
console.log(el)
})
- 第二個參數 2.還可以是一個對象,對象中是指令的鉤子函數;
Vue.directive('ff2', {
// bind 和 inserted 這兩個鉤子函數, 都是進入頁面就立即執行的
// 區別:inserted 能獲取到指令所在元素的父元素,bind 獲取不到父元素
bind(el) {
console.log('bind', el.parentNode)
},
inserted(el) {
console.log('inserted', el.parentNode)
})}
指令函數的入參:
<!-- 標識放到自定義指令的后面 .表示名 -->
<!-- 注意:如果 v-color="red" 那么,red指的值:data中的red屬性 -->
<div v-color.back=" 'blue' ">{{ msg }}</div>
<div v-color.col=" 'red' ">{{ msg }}</div>
Vue.directive('color', function (el, binding) {
if (binding.modifiers.col) {
el.style.color = binding.value
} else {
el.style.backgroundColor = binding.value
}
})
小案例
案例請到https://github.com/pengjunshan/WebPJS中查看
案例
Vue組件
組件是可復用的 Vue 實例,組件分為全局組件和局部組件。因為組件是可復用的 Vue 實例,所以它們與 new Vue 接收相同的選項,例如 data、computed、watch、methods 以及生命周期鉤子等,el 是根實例特有的屬性,組件中沒有。
全局組件
全局組件在所有的vue實例中都可以使用,注意:先注冊組件,再初始化根實例。
Vue.component('name',{配置項})
- 第一個參數是組件名
- 第二個參數是組件的配置項,與Vue根實例配置項差不多
- 組件中的data必須是個函數數據用return返回,Vue跟實例中的data是個對象
- 組件中template模板有兩種方式,“字符串” “html模板”
<body>
<div id="app">
<hello></hello>
</div>
<template id="temp">
<div>
<p>我是組件:{{msg}}</p>
<button @click='fn'> 點擊消滅新冠 </button>
</div>
</template>
<script>
/**
* 第一個參數是組件名
* 第二個參數是配置項,與Vue實例的配置幾乎一樣
*/
Vue.component('hello', {
//template是組件的模板 也就是要展示的內容
//組件中template模板有兩種方式,“字符串” “html模板”
//方式一:字符串
// template: `
// <div>
// <p>我是組件:{{msg}}</p>
// <button @click='fn'> 點擊消滅新冠 </button>
// </div>
// `,
//方式二:html模板
template: '#temp',
//組件中的data必須是個函數數據用return返回,Vue跟實例中的data是個對象
data() {
return {
msg: '武漢加油 中國加油'
}
},
methods: {
fn() {
this.msg = '新冠被消滅了,中國威武'
}
}
})
var vm = new Vue({
el: '#app',
data: {}
})
</script>
</body>
局部組件
局部組件,是在某一個具體的vue實例中定義的,只能在這個vue實例中使用;在Vue實例中使用components對象創建組件,可以創建多個組件;
<body>
<div id="app">
<!-- hello組件 -->
<hello></hello>
<!-- love組件 -->
<love></love>
</div>
<template id="temp">
<div>
<p>我是組件222:{{msg}}</p>
<button @click='fn'> 點擊消滅新冠 </button>
</div>
</template>
<script>
//1.局部組件,是在某一個具體的vue實例中定義的,只能在這個vue實例中使用
//2.在Vue實例中使用components對象創建組件,可以創建多個組件
//3.組件中template模板有兩種方式,“字符串” “html模板”
var vm = new Vue({
el: '#app',
data: {
},
components: {
//hello 組件名
'hello': {
//方式一 字符串
template: `
<div>
<p>我是組件111:{{msg}}</p>
<button @click='fn'> 點擊消滅新冠 </button>
</div>
`,
data() {
return {
msg: '武漢加油 中國加油'
}
},
methods: {
fn() {
this.msg = '新冠被消滅了,中國威武'
}
}
},
'love': {
//方式二 引用html中的代碼模板
template: '#temp',
data() {
return {
msg: '我們愛中國 愛武漢'
}
},
methods: {
fn() {
this.msg = '我們愛中華民族'
}
}
}
}
})
</script>
</body>
父組件傳遞子組件數據
- 方式:通過props屬性來傳遞數據
- 注意:屬性的值必須在組件中通過props屬性顯示指定,否則,不會生效
- 說明:傳遞過來的props屬性的用法與data屬性的用法相同
- 如果傳遞的數據是data中的屬性時,必須使用v-bind綁定屬性才可以傳遞過去
<body>
<div id="app">
<hello mm='中華民族萬歲'></hello>
<!-- 如果傳遞的數據是data中的屬性時,必須使用v-bind綁定屬性才可以傳遞過去 -->
<!-- <hello zz='msg'></hello> -->
<hello v-bind:zz='msg'></hello>
</div>
<script>
// 父組件 傳遞數據給 子組件:(父組件:Vue的實例對象,子組件:hello組件)
// 原理:通過 props 屬性來傳遞
// 注意:使用父組件傳遞的屬性方式和使用data中的屬性方式一樣
Vue.component('hello', {
template: `
<div>
<p>我是組件:{{msg}}</p>
<p v-if='mm'>我接收到父組件的內容:{{mm}}</P>
<p v-if='zz'>我接收到父組件的內容:{{zz}}</P>
<button @click='fn'> 點擊消滅新冠 </button>
</div>
`,
//指定props中的值,來接收父組件傳遞過來的值
props:['mm','zz'],
data() {
return {
msg: '武漢加油 中國加油'
}
},
methods: {
fn() {
this.msg=this.msg+'-----'+(this.mm===undefined? this.zz:this.mm);
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg:'浙江杭州'
}
})
</script>
</body>
子組件傳遞父組件數據
- 方式:父組件給子組件傳遞一個函數,由子組件調用這個函數
- 說明:借助vue中的自定義事件(v-on:cunstomFn="fn")
- $emit():觸發事件
<body>
<div id="app">
<p>{{msg}}</p>
<hello @pfn='parentFn'></hello>
</div>
<template id="temp">
<div>
<p>我是組件:{{msg}}</p>
<button @click='sonFn'> 點擊消滅新冠 </button>
</div>
</template>
<script>
//組件傳遞 子》父
//1.由父組件定義一個方法,通過@pfn傳給子組件
//2.子組件通過$emit方法把數據傳遞給父組件定義的方法
Vue.component('hello', {
template: '#temp',
data() {
return {
msg: '武漢加油 中國加油'
}
},
methods: {
sonFn() {
//通過$emit方法傳遞數據給父組件的方法,參數可以為多個
this.$emit('pfn', '新冠被消滅了,中國威武', '測試')
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg: '標題'
},
methods: {
parentFn(data, data2) {
this.msg = data;
console.log(data2)
}
},
})
</script>
</body>
非父子組件傳遞數據
- 可以使用一個空的 Vue 實例作為事件總線bus
- A組件傳遞B組件數據 1.B先通過bus.$on綁定事件,2.A通過bus.$emit方法調用B綁定的事件方法
- $on和$emit都是bus調用的
<body>
<div id="app">
<aaa></aaa>
<bbb></bbb>
</div>
<script>
//可以使用一個空的 Vue 實例作為事件總線
var bus = new Vue()
//A組件傳遞B組件數據 1.B先通過bus.$on綁定事件,2.A通過bus.$emit方法調用B綁定的事件方法
//$on和$emit都是bus調用的
var vm = new Vue({
el: '#app',
data: {},
components: {
aaa: {
template: `
<div>
<h3>我是組件AA</h3>
<button @click='fn'> 點我傳給B數據 </button>
</div>
`,
data() {
return {
msg: 'A組件'
}
},
methods: {
fn() {
bus.$emit('bfn', '組件A說:你好組件B')
}
}
},
bbb: {
template: `
<div>
<h3>我是組件BB</h3>
<p>{{msg}}</p>
</div>
`,
data() {
return {
msg: '我在等待數據...'
}
},
created() {
//綁定事件 接收數據,當進入頁面時走到這個鉤子函數后就自動綁定事件了
bus.$on('bfn', data => {
this.msg = data
})
}
}
}
})
</script>
</body>
組件中插槽使用
有時在使用組件的時候希望往組件中加入其它內容,在組件中通過插槽<slot></slot>來接收內容。插槽內可以包含任何模板代碼,包括 HTML,甚至其它的組件;
<body>
<div id="app">
<hello>
<p>趕走新冠</p>
</hello>
</div>
<template id="temp">
<div>
<p>{{msg}}</p>
<slot></slot>
<slot></slot>
</div>
</template>
<script>
//當組件渲染的時候,<slot></slot> 將會被替換為"趕走新冠"。插槽內可以包含任何模板代碼
//可以有多個插槽
Vue.component('hello',{
template:'#temp',
data(){
return{
msg:'武漢加油 中國加油'
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg:'趕走新冠'
}
})
</script>
</body>
vue中ref使用
使用ref注冊后 可以使用this.$refs獲取當前DOM對象,必須在mounted()鉤子函數之后才可以獲取DOM對象;元素和組件都可以使用ref注冊;
<body>
<div id="app">
<!-- 使用ref注冊后 可以使用this.$refs.pp獲取當前DOM對象 -->
<p ref='pp'>哈嘍,大家好</p>
<!-- 組件也可以 -->
<hello ref="ho"></hello>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
hello: {
template: `<h1>大家好</h1>`,
data() {
return {
msg: 'hello message'
}
},
methods: {
fn() {
console.log("觸發了事件11111")
}
}
}
},
mounted() {
console.log(this.$refs.pp)
this.$refs.pp.style.color = 'red';
console.log(this.$refs.ho.msg)
this.$refs.ho.fn()
}
})
</script>
</body>
vue-router路由
路由基本使用
- 1.在當前文件夾下執行npm init、npm init -y初始化package.json
- 2.然后執行npm i -s vue、npm i -s vue-router安裝vue和vue-router
- 3.在node_modules下找到vue和vue-router中的js并引入到項目中
- 4.創建組件
- 5.創建路由對象,并配置路由
- 6.在Vue實例中關聯router
- 7.路由入口
- 8.路由出口
<body>
<div id="app">
<!-- 6.路由入口 -->
<router-link to='/home'>首頁</router-link>
<router-link to='/me'>我的</router-link>
<!-- 7.路由出口 -->
<router-view></router-view>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
//首先先安裝vue和vue-router
//1.在當前文件夾下執行npm init、npm init -y初始化package.json
//2.然后執行npm i -s vue、npm i -s vue-router安裝vue和vue-router
//3.在node_modules下找到vue和vue-router中的js并引入到項目中
//4.先創建兩個組件
const Home = Vue.component('home', {
template: `<h1>我是Home組件</h1>`
})
const Me = Vue.component('me', {
template: `<h1>我是Me組件</h1>`
})
//5.創建路由對象
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/me', component: Me }
]
})
var vm = new Vue({
el: '#app',
data: {},
//將vue和router
router: router
})
</script>
</body>
重定向、高亮
當第一次打開頁面時,想要默認打開一個路由,就可以使用路由中的重定向;按鈕的高亮樣式不是我們喜歡的,可以使用linkActiveClass來自定義高亮元素的類名,然后再設置css樣式就可以了;
//5.創建路由對象
const router = new VueRouter({
routes: [
//如果當前路徑是'/'就redirect重定向 默認home組件
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/me', component: Me }
],
// 修改默認高亮的a標簽的類名
// 如果是配合第三方組件庫來實現菜單高亮,此時,只需要將類名設置為 第三方組件的類名即可
linkActiveClass: 'now'
})
<style>
/* .router-link-exact-active, */
/* .router-link-active { */
.now {
color: hotpink;
font-size: 30px;
text-decoration: none;
}
</style>
路由傳參方式
有時候多個路由都打開同一個組件,可以通過給組件傳不同的參數展示不同的內容就可以了;通過路由打開不同組件傳參也是一樣的;
導航分為兩種
- 1.聲明式導航(router-link)
- 2.編程式導航($router.push)
聲明式導航(router-link)
- to字符串:只能傳遞字符串
- :to對象:可以傳遞對象,可以通過name、path方式
編程式導航($router.push)
- this.
route.params獲取參數;
- this.
route.query獲取參數;
監聽路由變化
在組件中通過watch對象中監聽路由的變化$route(to, from) {},在這里可進行監聽數據變化 進行網絡請求等等;
詳細使用方式請看下面代碼
<body>
<div id="app">
<!-- 通過to字符串 -->
<router-link to="/home/1001">1001號賽車</router-link>
<router-link to="/home/1002">1002號賽車</router-link>
<!-- 通過$router.push{} -->
<a @click='fn'>1003號賽車</a>
<a @click='fn1'>1004號賽車</a>
<!-- 通過:to對象 -->
<router-link :to="{ name:'Home',params:{id:1005,title:'pjs'}}">1005</router-link>
<router-link :to="{ path:'/home',query:{id:1006,title:'pjs'}}">1006</router-link>
<router-view></router-view>
</div>
<script>
//導航分為兩種:1.聲明式導航(router-link) 2.編程式導航($router.push)
//1.通過to字符串
//2.$router.push{}
//3.通過:to對象
//通過name跳轉的狀態欄里看不到參數類似post,通過path跳轉的狀態欄里可以看到參數類似get
//通過$route.params或$route.query來獲取參數
const Home = Vue.component('home', {
template: `
<div>
<h1 v-if='$route.params.id'>歡迎來到主頁面{{ $route.params.id}}</h1>
<h1 v-if='$route.query.id'>歡迎來到主頁面{{ $route.query.id}}</h1>
</div>
`,
//監聽路由變化,獲取參數進行操作
watch: {
//只要路由發生的變化就會執行這個方法,to 跳轉的目的地, from 從哪里來
$route(to, from) {
//在這里可進行監聽數據變化 進行網絡請求
console.log(to)
console.log(from)
console.log(to.params.id)
console.log(to.query.id)
}
}
})
const router = new VueRouter({
routes: [
{ path: '/home/:id', component: Home },
{ path: '/home', name: 'Home', component: Home }
]
})
var vm = new Vue({
el: '#app',
router,
methods: {
fn() {
this.$router.push({
name: 'Home',//找到routes里匹配到name為Home
params: {
id: 1003,
title: 'pjs'
}
})
},
fn1() {
this.$router.push({
path: '/home',
query: {
id: 1004,
title: 'pjs'
}
})
}
}
})
</script>
</body>
路由嵌套-子路由
- 路由是可以嵌套的,即:路由中又包含子路由
- 規則:父組件中包含 router-view,在路由規則中使用 children 配置
- 使用children里配置子路由,子路由的path里不需要加/符號了
<body>
<div id="app">
<router-link to='/home'>首頁</router-link>
<router-link to='/user'>我的</router-link>
<router-view></router-view>
</div>
<script>
//路由是可以嵌套的,即:路由中又包含子路由
//規則:父組件中包含 router-view,在路由規則中使用 children 配置
const Home = Vue.component('home', {
template: `
<div>
<router-link to='/home/cartA'>買車</router-link>
<router-link to='/home/cartB'>賣車</router-link>
<router-view></router-view>
</div>
`
})
const CartA = {
template: `<h3>買什么樣的車?</h3>`
}
const CartB = {
template: `<h3>賣什么樣的車?</h3>`
}
const User = Vue.component('user', {
template: `
<div>
這里是個人信息
</div>
`
})
const router = new VueRouter({
routes: [
{
path: '/home', component: Home,
//使用children里配置子路由,子路由的path里不需要加/符號了
children: [
{
path: 'cartA',
component: CartA
},
{
path:'cartB',
component:CartB
}
]
},
{ path: '/user', component: User }
]
})
var vm = new Vue({
el: '#app',
router
})
</script>
</body>
vuex
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。意思就是所有組件共同操作一份數據,都可以對它進行增刪改查。
屬性|作用
:---:|:---:
State|共享數據都存在這里
Mutation|更改State中數據的唯一方法,同步操作
Action|異步操作Mutation來更改State中的數據
Getter|基于state的派生狀態,可理解為組件中的計算屬性
State
1.安裝vuex npm i -s vuex
2.創建store對象
3.把vue和store進行關聯
4.使用$store.state中的數據
<body>
<div id="app">
<!-- 4.使用$store.state中的數據 -->
<h1>{{this.$store.state.name}}</h1>
</div>
<script>
//1.安裝vuex npm i -s vuex
//2.創建store對象
let store = new Vuex.Store({
/**
* state中可以存儲任何類型的值
*/
state: {
name: '鄧紫棋'
}
})
let vm = new Vue({
el: '#app',
data: {},
//3.把vue和store進行關聯
store,
mounted() {
//可以獲取store對象
console.log(this.$store)
}
})
</script>
</body>
Mutation
mutation是更改store中狀態的唯一方法,vuex中規定只能通過提交mutation的方式去更改store中的狀態,store.commit()方法更改數據。
- 無參數
//調用方 只傳一個方法名
this.$store.commit('changeName')
//接收方 默認第一個參數是state,無參接收
changeName(state) {
state.name = '張韶涵'
}
- 載荷提交,只能提交一個參數
//調用方,第一個參數:方法名,第二個參數:參數
this.$store.commit('changeName',this.msg)
//接收方
changeName(state, name) {
state.name = name ? name : '張韶涵';
}
- 載荷對象提交
//調用方,傳遞一個對象
this.$store.commit('changeName',{
name:this.msg
})
//接收方
changeName(state, payload) {
state.name = payload.name ;
state.sex = payload.sex//給state新增一個屬性
}
- 純對象風格提交 type值是方法名
//提交一個純對象當做參數,type值為方法名
this.$store.commit({
type: 'changeName',
name: this.msg,
sex: '男'
})
//接收方
changeName(state, payload) {
state.name = payload.name ;
state.sex = payload.sex//給state新增一個屬性
}
- 案例
<body>
<div id="app">
<!-- 組件AAA和組件BBB共同操作$store.state中的數據 -->
<!-- 比我們之前學的組件之間通訊更加方便 -->
<AAA></AAA>
<BBB></BBB>
</div>
<script>
/**
* mutation是更改store中狀態的唯一方法
* vuex中規定只能通過提交mutation的方式去更改store中的狀態
* store.commit()更改數據
*/
let store = new Vuex.Store({
state: {
name: '鄧紫棋'
},
/**
* mutations對象中自定義方法來操作state中的數據
* 并且每個方法會接受 state 作為第一個參數
* 注意:Store對象中寫mutations;Mutation 必須是同步函數
*/
mutations: {
//不接收參數
changeName(state) {
state.name = '張韶涵'
},
//只接收一個參數
changeName(state, name) {
state.name = name ? name : '張韶涵';
},
//接收參數對象
changeName(state, payload) {
state.name = payload.name ? payload.name : '張韶涵';
state.sex = payload.sex//給state新增一個屬性
}
}
})
//創建兩個組件
const AAA = Vue.component('aaa', {
template: `
<div>
<!-- 4.使用$store.state中的數據 -->
<h1>{{this.$store.state.name}}</h1>
<h2>{{this.$store.state.sex}}</h2>
</div>
`
})
const BBB = Vue.component('bbb', {
template: `
<div>
<input type="text" placeholder="請輸入姓名" v-model='msg'>
<input type="button" value="確定" @click='change'>
</div>
`,
data() {
return {
msg: ''
}
},
methods: {
change() {
//1.無參數
// this.$store.commit('changeName')
//2.載荷提交,只能提交一個參數
// this.$store.commit('changeName',this.msg)
//3.載荷對象提交
// this.$store.commit('changeName',{
// name:this.msg
// })
//4.純對象風格提交 type值是方法名
this.$store.commit({
type: 'changeName',
name: this.msg,
sex: '男'
})
}
}
})
let vm = new Vue({
el: '#app',
//把vue和store進行關聯
store
})
</script>
</body>
Action
mutation中規則上是不允許異步操作的,于是vuex為我們提供了action。anctions對象中自定義方法來操作mutations,并且每個方法會接受 context 作為第一個參數,context對象與store對象具有相同的方法和屬性;action 與 mutation 除了使用了異步操作和調用mutation,其它使用并無差別 ;
- 異步更新數據
//action事件的觸發同樣可以使用載荷和對象兩種方式,其它方式就不寫了和mutations方式一樣
this.$store.dispatch({
type:'changeNameAsync',//Store.anctions中的方法名
name: this.msg,
sex: '男'
})
actions: {
//接收數據 延遲一秒提交數據
changeNameAsync(context, payload) {
//異步操作
setTimeout(() => {
context.commit('changeName',payload)
}, 1000)
}
}
mutations: {
//接收參數對象
changeName(state, payload) {
state.name = payload.name ;
state.sex = payload.sex//給state新增一個屬性
}
}
- 案例
<body>
<div id="app">
<AAA></AAA>
<BBB></BBB>
</div>
<script>
/**
* anction--異步更改狀態
* mutation中規則上是不允許異步操作的,于是vuex為我們提供了action。
* store.dispatch() 方法觸發
*/
let store = new Vuex.Store({
state: {
name: '鄧紫棋'
},
/**
* mutations只能同步執行
*/
mutations: {
//接收參數對象
changeName(state, payload) {
state.name = payload.name ? payload.name : '張韶涵';
state.sex = payload.sex//給state新增一個屬性
}
},
/**
* anctions對象中自定義方法來操作mutations,并且每個方法會接受 context 作為第一個參數;
* context對象與store對象具有相同的方法和屬性
* action 與 mutation 除了使用了異步操作和調用mutation,其它使用并無差別
*/
actions: {
//接收數據
changeNameAsync(context, payload) {
//異步操作
setTimeout(() => {
context.commit({
type: 'changeName',
name: payload.name,
sex: '男'
})
}, 1000)
}
}
})
//創建兩個組件
const AAA = Vue.component('aaa', {
template: `
<div>
<!-- 4.使用$store.state中的數據 -->
<h1>{{this.$store.state.name}}</h1>
<h2>{{this.$store.state.sex}}</h2>
</div>
`
})
const BBB = Vue.component('bbb', {
template: `
<div>
<input type="text" placeholder="請輸入姓名" v-model='msg'>
<input type="button" value="確定" @click='change'>
</div>
`,
data() {
return {
msg: ''
}
},
methods: {
change() {
//action事件的觸發同樣可以使用載荷和對象兩種方式,其它方式就不寫了和mutations方式一樣
this.$store.dispatch({
type:'changeNameAsync',//Store.anctions中的方法名
name: this.msg
})
}
}
})
let vm = new Vue({
el: '#app',
store
})
</script>
</body>
getter
getters類似Vue實例中的計算屬性,當綁定的屬性發生變化后才會重新計算;每個方法都默認接收state參數。
- getters使用
//在getters下創建一個getName方法,默認接收state,此方法和計算屬性用法一樣,當state中的name發生改變時會重新計算這個方法return結果
getters:{
getName(state){
let myName='';
if(state.name === '彭俊山'){
myName = '你是最帥的!'
}
return state.name + myName;
}
}
//在A組件中使用getters下的getName屬性,當B組件修改了state中的name后A組件中h2數據也會變化
<h2>{{this.$store.getters.getName}}</h2>
- 案例
<body>
<div id="app">
<AAA></AAA>
<BBB></BBB>
</div>
<script>
let store = new Vuex.Store({
state: {
name: '鄧紫棋'
},
mutations: {
//接收參數對象
changeName(state, payload) {
state.name = payload.name ? payload.name : '張韶涵';
}
},
/**
* getters類似Vue實例中的計算屬性,當綁定的屬性發生變化后才會重新計算
* 每個方法都默認接收state參數
*/
getters:{
getName(state){
let myName='';
if(state.name === '彭俊山'){
myName = '你是最帥的!'
}
return state.name + myName;
}
}
})
//創建兩個組件
const AAA = Vue.component('aaa', {
template: `
<div>
<!-- 使用$store.state中的數據 -->
<h1>{{this.$store.state.name}}</h1>
<!-- 使用$store.getters中的屬性 -->
<h2>{{this.$store.getters.getName}}</h2>
</div>
`
})
const BBB = Vue.component('bbb', {
template: `
<div>
<input type="text" placeholder="請輸入姓名" v-model='msg'>
<input type="button" value="確定" @click='change'>
</div>
`,
data() {
return {
msg: ''
}
},
methods: {
change() {
//純對象風格提交 type值是方法名
this.$store.commit({
type: 'changeName',
name: this.msg
})
}
}
})
let vm = new Vue({
el: '#app',
store
})
</script>
</body>
vuex刷新頁面store數據丟失
當刷新頁面后,store中的數據都會丟失;將store的數據存儲在storage里,由于vue多為單頁面應用,且每次重新打開頁面需要保持數據為空 所以這里我們不選用localstorage,用sessionStorage會話機制;
created() {
//在頁面加載時讀取sessionStorage里的狀態信息
if (sessionStorage.getItem("store")) {
this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem("store"))))
}
//在頁面刷新時將vuex里的信息保存到sessionStorage里
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("store", JSON.stringify(this.$store.state))
})
}