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>
上述程式碼中定義了兩個元件物件cpn1
和cpn2
,在元件cpn2
中使用區域性元件註冊了cpn1
,並在template
中使用了註冊的cpn1
,然後在vue例項中使用註冊了局部元件cpn2
,在vue例項掛載的div中呼叫了cpn2
,cpn2
與cpn1
形成父子元件關係。
注意:元件就是一個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>