template(模板)
- 如果有template属性,当初始化实例时,会把template的属性值编译并替换掉App元素;
- template中只能有一个根元素,如果有两个,会默认取第一个作为根元素
- template中可以获取到data中数据和methods中的方法
<body>
<div id="app">
<span>{{msg}}</span>
</div>
<div id="app1"></div>
<template id="first">
<div>{{msg}}<div @click='fn'>888</div></div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 1. 如果有template属性,当初始化实例时,会把template的属性值编译并替换掉App元素;
// 2. template中只能有一个根元素,如果有两个,会默认取第一个作为根元素
// 3.template中可以获取到data中数据和methods中的方法
// let vm = new Vue({
// el:"#app",
// data:{
// msg:"hello",
// flag:false
// },
// methods:{
// fn(){
// console.log(888);
// }
// },
// template:"<div>{{msg}}<div @click='fn' v-if='flag'>888</div></div>"
// })
let vm = new Vue({
el:"#app",
data:{
msg:"hello",
flag:false
},
methods:{
fn(){
console.log(888);
}
},
template:"#first"
})
let vm1 = new Vue({
el:"#app1",
data:{
msg:"zfpx",
flag:false
},
methods:{
fn(){
console.log(888);
}
},
template:"#first"
});
// 抽离公共部分的结构;
</script>
</body>
组件
组件化思想
单页面应用:框架一般适用于开发大型的单页面应用;整个项目只有一个页面,其他页面都是通过路由方式进行切换的;
组件:把相同功能的代码封装成一个组件,当想再次适用这个结构时,直接调用这个组件就可以,体现了代码的复用性。
如何创建组件:
- 全局组件
- 局部组件
- VUE中的每一个组件都是一个小实例;每一个组件都有自己独立的生命周期
全局组件
在Vue的属性上一个component函数,执行可以创建一个组件,接收两个参数,第一个是组件的名字,第二个是组件的信息对象
注册一个全局组件,在页面中组件名和闭合标签的方式使用该组件:接收两个参数,第一个是组件名,第二个就是组件的信息;
VUE中的每一个组件都是一个小实例;
全局组件可以在任何的Vm实例中使用,每一个组件都相当于一个VUE的实例,当调用这个组件时,就会生成Vue的生命周期;每当使用一次,就会执行一遍生命周期钩子函数
- 组件名
- 组件的名字-单词的首字母可以大写
- 页面中的组件的名字不支持驼峰,如果是驼峰,需要加-;
- 不能使用内置的标签名作为组件名称
<body>
<div id="app">
<!-- <ZFPX></ZFPX> -->
<hand-some></hand-some>
<ul></ul>
</div>
<div id="app1">
<!-- <zfpx></zfpx> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 组件化思想
// 单页面应用:框架一般适用于开发大型的单页面应用;整个项目只有一个页面,其他页面都是通过路由方式进行切换的;
// 组件:把相同功能的代码封装成一个组件,当想再次适用这个结构时,直接调用这个组件就可以,体现了代码的复用性。
// 如何创建组件: 1. 全局组件 2. 局部组件
// 在Vue的属性上一个component函数,执行可以创建一个组件,接收两个参数,第一个是组件的名字,第二个是组件的信息对象
// 注册一个全局组件,在页面中组件名和闭合标签的方式使用该组件:接收两个参数,第一个是组件名,第二个就是组件的信息;
// VUE中的每一个组件都是一个小实例;
// 全局组件可以在任何的Vm实例中使用,每一个组件都相当于一个VUE的实例,当调用这个组件时,就会生成Vue的生命周期;每当使用一次,就会执行一遍生命周期钩子函数;
// 1. 组件的名字-单词的首字母可以大写
// 2. 页面中的组件的名字不支持驼峰,如果是驼峰,需要加-;
// 3. 不能使用内置的标签名作为组件名称
Vue.component("handsome",{
data:function(){
return {a:"今天有雨"}
},
created(){
console.log(1);
},
methods:{
},
template:"<div>{{a}}</div>"
});
let vm = new Vue({
el:"#app",
data:{
},
methods:{
}
});
let vm1 = new Vue({
el:"#app1",
data:{
},
methods:{
}
});
</script>
</body>
局部组件
1.创建组件
2.局部注册组件
3.在组件定义中,data必须返回一个函数,并且函数返回一个对象,不能直接写对象
template: 模板 components : 组件
局部组件:只能在注册的vue实例中使用该组件,不能跨组件使用;但是可以另一个vue实例中再注册一次;
<body>
<div id="app">
<son></son>
</div>
<div id="app1">
<span>111</span>
<son></son>
</div>
<template id="son">
<div @click='fn'>{{msg}}</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 1.创建组件
// 2.局部注册组件
// 在组件定义中,data必须返回一个函数,并且函数返回一个对象,不能直接写对象
// template: 模板 components : 组件
// 局部组件:只能在注册的vue实例中使用该组件,不能跨组件使用;但是可以另一个vue实例中再注册一次;
let son = {
data(){
return {
msg:"hello"
}
},
methods:{
fn(){
console.log(this);// this 就指向了当前组件的实例
console.log("好好学习");
}
},
template:"#son"
}
// template: 模板 components : 组件
// 局部组件:只能在注册的vue实例中使用该组件,不能跨组件使用;但是可以另一个vue实例中再注册一次;
let vm = new Vue({
el:"#app",
data:{
},
methods:{
},
components:{// 注册局部组件
// 这个属性名和页面使用组件时名字一样;
//child:son
son
}
});
let vm1 = new Vue({
el:"#app1",
data:{
},
methods:{
},
components:{// 注册局部组件
son:son
}
});
</script>
</body>
组件嵌套
组件嵌套: 组件和组件之间可以嵌套,但是子组件需要在父组件的components的属性中进行注册,才能在父组件中使用,在html结构中不能嵌套;
每一个组件都有自己独立的生命周期
先解析父组件,然后再解析子组件;
在mounted的钩子函数中,child先执行;挂载到parent组件上,最后parent一次性挂载到真实的DOM上;
<body>
<div id="app">
<parent>
</parent>
</div>
<div id="app1">
<parent></parent>
</div>
<template id="parent">
<div>
<child></child>
父组件1000
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 1.创建组件
// 2. 注册组件
// 3. 使用组件: 用闭合标签的方式写在app结构中
let child = {
data(){
return {}
},
beforeMount(){
console.log("child");
},
methods:{
},
template:"<div>子组件100</div>"
};
// 组件嵌套: 组件和组件之间可以嵌套,但是子组件需要在父组件的components的属性中进行注册,才能在父组件中使用,在html结构中不能嵌套;
// 每一个组件都有自己独立的生命周期
// 先解析父组件,然后再解析子组件;
// 在mounted的钩子函数中,child先执行;挂载到parent组件上,最后parent一次性挂载到真实的DOM上;
let parent ={
data(){
return {}
},
beforeMount(){
console.log("parent");
},
components:{
child
},
template:"#parent"
}
Vue.component("parent",{
template:"<div>111</div>"
})
let vm = new Vue({
el:"#app",
data:{
},
methods:{
},
components:{
parent
}
});
let vm1 = new Vue({
el:"#app1",
data:{
},
methods:{
}
});
</script>
</body>
数据传递–>VueX(store)
组件: 组件就是抽离的公共的部分,这个公共的部分可以有自己独立的html,css,js
实现复用:可复用,可维护
数据传递: vue中数据传递有哪些方式?
Vue是单项数据流,当父组件的数据发生更改,如果子组件也使用了父组件的数据,那么子组件也会随着更新;如果改变子组件这个数据,那么父组件不能更改;
- 父传子(props) props props=[“a”] :a=“num”
- 子传父(自定义事件)
@change=‘num’ this.$emit(“change”,1000)
.sync修饰符 :a.sync this.$emit(“updata:a”,2000)- 兄弟组件(eventBus)
let eventBus=new Vue eventBus.$on(事件池,方法) eventBus.$emit(事件池)- VueX(store) 把每一个stroe注入到各个组件里 [state,mutations,actions,modules,getters]
vue的单向数据流(父=>子)
当父组件中的数据通过某些方法发生改变,更改了自己的数据,子组件如果使用到这个数据也会随着发生更改,父组件的数据流向了子组件,子组件也更新,这就是组件之间的数据传递的单向数据流
父组件==>子组件,但是子组件变化,不能引发父组件变化;
<body>
<div id="app">
<span>{{num}}</span>
<button @click="add">赞</button>
<child :m="num"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 当父组件中的数据通过某些方法发生改变,更改了自己的数据,
子组件如果使用到这个数据也会随着发生更改,父组件的数据流向了子组件,子组件也更新,
这就是组件之间的数据传递的单向数据流
// 父组件==>子组件,但是子组件变化,不能引发父组件变化;
let child = {
data() {
return {}
},
methods: {
fn() {
// this ==> 组件的实例
//组件的methods中的方法中的this,也指向这个组件实例
//console.log(this.m);
this.m--;
}
},
beforeUpdate() {
console.log("child beforeUpdate");
},
updated() {
console.log("child updated");
},
props: ["m"], // props继承过来的属性也会放在这个组件的实例上
template: "<div>{{m}}<button @click='fn'>反对</button></div>"
};
let vm = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
add() {
this.num++;
}
},
components: {
child
},
beforeUpdate() {
console.log("parent beforeUpdate");
},
updated() {
console.log("parent updated");
},
});
// 父组件数据更新时:
//parent beforeUpdate==child beforeUpdate ==> child updated==> parent updated
</script>
</body>
组件之间数据传递(父=>子)
数组传递
- 把父组件的数据以动态属性的方式放在当前子组件的行间属性上
- 在子组件中用props接收到这个属性 (数组、对象)
- 在子组件取值使用动态的属性名取值
<body>
<div id="app">
<input type="text" v-model="msg">
<!-- <div :c="msg"></div> -->
<child :a="msg"></child>
<child :b="book"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// child 是Vm这个vue实例的子组件;判断组件在哪个实例中注册,就是谁的子组件;
// 数组传递
// 1. 把父组件的数据以动态属性的方式放在当前子组件的行间属性上
// 2. 在子组件中用props接收到这个属性 (数组、对象)
// 3. 在子组件取值使用动态的属性名取值
let child = {
data(){
return {
// msg:"abc"
}
},
props:["a","b"],// props:和行间属性名的属性名保持一致
template:"<div>{{a}}{{b}}</div>"// 取值和props保持一致
};
let vm = new Vue({
el:"#app",
data:{
msg:"hello",
book:"vue.js"
},
methods:{
},
components:{
child
}
});
</script>
</body>
props的验证
props: {
a: {
type: [Number], // 对传递过来的数据进行校验,验证不满足则跑出警告
//required:true,// 这是一个必须传递的值
default: 200, // 这是一个默认值 子组件属性名a对应的属性值不传默认为200
required: true, //必传项 设置为这是一个必须传递的值,子组件属性名a对应的属性值必传
validator(val) { // val 就是传递过来的值
//这个函数返回一个布尔值;如果返回true,说明这个值没有问题,如果返回false;那么会抛出异常
console.log(val);
return val < 100 && val > 10;
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<child :a="msg"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let child = {
data() {
return {}
},
props: {
a: {
type: [Number], // 对传递过来的数据进行校验,验证不满足则跑出警告
//required:true,// 这是一个必须传递的值
default: 200, // 这是一个默认值 子组件属性名a对应的属性值不传默认为200
required: true, //必传项 设置为这是一个必须传递的值,子组件属性名a对应的属性值必传
validator(val) { // val 就是传递过来的值
//这个函数返回一个布尔值;如果返回true,说明这个值没有问题,如果返回false;那么会抛出异常
console.log(val);
return val < 100 && val > 10;
}
}
},
template: "<div>{{a}}</div>" //取值和props的属性名保持一致
};
let vm = new Vue({
el: "#app",
data: {
msg: "60",
book: "vue.js"
},
methods: {},
components: {
child
}
});
</script>
</body>
</html>
子=>父(数据传递)
props: [“a”, “b”], //props接收到的属性也会放到组件实例上一份
methods: {
add() {
// this==> 组件的实例
// 子组件不能直接修改父组件的数据
// $emit : 用来发布自定义事件
// 自定义的名称必须小写
// 1.直接调取父亲(实例)上的方法
// vm.money++ // 这样改粗暴
// 2.v-bind:b 绑定事件
// this.b();// 3, emit(“changemoney”, 1080);
}
},
<body>
<div id="app">
父亲:{{money}} <button @click="fn1">少给点</button>
<br></br>
<!-- 自定义事件 @ 订阅-->
<son :a="money" @changemoney="fn2" :b="fn2"></son>
</div>
<!-- IMPORT JS -->
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
//创建Vue实例
// 组件的数据传递
// 父传子 子传父 兄弟组件之间的传递
let son = {
data() {
return {
}
},
props: ["a", "b"], //props接收到的属性也会放到组件实例上一份
methods: {
add() {
// this==> 组件的实例
// 子组件不能直接修改父组件的数据
// $emit : 用来发布自定义事件
// 自定义的名称必须小写
// 1.直接调取父亲(实例)上的方法
// vm.money++ // 这样改粗暴
// 2.v-bind:b 绑定事件
// this.b();
// 3,$emit:用来可以发布自定义事件
this.$emit("changemoney", 1080);
}
},
template: "<div>儿子:{{a}}<button @click='add'>多要点</button></div>"
}
let vm = new Vue({
el: '#app',
data: {
money: 888
},
methods: {
fn1() {
this.money--
},
fn2(val) {
console.log(val);
this.money++
}
},
components: {
son
},
});
</script>
</body>
.sync修饰符
<son :a.sync=“money” :v.sync=“val”>
下面: this.$emit(“update:a”, );
<body>
<div id="app">
父亲:{{money}} <button>少给点</button>
<br></br>
<!-- 自定义事件 @ 订阅-->
<!-- $event 接收的是$emit的第二个参数
@updata @updaa:m 名字随便起
-->
<!-- <son :a="money" @update:a="money+=$event"></son> -->
<son :a.sync="money" :v.sync="val"></son>
<!-- 下面不可以改: this.$emit("update:a", 2000); -->
</div>
<!-- IMPORT JS -->
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
//语法糖
let son = {
data() {
return {
}
},
props: ["a", "b", "v"],
methods: {
add() {
this.$emit("update:a", 2000);
this.$emit("update:v", 8899);
}
},
template: "<div>儿子:{{a}}<button @click='add'>多要点</button></div>"
}
let vm = new Vue({
el: '#app',
data: {
money: 888,
val: 555
},
methods: {
},
components: {
son
},
});
</script>
</body>
兄弟组件之间数据传递
兄弟之间的组件数据传递:eventBus;
$on : 订阅
$emit : 发布;轮询对应的事件池,让其中的方法执行
let eventBus = new Vue; // 这就是一个容器;联系了兄弟之间的纽带
created() {
eventBus.KaTeX parse error: Expected 'EOF', got '}' at position 49: …n) }̲, methods: { …emit(‘changeRed’);
},
},
<body>
<div id="app">
<bro1></bro1>
<bro2></bro2>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 兄弟之间的组件数据传递:eventBus;
// $on : 订阅
// $emit : 发布;轮询对应的事件池,让其中的方法执行
let eventBus = new Vue; // 这就是一个容器;联系了兄弟之间的纽带
let bro1 = {
data() {
return {
color: "红色"
}
},
created() {
eventBus.$on("changeGreen", this.changeGreen)
},
methods: {
fn1() {
eventBus.$emit('changeRed');
},
changeGreen() {
this.color = "绿色"
}
},
template: "<div>{{color}}<button @click='fn1'>变红</button></div>"
};
let bro2 = {
data() {
return {
color: "绿色"
}
},
created() {
eventBus.$on("changeRed", this.changeRed)
},
methods: {
changeRed() {
this.color = "红色";
},
fn2() {
eventBus.$emit('changeGreen');
}
},
template: "<div>{{color}}<button @click='fn2'>变绿</button></div>"
};
let vm = new Vue({
el: "#app",
data: {},
components: {
bro1,
bro2
}
})
</script>
</body>
爷=>孙(数据传递)
父子props
祖先元素传递子孙元素的数据
attrs属性 $listeners :方法
<body>
<div id="app">
<child :val="a" name="北京" age="10" @click="()=>{this.a=888}"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 父子props
// 祖先元素传递子孙元素的数据
// $attrs: 把行间属性放在一个对象上,传给孙子组件的$attrs属性 $listeners :方法
let grandson = {
template: "<div>{{$attrs}}==={{$attrs.name}}<button @click='$listeners.click()'>
</button></div>"
}
let child = {
data() {
return {}
},
components: {
grandson
},
// props:["val"],
template: "<div>{{val}}<grandson v-bind='$attrs' v-on='$listeners'>
</grandson></div>"
}
let vm = new Vue({
el: "#app",
data: {
a: 100
},
components: {
child
}
})
</script>
</body>
插槽:slot
slot:内置的组件:可以将父组件中的子组件里面的模板数据进行显示
<body>
<div id="app">
{{msg}}
<h2>这是父组件</h2>
<child>
{{msg}}
<div>菜单一</div>
<div>菜单二</div>
<div>菜单三</div>
</child>
</div>
<template id="child">
<div>
<slot></slot>
<h2>这是子组件</h2>
{{msg}}
<!-- slot:内置的组件:可以将父组件中的子组件里面的模板数据进行显示 -->
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let child = {
data(){
return {
msg:"world"
}
},
template:"#child"
}
let vm = new Vue({
el:"#app",
data:{
msg:"hello"
},
components:{
child
}
})
</script>
</body>
具名插槽
- slot:内置的组件:可以将父组件中的子组件里面的模板数据进行显示
- slot属性值和子组件中slot插槽中的name属性对应
- 没有name属性的slot会解析默认的标签
- 子组件的slot组件上的name属性会和父组件中的slot属性进行匹配,并按照子组件的顺序显示对应的模板数据
<body>
<div id="app">
<h2>这是父组件</h2>
<child>
<!-- // slot属性值和子组件中slot插槽中的name属性对应 -->
<div slot="first">菜单一</div>
<div>菜单二</div>
<div>菜单三</div>
</child>
</div>
<template id="child">
<div>
<h2>这是子组件</h2>
<!-- 没有name属性的slot会解析默认的标签 -->
<slot></slot>
<!-- 子组件的slot组件上的name属性会和父组件中的slot属性进行匹配,
并按照子组件的顺序显示对应的模板数据 -->
<slot name="first"></slot>
<!-- slot:内置的组件:可以将父组件中的子组件里面的模板数据进行显示 -->
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let child = {
data(){
return {
msg:"world"
}
},
template:"#child"
}
let vm = new Vue({
el:"#app",
data:{
msg:"hello"
},
components:{
child
}
})
</script>
</body>
作用域插槽
- 能够让父组件使用子组件中的组件
- slot-scope 作用域插槽,可以让我们在父组件这个位置获取到组件的数据
<child>
<div slot-scope="{user}">{{user}}
<div slot-scope=“abc”>{{abc.user}}
</child>
<template id=“child”>
<div>
<h2>这是子组件</h2>
<slot :user=“msg”></slot>
</div>
</template>
<body>
<div id="app">
<h2>这是父组件</h2>
<child>
<!-- slot-scope 作用域插槽,可以让我们在父组件这个位置获取到组件的数据 -->
<div slot-scope="{user}">{{user}}</div>
<div slot-scope="abc">{{abc.user}}</div>
</child>
</div>
<template id="child">
<div>
<h2>这是子组件</h2>
<slot :user="msg"></slot>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 能够让父组件使用子组件中的组件
let child = {
data() {
return {
msg: "world"
}
},
template: "#child"
}
let vm = new Vue({
el: "#app",
data: {
msg: "hello"
},
components: {
child
}
})
</script>
</body>
组件内的render方法
Vue.component('my',{
// template:"<div></div>"
// render(h){
// // h==> 函数
// // h:第一个参数可以标签名 对象:行间属性 标签的文本内容
// //console.log(h("h1",{class:"bar",a:1},"hello",{c:1}));
// // 这个函数就会返回一个虚拟的DOM,那么vue再将这个虚拟的DOM转成
真实的DOM放在页面上
// //console.log(h);
// return h("h1",{class:"bar",attrs:{id:100}},"hello");
// }
template:"<div>{{msg}}</div>"
})