组件化
组件化概念:
  注:组件化和模块化是有区别的
组件的基本使用步骤
 组件初步接触:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app1">
<h2>h2-context</h2>
<p>p-context</p>
<my-cpn></my-cpn>
</div>
<my-cpn></my-cpn>
<div id="app2">
<my-cpn></my-cpn>
<cpn></cpn>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpnConstructor = Vue.extend({
template: `
<div>
<h2>h2-context</h2>
<p>p-context</p>
</div>`
})
Vue.component('my-cpn', cpnConstructor);
const app1 = new Vue({
el: "#app1",
data: {
},
methods: {
},
computed: {
},
filters: {
}
})
const app2 = new Vue({
el: "#app2",
components:{
cpn: cpnConstructor
}
})
</script>
</body>
</html>
父子组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpnC1 = Vue.extend({
template: `
<div>
<h2>cpnC1-h2-context</h2>
<p>p-context</p>
</div>`
})
const cpnC2 = Vue.extend({
template: `
<div>
<h2>cpnC2-h2-context</h2>
<p>p-context</p>
<cpn1></cpn1>
</div>
<cpn1></cpn1>`
,
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: "#app",
components: {
cpn2: cpnC2
}
})
</script>
</body>
</html>
 组件语法糖形式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
<cpn3></cpn3>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = Vue.extend({
template: `
<div>
<h2>h2-context</h2>
<p>p-context</p>
</div>`
});
Vue.component('cpn1', cpn1);
const Feature = Vue.component('cpn2', {
template: `
<div>
<h2>h2-context</h2>
<p>p-context</p>
</div>`
});
const app = new Vue({
el: "#app",
components: {
cpn3: {
template: `
<div>
<h2>h2-context</h2>
<p>p-context</p>
</div>`
}
}
})
</script>
</body>
</html>
理论上说,js代码里是不该出现html代码的,这样会导致混乱,因此,组件中的template应该分离出去:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
<cpn3></cpn3>
</div>
<script type="text/x-template" id="cpn1">
<div>
<h4>1-h4-context</h4>
</div>
</script>
<template id="cpn2">
<div>
<h4>2-h4-context</h4>
</div>
</template>
<template id="cpn3">
<div>
<h4>3-h4-context</h4>
<cpn1></cpn1>
</div>
<cpn2></cpn2>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('cpn1', {
template: "#cpn1"
});
Vue.component('cpn2', {
template: "#cpn2"
});
Vue.component('cpn3', {
template: "#cpn3"
});
const app = new Vue({
el: "#app",
components: {
}
})
</script>
</body>
</html>
上面只是简单分离写法,在cli脚手架部分有最常规的分离写法。
组件数据
关于组件的data必须是个函数的解释:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
<cpn2></cpn2>
<cpn3></cpn3>
<cpn3></cpn3>
</div>
<template id="cpn1">
<div>
<h4>cpn1-h4-context</h4>
<p>{{message}}</p>
</div>
</template>
<template id="cpn2">
<div>
<h4>counter:{{counter}}</h4>
<button @click="increase()">+</button>
<button @click="decrease()">-</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('cpn1', {
template: "#cpn1",
data() {
return {
message: 'message-context'
}
}
});
Vue.component('cpn2', {
template: "#cpn2",
data() {
return {
counter: 0
}
},
methods: {
increase() {
this.counter++
},
decrease() {
this.counter--
}
}
});
const obj = {
counter: 0
}
Vue.component('cpn3', {
template: "#cpn2",
data() {
return obj
},
methods: {
increase() {
this.counter++
},
decrease() {
this.counter--
}
}
});
const app = new Vue({
el: "#app",
components: {
},
})
</script>
</body>
</html>
父子组件通信:
就目前对知识的理解来看:由于组件之间往往是嵌套的,实际开发中,一般是最外层的大组件发url请求到数据,然后将数据通过父子组件的通信传递给子组件,子组件再传给孙子组件,一层一层传下去;而且由于全部数据的信息量庞大,一般会把整体框架类的数据传过来,图片视频什么的开销大的数据再慢慢传。 如何实现:
- 通过props向子组件传递数据
- 通过事件向父组件发送消息

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
<cpn v-bind:cmovies="movies"></cpn>
<cpn1 :movies="movies"></cpn1>
<cpn1 ></cpn1>
</div>
<template id="cpn">
<div>
<p v-for="movies in cmovies">{{movies}}</p>
<h4>{{cmessage}}</h4>
</div>
</template>
<template id="cpn1">
<div>
<select v-model="selectedmovie">
<option v-for="movie in movies" :value="movie">{{movie}}</option>
</select>
<h4>{{selectedmovie}}</h4>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const Feature = Vue.component('cpn1',{
template: "#cpn1",
props:{
movies: {
type: Array,
default(){
return ['one','two','three']
}
}
},
data() {
return {
selectedmovie: '',
};
},
});
const app = new Vue({
el: "#app",
data: {
message: 'message-context',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn: {
template: "#cpn",
props: {
cmovies: Array,
cmessage: {
type: String,
default: 'aaaaaaa',
required: true
}
}
}
}
})
</script>
</body>
</html>

