Vue组件相关记录
Vue组件开发
非单文件组件
创建组件api Vue.extend({})
const student = Vue.extend({template: `<div>{{studentName}} - {{age}}</div>`,data() {return {studentName: 'jjking',age: 12}}})new Vue({el: '#app',//局部注册components: {student: student}})
不能使用el,因为按理来说,组件不应该固定挂载谁,
并且data返回的是函数,不能写成对象
局部注册
在components里边
使用就是用<student></student>
全局注册
Vue.component('student',student);
前面是组件名,后面是组件
注意事项
组件名相关问题
组件名在开发者工具中,都是首字母大写的
一个单词组成: 大小写都可以
school
School
但是组件注册的时候写什么名字,你的标签就得写什么名字,避免出错
局部注册
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
其他,我们可以使用name来指定开发者工具里边的呈现的名字,所以只是为了好看,我么实际用还是用注册用的名字
关于组件的本质
- school组件的本质是VueComponent的构造函数,每次vm会帮助我们创建一个全新的VueComponent,但是这样的工作不用我们程序员干
- 关于this的指向
(1) 在组件中,this指向组件实例对象
(2) 在vm中,指向的是Vue实例对象
一个重要的内置关系
vc的prototype.__proto__
指向的是Vue的原型对象
也就是vc可以访问到Vue原型对象上的属性和方法
换个意思讲,如果我们在Vue的原型对象上写了一个属性,我们在vc中可以拿得到
单文件组件
默认来看,生成一个vue组件是
const a = Vue.extend({options})
但是我们写单文件组件的时候
<template><div><h2>学校名称: {{name}}</h2><h2>学校地址: {{address}}</h2></div>
</template><script>
export default {name: 'School',data() {return {name: '光景',address: '白云区'}}
}
</script><style></style>
export的时候,是直接抛出一个{}也就是一个对象,这里是简写的形式
const a = options
在app页面中,我们导入一个组件的时候,他会自动识别
组件通信
父子组件通信
父传子 -> props
子传父(初级用法)
例如父亲是app.vue
儿子是MyHeader.vue
父组件发一个函数给儿子
儿子在这个函数中把要传的数据传给父亲
准确的说,儿子把要传的数据传到这个函数的参数上
App.vue
<!-- 头部 -->
<MyHeader :addTodo="addTodo"></MyHeader>...
methods: {addTodo(todoObj) {// console.log('我是App组件,我收到了数据x',todoObj);this.todoList.unshift(todoObj)}
}
子组件是MyHeader
export default {name: 'MyHeader',props: ['addTodo'],methods: {add(e) {const todoObj = ...;this.addTodo(todoObj);}}
}
这个方法十分的诡异,但是确实是可以用的
组件自定义事件 子 -> 父
子传父,我们可以通过props 父亲给儿子传递一个函数,儿子在合适的时候触发这个事件,父亲就可以收到儿子传递过来的数据
这里的小项目就是为了说明组件自定义事件的,非常小的项目
父亲是app.vue
儿子分别是student.vue 和 school.vue
App.vue
<template><div class="app"><h1>{{msg}}</h1><School/><Student/></div>
</template><script>import Student from './components/Student'import School from './components/School'export default {name:'App',components:{School,Student},data() {return {msg:'你好啊!',}}}
</script><style scoped>.app{background-color: gray;padding: 5px;}
</style>
Student.vue
<template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2></div>
</template><script>export default {name:'Student',data() {return {name:'张三',sex:'男',}},}
</script><style scoped>.student{background-color: pink;padding: 5px;margin-top: 30px;}
</style>
School.vue
<template><div class="school"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div>
</template><script>export default {name:'School',props:['getSchoolName'],data() {return {name:'Tom学校',address:'北京',}}}
</script><style scoped>.school{background-color: skyblue;padding: 5px;}
</style>
页面显示
props传
props传当然是最为简陋的方式,并且传的是,函数
在App.vue上,我们绑定一个函数来接收来自儿子的数据
<School :getSchoolName="getStudentData"/>
...
methods: {getStudentData(data) {console.log('App组件收到了来自儿子的事件',data);}}
在子组件上,我们在合适的时机来触发这个事件,把数据传回App.vue
在School.vue
<!-- 通过props,子传父数据 --><button @click="sendSchoolName">点我发送给父亲app.vue数据</button>...```bashprops:['getSchoolName'],data() {return {name:'Tom学校',address:'北京',}},methods: {sendSchoolName() {this.getSchoolName(this.name)}}
这样的写法,实在称不上好,但是能用
绑定自定义事件
通过v-on绑定自定义事件
我们给组件绑定绑定自定义事件
在App.vue上
<School v-on:jjking="getStudentData"/>
...
methods: {getStudentData(data) {console.log('App组件收到了来自儿子的事件',data);}
}
这个自定义事件是在vc上的,触发条件就是我们在子组件调用
this.$emit('自定义事件名换',data)
这个emit,意思就是发射,就像子组件发射数据到父组件一样,实际上,也是触发父组件的方法
在这里就是在子组件中,我们绑定了一个事件叫做jjking
我们子组件只要触发这个jjking事件就行
事件的回调方法叫做getStudentData
在子组件
<!-- 通过自定义事件$emit触发父亲给儿子绑定的事件 -->
<button @click="sendSchoolName">点我发送给父亲app.vue数据</button>
...
methods: {sendSchoolName() {this.$emit('jjking',this.name)}
}
通过ref绑定自定义事件
<!-- 通过ref绑定自定义事件 -->
<School ref="student"/>methods: {getStudentData(data) {console.log('App组件收到了来自儿子的事件',data);}
},
mounted() {this.$refs.student.$on('jjking',this.getStudentData)
}
this.$refs.student 其实就是组件vc我们在这上面绑定jjking自定义事件,回调函数时this.getStudentData
相比看下来,其实第一种绑定的方式更为简单,但为什么我们要用ref来绑定呢,原因是这样子会更加的灵活
例如我们如果想要触发3秒之后,再启用getStudentData
的回调函数,此时第一种方式就直接写死了,模版解析到<School/>
组件的时候,啪一下,就执行了回调函数了,然而我们通过ref来绑定,我们可以直接直接写
mounted() {setTimeout(() => {this.$refs.student.$on('jjking',this.getStudentData)},3000)
}
如果我们想要触发一次,我们就可以换成$once
mounted() {this.$refs.student.$once('jjking', this.getStudentData)
}
注意:
这里的this.getStudentData不能写成普通的函数类似于下面这样
this.$refs.student.$on('jjking', function getStudentData(data) {console.log('App组件收到了来自儿子的事件', data);})
眼下看的时候没有问题,但是如果涉及到this的操作的话,就会出问题,这里的this不再是App
这个组件了,而是他的儿子组件School.vue
,原因是在Vue中,谁触发了事件,this就指向谁,此时this就是School的vc
这里的解决办法就是我们可以写箭头函数,因为箭头函数没有this,他的this会去外边一层去找this的指向,所以这里的this会找到mounted的this
而vue中,已经把mounted的this指向指向了App,所以箭头函数是不会出bug的
触发事件时传递多个参数
// 方式一school.vue
<script>methods: {sendStudentlName(){this.$emit('atguigu',this.name,666,888,900)},},
</script>app.vue
<script>methods: {getStudentName(name,x,y,z){console.log('App收到了学生名:',name,x,y,z)},},}
</script>
开发中的方式
// 方式一 :把数据包装成一个对象传递过去
school.vue
<script>methods: {sendStudentlName(){this.$emit('atguigu',{})},},
</script>// 方式二:es6 写法 正常传递,接收
school.vue
<script>methods: {sendStudentlName(){this.$emit('atguigu',this.name,666,888,900)},},
</script>app.vue
<script>methods: {// name 正常结构,其他的参数不管传递多少,整理到params数组上getStudentName(name,...params){console.log('App收到了学生名:',name,params)},},}
</script>
要么参数一个一个写,要么用...params
这是es6的语法
解绑自定义事件
<!-- 解绑自定义事件 -->
<button @click="unbind">点我解绑自定义事件</button>unbind() {this.$off('jjking')// this.$off(['jjking','demo']) //解绑多个自定义事件// this.$off() //解绑所有的自定义事件
}
组件绑定事件默认不使用内置事件
// 这么写会被默认当做自定义事件
<Student ref="student" @click="show"/> //加上native 原生的,本来的,才会使用到内置事件
<Student ref="student" @click.native="show"/>
必须写native才行
总结