1. 程式人生 > 實用技巧 >03vue元件化

03vue元件化

vue元件

1.vue元件三板斧

1.建立元件構造器物件
2.註冊元件
3.使用元件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<!-- 3.使用元件 -->
			<my-cpn></my-cpn>
			<h1>***</h1>
			<cpnc></cpnc>
			<cpnc2></cpnc2>

		</div>

		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
			//1.建立元件構造器的物件
			const cpnc = Vue.extend({
						template: `
				        <div>
				          <h2>標題:我是cpnc元件</h2>
				          <p>內容1...</p>
				          <p>內容2...</p>
				        </div>`,
			})
			
			const cpnc2 = Vue.extend({
						template: `
				        <div>
				          <h2>我是cpnc2元件</h2>
				          <p>內容1...我是一個區域性元件</p>
				          <p>內容2...</p>
				        </div>`,
			})
			
			//2.全域性註冊元件
			Vue.component('my-cpn', cpnc)
			
			const app = new Vue({
				el:"#app",
				components:{
					//區域性元件建立
					cpnc:cpnc,
					cpnc2:cpnc2,
				}
			})
		</script>

	</body>
</html>

元件是可複用的 Vue 例項,且帶有一個名字:在這個例子中是 my-cpn。我們可以在一個通過 new Vue 建立的 Vue 根例項中,把這個元件作為自定義元素來使用: <my-cpn></my-cpn>

2.vue 全域性元件和區域性元件

全域性元件,可以在多個vue例項中使用,類似於全域性變數。

使用Vue.component('my-cpn', cpnc)方式註冊,直接使用``呼叫。my-cpn是全域性元件的名字,cpnc是定義的元件物件。

區域性元件,只能在當前vue例項掛載的物件中使用,類似於區域性變數,有塊級作用域。

使用方式與全域性變數一樣,直接使用<cpnc></cpnc>

呼叫。cpnc:cpnc第一個cpnc是給元件命名的名字,第二個是定義的元件物件。如果倆個同名也可以直接使用es6語法:

components:{//區域性元件建立
        cpnc
}
// 註冊全域性元件(全域性元件,可以在多個vue例項中使用)
Vue.component('my-cpn', cpnc)

// 註冊區域性元件,components:{cpnc:cpnc}
const app = new Vue({
      el:"#app",
      components:{//區域性元件建立
        cpnc:cpnc
      }
    })

3.vue 父元件和子元件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>父子元件</title>
	</head>
	<body>
		<div id="app">
			<!-- 使用元件 -->
			<cpn2></cpn2>
		</div>
		
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
			//1.建立元件構造器物件
			const cpn1 = Vue.extend({
				template: `
					<div>
						<h2>我是子元件1</h2>
						<p>哈哈</p>
					</div>`,
			})

			//2.構建父元件,並使用子元件cpn1
			const cpn2 = Vue.extend({
				template: `
			 		<div>
			 			<h2>我是父元件</h2>
			 			<p>哈哈<br>******</p>
						<cpn1></cpn1>
			 		</div>`,
				components:{
				        cpn1:cpn1
				      }
			})
			
			const app = new Vue({
				el:"#app",
				components:{ //建立區域性元件
					cpn2:cpn2,
				}
			})
		</script>
	</body>
</html>

上述程式碼中定義了兩個元件物件cpn1cpn2,在元件cpn2中使用區域性元件註冊了cpn1,並在template中使用了註冊的cpn1,然後在vue例項中使用註冊了局部元件cpn2,在vue例項掛載的div中呼叫了cpn2cpn2cpn1形成父子元件關係。

注意:元件就是一個vue例項,vue例項的屬性,元件也可以有,例如data、methods、computed等。

4、註冊元件的語法糖寫法

<script>
    // 1.註冊全域性元件語法糖
    Vue.component('cpn1', {
      template:`
        <div>
          <h2>全域性元件語法糖</h2>
          <p>全域性元件語法糖</p>
        </div>`
    })

    const app = new Vue({
      el:"#app",
      components:{//區域性元件建立
        cpn2:{
          template:`
        <div>
          <h2>區域性元件語法糖</h2>
          <p>區域性元件語法糖</p>
        </div>`
        }
      }
    })
  </script>

註冊元件時候可以不例項化元件物件,直接在註冊的時候例項化。{}就是一個元件物件。

5. 元件模板的分離寫法

5.1 script標籤

使用script標籤定義元件的模板,script標籤注意型別是text/x-template

<!-- 1.script標籤注意型別是text/x-template -->
  <script type="text/x-template" id="cpn1">
    <div>
        <h2>元件模板的分離寫法</h2>
        <p>script標籤注意型別是text/x-template</p>
      </div>
  </script>

5.2 template標籤

