主頁 > 前端設計 > 學習Vue,這一篇文章就夠了(持續更新中)

學習Vue,這一篇文章就夠了(持續更新中)

2021-09-19 07:38:57 前端設計

Vue.js

目錄:

Vue.js
語法:?el:掛載點?data:資料物件?computed:計算屬性?methods:定義函式
指令?插值運算式?v-once:只展示初始值?v-pre:不決議語法?v-cloak:等待vue決議完資料后再顯示頁面?v-text:設定文本值?v-html:設定innerHtml
v-if :判斷?虛擬Dom?特殊情況需要解決虛擬Dom的復用時
?v-show:是否展示此元素
v-for:回圈?常用場景:
注意事項
1. 關于提高性能?案例:
?2. 關于回應布局實時渲染
v-on:事件系結?事例:系結methods中的方法名?事件數量:
修飾符?事例
v-bind:動態系結?1. 用以掛載data中的資料
2. 切換class屬性?a. 傳入陣列,根據布林值進行是否拼接class屬性?b. 與v-model結合使用,達到切換class效果?c. 傳入陣列,批量系結屬性
?3. 動態系結style
v-model:資料雙向系結?input示例?radio示例?checked示例?值系結
修飾符?.lazy ?.number?.trim
?動態引數
過濾器?示例
Vue的生命周期?整體流程圖:?文字決議?實體決議
Vue的Ajax異步請求:axios?通用寫法格式:?GET 請求?POST 請求?多個并發請求?axios(config) 寫法?使用 application/x-www-form-urlencoded 請求頭
Vue組件
入門案例
組件名?使用 kebab-case(推薦)?使用 PascalCase
?模板分離
組件注冊?全域注冊?區域注冊
組件通訊--父傳子
Props?Props的命名?Props的型別限定?Props語法?Props型別檢查的種類?Props的單向資料流?非Props的Attribute屬性
?禁用Attribute繼承?$attrs?$listener:事件監聽器
組件通訊--子傳父
$emit?自定義事件名
父子組件的雙向資料系結?表單?自定義組件的v-model -lyx? todo?.sync修飾符
插槽?具名插槽:帶名字的插槽
作用域插槽:帶資料的插槽?解構插槽 Prop
?后備內容

點擊文字跳轉,點擊背景或三角展開

使用Vue的好處:

  1. 解耦視圖和資料
  2. 可復用的組件
  3. 前端路由技術
  4. 狀態管理
  5. 虛擬Dom

檔案傳送門:Vue官網

語法:

el:掛載點

  1. el設定Vue實體的掛載的元素

  2. Vue會管理el選中的及其內部的后代元素(不用js或jq操作不了掛載點之外的)

  3. 可以使用選擇器(dom也可以),但推薦使用id選擇器

  4. 可以選擇使用其他雙標簽,但不能是html和body(id或類選擇器等則也可以選單標簽)

注:在methods中,想用dom就可以this.$el(等價于document.getElementById("app"))

data:資料物件

  1. Vue中用到的資料都定義在data

  2. data中可以寫復雜型別的資料

  3. 渲染復雜型別的資料時,遵守js的語法即可

computed:計算屬性

  1. 用于預處理data中的屬性
  2. 內容都是方法,相較于methods而言,其方法大多為名詞
  3. 除非方法內的data值發生改變,會再次執行方法,否則方法只執行一次,執行后會將值進行快取,下次呼叫直接從快取中獲取
  4. 例如用插值運算式使用時,是不需要在方法名后加()進行使用的

示例:

<div id="app">
    {{fullName}} 他花了 {{totalMoney}} 巨資充實了自己
</div>	
<script>
    var app = new Vue({
        el: '#app',
        data: {
            finalName: "貓巷",
            lastName: "Number One",
            books: [{
                id: 1,
                name: "Thinking In Java",
                price: 40
            },
                    {
                        id: 2,
                        name: "On Java",
                        price: 30
                    },
                    {
                        id: 3,
                        name: "深入理解計算機原理",
                        price: 20
                    },
                    {
                        id: 4,
                        name: "現代作業系統",
                        price: 10
                    }
                   ]
        },
        computed: {
            fullName: function() {
                return this.finalName + " " + this.lastName
            },
            totalMoney: function() {
                let totalMoney = 0;
                for (let i = 0; i < this.books.length; i++) {
                    totalMoney += this.books[i].price;
                }
                return totalMoney;
            }
        }
    });

結果:image-20210830210945895

以上computed里的兩個方法是簡寫,每個計算屬性的方法都有一對getter和setter,取值時呼叫get(),賦值時呼叫set(),具體如下:

computed: {
            fullName: {
				get: function(){
					return this.finalName + " " + this.lastName
				},
				set: function(newValue){
					// newValue為此fullname的新賦的值
					// set方法通常都不寫,因為computed為計算屬性,因此通常也被稱為只讀屬性
				}
            },
            fullName: {
				get: function(){
					let totalMoney = 0;
                	for (let i = 0; i < this.books.length; i++) {
                    	totalMoney += this.books[i].price;
                	}
                	return totalMoney;
				},
				set: function(newValue){
					// newValue為此fullname的新賦的值
					// set方法通常都不寫,因為computed為計算屬性,因此通常也被稱為只讀屬性
				}
            }
        }

methods:定義函式

  1. Vue中用到的方法都定義在methods中,且大多為動詞形式
  2. 寫法與普通方法無異,遵守js的語法即可
  3. 相較于computed,沒有快取,因此效率與computed相比較低

指令

插值運算式

掛載完對應元素后,可通過{{message}} 取出data中的message值

插值運算式之間支持簡單Js,例如:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 測驗實體 - 菜鳥教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
	{{5+5}}<br>
	{{ ok ? 'YES' : 'NO' }}<br>
	{{ message.split('').reverse().join('') }}
	<div v-bind:id="'list-' + id">菜鳥教程</div>
</div>
	
<script>
new Vue({
  el: '#app',
  data: {
	ok: true,
    message: 'RUNOOB',
	id : 1
  }
})
</script>
</body>
</html>

效果如下:

10
YES
BOONUR
菜鳥教程 (div的id為list-1)

v-once:只展示初始值

使用v-once后,在渲染資料后,修改data的值,將不會再進行回應式更新,即只展示最初始的值

<div id="app">
    <span v-once>{{message}}</span>
</div>
<script>
	new Vue({
        el: "#app",
        data:{
            message: "hello vue"
        }
    })
</script>

注意:v-once 后面不需要加 =""

v-pre:不決議語法

等價于<pre></pre>標簽,例如:

<div id="app">
    <span v-pre>{{message}}</span>
</div>
<script>
	new Vue({
        el: "#app",
        data:{
            message: "hello vue"
        }
    })
</script>

此時頁面展示不是hello vue ,而是{{message}}

v-cloak:等待vue決議完資料后再顯示頁面