驼峰标识的问题:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1 :c-info="info"></cpn1>
</div>
<template id="cpn1">
<h2>{{cInfo}}</h2>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
Vue.component('cpn1', {
template: "#cpn1"
});
const app = new Vue({
el: "#app",
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
},
components: {
cpn1: {
template: "#cpn1",
props: {
cInfo: {
type: Object,
default(){
return {}
}
}
}
}
}
})
</script>
</body>
</html>
组件通信:子传父

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1 @item-click="handler"></cpn1>
<cpn2 :selected-id="selectedId"></cpn2>
</div>
<template id="cpn1">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<template id="cpn2">
<div>
<h4>id:{{selectedId}}</h4>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: '#cpn1',
data() {
return {
categories: [
{ id: 001, name: '热门推荐' },
{ id: 002, name: '手机数码' },
{ id: 003, name: '家用家电' },
{ id: 004, name: '电脑办公' }
]
}
},
methods: {
btnClick(item) {
console.log('btnClick', item)
this.$emit('item-click', item);
},
},
}
const cpn2 = {
template: '#cpn2',
data() {
return {
}
},
props: {
selectedId: {
type: Number
}
}
}
const app = new Vue({
el: '#app',
data() {
return {
selectedId: 001,
};
},
components: {
cpn1,
cpn2
},
methods: {
handler(item) {
console.log('handler', item)
this.selectedId = item.id
}
}
});
</script>
</body>
</html>
后面要用的父子组件的使用演示: 如果是像上面那样每个组件都有个template\data\methods\props之类的,组件多了太乱。 单个组件写法:  组件里用别的组件就import导入、再在components里注册 
父子组件通信+双向绑定案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1 :number1="num1" :number2="num2" @datanumber1change="dn1c" @datanumber2change="dn2c"></cpn1>
</div>
<template id="cpn1">
<div>
<input type="text" :value="datanumber1" @input="inputnumber1">
<h4>number1:{{number1}}</h4>
<h4>datanumber1:{{datanumber1}}</h4>
<input type="text" :value="datanumber2" @input="inputnumber2">
<h4>number2:{{number2}}</h4>
<h4>datanumber2:{{datanumber2}}</h4>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data() {
return {
num1: 1,
num2: 2
};
},
methods: {
dn1c(value) {
this.num1 = parseFloat(value)
},
dn2c(value) {
this.num2 = parseFloat(value)
}
},
components: {
cpn1: {
template: "#cpn1",
data() {
return {
datanumber1: this.number1,
datanumber2: this.number2
};
},
props: {
number1: {
type: Number
},
number2: {
type: Number
}
},
methods: {
inputnumber1(event) {
this.datanumber1 = event.target.value
this.$emit('datanumber1change', this.datanumber1);
this.datanumber2=this.datanumber1*10
},
inputnumber2(event) {
this.datanumber2 = event.target.value
this.$emit('datanumber2change', this.datanumber2);
this.datanumber1=this.datanumber2/10
},
}
}
}
})
</script>
</body>
</html>
上面这个案例有点像汇率转化:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
</div>
<template id="cpn1">
<div>
汇率:<input type="number" :value="exchange" @input="inputexchange">
货币1:<input type="number" :value="money1" @input="inputmoney1">
货币2:<input type="number" :value="money2" @input="inputmoney2">
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
exchange: 6.4,
money1: 0,
money2: 0
};
},
methods: {
inputmoney1(event) {
this.money1=event.target.value
this.money2=this.money1*this.exchange
},
inputmoney2(event) {
this.money2=event.target.value
this.money1=this.money2/this.exchange
},
inputexchange(event){
this.exchange=event.target.value
this.money2=this.money1*this.exchange
}
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
}
})
</script>
</body>
</html>
父子组件的访问:

