💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 前言 本篇主要介绍组件的使用以及父子组件的传值和事件互相调用。 ## 定义组件 无论那种组件,需要注意的是组件内的data必须为函数,通过函数内return data来实现数据使用;组件的el也必须使用函数 ### 组件命名规范 * [参考官网文档](https://pablohpsilva.github.io/vuejs-component-style-guide/?from=singlemessage&isappinstalled=1#/chinese) * 项目实践描述 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:[] }) ~~~ ### 单文件组件 * 单独的一个组件作为一个文件,包括该组件需要的样式以及脚本数据等,如果你想组件也使用共用资源或者同类资源管理的方式也是可以的。其他:[参考官网介绍](https://cn.vuejs.org/v2/guide/single-file-components.html) ~~~ <!-- 单文件组件定义--> <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方法)对应去处理即可。 * 同级组件(非父子组件)数据传递 兄弟组件或者完全不相关的组件通信就需要使用一个你说的 中央事件总线去处理, 这个new Vue()写在一个单独的文件中作为公用的util方法 如 a,b通信, 在a组件的生命周期函数里面去写对应的监听事件如在mounted处理: ~~~javascript //一般定义在公共的脚本中,如果你是spa项目的话,写在main.js文件中 window.eventBus = new Vue(); //a组件, 监听my-event事件,进行对应的处理,组件创建前添加钩子函数 mounted(){ eventBus.$on('my-event', (data) => { //do something... }) } //b组件 比如在某个元素点击后触发 my-event methods: { 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 的 <component> 元素加一个特殊的 is 特性来实现:如果你希望组件不会每次都渲染、销毁,那么你可以设置v-once .其中两个组件的属性注入都可以写入。 ~~~ <!-- 组件会在 `currentTabComponent` 改变时改变 --> <component v-bind:is="currentTabComponent"></component> ~~~ 在上述示例中,currentTabComponent 可以包括已注册组件的名字,或一个组件的选项对象。 ## 插槽分析内容 - [插槽分发内容](https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87%E6%8F%92%E6%A7%BD%E5%88%86%E5%8F%91%E5%86%85%E5%AE%B9) - [插槽详解](https://cn.vuejs.org/v2/guide/components-slots.html) 和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:它可以是内容也可以是html代码。 ~~~ <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> ~~~ ### 编译作用域 该插槽可以访问跟这个模板的其它地方相同的实例属性 (也就是说“作用域”是相同的)。但这个插槽不能访问 <navigation-link> 的作用域。例如尝试访问 url 是不会工作的。牢记一条准则: **父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译** ~~~ <navigation-link url="/profile"> Logged in as {{ user.name }} </navigation-link> ~~~ 想我插槽内容提供数据的时候,可以通过常规的属性绑定实现: ~~~ <ul> <li v-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 不再限制在 <template> 元素上使用,而可以用在插槽内的任何元素或组件上) ~~~ <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> ~~~ ## 相关文档 - [组件规范文档](https://github.com/pablohpsilva/vuejs-component-style-guide/blob/master/README-CN.md)