Vue中的组件
- 组件是可复用的Vue实例
- 命名组件推荐使用小写字母,用-连接
- 在组件定义中,除了template,其它选项还有:data,methods,computed
- 组件定义中的data必须是一个方法
一、组件的两种使用方法
全局注册
<my-component></my-component>
Vue.component('my-component',{ template: '<div>组件内容</div>' })
局部注册
var app = new Vue({ el:'#app', components:{ ''my-components:{ template:'<div>组件内容</div>' } } })
二、使用props传递数据
这边的props采用数组方式
父组件向子组件传递数据
v-bin动态绑定父组件传的内容
<div id="app" style="300px;height:200px;border:2px solid skyblue"> <child-component msg="我是父组件的内容"></child-component> <hr> <!-- v-bind进行动态数据动态绑定 将input中的sth传给子组件 --> <input type="text" v-model="dadmsg"> <bind-component :sth="dadmsg"></bind-component> </div>
var app = new Vue({ el: '#app', data: { dadmsg: 'happy' }, components: { 'child-component': { props: ['msg'], template: '<div>{{msg}}</div>' }, 'bind-component': { props: ['sth'], template: '<div>{{sth}}</div>' } } })
在组件中使用props来从父组件接收参数,在props中的属性,都可以在组件中直接使用。
三、单向数据流
概念理解:通过props传递数据是单向的,父组件变化时数据会传给子组件,但是反过来不行。
目的:是将父子组件解稿,避免子组件修改无意间修改了父组件的状态。
两种改变prop的情况的应用场景
- 父组件传递初始值,子组件可以将它作为初始值保存起来,在自己的作用域下可以随意进行修改;
- 将传递进来的数据作为初始值进行保存
- 注册组件
- 将父组件的数据传递进来,并在子组件中props接收
- 将传递进来的数据通过初始值保存起来
<div id='app'> <child-component msg='今天也要努力啊'></child-component> </div>
let app = new Vue({ el: '#app', components: { 'child-component': { props: ['msg'], template: '<div>{{count}}</div>', data() { return { count: this.msg } } } } })
prop作为需要被转变的原始值传入,用计算属性对其再次进行计算
- 注册组件
- 将父组件的数据传递进来,并在子组件中使用props接收
- 将传递进来的数据通过计算属性进行重新计算并渲染到页面
<div id="app"> <input type="text" v-model="width"> <width-component :width='width'></width-component> </div>
let app = new Vue({ el: "#app", data: { 0 }, components: { 'width-component': { props: ['width'], template: '<div :style="style"></div>', computed: { style() { return { this.width + 'px', background: 'red', height: '30px' } } } } } })
四、组件中的命名方式
camelCased (驼峰式)
kebabcase(短横线命名)
- 组件中的html中、父组件给子组件传递数据,必须使用短横线命名。 (a-bc √ aBc ×)
- 在props中,短横线命名和驼峰命名都可以。
- 在template中,必须使用驼峰命名,短横线会报错。
- 在data中,使用this.xxx时,必须使用驼峰命名,短横线会报错。
- 组件的命名,短横线命名和驼峰命名都可以。
五、数据验证
这边的props采用对象方式
可验证的类型:Number String Boolean Array Object Function 自定义
<div id="app"> <style-component :a='a' :b='b' :c='c' :d='d' :e='e' :g='g'></style-component> </div>
let app = new Vue({ el: '#app', data: { a: 1, b: '2', c: '', //空字符串,就取默认的true d: [111, 222, 333], e: console.log(), g: 3 }, components: { 'styleComponent': { props: { //数字类型 a: { type: Number, required: true //必传 }, //字符串类型 b: { type: [String, Number] }, //布尔类型 c: { type: Boolean, default: true //默认值 }, //数组或对象 默认值是函数形式返回 d: { type: Array, default: function() { return [] } }, //函数类型 e: { type: Function }, //自定义一个函数 g: { validator: function(value) { return value < 10 } } }, template: '<div>{{a}}--{{b}}--{{c}}--{{d}}--{{g}}</div>' } } })
六 、组件通信
子组件向父组件传递数据
——给父组件添加自定义事件
——子组件通过$emit触发事件
- 给父组件添加自定义事件
- 子组件$emit传出数据
- 自定义事件函数接收数据参数并赋值,渲染
<div id="app"> <p>您的账户余额为{{num}}</p> <btn-component @change='change'></btn-component> </div>
new Vue({ el: '#app', data: { num: 3000 }, methods: { change(value) { this.num = value } }, components: { 'btn-component': { template: '<div> <button @click="hangle_ad">+1000</button> <button @click="hangle_re">-1000</button> </div>', data() { return { count: 3000 } }, methods: { hangle_ad() { this.count += 1000 this.$emit('change', this.count) }, hangle_re() { this.count -= 1000 this.$emit('change', this.count) } } } } })
——v-model代替自定义事件
v-model实质背后做了两个操作
- v-bind绑定一个value属性 (父组件要接收一个value值)
- v-on指令给当前元素绑定input事件 (子组件在有新的value值得时候要触发一个input事件)
所以上面那个银行存款的demo可以改为
<div id="app"> <p>您的账户余额为{{num}}</p> <btn-component v-model='num'></btn-component> 这边改啦!! </div>
new Vue({ el: '#app', data: { num: 3000 }, //父组件没有自定义函数啦!! components: { 'btn-component': { template: '<div> <button @click="hangle_ad">+1000</button> <button @click="hangle_re">-1000</button> </div>', data() { return { count: 3000 } }, methods: { hangle_ad() { this.count += 1000 this.$emit('input', this.count) //这边改啦!! }, hangle_re() { this.count -= 1000 this.$emit('input', this.count) //这边改啦!! } } } } })
非父组件之间的通信
两个非父子关系的组件进行通信,可以使用一个空的Vue实例作为中央事件总线(中介)
<div id="app"> <ahandle></ahandle> <bhandle></bhandle> </div>
var app = new Vue({ el: '#app', data: { bus: new Vue() //bus中介 }, components: { 'ahandle': { template: '<div><button @click="aclick">点击向b组件传递数据</button></div>', data() { return { aaa: '我是来自a组件的内容' } }, methods: { aclick() { //a组件创建事件,供b组件监听 由bus中介$emit提交 this.$root.bus.$emit('givetob', this.aaa) } } }, 'bhandle': { template: '<div>我是b组件</div>', created() { //b组件监听a组件创建的事件 由bus中介$on监听 this.$root.bus.$on('givetob', function(value) { //这里的value就是a组件传进来的this.aaa alert(value) }) } } } })
父链 $.parent
<div id="app"> <btn-component></btn-component>--{{msg}} </div>
let app = new Vue({ el: '#app', data: { msg: '我是父组件,我现在没有内容' }, components: { 'btn-component': { template: '<button @click="changeFather">点击修改父组件中的内容</button>', methods: { changeFather() { this.$parent.msg = '我现在有内容啦' } } } } })
子链 $children
vue提供索引:$ref
<div id="app"> <button @click='getchild'>点击父组件按钮获取a组件内容</button> <a-component ref='a'></a-component> 添加索引ref <b-component ref='b'></b-component> {{msg}} </div>
let app = new Vue({ el: '#app', data: { msg: '子组件数据未获得' }, methods: { getchild() { this.msg = this.$refs.a.msg //获取a组件的内容 ---refs } }, components: { 'a-component': { template: '<span></span>', data() { return { msg: '我是a组件中的内容' } } }, 'b-component': { template: '<span></span>', data() { return { msg: '我是b组件中的内容' } } } } })
七、slot(插槽)
使用slot进行分发内容
编译的作用域
父组件模板的内容在父组件作用域中编译;
子组件模板的内容在子组件作用域内编译。
插槽的用法
混合父组件的内容与子组件自己的模板
- 单个插槽
<div id="app"> <slotcomponent> <p>父组件插入到子组件的内容——我把子组件的slot替换掉啦</p> </slotcomponent> </div>
new Vue({ el: '#app', components: { 'slotcomponent': { template: '<slot>父组件没有插入内容没有内容就显示这个</slot>' } } })
- 具名插槽
<div id="app"> <name-component> <h2 slot="header">标题</h2> <p>内容1</p> <p>内容2</p> <p slot="footer">底部</p> </name-component> </div>
let app = new Vue({ el: '#app', components: { 'name-component': { template: '<div> <div class="header"> <slot name="header"></slot> </div> <div class="container"> <slot></slot> </div> <div class="footer"> <slot name="footer"></slot> </div> </div>' } } })
- 作用域插槽
<div id="app"> <mycomponent> <p slot='aaa' slot-scope='child'> {{child.text}} name拿不到 </p> </mycomponent> </div>
let app = new Vue({ el: '#app', components: { 'mycomponent': { template: '<div> <slot text="我是子组件插槽的内容" name="aaa"></slot> </div>' } } })
*2.5.0之前都是用template中写,渲染的是template中的内容
<div id="app"> <mycomponent> <template slot='aaa' slot-scope='child'> {{child.text}} name拿不到 </template> </mycomponent> </div>
访问slot
this.$slot.插槽名称
在具名插槽的例子前提下添加代码
...... components:{ ...... mounted(){ var header = this.$slots.header console.log(header) //此处拿到的是虚拟节点 var headerin = this.$slots.header[0].elm.innerText console.log(headerin) } }
八、动态组件
使用Vue提供的component元素,动态挂载不同的组件
使用is特性来实现
待补充待补充待补充......