父访问子(父直接操作子,调用子的方法)$children,$refs.组件ref属性名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn1 ref="aaa"></cpn1>
<button @click="btnClick1">this.$children</button>
<button @click="btnClick2">this.$refs</button>
</div>
<template id="cpn1">
<div>
<h4>cpn1</h4>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
};
},
methods: {
showMessage() {
console.log('showMessage');
},
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
},
methods:{
btnClick1(){
console.log(this.$children);
this.$children[0].showMessage();
},
btnClick2(){
console.log(this.$refs);
this.$refs.aaa.showMessage();
}
}
})
</script>
</body>
</html>
子访问父:$parent,$root
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
</div>
<template id="cpn1">
<div>
<button @click="btnClick1">this.$parent</button>
<button @click="btnClick2">this.$root</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
};
},
methods: {
btnClick1() {
console.log(this.$parent);
this.$parent.parentMethod();
},
btnClick2() {
console.log(this.$root);
this.$root.parentMethod();
}
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
},
methods:{
parentMethod(){
console.log('parentMethod()');
}
}
})
</script>
</body>
</html>
slot插槽:
 比如下图的例子,都有个导航栏,因此导航栏可以抽成个复用的组件,但这个导航栏在不同情境下略有区别,因此,可以开放成插槽,需要什么子组件再添加,提高了组件的扩展性。 
slot的基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1>
<button>button</button>
</cpn1>
<cpn1>
<span>span</span>
</cpn1>
<cpn1></cpn1>
</div>
<template id="cpn1">
<div>
<h4>cpn1</h4>
<slot><button>button</button></slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
};
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
}
})
</script>
</body>
</html>
具名插槽
多个插槽的话会造成混乱,所以使用具名插槽一一对应。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1><button slot="slot1">button</button></cpn1>
</div>
<template id="cpn1">
<div>
<h4>cpn1</h4>
<slot name="slot1"><span>slot1</span></slot>
<slot name="slot2"><span>slot2</span></slot>
<slot name="slot3"><span>slot3</span></slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
};
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
}
})
</script>
</body>
</html>
编译作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1 v-show="isShow"></cpn1>
</div>
<template id="cpn1">
<div>
<h4>cpn1</h4>
<button v-show="isShow"></button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
isShow: flase
};
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
},
data() {
return {
isShow: true
};
},
})
</script>
</body>
</html>
作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn1>
<template slot-scope="slot">
<span v-for="item in slot.data">{{item}}-</span>
</template>
</cpn1>
<cpn1>
<template slot-scope="slot">
<span>{{slot.data.join('*')}}</span>
</template>
</cpn1>
</div>
<template id="cpn1">
<div>
<slot :data="this.pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const cpn1 = {
template: "#cpn1",
data() {
return {
pLanguages: ['javaScript', 'Python', 'Swift', 'Go', "C++"]
};
},
}
const app = new Vue({
el: "#app",
components: {
cpn1
}
})
</script>
</body>
</html>
|