瀏覽器決議html檔案是從上往下,有時候js代碼卡住,用戶就會先看見還未渲染的資料,如{{message}},而不是data中的hello vue,這體驗是不好的,v-cloak原理是,加上v-cloak屬性的元素,默認會看不見,等待vue渲染完畢后就會洗掉所有v-cloak屬性,然后就自動展示出資料了,

v-text:設定文本值

  1. 設定雙標簽內的文本值
  2. 會覆寫原雙標簽里的文本值
  3. 內部可寫運算式,也會識別為文本

v-html:設定innerHtml

  1. 設定雙標簽的innerHtml值
  2. 需要在內部寫html陳述句采用v-html,只需要純文本則用v-text,v-html寫的會被決議為html陳述句

v-if :判斷

根據 if 運算式后的值(true 或 false )來決定是否插入此元素

<div id="app">
    <span v-if="flag">
        <label>賬號:</label>
        <input type="text" id="account" placeholder="請輸入賬號">
    </span>
    <span v-else>
        <label>郵箱:</label>
        <input type="text" id="emails" placeholder="請輸入郵箱">
    </span>
    <button type="button" @click="flag = !flag">切換登錄</button>
</div>

<script type="text/javascript">
    new Vue({
        el:"#app11",
        data:{
            flag : true
        }
    })
</script>

點擊按鈕效果如圖:image-20210901212032121image-20210901212052854

注意:表單里的值之所以沒清空,是因為Vue底層采用的虛擬Dom元素Dom結構極為相似,且是對立情況出現的情況下,Vue底層會渲染同一個的虛擬Dom到頁面上,再比較所渲染元素的具體區別(屬性值等),進行頁面的渲染,

虛擬Dom

瀏覽器決議Dom時,會將元素直接渲染到頁面上,而Vue、React等底層采用了虛擬Dom,元素會先被Vue渲染到虛擬Dom中,在渲染到頁面的同時會比較是否有可重復利用的元素,如果有 則渲染時直接復制原虛擬Dom的元素,再根據具體區別(屬性值等)進行修改,這么做大大提升了頁面渲染的效率,

總結:

  • 獲取監聽變化后生成的虛擬節點樹
  • 與上一次虛擬DOM節點樹進行比較
  • 找到差異的部分,渲染到真實的DOM節點上面
  • 更新視圖

特殊情況需要解決虛擬Dom的復用時

上述例子,切換表單明顯需要清空表單內的值,此時虛擬Dom帶來了反面效果,可通過添加key屬性來解決,

  • key值相同,代表可以復用
  • key值不同,代表不能被復用

示例:

	<div id="app11">
		<span v-if="flag">
			<label>賬號:</label>
			<input type="text" id="account" placeholder="請輸入賬號" key="username">
		</span>
		<span v-else>
			<label>郵箱:</label>
			<input type="text" id="emails" placeholder="請輸入郵箱" key="email">
		</span>
		<button type="button" @click="flag = !flag">切換登錄</button>
	</div>

上述例子label沒寫key,所以可被復用,input二者的key不同,即不可復用,在點擊按鈕時,input內的值自然就被清空了

v-show:是否展示此元素

  1. 當顯示條件為true時:

    v-ifv-show效果相同

  2. 當顯示條件為false時:

    v-if不渲染dom元素,而v-show仍然渲染;渲染時會加上行內樣式:style = "display: none"

當資料要在顯示隱藏間頻繁切換時,使用v-show

當資料只有一次切換根據條件及進行渲染時使用v-if

v-for:回圈

常用場景:

  1. 陣列集合回圈

    <div id="app10">
        <ul>
         	<!-- 
    			1.item為值,index為索引
    			2.就寫一個默認就為item,即是值
    			3.括號可省略但不建議
    		-->   
            <li v-for="(item,index) in arr">{{item}}==={{index}}</li>
        </ul>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                arr: [1,2,3,4,5]
            },
        })
    </script>
    

    結果:v-for回圈圖1

  2. 遍歷Json形式的資料(超好用)

    <div id="app10">
        <ul>
            <!-- 
    			1.注意value和key的順序是反的,先寫的始終是值value,后寫的是索引index或鍵key
    			2.其他點同上
    		-->   
            <li v-for="(value,key) in person">{{key}}==={{value}}</li>
        </ul>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                person: {
                    id:1001,
                    username:"admin",
                    password:"123"
                }
            },
        })
    </script>  
    

    結果:v-for回圈圖2

  3. json集合(兩者結合)

    <div id="app10">
        <table cellspacing="1" border="1">
            <tr>
                <th>編號</th>
                <th>id</th>
                <th>產品名稱</th>
                <th>產品價格</th>
            </tr>
            <tr v-for="(product,index) in persons">
    			<td>{{ index + 1 }}</td>
                <td v-for="value in product">{{ value }}</td>
                
            </tr>
        </table>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                persons: [
                    {id: 1001, proName: "手機", proMoney: "123"},
                    {id: 1002, proName: "平板", proMoney: "1090"},
                    {id: 1003, proName: "電腦", proMoney: "2222"},
                    {id: 1004, proName: "攝像機", proMoney: "3041"},
                ]
            },
        })
    </script>  
    

    結果:v-for結果圖3

注:獲取物件索引還可以:v-for="(value, key, index) in Person"

注意事項

1. 關于提高性能

官方推薦我們在使用v-for時,給對應的元素或組件加上:key,即系結key屬性,且系結的屬性值為當前元素(item)

原因:這么做其實與其虛擬Dom的Diff演算法有關

Diff演算法:Diff演算法的作用是用來計算出 Virtual DOM (即虛擬Dom) 中被改變的部分,然后針對該部分進行原生DOM操作,而不用重新渲染整個頁面

案例:

頁面使用ul-li標簽展示陣列arr=[A,B,C,D,E],此時往第三個位置插入個F元素(即創建新元素),

此時高效率做法應是直接往第三個位置插入F元素①,而虛擬Dom采用的是極低效率的“將C變成F,D變成C,E變成D,創建新li標簽且值為E“,資料更新后,采用Diff演算法渲染時會”判斷不同的數量,再一個一個更替不同的地方“②,此做法會提升消耗,降低效率

此時,若在遍歷時加入key屬性,且與單個遍歷物件item一一系結,遍歷時虛擬Dom會優先將keyitem對應的值進行系結渲染,再將新創建的(無對應key值)元素進行插入,即可達到①的效果

用代碼總結就是:<ul><li v-for="item in arr" :key="item"> </li></ul>

注意:

  1. 系結index是沒用的,因為新增或洗掉元素時,index會發生變化,此時固定的item對應的index就會發生改變,
  2. 系結item的前提是保證arr陣列中的元素是唯一(不重復的)
2. 關于回應布局實時渲染

對于陣列,呼叫push、pop、shift、unshift、splice、sort、reverse等方法,都可以達到回應式布局,

但是arr[2] = "aaa"這種直接通過索引下標修改元素的,Vue是沒有監聽的,因此不會實時渲染到頁面上,

