组件开发的属性:
1.ref属性:
如果在vue里,想要获取DOM对象,并且不想使用JS的原生语法,那么就可以使用ref属性
ref属性的用法:
1)在HTML元素的开始标记中,或者在Vue子组件中的开始标记中定义,相当于id的替代者
html元素上: <h1 ref="xxx"></h1>
Vue组件上: <School ref="xxx"></School>
2)获取方式: 在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc)
this.$refs.ref属性的名字
2.scoped样式:就是让style样式只针对当前组件生效(是style标签的一个无值属性)
所有组件的style,都会被汇总到一起,如果style中设置的样式名有重复的,那么可能就会后引入的组件的样式会覆盖掉先引入组件的样式。
此时,我们可以在组件的style开始标记里,使用scoped属性,来设置style样式只针对当前组件生效,即局部生效。
3.组件通信属性
什么是组件通信:组件的数据是独立的,无法直接访问其他组件的数据,想使用其他组件的数据,就需要组件通信。组件通信,就是指组件与组件之间的数据传递。
通信解决方案:
有父组件School和子组件Student
父子通信流程:1.父组件School通过 props 将数据传递给子组件Student
<!-- 第一步: 先在子组件Student开始标记上添加属性和值 -->
<!-- 注意:如果属性的值是表达式,需要在属性名前添加v-bind: 或者简写形式, 子组件会自动推断类型 -->
<Student
:name="schoolName"
:address="schoolAddress"
:age="age"
:isMarry="isMarry"
></Student>
// 第二步:在子组件中添加props配置项, 配置在父组件中添加的各个属性名
// 方式1:简单配置
props: ["name", "address", "age", "isMarry"],
//方式2:可以对属性进行约束类型,设置必要性,默认值
// props:{
// name:{
// type:String,
// required:true
// },
// age:{
// type:Number,
// default:100
// },
// isMarry:{
// type:Boolean,
// default:false
// }
// },
// 第三步:可以直接在子组件中,使用props配置的各个属性名了。
methods: {
showSchoolSendData() {
console.log(this.name, this.address, this.age, this.isMarry); //获取到父组件School发送过来的数据
},
},
2.子组件Student向父组件School发送数据
方式一:通过props方法向子组件传递一个函数
第一步:在子组件开始标签上通过props方法向子组件传递一个函数
<!-- 如果想要子组件传递过来的信息,可以向子组件通过props方法传递一个函数。该函数定义一些你想要的形参 -->
<Student :getStudentData="getStudentData"></Student>
第二步:// 定义一个自己的函数,这个函数要传递给子组件,形参根据自己的需求来定义,
methods: {
getStudentData(name, age, gender, idcard) {
console.log("----我是父组件--");
console.log(name, age, gender, idcard);
},
},
第三步:
// 获取父组件传递过来的函数属性
props: ["getStudentData"],
第四步:
//定义一个方法来执行传过来的函数,也就是向父组件发送数据
sendDataToSchool() {
// 调用父组件传递过来的函数进行传参。
this.getStudentData(this.studentName,this.studentAge,"abc",123);
},
方式二: 使用v-on或者简写方式绑定事件。
父组件监听:父组件通过在子组件的引用上使用v-on指令来监听子组件触发的事件。
绑定原理:
在编写的子组件标签上使用v-on
或者@
绑定自定义事件,实际上是将事件绑定到子组件实例对象的VueComponent上。当有人触发了子组件上的这个事件,就会回调绑定到该事件上的函数。
第一步: 使用v-on或者简写方式绑定事件。 注意:是在使用子组件的标签上,来绑定自定义事件 -->
<Student @getStudentData="getStudentInfo"></Student>
第二步:
实现getStudentInfo函数,获取子组件传过来的数据
methods:{
getStudentInfo(name,age){
console.log("我是父组件");
console.log(name,age);
this.sname=name
this.sage=age
}
}
第三步:
写一个普通的点击事件向父组件发送消息
<button @click="sendDataToSchool">点我向School发送数据</button>
methods:{
sendDataToSchool(){
//当按钮被人点击时,会执行进入该方法,然后在该方法中,使用this.$emit来触发子组件身上的自定义事件getStudentData
this.$emit("getStudentData",this.studentName,this.studentAge)
}
}
方式3:通过ref属性和$on绑定自定义事件
子组件触发:子组件通过调用vm.$emit来触发一个事件,该事件可以被父组件监听。
第一步:给子组件实例直接绑定一个自定义事件,在mounted钩子函数中绑定
<!-- 子传父通信方式3: 在子组件实例上绑定一个自定事件 -->
<Student ref="student"></Student>
配置项:
mounted() {
// 在该组件被挂在为真实DOM时,给子组件实例直接绑定一个自定义事件
// 注意:不能使用匿名函数,因为匿名函数里的this是子组件实例
// this.$refs.student.$on('getStudentData',function(name,age){
// console.log("------$on绑定事件--------");
// console.log(this);//this是子组件实例
// this.sname = name;
// this.sage = age;
// })
// 可以使用箭头函数,或者是methods里配置的方法, 这样的写法,方法体里的this都是本组件实例
// this.$refs.student.$on('getStudentData',(name,age)=>{
// console.log("------$on绑定事件--------");
// console.log(this); //本组件实例
// this.sname = name;
// this.sage = age;
// })
this.$refs.student.$on('getStudentData',this.getStudentInfo)
},
第二步:获取子组件传过来的数据
methods: {
getStudentInfo(name, age) {
console.log("----我是父组件--");
this.sname = name;
this.sage = age;
},
},
第三步:
写一个普通的点击事件向父组件发送消息
<button @click="sendDataToSchool">点我向School发送数据</button>
//需要销毁自定义的事件 ,写一个按钮来自杀
<button @click="selfOff">移除自定义事件</button>
methods: {
sendDataToSchool() {
// 当按钮被人点击时,会执行进入该方法,然后在该方法中,使用this.$emit来触发子组件身上的自定义事件
this.$emit("getStudentData", this.studentName, this.studentAge);
},
selfOff() {
this.$off("getStudentData");
},
},
//销毁自定义事件
beforeDestroy() {
this.$off("getStudentData");
},
};
4. 自定义事件:
除了js中的内置事件,Vue还允许开发者自定义事件。在Vue实例上有以下四个方法,来完成自定义事件的各种操作:
-
vm.$on
: 自定义事件监听的核心方法。它允许开发者在Vue实例上注册监听器,以响应特定的事件。-1).监听器注册:通过vm.$on(eventName, callback)的形式,将callback函数注册为eventName事件的监听器。当事件被触发时,callback将被调用。(可以单独写callback,然后再进行调用 this.回调函数的名字)
-2).执行顺序:事件触发时,所有注册的回调函数将按照它们注册的顺序依次执行。 -
vm.$emit
: 自定义事件派发的核心方法。它允许开发者触发自定义事件,从而实现组件之间的通信。-1). 事件触发:通过vm.$emit(eventName)的形式,可以触发一个名为eventName的事件。
-2). 参数传递:使用vm.$emit(eventName, arg1, arg2, ...)触发事件时,可以传递多个参数。这些参数将按顺序传递给监听器的回调函数callback。==vm.$emit在Vue组件系统中扮演着至关重要的角色,尤其是在子>父组件的通信中==
-1).子组件触发:子组件通过调用vm.$emit来触发一个事件,该事件可以被父组件监听。
-2).父组件监听:父组件通过在子组件的引用上使用v-on指令来监听子组件触发的事件。 -
vm.$off
:移除自定义事件监听器的方法,它提供了一种机制来清理不再需要的事件监听,从而避免潜在的内存泄漏问题。-1) 移除机制:vm.$off([event, callback]),如果提供了事件名称和回调函数,则只移除特定的监听器;
-2).如果没有提供参数,则移除所有事件的所有监听器。: vm.$off()
-3).灵活性:vm.$off的灵活性体现在可以根据实际情况选择移除单个监听器或全部监听器,以满足不同的开发需求。应用场景:
-1).组件销毁:在Vue组件被销毁前,使用vm.$off移除所有注册的事件监听器,确保组件不会在销毁后继续触发事件,导致不可预期的行为。
-2).条件性监听:在某些情况下,事件监听可能只在特定条件下需要,一旦条件不再满足,就应该使用vm.$off移除监听器,以避免不必要的资源占用。 -
vm.$once
:提供了一种机制,允许事件只被监听一次,在事件触发后自动移除监听器。-1).场景应用:适用于那些只需要执行一次的事件处理,例如初始化操作或一次性资源的加载。
-2).资源管理:通过确保监听器只触发一次,vm.$once有助于避免不必要的资源消耗和潜在的内存泄漏。
总结:组件的自定义事件
1)一种组件间通信的方式,适用于:子组件 ==发送数据==> 父组件通信
2)使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调函数在A中),在mounted钩子函数中绑定自定义事件
3)绑定自定义事件:
第一种方式,在父组件中在子组件的开始标签中使用v-on或者@绑定自定义事件
<Student @getStudentData="getStudentInfo"></Student>
第二种方式,在父组件中: 使用ref和 $on 在mounted钩子函数中进行绑定
4)若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
5)触发自定义事件:this.$emit('pClick',数据)
6)解绑自定义事件:this.$off('pClick')
7)组件上也可以绑定原生DOM事件,需要使用native修饰符
<Demo @click.native=“f1()”>
注意:通过
this.$refs.xxx.$on('pClick',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
5.全局事件总线: 任意组件之间通信
两个并列组件的通信Teacher、Student组件
全局事件总线(GlobalEventBus)是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:
1. 所有的组件对象都必须能看见他
2. 这个对象必须能够使用$on、$emit和$off方法去绑定、触发和解绑事件
步骤1)安装全局事件总线:
new Vue({
...
beforeCreate() { //vm实例出生之前
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
...
})
步骤2)使用事件总线:
Student给Teacher发送数据:
Student组件中:触发Teacher里绑定的事件
<button @click="sendDataToTeacher">发送数据给Teacher</button>
methods: {
sendDataToTeacher() {
// 应该触发Teacher里绑定的事件
this.$bus.$emit("getStudentData", this.name, this.age);
},
},
beforeDestroy() { //销毁绑定事件
this.$bus.$off("getStudentData");
},
Teacher组件中:绑定事件,获取Student组件中的数据
// 在mounted里绑定自定义事件
mounted() {
this.$bus.$on("getStudentData", this.getStudentData);
},
methods: {
getStudentData(name, age) { 把接收到的数据给自己的属性赋值
this.sname = name;
this.sage = age;
},
},
Teacher组件给Student组件发送数据:
在Teacher组件中:触发Student中绑定的事件
<button @click="sendDataToStudent">发送数据给Student</button>
sendDataToStudent() {
this.$bus.$emit("getTeacherData", this.name, this.age);
},
beforeDestroy(){
this.$bus.$off("getTeacherData");
}
Student组件中:
// 在mounted里绑定自定义事件,获取teacher里的数据
mounted() {
this.$bus.$on("getStudentData", this.getStudentData);
},
methods: {
//把接收到的数据给自己的属性赋值
getStudentData(name, age) {
this.sname = name;
this.sage = age;
},
},
6.消息订阅
装备工作:安装插件pubsub-js
在终端上输入如下命令:
npm i pubsub-js
或者
npm install pubsub-js
Teacher订阅消息,Student发布消息(即数据从Student到Teacher)
第一步:插件导入到想要使用的组件中
import PubSub from "pubsub-js";
export default {
name: "Teacher",
//....
};
第二步:Teacher订阅消息,一般在mounted里订阅
mounted() {
// 使用subscribe("")来订阅消息,第一个参数表示主题,第二个参数是回调函数
this.pubsubId = PubSub.subscribe("studentMessage", this.getStudentData);
},
methods: {
getStudentData(theme, data) {
this.sname = data.name;
this.sage = data.age;
},
},
需要取消订阅:
beforeDestroy() {
// 取消订阅
PubSub.unsubscribe(this.pubsubId);
},
第三步:Student发布者发布消息,别忘记也需要导入插件
<button @click="sendDataToTeacher">发送数据给Teacher</button>
// 导入消息订阅发布插件
import PubSub from "pubsub-js";
methods: {
// 该函数的第一个参数,就是用来描述主题的。 第二个参数用来获取对方发送过来的具体数据
sendDataToTeacher() {
PubSub.publish("studentMessage", { name: this.name, age: this.age });
},
},
Student订阅消息,Teacher发布消息(即数据从Teacher到Student)
第一步:导入插件
// 导入消息订阅发布插件
import PubSub from "pubsub-js";
第二步:Student订阅消息(Student组件中)
mounted() {
// 使用subscribe("")来订阅消息,第一个参数表示主题,第二个参数是回调函数
this.pubsubId= PubSub.subscribe("teacherMessage", this.getTeacherData);
},
methods: {
// 该函数的第一个参数,就是用来描述主题的。 第二个参数用来获取对方发送过来的具体数据 (回调函数 获取接受到的值,并且给属性赋值)
getTeacherData(theme, data) {
this.tname = data.name;
this.tage = data.age;
},
},
//取消订阅
beforeDestroy(){
PubSub.unsubscribe(this.pubsubId)
}
第三步:Teacher发布消息(即向Student发布数据),也要导入插件
<button @click="sendDataToStudent">发送数据给Student</button> //发送数据给Student
import PubSub from "pubsub-js";
sendDataToStudent() {
// 主动发布消息: publish方法的解析:
// 第一个参数:要写对方订阅的主题,
// 第二个参数:准备发送的数据,最好使用对象形式,方便对方处理数据
PubSub.publish("teacherMessage", { name: this.name, age: this.age });
},
7.混入属性:mixins
混入,其实就是将多个组件共同的选项配置,单独封装起来,然后再引用回去。类似于java语言的静态方法封装,然后调用。
第一步:定义混入
export const mixin = {
data(){ },
methods: {},
computed:{}
//....
}
第二步:应用混入
<script>
//引入混入
import {mixin} from '../mixin'
export default {
name:'School',
data() {
return {
name:'北京大学',
address:'海淀区颐和园路6号'
}
},
mixins:[mixin]
}
</script>
混入的方式有两种:
-
局部混入: 在组件中导入,然后使用选项 mixins:[…,…,..,]
-
全局混入: 在main.js中导入,然后Vue.mixin(….)
特点:
1.组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先
。
2.同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
8.plugins
第一步:定义插件,将插件内容写在一个plugins.js文件中
//可以写一些全局的配置,如过滤器,自定义事件
export default {
//因为涉及到全局配置,需要传参Vue
install(Vue){
// 全局配置过滤器
Vue.filter('strFilter', function (value) {
return value.substring(0, 4)
})
}
}
第二步:引入插件,并应用。 在Vue创建实例之前
//引入插件文件
import plugins from './plugins'
//使用插件
Vue.use(plugins)
第三步:使用插件中的一些功能即可
比如使用过滤器,自定义指令等
9.slot插槽
插槽(Slot)是Vue提出来的一个概念。是Vue中常见的一种组件间的相互通信方式,作用是让父组件可以向子组件的指定位置插入html代码片段
父组件来决定要插入什么样的HTML代码片段(在使用子组件标签时,在标记体中给插槽slot赋值)。子组件使用<slot></slot>
来决定传过来的HTML代码片段插入的具体位置(即子组件不写内容,使用slot占个位置)。
1.默认插槽:
-1. 在子组件的template中定义插槽
<template>
<div>
<!-- 定义插槽 -->
<Slot>还没有被占用</Slot>
<div>
</template>-2. 在父组件的template中应用插槽
<template>
<div>
<子组件标签名>
<!-- html代码片段 -->
</子组件标签名>
<div>
</template>
注意:默认插槽在子组件中只能使用一次,只能占据一个位置。如果重复使用默认插槽。那么父组件传过来的代码片段都会重复。
2.具名插槽:就是在<Slot>
标签中使用name属性指定了具体名字的插槽。这样在子组件中,就可以使用多个插槽来占位了。父组件中的HTML代码片段想要插入哪个插槽,就在代码的根标记上 使用 name='插槽的名字'
就可以。
3.作用域插槽:
作用域插槽的特点,数据可以定义在子组件中,组件的使用者(父组件)来定义结构,并处理子组件传过来的数据或者部分自己的数据。
前两种插槽的特点是:数据和结构都是在父组件中定义的,只需要传入到子组件中的插槽位置即可。
子组件中:
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 在插槽上自定义属性名以及data中的数据
:自定义属性名='data中的数据'
-->
<slot :gs="games">第一个插槽</slot>
</div>
</template><script>
export default {
name:'Category',
props:['title'],
data() { //子组件自己的数据
return {
games:["王者荣耀","和平精英","植物大战僵尸","黑神话:悟空"]
}
},
}
</script>
父组件中:
<template>
<div class="container">
<Category title="游戏">
<!-- 在template标签中使用slot-scope='{子组件中自定义属性名}' -->
<template slot-scope="{gs}">//遍历数据
<h4 v-for="(game,index) of gs" :key="index">{{game}}</h4>
<a href="#">{{info}}</a> //处理自己的数据
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category.vue'
export default {
name:'App',
components: { Category },
data() {
return {
info:"查看更多电影"
}
},
}
</script>
10.Vue的axios
1.安装axios
npm install axios (在vscode中)
2.引入axios
在script标签中:
import axios from 'axios';
直接发送数据:会出现跨域的问题,即不能收到返回值
使用代理服务器:
第一种代理方式:
devServer: {
// 配置代理服务器,让代理服务器帮助我们访问另一个后端服务器,即8080
// 第一种方式:简单配置一个单例,但是有缺点,不能配置多个,如果项目
// public下有同名的文件路径,就不在执行代理服务器
proxy: 'http://localhost:8088' //要访问的服务器
}
axios发送数据:
//此时url访问的地址是代理服务器,也就是自己的服务器8080,(即前端自己的端口)
f1(){
axios({
url:"http://localhost:8080/ssm_netctoss/fee/testVue",
method:"post",
headers:{'content-type':'application/x-www-form-urlencoded'},
data:{"name": this.name, "age": this.age, "flag": this.flag},
}).then(res=>{
console.log("---res---");
console.log(res.data);
}).catch(error=>{
console.log("------error-----");
console.log(error);
})
},
第二种代理方式:
devServer: {
proxy:{
// // 可以配置多个
// // key:是一个url的过滤前缀,如果url上有key就执行对应的代理服务
// // http://localhost:8080/wang/ssm_netctoss/fee/testVue
'/wang':{
target:'http://localhost:8088',//用来配置最终的目标服务器的路径
//http://localhost:8088/wang/ssm_netctoss/fee/testVue
pathRewrite:{'^/wang':''},//将url里的前缀替换成空字符串,变成对应的正确路径
//http://localhost:8088/ssm_netctoss/fee/testVue
ws:true,//用于支持websocket。默认就是true
changeOrigin: true,//用于控制请求头中的host值。true表示使用代理目标服务器 的host。默认就是true
},
'/cong':{
target:'https://api.oick.cn/yiyan/api.php',
pathRewrite:{'^/cong':''},
}
}
}
发送数据:
//此时url访问的地址是代理服务器,也就是自己的服务器8080,(即前端自己的端口)
f1(){
axios({
url:"http://localhost:8080/wang/ssm_netctoss/fee/testVue",
method:"post",
headers:{'content-type':'application/x-www-form-urlencoded'},
data:{"name": this.name, "age": this.age, "flag": this.flag},
}).then(res=>{
console.log("---res---");
console.log(res.data);
}).catch(error=>{
console.log("------error-----");
console.log(error);
})
},
f2(){
axios({
url:"http://localhost:8080/cong",
method:"post",
headers:{'content-type':'application/x-www-form-urlencoded'},
data:{"name": this.name, "age": this.age, "flag": this.flag},
}).then(res=>{
console.log("---res---");
console.log(res.data);
}).catch(error=>{
console.log("------error-----");
console.log(error);
})
}
},