前言
本篇主要介绍组件的使用以及父子组件的传值和事件互相调用。
定义组件
无论那种组件,需要注意的是组件内的data必须为函数,通过函数内return data来实现数据使用;组件的el也必须使用函数
组件命名规范
- 参考官网文档
- 项目实践描述
1.有意义的,如果是业务组件要用业务的名称;如果是ui组件要用ui的名称
2.名称简短
3.具有可读性,便于沟通
4.符合自定义元素规范,避免使用保留字,使用统一的连接符中划线
5.app-作为统一的命名空间,想到其他项目可以共用
页面内定义的组件
- 页面内可以定义全局或者页面组件
// 注册全局组件Vue.component('my-component', {template: '<div>A custom component!</div>',props:[]})//局部组件var app=new Vue({el:"#app",components:{[ 'my-component': {template: '#myComponent',data: function() {return {msg: 'This is a Component!' //Vue中component的data必须通过function() return}},methods: {showMsg: function() {alert(this.msg);}}]})//使用template标签实现组件<template id="myComponent"><div>This is a component!</div></template>Vue.component('my-component', {template: '#myComponent',props:[]})
单文件组件
- 单独的一个组件作为一个文件,包括该组件需要的样式以及脚本数据等,如果你想组件也使用共用资源或者同类资源管理的方式也是可以的。其他:参考官网介绍
<!-- 单文件组件定义--><template><div ><h2>comp的组件</h2>data</ul></div></template><script>export default {name: 'comp',data () {return {data:'data'}}}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped less></style>
引入组件
引入文件组件,然后在使用的组件内注册该组件即可
<!--> 其他页面使用该组件<--><comp></comp><script>import comp from './comp'export default {name: 'app',components:{comp},data () {return {}}}</script>
在全局中注册文件组件,就可以直接使用,在components中将所有组件注册一遍,页面中可以直接使用
import Vue from 'vue'import booklist from './booklist'import comp from './comp'const components = [booklist, comp]components.map(component => {Vue.component(component.name, component)})export default {booklist, comp}
- 组件传值以及绑定方法
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。父组件可以使用 props 把数据传给子组件。注意:在子组件中定义prop时,使用了camelCase命名法。由于HTML特性不区分大小写,camelCase的prop用于特性时,需要转为 kebab-case(短横线隔开)。例如,在prop中定义的myName,在用作特性时需要转换为my-name。
//组件中声明可以直接使用,组件中直接使用属性名称即可,与data中定义的具有相同的效果,可以直接使用,但不是同一个数据props:['attr']props:['mySize']//使用的时候 属性显性声明注入,直接冒号绑定数据就可以,如果你不是动态数据的内容,那么去掉冒号即可(另外需要注意的是如果加了冒号,引号内支持的是js表达式,否则只是字符串)<comp :message="msg" :my-size='size'></comp>data () {return {msg:'示例的子组件'}}
//父组件<father><child :love="msg"></child></father>//子组件export default {...props: ['love']}
子组件向父组件传递数值,通过event传递
如在par中使用子组件sub,可以使用自定义事件(myClick)来完成:如果你 传递的是多个值,封装到一个对象中。<sub @myClick="handlerClick" />
在sub中使用this.$emit('myClick')去触发。 par中写好handlerClick(常规的method方法)对应去处理即可。同级组件(非父子组件)数据传递 (另外一种方法是使用vuex)
兄弟组件或者完全不相关的组件通信就需要使用一个你说的 中央事件总线去处理, 这个new Vue()写在一个单独的文件中作为公用的util方法
如 a,b通信, 在a组件的生命周期函数里面去写对应的监听事件如在mounted处理:
//一般定义在公共的脚本中,如果你是spa项目的话,写在main.js文件中,定义一个全局的,获取全局Vue对象window.eventBus = new Vue();//a组件, 监听my-event事件,进行对应的处理,组件创建前添加钩子函数mounted(){eventBus.$on('my-event', (data) => {//do something...})}//b组件 比如在某个元素点击后触发 my-eventmethods: {click: function(){eventBus.$emit('my-event', data); // data是要传递的数据}}
- 注意事项
组件中传入的值如果后续发生改变,那么原始注入的值就会发生改变,建议将值使用赋值到另一个变量,不要直接改变注入属性的值。虽然这不会影响原来的。如果使用了v-model的模型,会实时改变到使用这个值的组件。
组件事件触发
单向数据流
在vue2.x之后,组件中是只建议使用单向数据流,也就是props可以父组件向子组件传值之后,通过data的数据项来读取,不建议直接使用prop的值,
- 如果你传值是简单数据类型,那么可以在子组件的data属性中,将data值赋值为传递的属性值。(否则会报错)
<child :status='status'></child>Vue.component("child",{template:"<span>{{value}}</span>",props:{status},data(){return {value:this.status}}})
- 如果传值是引用数据类型,那么需要
子组件
props:{[“initCount”}
由于js中对象以及数组是引用类型,所以当prop为对象或者数组时,会有双向绑定的效果,这点有利有弊
数据验证
- 属性传入支持常规的数据格式验证,也支持自定义验证。
常规的类型有String,Number,Boolean,Object,Array,Function ,可以设置多个类型,用数组实现.在其内部是通过insanceof来实现数据类型的判断。
props:{len :{type:[String,Number] // 支持数组,存储多类型参数}}
- 支持required是否必须字段,支持default设置默认值,dafault为一个值或者返回默认值的方法。
- 支持validator ,对数据的内容进行严格的校验,可能是长度,格式,具体内容等,返回是否符合结果的布尔型结果即可。
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
上述内容可以通过 Vue 的 元素加一个特殊的 is 特性来实现:如果你希望组件不会每次都渲染、销毁,那么你可以设置v-once .其中两个组件的属性注入都可以写入。
<!-- 组件会在 `currentTabComponent` 改变时改变 --><component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括已注册组件的名字,或一个组件的选项对象。
<template><component :is="type" v-bind="linkProps(to)"><slot /></component></template><script>import { isExternal } from '@/utils/validate'export default {props: {to: {type: String,required: true}},computed: {isExternal() {return isExternal(this.to)},type() {if (this.isExternal) {return 'a'}return 'router-link'}},methods: {linkProps(to) {if (this.isExternal) {return {href: to,target: '_blank',rel: 'noopener'}}return {to: to}}}}</script>
异步加载组件

如何缓存组件
使用keep-alive抽象组件,并不会真正渲染到dom中
应用场景:多数在tab切换中
Vue组件抽离公共逻辑
查看Mixins
插槽分析内容
<alert-box>Something bad happened.</alert-box>
基本使用
首先需要在组件定义的时候,定义好插槽的位置。那么引用时
Vue.component("link",{template:"<a v-bind:href="url" class="nav-link"> <slot></slot></a>"})
具名插槽 ,允许你将多个插槽通过name区分
//模板<div class="container"><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer></div>//使用时<base-layout><template slot="header"><h1>Here might be a page title</h1></template><p>A paragraph for the main content.</p><p>And another one.</p><template slot="footer"><p>Here's some contact info</p></template></base-layout>
默认内容
可以在插槽内写好内置内容,支持传入新内容时覆盖。
<button type="submit"><slot>Submit</slot></button>
编译作用域
该插槽可以访问跟这个模板的其它地方相同的实例属性 (也就是说“作用域”是相同的)。但这个插槽不能访问 的作用域。例如尝试访问 url 是不会工作的。牢记一条准则:
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
<navigation-link url="/profile">Logged in as {{ user.name }}</navigation-link>
想要插槽内容提供数据的时候,可以通过常规的属性绑定实现:
<ul><liv-for="todo in todos"v-bind:key="todo.id"><!-- 我们为每个 todo 准备了一个插槽,--><!-- 将 `todo` 对象作为一个插槽的 prop 传入。--><slot v-bind:todo="todo"><!-- 回退的内容 -->{{ todo.text }}</slot></li></ul>
那么进一步,如果我们想实现自定义的模板字符串,可以通过template,以及slot-scope实现插槽内容。(在 2.5.0+,slot-scope 不再限制在 元素上使用,而可以用在插槽内的任何元素或组件上)
<todo-list v-bind:todos="todos"><!-- 将 `slotProps` 定义为插槽作用域的名字 --><template slot-scope="slotProps"><!-- 为待办项自定义一个模板,--><!-- 通过 `slotProps` 定制每个待办项。--><span v-if="slotProps.todo.isComplete">✓</span>{{ slotProps.todo.text }}</template></todo-list>
更进一步,在支持es2015的浏览器可以用结构拿到某些对应的参数。这样可以使得插槽内容更纯粹,只保留有用的数据。
<todo-list v-bind:todos="todos"><template slot-scope="{ todo }"><span v-if="todo.isComplete">✓</span>{{ todo.text }}</template></todo-list>