通常解決方式有兩種:

  1. arr.splice(2, 1, "aaa");
  2. Vue.set(arr, 2, "aaa"), 即Vue(要修改的物件,要修改的索引值,修改后的值)

v-on:事件系結

  1. 指令作用為:為元素系結事件
  2. 事件名不需要寫on (v-on:click)
  3. 指令可簡寫成@(@click)
  4. 系結的方法定義在methods中
  5. 方法內部可通過this訪問定義在data中的屬性
  1. 當系結的方法沒有引數時,括號可加可不加,

  2. this中有很多$開頭的屬性,如$event表示原生dom的事件,this.$el = document.getElementById("app")

  3. 使用@click后想要獲取點擊本事件本體,有兩種方式:

    ? a. this.$refs.ref可獲得el系結的所有帶有ref屬性的標簽,再通過下標即可獲取對應元素,

    ? b. 通過event.currentTarget獲取本身標簽元素this

事例:系結methods中的方法名

<div id="example-2">
  <!-- `greet` 是在下面定義的方法名,不寫括號時,默認方法的`第一個引數`可接收原生event事件-->
  <button v-on:click="greet">Greet</button>
</div>
<script>
    var example2 = new Vue({
      el: '#example-2',
      data: {
        name: 'Vue.js'
      },
      // 在 `methods` 物件中定義方法
      methods: {
        greet: function (event) {
          // `this` 在方法里指向當前 Vue 實體
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
            alert(event.target.tagName)
          }
        }
      }
    })
</script>

事件數量:

Dom有的事件Vue基本都有,總的事件如圖:

v-on事件

修飾符

修飾符是以半角句號 . 指明的特殊后綴,用于指出一個指令應該以特殊方式系結,例如.prevent 修飾符告訴 v-on 指令對于觸發的事件呼叫 event.preventDefault(),即阻止元素的默認行為,event.stopPropagation()阻止事件冒泡等,常用的修飾符有以下幾種:

  • .stop - 阻止冒泡
  • .prevent - 阻止默認事件
  • .capture - 阻止捕獲
  • .self - 只監聽觸發該元素的事件
  • .once - 只觸發一次
  • .left - 左鍵事件
  • .right - 右鍵事件
  • .middle - 中間滾輪事件
事例
<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再多載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修飾符可以串聯 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件監聽器時使用事件捕獲模式 -->
<!-- 即內部元素觸發的事件先在此處理,然后才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只當在 event.target 是當前元素自身時觸發處理函式 -->
<!-- 即事件不是從內部元素觸發的 -->
<div v-on:click.self="doThat">...</div>

<!-- 點擊事件將只會觸發一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滾動事件的默認行為 (即滾動行為) 將會立即觸發 -->
<!-- 而不會等待 `onScroll` 完成  -->
<!-- 這其中包含 `event.preventDefault()` 的情況 -->
<!-- 這個 .passive 修飾符尤其能夠提升移動端的性能 -->
<div v-on:scroll.passive="onScroll">...</div>

注:

  1. 使用修飾符時,順序很重要;相應的代碼會以同樣的順序產生,因此,用 v-on:click.prevent.self 會阻止所有的點擊,而 v-on:click.self.prevent 只會阻止對元素自身的點擊,
  2. 不要把 .passive.prevent 一起使用,因為 .prevent 將會被忽略,同時瀏覽器可能會向你展示一個警告,請記住,.passive 會告訴瀏覽器你不想阻止事件的默認行為,

v-bind:動態系結

v-bind 被用來回應地更新 HTML 屬性,可簡寫為一個冒號:

例子:

1. 用以掛載data中的資料

<div id="app">
    <pre>
    	<a v-bind:href="https://www.cnblogs.com/catcode/p/url">菜鳥教程</a>
    </pre>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    url: 'http://www.runoob.com'
  }
})
</script>

注意:插值運算式無法直接賦值標簽的屬性值,

2. 切換class屬性

可根據布林值來確定是否拼接class屬性

固定的class可直接在前面定義,即 ,因為:class是拼接上去的,不會覆寫原來

a. 傳入陣列,根據布林值進行是否拼接class屬性
<html>
	<head>
		<meta charset="utf-8">
		<title>v-bind</title>
	</head>
	<body>
		<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

		<div id="app">
			<div v-bind:>
				v-bind:class 指令
			</div>
		</div>

		<script>
			new Vue({
				el: '#app',
				data: {
					isActive: true,
					isLine:false
				}
			});
		</script>
	</body>
</html>

效果如圖:image-20210828153643637

上述例子等價于:

<html>
	<head>
		<meta charset="utf-8">
		<title>v-bind</title>
	</head>
	<body>
		<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

		<div id="app">
            <!-- 也可以指向method或computed -->
			<div v-bind:>
				v-bind:class 指令
			</div>
		</div>

		<script>
			new Vue({
				el: '#app',
				data: {
					isActive: true,
					isLine: false
				},
				methods: {
					getClasses: function() {
						return {
							'active': this.isActive,
							'line': this.isLine
						};
					}
				}
			});
		</script>
	</body>
</html>
b. 與v-model結合使用,達到切換class效果
<html>
<head>
<meta charset="utf-8">
<title>Vue 測驗實體 - 菜鳥教程(runoob.com)</title>
</head>
<style>
    .class1{
      background: #444;
      color: #eee;
    }
</style>
<body>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

<div id="app">
  <label for="r1">修改顏色</label><input type="checkbox" v-model="use" id="r1">
  <br><br>
  <div v-bind:>
    v-bind:class 指令
  </div>
</div>
    
<script>
new Vue({
    el: '#app',
  data:{
      use: false
  }
});
</script>
</body>
</html>

效果: image-20210828160321623,點擊選擇框,下方div背景會跟隨選擇框的狀態改變

c. 傳入陣列,批量系結屬性
<div id="app">
    <div  v-bind:>
        v-bind:class 指令
    </div>
</div>

<script>
    new Vue({
        el: '#app',
        data: {
            test2: "class2",
            test3: "class3"
        }
    });
</script>

結果:image-20210828161501270

3. 動態系結style

實體:

<div id="app">
    <!-- 格式: :style="key(屬性名): value(屬性值)" -->
    <h2 :style="{fontSize: '5px'}">動態系結style</h2>
    <h2 :style="{fontSize: finalSize+'px'}">動態系結style</h2>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            finalSize: 5
        }
    });
</script>

結果:image-20210828191248217

同上,也可以傳入陣列,進行批量設值

v-model:資料雙向系結

用來在 inputselecttextareacheckboxradio等表單控制元件元素上創建雙向資料系結,根據表單上的值,自動更新系結的元素的值

input示例

<div id="app">
    <p>{{ message }}</p>
    <input v-model="message">
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Runoob!'
  }
})
</script>

radio示例