​ 使用template標籤,將內容寫在標籤內。

 <!-- 2.template標籤 -->
  <template id="cpn2">
    <div>
      <h2>元件模板的分離寫法</h2>
      <p>template標籤</p>
    </div>
  </template>

使用分離的模板 ,使用template:'#cpn1'

const app = new Vue({
      el: "#app",
      components: { //區域性元件建立
        cpn1:{
          template:'#cpn1'
        },
        cpn2: {
          template: '#cpn2'
        }
      }
    })
  </script>
  
//全域性元件建立
Vue.component('cpn1', {
      template:'#cpn1',      
    })

例子:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<!-- 3.使用元件 -->
			<cpn1></cpn1>
			<cpn2></cpn2>
		</div>
		
		<!-- 1.script標籤注意型別是text/x-template -->
		<script type="text/x-template" id="cpn1">
		  <div>
		      <h2>元件模板的分離寫法</h2>
		      <p>script標籤注意型別是text/x-template</p>
		    </div>
		</script>
		 
		 <template id="cpn2">
		    <div>
		      <h2>元件模板的分離寫法</h2>
		      <p>template標籤</p>
		    </div>
		  </template>
		  
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
			Vue.component('cpn1', {
				template:'#cpn1',      
			})
			
			const app = new Vue({
			      el:"#app",
			      components:{//區域性元件建立
			        cpn2:{
						template:'#cpn2'
					},
			       }
			    })
		</script>
	</body>
</html>

6. 元件的資料

6.1資料存放問題

前面說過vue元件就是一個vue例項,相應的vue元件也有data屬性來存放資料。

放在主app下面是沒有效果的。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
		    <cpn1></cpn1>
		 </div>
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		 <script>
		    const app = new Vue({
		      el: "#app",
		      components: { //區域性元件建立
		        cpn1:{
		          template:'<div>{{msg}}</div>',
		          data(){
		            return {
		              msg:"元件的資料存放必須要是一個函式"
		            }
		          }
		        }
		      }
		    })
		  </script>	
	</body>
</html>

template中使用元件內部的資料msg

6.2 元件data為啥必須是個函式

元件的思想是複用,定義元件當然是把通用的公共的東西抽出來複用。

計算器1

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
			<cpn1></cpn1>
			<cpn1></cpn1>
		</div>

		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<template id="cpn1">
			<div>
				當前計數:{{ count }}<br>
				<button @click="decrement">-</button>
				<button @click="increment">+</button>
			</div>
		</template>

		<script>
			const app = new Vue({
						el: "#app",
						components: { //區域性元件建立
							cpn1: {
								template: '#cpn1',
								data() {
									return{
										count:0,
									}
								},
								methods:{
									increment(){
										this.count++
									},
									decrement(){
										this.count--
									}
								}
							}
						}
					})
		</script>
	</body>
</html>

計算器二

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
			<cpn1></cpn1>
			<cpn1></cpn1>
		</div>

		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<template id="cpn1">
			<div>
				當前計數:{{ count }}<br>
				<button @click="count--">-</button>
				<button @click="count++">+</button>
			</div>
		</template>

		<script>
			const app = new Vue({
						el: "#app",
						components: { //區域性元件建立
							cpn1: {
								template: '#cpn1',
								data() {
									return{
										count:0,
									}
								}
							}
						}
					})
		</script>
	</body>
</html>

計數器1和計數器2本質是一樣的,實現計數器的複用,各個計數器互不干擾。

計數器三,元件共用一個數字

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<cpn1></cpn1>
			<cpn1></cpn1>
			<cpn1></cpn1>
		</div>

		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<template id="cpn1">
			<div>
				當前計數:{{ count }}<br>
				<button @click="count--">-</button>
				<button @click="count++">+</button>
			</div>
		</template>

		<script>
			const obj={
				count:0
			}
			const app = new Vue({
						el: "#app",
						components: { //區域性元件建立
							cpn1: {
								template: '#cpn1',
								data() {
										return obj;
								}
							}
						}
					})
		</script>
	</body>
</html>

計數器3不使用函式data,好像共用一個count屬性,而使用函式的data的count是各自用各自的,像區域性變數一樣有塊級作用域,這個塊級就是vue元件的作用域。

我們在複用元件的時候肯定希望,各自元件用各自的變數,如果確實需要都用一樣的,可以全域性元件註冊,也可以是用vuex來進行狀態管理。

7、父子元件通訊之父傳子

使用props屬性,父元件向子元件傳遞資料

靜態寫法:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
		    <child message="hello!父傳子靜態寫法"></child>
		</div>
		 
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
		// 註冊
		Vue.component('child', {
		  // 宣告 props
		  props: ['message'],
		  // 同樣也可以在 vm 例項中像 "this.message" 這樣使用
		  template: '<span>{{ message }}</span>'
		})
		// 建立根例項
		new Vue({
		  el: '#app'
		})
		</script>
	</body>