<div id="app">
    <label>
        <input type="radio" value="https://www.cnblogs.com/catcode/p/男" v-model="sex" />男
    </label>
    <label>
        <input type="radio" value="https://www.cnblogs.com/catcode/p/女" v-model="sex" />女
    </label><br>
    性別是: {{ sex }}
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            sex: "女"
        }
    })
</script>

結果:image-20210912212708461

checked示例

<div id="app">
    <input type="checkbox" value="https://www.cnblogs.com/catcode/p/籃球" v-model="hobbies">籃球
    <input type="checkbox" value="https://www.cnblogs.com/catcode/p/足球" v-model="hobbies">足球
    <input type="checkbox" value="https://www.cnblogs.com/catcode/p/羽毛球" v-model="hobbies">羽毛球
    <input type="checkbox" value="https://www.cnblogs.com/catcode/p/乒乓球" v-model="hobbies">乒乓球
    <p>你的愛好有:{{ hobbies}}</p>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            hobbies:['籃球', '足球']
        }
    })
</script>

結果:image-20210912213253887

值系結

示例:

<div id="app">
    <label v-for="item in hobbies" :for="item">
        <input type="checkbox" :id="item" :value="https://www.cnblogs.com/catcode/p/item" v-model="myHobbies" />
    </label>
    <p>你的愛好有:{{ myHobbies}}</p>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            hobbies: ['籃球', '足球', '乒乓球', '羽毛球', '保齡球', '高爾夫球'],
            myHobbies:[]
        }
    })
</script>

結果:image-20210912215625508

修飾符

.lazy

在默認情況下,v-model 在每次 input 事件觸發后將輸入框的值與資料進行同步,你可以添加 lazy 修飾符,從而轉為在 change 事件_之后_進行同步:

<!-- 
	在“change”時而非“input”時更新 
	即敲下回車或表單失焦后觸發,而不是敲一下觸發一次事件,降低事件使用頻率
-->
<input v-model.lazy="msg">
.number

如果想自動將用戶的輸入值轉為數值型別,可以給 v-model 添加 number 修飾符(用戶只能輸入數字型別):

<input v-model.number="age" type="number">

這通常很有用,因為即使在 type="number" 時,HTML 輸入元素的值也總會回傳字串,如果這個值無法被 parseFloat() 決議,則會回傳原始的值,

.trim

如果要自動過濾用戶輸入的首尾空白字符,可以給 v-model 添加 trim 修飾符:

<input v-model.trim="msg">

動態引數

從 2.6.0 開始,可以用方括號括起來的 JavaScript 運算式作為一個指令的引數:

<div id="app">
    <input v-on:[eventname]="todo" v-bind:[attributename]="msg" value="https://www.cnblogs.com/catcode/p/測驗">
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            eventname: "click",
            attributename: "type",
            msg: "button"
        },
        methods: {
            todo() {
                alert(this.eventname)
            }
        }
    })
</script>

結果: image-20210918160001197,點擊后彈出``

過濾器

用作一些常見的文本的格式化,并且多個過濾器可串聯{{ message | filterA | filterB }}

格式:

<!-- 在兩個大括號中 -->
{{ message | capitalize }}

<!-- 在 v-bind 指令中 -->
<div v-bind:id="rawId | formatId"></div>

示例

插值運算式以及class屬性的過濾(首個字母大寫):

<!-- 過濾器 -->
<div id="app9">
	<div v-bind:>
		{{message | toLowerCase}}
	</div>
</div>

<script type="text/javascript">
			new Vue({
				el:"#app9",
				data:{
					message:"runoob!",
					clsTest:"class"
				},
				filters:{
					toLowerCase:function(value){
						return value.charAt(0).toUpperCase() + value.substr(1)
					},
					clsTest:function(cls){
						return cls+"1";
					}
				}
			})
</script>

結果為:

<div >Runoob</div>

過濾器是 JavaScript 函式,因此可以接受引數:

{{ message | filterA('arg1', arg2) }}

Vue的生命周期

整體流程圖:

文字決議

Vue 實體從創建到銷毀的程序,就是生命周期,從開始創建、初始化資料、編譯模板、掛載Dom→渲染、更新→渲染、銷毀等一系列程序,稱之為 Vue 的生命周期,

Vue一共有8中生命周期(4種狀態的前后):分別為:

  • 創建Vue實體前 ---beforeCreate
  • 創建Vue實體后 ---created
  • 掛載前 ---beforeMount
  • 掛載后 ---mounted
  • 資料更新前 ---beforeUpdate
  • 資料更新后 ---update
  • 實體銷毀前 ---beforeDestroy
  • 實體銷毀后 ---destroyed

其中,資料的渲染在mounted階段渲染完畢,

每個程序對應一個事件鉤子(callHook()),對應一個函式的執行,如 掛載前會執行beforeMount()方法

實體決議

<div id="app11">
    {{ message }}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app11",
        data: {
            message: "Hello Vue.js!"
        },
        beforeCreate: function () {
            showDate("vue實體創建前",this);
        },
        created: function () {
            showDate("vue實體創建后",this);
        },
        beforeMount: function () {
            showDate("掛載前",this);
        },
        mounted: function () {
            showDate("掛載后",this);
        },
        beforeUpdate: function () {
            showDate("資料更新前",this);
        },
        updated: function () {
            showDate("資料更新后",this);
        },
        beforeDestroy: function () {
            showDate("vue實體銷毀前",this);
        },
        destroyed: function () {
            showDate("vue實體銷毀后",this);
        }
    });

    /** 展示真實dom結構 **/
    function realDom() {
        console.log("真實dom結構:"+document.getElementById("app11").innerHTML)
    }

    /** 展示data的資料以及將要掛載的物件 **/
    function showDate(process,obj) {
        console.log(process);
        console.log("data資料:"+obj.message);
        console.log("掛載的物件:");
        console.log(obj.$el);
        realDom();
        console.log('-------------------')
        console.log('-------------------')
    }
    app.message="good"
    app.$destroy();
</script>

結果:

  1. 當app.message="good"作用,app.$destroy()注釋時:
vue實體創建前
data資料:undefined
掛載的物件:
	undefined
真實dom結構:
    {{ message }}
-------------------
-------------------
vue實體創建后
data資料:Hello Vue.js!
掛載的物件:
	undefined
真實dom結構:
    {{ message }}
-------------------
-------------------
掛載前
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> {{ message }} </div>
真實dom結構:
    {{ message }}
-------------------
-------------------
掛載后
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> good </div>
真實dom結構:
    Hello Vue.js!
<!-- 注:此時這里掛載物件為“good”是因為message已經被賦值,但掛載上去的仍是“Hello Vue.js!”,原因是因為賦值一定會有一個值的變化的程序,而這個程序在Vue中發生在Mounted階段,即掛載后 -->
-------------------
-------------------
資料更新前
data資料:good
掛載的物件:
	<div id="app11"> good </div>
真實dom結構:
    Hello Vue.js!
-------------------
-------------------
資料更新后
data資料:good
掛載的物件:
	<div id="app11"> good </div>
真實dom結構:
    good
-------------------
-------------------
  1. 當app.$destroy()作用,app.message="good"注釋時:
vue實體創建前
data資料:undefined
掛載的物件:
	undefined
真實dom結構:
    {{ message }}
-------------------
-------------------
vue實體創建后
data資料:Hello Vue.js!
掛載的物件:
	undefined
真實dom結構:
    {{ message }}
-------------------
-------------------
掛載前
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> {{ message }} </div>
真實dom結構:
    {{ message }}
-------------------
-------------------
掛載后
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> Hello Vue.js! </div>
真實dom結構:
    Hello Vue.js!
-------------------
-------------------
vue實體銷毀前
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> Hello Vue.js! </div>
真實dom結構:
    Hello Vue.js!
-------------------
-------------------
vue實體銷毀后
data資料:Hello Vue.js!
掛載的物件:
	<div id="app11"> Hello Vue.js! </div>
真實dom結構:
    Hello Vue.js!
<!-- 之所以銷毀前后與之前沒變化,是因為destroy只是銷毀了vue物件,然而vue已渲染、產生的效果是不會消失的,   -->
-------------------
-------------------

Vue的Ajax異步請求:axios

通用寫法格式:

// 發送 POST 請求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }.then(function(){...}).catch(function(){...})
});

在axios中的this無法指代vue實體,拿取不到vue中data的資料,因此可在axios定義上方定義類似:

var _this = this; 來獲取vue實體物件,然后獲得user物件可通過_this.user獲取

GET 請求

axios.get('/user?ID=12345')
.then(function (response) {
// then方法等價于success方法,Ajax正確執行后的回呼函式
    console.log(response);
})
.catch(function (error) {
// catch方法等價于error方法,Ajax執行出現例外后的回呼函式
    console.log(error);
});

// 上面的請求也可以這樣做(get的引數必須加params)
axios.get('/user', {
    params: {
        ID: 12345
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});

POST 請求

// post請求不需要加params,直接花括號里加Json資料
axios.post(‘/user’, {
firstName: ‘Fred’,
lastName: ‘Flintstone’
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

<!-- =========== 另一種例子 ============== -->
 var app = new Vue({
        el: "#app1",
        data: {
            user: {
                username: "",
                password: ""
            }
        },
        methods: {
            reg: function () {
                var _this = this;
                axios.post('/sshTest/user/save',_this.user).then(function (response) {
                    console.log(response);
                }).catch(function (message) {
                    console.log(message);
                })
            }
        }
    })

多個并發請求

function getUserAccount() {
	return axios.get(‘/user/12345’);
}
function getUserPermissions() {
	return axios.get(‘/user/12345/permissions’);
}
// axios.all()方法里傳入Ajax方法的陣列
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 兩個請求現在都執行完成
}));

axios(config) 寫法

// 發送 POST 請求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

使用 application/x-www-form-urlencoded 請求頭

import qs from 'qs';
const data = https://www.cnblogs.com/catcode/p/{'bar': 123 };
const options = {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  data: qs.stringify(data),
  url,
};
axios(options);

Vue組件

入門案例

<div id="app">
        <button-counter></button-counter>
</div>

<script>
    // 定義一個名為 button-counter 的新組件
    Vue.component('button-counter', {
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我點擊了 {{ count }} 次</button>'
    })
	/* 以上也可以等價于以下
	// 創建組件構造器物件
    const cpn = Vue.extend({
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我點擊了 {{ count }} 次</button>'
    });
    // 組件注冊
    Vue.component('button-counter',cpn)
	*/
    
    new Vue({ el: '#app' })
</script>

效果如圖:image-20210902142042895 ,點擊按鈕則count+1. 且在頁面上是看不到button系結的事件的,

注意:

  1. 例中的 ”button-counter" 即為組件名,自定義組件名遵循規則 (字母全小寫且必須包含一個連字符),這會避免和當前以及未來的 HTML 元素相沖突,
  2. 組件是可以重復使用的,組件內的data相互獨立互不影響
  3. data必須是個函式,而不能是物件(Vue的規定,以此來保證每個組件的data獨立)
  4. data函式的回傳是個物件(即要{}包裹)

組件名

定義組件名的方式有兩種:

使用 kebab-case(推薦)
Vue.component('my-component-name', { /* ... */ })

當使用 kebab-case (短橫線分隔命名) 定義一個組件時,你也必須在參考這個自定義元素時使用 kebab-case,例如 <my-component-name>

使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })

當使用 PascalCase (首字母大寫命名) 定義一個組件時,在JS中參考兩種命名法都可以使用,但是,HTML不區分大小寫,因此只能使用 kebab-case

模板分離

在實際開發中,直接寫template='<div>...</div>'一個是可讀性不好,一個是沒有復用性,還有就是在寫字串時許多開發工具并不會語法提示,因此就有了模板分離:將HTML字串用特殊標簽包住后,直接寫在其內部,參考其id給template即可直接使用

<div id="app">
    <my-button></my-button>
</div>


<template id="test">
	<div>
        <input type='text'>
        <input type='button' value="https://www.cnblogs.com/catcode/p/模板">
        <input type='checkbox' checked="checked">模板顯示
    </div>
</template>

<script>
    Vue.component('MyButton', {
        // 參考其模板的id
        template: test
    })

    new Vue({el: '#app'})
</script>

效果:image-20210916100200823

  1. 可用<template></template>與<script type="text/x-template" id="test"></script>進行包裹,效果一樣,更推薦前者
  2. 模板不用寫在Vue實體所掛載的內部

組件注冊

全域注冊

Vue.component('my-component-name', {
  // ... 選項 ...
})

這些組件是全域注冊的,它們在注冊之后可以用在任何新創建的 Vue 實體 (new Vue) 的模板中,比如:

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })
<div id="app">
  <component-a></component-a>
  <component-b></component-b>
  <component-c></component-c>
</div>

在所有子組件中也是如此,也就是說這三個組件在各自內部也都可以相互使用,

區域注冊

全域注冊往往是不夠理想的,比如,如果你使用一個像 webpack 這樣的構建系統,全域注冊所有的組件意味著即便你已經不再使用一個組件了,它仍然會被包含在你最終的構建結果中,這造成了用戶下載的 JavaScript 的無謂的增加,

在這些情況下,可以通過一個普通的 JavaScript 物件來定義組件,然后在 Vue實體內用components 選項中定義你想要使用的組件:


<div id="app">
    <!-- 標簽名不區分大小寫,因此必須采用kebab-case風格 -->
    <component-a></component-a>
</div>

<script>
    // JS定義物件
    var ComponentA = {
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我點擊了 {{ count }} 次</button>'
    }

    new Vue({
        el: '#app',
        components: {
            // 等價于 "CompunentA" : ComponentA
            // 注意,全域注冊的組件不能寫在這,會報ComponentA is not define
            ComponentA
        }
    })
</script>