</html>
動態v-bind props 寫法
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
		    <div>
		      <input v-model="parentMsg">
		      <br>
		      <child v-bind:message="parentMsg"></child>
		    </div>
		</div>
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
		// 註冊
		Vue.component('child', {
		  // 宣告 props
		  props: ['message'],
		  // 同樣也可以在 vm 例項中像 "this.message" 這樣使用
		  template: '<span>{{ message }}</span>'
		})
		// 建立根例項
		new Vue({
		  el: '#app',
		  data: {
		    parentMsg: '父元件內容'
		  }
		})
		</script>
	</body>
</html>

props 屬性

Vue.component('my-component', {
  props: {
    // 基礎的型別檢查 (`null` 和 `undefined` 會通過任何型別驗證)
    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
      }
    }
  }
})

綜合使用

  <div id="app">
    <cpn :cMovies="movies" :cMessage="message"></cpn>
  </div>
  <template id="cpn">
    <div>
      <ul>
        <li v-for="(item, index) in cmovies" :key="index">{{item}}</li>
      </ul>
      <h2>{{cmessage}}</h2>
    </div>
  </template>
  <script src="../js/vue.js"></script>

  <script>
    function Person(firstName,lastName) {
      this.firstName = firstName
      this.lastName = lastName
    }
    // 父傳子:props
    const cpn = {
      template: "#cpn",
      // props: ['cmovies', 'cmessage'],//陣列寫法
      props: { //物件寫法
        // 1.型別限制(多個類使用陣列)
        // cmovies:Array,
        // cmessage:String,
        // cmessage:['String','Number'],
        // 2.提供一些預設值,以及必傳值
        cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用元件必傳值
        },
        //型別是Object/Array,預設值必須是一個函式
        cmovies: {
          type: Array,
          default () {
            return [1, 2, 3, 4]
          }
        },
        // 3.自定義驗證函式
        // vaildator: function (value) {
        //   //這個傳遞的值必須匹配下列字串中的一個
        //   return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
        // }
        // 4.自定義型別
        // cmessage:Person,
      },
      data() {
        return {
        }
      },
      methods: {

      },
    };
    const app = new Vue({
      el: "#app",
      data: {
        message: "你好",
        movies: ["復仇者聯盟", "鋼鐵俠", "星際穿越", "哪吒傳奇"]
      },
      components: {
        cpn
      }
    })
  </script>

補充:props的駝峰標識

​ v-bind是 不支援使用駝峰標識的,例如cUser要改成c-User

<div id="app">
    <!-- v-bind不支援駝峰 :cUser改成 :c-User-->
    <!-- <cpn :cUser="user"></cpn> -->
    <cpn :c-User="user"></cpn>
    <cpn :cuser="user" ></cpn>
  </div>
  <template id="cpn">
    <div>
      <!-- 使用駝峰 -->
      <h2>{{cUser}}</h2>
      <!-- 不使用 -->
      <h2>{{cuser}}</h2>
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>
    // 父傳子:props
    const cpn = {
      template: "#cpn",
      props: { //物件寫法
        //駝峰
        cUser:Object,
        //未使用駝峰
        cuser:Object
      },
      data() {return {}},
      methods: {},
    };
    const app = new Vue({
      el: "#app",
      data: {
        user:{
          name:'zzz',
          age:18,
          height:175
        }
      },
      components: {
        cpn
      }
    })
  </script>

8、子傳父

子元件向父元件傳值,使用自定義事件$emit
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!-- 父元件 -->
		<div id="app">
			<!-- 不寫引數預設傳遞btnClick的item -->
			<cpn @itemclick="cpnClcik"></cpn>

		</div>

		<!-- 子元件模板 -->
		<template id="cpn">
			<div>
				<button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
			</div>
		</template>
		<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
		<script>
			//子元件
			const cpn = {
				template: "#cpn",
				data() {
					return {
						categoties: [{
								id: 'aaa',
								name: '熱門推薦'
							},
							{
								id: 'bbb',
								name: '手機數碼'
							},
							{
								id: 'ccc',
								name: '家用家電'
							},
							{
								id: 'ddd',
								name: '電腦辦公'
							},
						]
					}
				},
				methods: {
					btnClick(item) {
						this.$emit('itemclick', item)
					}
				},
			};
			//父元件
			const app = new Vue({
				el: "#app",
				data() {
					return {
					}
				},
				methods: {
					cpnClcik(item) {
						console.log('cpnClick' + item.name);
					}
				},
				components: {
					cpn
				},
			})
		</script>
	</body>
</html>