? 對于 components 物件中的每個 property 來說,其 property 名就是自定義元素的名字,其 property 值就是這個組件的選項物件,

注意區域注冊的組件在其子組件中不可用,例如,如果希望 ComponentAComponentB 中可用,則需要這樣寫:

var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

或者如果你通過 Babel 和 webpack 使用 ES6 模塊,那么代碼看起來更像:

import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

在 ES6 中,在物件中放一個類似 ComponentA 的變數名其實是 ComponentA: ComponentA 的縮寫,即這個變數名同時是:

  • 用在模板中的自定義元素的名稱
  • 包含了這個組件選項的變數名

組件通訊--父傳子

Props

寫在props中的值,接收資料通過組件上的同名屬性接收,使用方式與在data中無異(因此可使用v-bind進行資料系結),

示例:

<div id="app">
    <!-- 
        posts為Vue實體中data的資料
        v-bind:post,:arr系結post,arr屬性值,通過props傳入,使用時與從data中取值無異
        在組件上使用vue實體的方法要加.native,宣告是本頁面的方法,否則只能呼叫Vue中的方法
	-->
    <component-a v-for="post in posts" @click.native="vueFun(post.id)" :post="post" :arr="[1,2,3,4,5]"></component-a>
</div>


<script>
    var ComponentA = {
        data() {
            return {
                count: 0
            }
        },
        props: ["post","arr"],
        template:
        '<ul><li @click.stop="componentFun(post.title)" v-for="(item, index) in arr">{{item}} {{post.title}} {{index + count}}</li></ul>',
        methods: {
            componentFun(obj) {
                console.log(obj)
            }
        }
    }

    new Vue({
			el: '#app',
			components: {
				ComponentA
			},
			data: {
				posts: [
						{ id: 1, title: 'My journey with Vue' },
						{ id: 2, title: 'Blogging with Vue' },
						{ id: 3, title: 'Why Vue is so fun' }
					]
			},
			methods: {
				vueFun(obj) {
					showData(obj)
				}
			}
		})
    
		function showData(obj) {
			console.log(obj)
		}
</script>

結果:image-20210913161233337,點擊后

data與props用法相似,組件私有的資料、可讀可寫的寫在data中,只讀不可寫的寫在props中

Props的命名
  1. 由于HTML是大小寫不敏感的,使用標簽的屬性統一采用小寫,
  2. 對于Props中駝峰命名的資料,需要使用其等價的 kebab-case (短橫線分隔命名)

建議:在JS中采用駝峰命名,屬性使用kebab-case(短橫線分隔命名)

Props的型別限定

簡略寫法(陣列):

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

限定型別寫法(物件):

props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
    callback: Function,
    contactsPromise: Promise // or any other constructor
}

使用錯誤型別時頁面不會報錯,但控制臺會報錯,

注意[]{},簡略寫法必須加引號'',限定型別寫法可省略,

Props語法
Vue.component('my-component', {
  props: {
    // 基礎的型別檢查
    propA: Number,
    // 多個可能的型別
    propB: [String, Number],
    // 必填的字串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認值的數字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認值的物件
    propE: {
      type: Object,
      // 物件或陣列默認值必須從一個工廠函式獲取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函式
    propF: {
      validator: function (value) {
        // 這個值必須匹配下列字串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

nullundefined 會通過任何型別驗證

Props型別檢查的種類

type 可以是下列原生建構式中的一個:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

額外的,type 還可以是一個自定義的建構式,并且通過 instanceof 來進行檢查確認,例如,給定下列現成的建構式:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可以使用:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

來驗證 author prop 的值是否是通過 new Person 創建,

Props的單向資料流

所有的 prop 都使得其父子 prop 之間形成了一個單向下行系結:父級 prop 的更新會向下流動到子組件中,但是反過來則不行,這樣會防止從子組件意外變更父級組件的狀態,從而導致你的應用的資料流向難以理解,

每次父級組件發生變更時,子組件中所有的 prop 都將會重繪為最新的值,這意味著你應該在一個子組件內部改變 prop,如果你這樣做了,Vue 會在瀏覽器的控制臺中發出警告(Avoid mutating a prop directly since the value will be overwritten whenever the parent component),

在兩種情況下,我們很容易忍不住想要去修改prop中資料:

  1. Prop作為初始值傳入后,子組件想把它當作區域資料來用;
  2. Prop作為原始資料傳入,由子組件處理成其它資料輸出,

對這兩種情況,正確的應對方式是:

  1. 在子組件中定義一個區域變數,并用prop的值初始化它:
props: ['initialCounter'],
data: function () {
  // this可呼叫props中的屬性值
  return { counter: this.initialCounter }
}
  1. 定義一個計算屬性,處理prop的值并回傳:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

在JavaScript中物件和陣列是參考型別,指向同一個記憶體空間,如果prop是一個物件或陣列,在子組件內部改變它會影響父組件的狀態

非Props的Attribute屬性

當在組件上寫有Attribute屬性,卻在props中沒有進行定義(接收資料),即為非Props的Attribute屬性

因為顯式定義的 prop 適用于向一個子組件傳入資訊,然而組件庫的作者并不總能預見組件會被用于怎樣的場景,這也是為什么組件可以接受任意的 attribute,而這些 attribute 會被添加到這個組件的根元素上,

實體:

<div id="app">
    <component-b test type="text"  style="margin-left: 50px;">		</component-b>
</div>

<script>
    var ComponentB = {
        template:'<input type="date"  style="width : 500px">'
    }
</script>

結果:image-20210908110658651

  1. test可以是任意屬性,可以是hiden、disabled等具有實際意義屬性,或是test這種自定義屬性,
  2. 當組件與模板根元素有相同屬性type時,組件的屬性值會覆寫模板根元素的屬性值,
  3. classstyle比較特殊,不會覆寫原來的屬性值,而是拼接上去,
  4. 如果不想讓組件上的屬性影響到模板根元素,可設定禁用Attribute繼承

禁用Attribute繼承

不希望組件的根元素繼承 attribute,你可以在組件的選項中設定 inheritAttrs: false

<div id="app">
    <component-a type='text' ></component-a>
</div>

<script>
	var ComponentA = {
        template:'<input  type="date" style="width : 500px">',
        // 不繼承任何組件上傳來的屬性
        inheritAttrs: false
    }
</script>

結果:image-20210913114645142

  1. type=date不會被type=text覆寫
  2. inheritAttrs對class與style無效,仍然會繼續拼接

$attrs

作用:在指定元素位置傳遞attribute屬性,

使用v-bind='$attrs'將非Props的Attribute屬性系結到指定的元素上,而不是只能系結在根元素上

父組件想傳值給曾子組件時,如果使用inheritAttrs,子組件會有很多無關的屬性在組件上,此時禁用子組件的Attribute繼承后,使用$attrs子組件上使用即可達到傳值效果,

inheritAttrs是會覆寫相同的屬性值的,但$attrs是不會覆寫的(value會覆寫?) -lyx todo

<body>
    <div id="app">
        <component-a type='submit' width="20px"></component-a>
    </div>

    <script>
        var ComponentB = {
            template: `
			<div>
<!-- 在此位置使用type='submit',width因為已經有了,$attrs無法覆寫原屬性值,因此width仍然是30 -->
				<input width="30px" v-bind="$attrs" value="https://www.cnblogs.com/catcode/p/測驗">
     	   </div>
			`,
            inheritAttrs: false
        }

        var ComponentA = {
            components:{
                ComponentB
            },
            template: `
				<div>
					<input  type="date" style="width : 500px">
					<!-- 在組件上使用v-bind='$attrs' -->
					<component-b v-bind="$attrs"></component-b>
 		       </div>`,
            // 不繼承任何組件上傳來的屬性
            inheritAttrs: false
        }

        new Vue({el:"#app",components:{ComponentA}})
    </script>

結果:image-20210913150232454

$listener:事件監聽器

<div id="app">
    A{{msg}}
    <my-button :msg="msg" @todo="handleClick"></my-button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            msg: '100'
        },
        methods: {
            handleClick() {
                console.log('點擊事件')
            }
        },
        components: {
            'MyButton': {
                // 這里的v-on='$listeners' 與 v-bind='$attrs'異曲同工
                template: `<div>B<my-radio v-on="$listeners"></my-radio></div>`,
                components: {
                    'MyRadio': {
                        template: `<div @click="$listeners.todo">C</div>`,
                    }
                },
                created() {
                    console.log(this.$listeners)
                }
            },
        }
    })
</script>

注:

  1. 爺孫組件傳遞規則,同$attrs傳遞規則一樣,逐級傳遞

  2. v-bind='$attrs'一樣,直接使用v-on='$listeners'也可以把所有原生事件附加在指定氧元素上,自定義事件由于不知道處罰方式(event = click or blur 等),所以無法觸發,

  3. 觸發指定指定的事件有兩種情況:

    <my-button @todo="handleClick" @click='clickFun'></my-button>
    
    1. 觸發原生事件:此時要在附加的元素上添加@click="$listeners.click"即可添加事件

      注意:click為觸發的事件event

    2. 觸發自定義事件:@click="$listeners.todo",@event='$listeners.點擊事件名'

組件通訊--子傳父

$emit

格式:$emit.("父組件方法名", 傳給父組件的值)

作用:可觸發指定父組件的方法,父組件會監聽子組件,而觸發條件就是$emit

我們開發一個子組件,有時他需要與父級組件溝通,如:通知父組件資料變化、根據子組件給父組件賦值等,

示例:

<div id="app">
    <div :style="{ fontSize: finalSize + 'em' }">
        <!-- 自定義事件 -->
        <component-a @sendvalue='https://www.cnblogs.com/catcode/p/getChildValue' @big='finalSize += 0.5' :key="post.id" :post="post" :finalSize="finalSize"></component-a>
        <div>
            {{childValue}}
        </div>
    </div>
</div>

<script>
    var ComponentB = {
        data() {
            return {
                son: -1
            }
        },
        template: `
            <div>
                <button @click='sendToChild(1)'>點擊傳值1</button>
            </div>
			`,
        methods: {
            sendToChild(value) {
                this.$emit('sendParent', value)
            }
        }
    }

    var ComponentA = {
        props: ['post'],
        components: {
            ComponentB
        },
        template: `
            <div >
           		<button @click="$emit('big')">
           			{{post.name1}}
                </button>
                <component-b @sendParent='sendPatent'></component-b>
                <div v-html="post.content"></div>
             </div>
            `,
        methods: {
            sendPatent(value) {
                this.$emit('sendvalue', value)
            }
        }
    }

    new Vue({
        el: "#app",
        components: {
            ComponentA
        },
        data: {
            post: {
                id: 1,
                name1: "文本變大",
                name2: "子組件傳遞值給父組件",
                age: 13,
                content: "測驗文本"
            },
            obj: "test",
            finalSize: 1,
            childValue: 0
        },
        methods: {
            getChildValue(value) {
                this.childValue = https://www.cnblogs.com/catcode/p/value;
            }
        }
    })
</script>

點擊之前: image-20210913150647153 點擊之后:image-20210913150547021

自定義事件名

不同于組件和 prop,事件名不存在任何自動化的大小寫轉換,而是觸發的事件名需要完全匹配監聽這個事件所用的名稱,例如:我們監聽這個事件

this.$emit('myEvent')

則監聽這個名字的 kebab-case 版本是不會有任何效果的:

<!-- 沒有效果 -->
<my-component v-on:my-event="doSomething"></my-component>

并且 v-on 事件監聽器在 DOM 模板中會被自動轉換為全小寫 (因為 HTML 是大小寫不敏感的),所以 v-on:myEvent 將會變成 v-on:myevent——導致 myEvent 不可能被監聽到,

因此,推薦始終使用 kebab-case 的事件名

父子組件的雙向資料系結

表單

有時候需要在組件上使用v-model,輸入修改子組件的表單input的同時修改父組件的data值,

示例:

<div id="app">
    <my-input v-model="message"></my-input>{{message}}
</div>

<script>
    var MyInput = {
        props:["value"],
        template:`
	<div>
		<input :https://www.cnblogs.com/catcode/p/value="value" @input="$emit('input',$event.target.value)">
    </div>
	`,
        inheritAttrs:false
    }
    new Vue({
        el:"#app",
        components:{
            MyInput
        },
        data:{
            message: "測驗文本"
        }
    })

原理:

v-model本質上可分解為v-bind:value以及@input事件,在組件上使用v-model沒有什么差別,差別在于:使用子組件時,@input事件不再是默認的改變自己this.value = https://www.cnblogs.com/catcode/p/$event.target.value,而是修改呼叫父類的input方法,并將$event.target.value賦值給message

自定義組件的v-model -lyx? todo

一個組件上的 v-model 默認會利用名為 value 的 prop 和名為 input 的事件,但是像單選框、復選框等型別的輸入控制元件可能會將 value attribute 用于不同的目的,model 選項可以用來避免這樣的沖突:

不寫:checked='select' 沒有初始化,且子組件的checked值始終為false,即使復選框被選中也為false

<div id="app2">
    {{select}}
    <my-checkbox v-model='select' :checked='select'></my-checkbox>
    <input type="checkbox" v-model="select" />
</div>
<script>
var MyCheckbox = {
    model: {
        props: "checked",
        event: "change"
    },
    props: {
        checked: Boolean
    },
    template: `
<div><input type='checkbox' :checked='checked' @change='$emit("change",$event.target.checked)'>111</div>
`,
    inheritAttrs: false
}

new Vue({
    el: "#app2",
    data: {
        select: true
    },
    components: {
        MyCheckbox
    },
    inheritAttrs: true
})
</script>
.sync修飾符

在有些情況下,我們可能需要對一個 prop 進行“雙向系結”,但是,子組件不能直接修改父組件的值,通過子父組件的資料雙向系結也可以達到效果,但是十分繁瑣,在Vue中提供了.sync的語法糖 -lyx todo

<div id="app">
    <div style="width: 309px;height: 200px;border: 1px solid black;">
        <my-button :child-show.sync="show" v-show="show"></my-button>
    </div>
</div>

<script>
    let vm = new Vue({
            el: '#app',
            data: {
                show: true
            }
            components: {
                'MyButton': {
                props: ["child-show"],
                template: 
                `<div>
					<button @click.stop='$emit("update:childshow",false)'>
                        test
                    </button>
    			</div>`
                }
            }
        })
</script>

當我們用一個物件同時設定多個prop雙向系結的時候,也可以將這個 .sync 修飾符和 v-bind 配合使用:

<my-button v-bind.sync="doc"></my-button>

插槽

插槽,也就是slot,是組件的一塊HTML模板,這塊模板顯示不顯示、以及怎樣顯示由父組件來決定;但是插槽顯示的位置卻由子組件自身決定

實際上,插槽最核心的兩個問題在這里就點出來了,是顯示不顯示怎樣顯示

具名插槽:帶名字的插槽

實體:

<div id="app">
    <my-button>
        <!-- 等價于<template #parents> -->
        <template v-slot:parents>
			<div>
    			<input value='https://www.cnblogs.com/catcode/p/我是父類的插槽內容'>
            </div>
        </template>
    </my-button>
</div>

<template id="temp1">
	<div>
        <div >
            我是子類的插槽內容
        </div>
        <slot name='parents'></slot>
    </div>
</template>

<script>
    Vue.component('MyButton', {
        template: temp1
    })

    new Vue({el: '#app'})
</script>

效果:image-20210916155749787

  1. 當組件渲染的時候,<slot></slot> 將會直接被替換為組件雙標簽內的內容,插槽內可以包含任何模板代碼,包括 HTML,甚至可以是其他的組件,

  2. <slot></slot>標簽無name屬性時,默認name='default',會渲染出組件起始標簽內的所有未設定v-sloat的節點以及v-sloat:default的節點,這類插槽也稱作:默認插槽/匿名插槽/單個插槽

  3. v-onv-bind 一樣,v-slot 也有縮寫,即把引數之前的所有內容 (v-slot:) 替換為字符 #

    例如 v-slot:header 可以被重寫為 #header

    注:采用#縮寫時,default不可省略

  4. 可用動態引數來達到動態插槽名的效果

  5. slot='header'也可以達到命名效果,但是已被廢棄,了解即可

作用域插槽:帶資料的插槽

作用域插槽,與前一種插槽相比,區別就是攜帶資料

<!-- 匿名插槽 -->
<slot></slot>
<!-- 具名插槽 -->
<slot name="up"></slot>
<!-- 作用域插槽 -->
<slot name="up" :https://www.cnblogs.com/catcode/p/data="data"></slot>

實體:

<div id="app">
    <parent></parent>
</div>


<template id="parents">
    <div >
        <h3>這里是父組件</h3>
        <child>
            <!-- 接收子組件的資料,命名為obj -->
            <template v-slot:default="obj">
                <ul>
                    <!-- 由父組件決定如何顯示接收到的組件 -->
                    <li v-for="p in obj.person">{{p}}</li>
                </ul>
            </template>
        </child>
    </div>
</template>

<template id="childs">
    <div >
        <h3>這里是子組件</h3>
        <!-- 在slot中掛載Attribute,可傳遞資料給插槽 -->
        <slot :person="person"></slot>
    </div>
</template>
<script>
    Vue.component('child', {
        template: childs,
        data() {
            return {
                person: ['張三', '李四', '王五', '趙六']
            }
        }
    })

    Vue.component('parent', {
        template: parents
    })

    new Vue({el: "#app"})

結果:image-20210918135232012,![image-20210918135322223](G:/IDEAProject/A-practice/A-myNotes/我的筆記/2. JavaWeb/2. Vue/.assets/image-20210918135322223.png)

  1. 當為默認插槽時,v-slot:default="obj"等價于v-slot="obj"
  2. 同樣的,具名插槽有slot="default"已被廢棄的,作用域插槽也有slot="default" slot-scope = "person"v-slot:default="person"所取代,slot-scope也可以寫成scope,了解即可,
解構插槽 Prop

作用域插槽的內部作業原理是將你的插槽內容包裹在一個擁有單個引數的函式里:

function (slotProps) {
  // 插槽內容
}

這意味著 v-slot 的值實際上可以是任何能夠作為函式定義中的引數的 JavaScript 運算式,所以也可以傳入具體的插槽 prop,如下:

<child>
    <!--
		這里的{person}直接就取到了子組件傳來的所有資料中person物件
		而原來等價于原來v-slot:default="obj"是以一個物件命名成obj,其資料為子組件傳來的所有資料,因此需要obj.person
	-->
    <template v-slot:default="{ person }">
        <ul>
            <!-- 由父組件決定如何顯示接收到的組件 -->
            <li v-for="p in person">{{p}}</li>
        </ul>
    </template>
</child>

這樣可以使模板更簡潔,尤其是在該插槽提供了多個 prop 的時候,它同樣開啟了 prop 重命名等其它可能,例如將 person 重命名為 user

<child>
    <template v-slot="{person : user}">
        <ul>
            <li v-for="p in user">{{p}}</li>
        </ul>
    </template>
</child>

甚至可以定義后備內容,用于展示當要顯示的資料為undefined的時候

<child>
    <template v-slot="{msg = "暫無可顯示資訊"}">
        <div>
            <!-- 當子組件插槽有msg屬性值時,顯示屬性值,沒有屬性值時,顯示"暫無可顯示資訊" -->
            { msg }
        </div>  
    </template>
</child>

后備內容

有時為一個插槽設定具體的后備 (也就是默認的) 內容是很有用的,它只會在沒有提供內容的時候被渲染,

<div id="app">
    <my-button>
        <template #slot1>
            <div>
                <input class='parent' value='https://www.cnblogs.com/catcode/p/我是父類的插槽內容1'>
            </div>
		</template>
        <template>
            <div>
                <input class='parent' value='https://www.cnblogs.com/catcode/p/我是父類的插槽內容2'>
            </div>
		</template>
    </my-button>
</div>

<template id="temp1">
	<div>
        <slot name='slot1'>一個slot='sloat1'的插槽都沒有</slot>
    	<slot name='slot2'>一個slot='sloat2'的插槽都沒有</slot>
    </div>
</template>

<script>
    Vue.component('MyButton', {
        template: temp1
    })

    new Vue({el: '#app'})
</script>

結果:image-20210916142141990

注:后備內容也同樣支持HTML語法

后續內容待更新,,,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/301183.html

標籤:HTML5

上一篇:基于Echarts+HTML5可視化資料大屏展示—大資料管理平臺中心

下一篇:JavaScript遞回查詢省市區資料

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more