官方文档
https://v2.cn.vuejs.org/v2/guide/
组件传值
对象
1 2 3 4 5 6 7 8 9 10 11 12
| export default { props: { eleProps: { type: Object, default() { return { title: "", }; }, }, }, }
|
传值的时候
1
| <SubjectNum :ele-props="subjectList" />
|
列表
1 2 3 4 5 6 7 8 9 10
| export default { props: { subjectList: { type: Array, default() { return []; }, }, }, };
|
传值的时候
1
| <SubjectNum :subject-list="subjectList" />
|
组件名大小写
定义组件名的方式有两种:
使用 kebab-case
1
| Vue.component('my-component-name', { })
|
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>。
使用 PascalCase
1
| Vue.component('MyComponentName', { })
|
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。
也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的。
注意:
尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
<template>标签内的就是字符串模版,直接操作DOM的就不是字符串模版。
组件通讯
父传子
子组件中定义
1
| props: ["showpage", "currpage", "pages"]
|
父组件中传递
1
| v-bind:showpage="showpage"
|
子传父
子组件中调用
1
| this.$emit('gotopage', newValue, oldValue);
|
父组件中传递
1
| v-on:gotopage="gotopage"
|
非父子组件的通信
事件定义
1
| window.zevent = new Vue()
|
发送事件
1
| window.zevent.$emit('formNodeCLick',data);
|
接收事件
1 2 3
| window.zevent.$on("formNodeCLick", (data) => { this.selectId = data.id; });
|
双向绑定
双向绑定是通过发送input来实现的。
1
| this.$emit("input", this.selectArr);
|
插槽
插槽详解
单个插槽
组件模板中可能会写为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="card_outer"> <slot/> </div> </template>
<script> export default { name: 'ZCard' } </script>
<style scoped> .card_outer { width: calc(100% - 20px); margin: 0 auto; min-height: 30px; background: #ffffff; border-radius: 4px; box-shadow: 0 0 6px 2px #eee; padding: 10px; box-sizing: border-box; } </style>
|
调用组件时这样写:
1 2 3
| <z-card style="margin-top: 10px"> <div>Hello</div> </z-card>
|
当组件渲染的时候,<slot></slot> 将会被替换为<div>Hello</div>。插槽内可以包含任何模板代码,包括 HTML。
注意:
组件标签内部用来替换<slot></slot>中的取值标签不能取到组件内的变量。
如果 navigation-link 没有包含一个 <slot></slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
多个插槽
<slot> 元素有一个特殊的 attribute:name。
这个 attribute 可以用来定义额外的插槽:
1 2 3 4 5 6 7 8 9 10 11
| <div class="container"> <div> <slot name="header"></slot> </div> <div> <slot></slot> </div> <div> <slot name="footer"></slot> </div> </div>
|
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
1 2 3 4 5 6 7 8 9 10 11 12
| <base-layout> <template v-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 v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
|
现在 <template> 元素中的所有内容都将会被传入相应的插槽。
任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
一个不带 name 的 <slot> 出口会带有隐含的名字default。
然而,如果你希望更明确一些,仍然可以在一个 <template> 中包裹默认插槽的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template>
<template v-slot:default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template>
<template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
|
应用范围
全局组件
方式1
1 2
| import LoadingComponents from './components/loading/loading.vue'; Vue.component('Loading', LoadingComponents)
|
方式2
如何通过Vue.use()来使用呢?
在组件的同级目录下,再创建一个index.js文件(名字可以随便起)
1 2 3 4 5 6 7 8 9
| import LoadingComponents from './loading.vue';
const loading = { install:function(Vue) { Vue.component('Loading', LoadingComponents) } }
export default loading;
|
引入自定义组件,并使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| import Vue from 'vue' import App from './App.vue' import Loading from './components/loading/index'
Vue.use(loading);
new Vue({ el: '#app', data:{ eventHub: new Vue() }, render: h => h(App) })
|
局部组件
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
引用文件
如果你通过 Babel 和 webpack 使用 ES2015 模块,那么代码看起来更像:
1 2 3 4 5 6 7 8
| import ComponentA from './ComponentA.vue'
export default { components: { ComponentA }, }
|
注意在 ES2015+ 中,在对象中放一个类似 ComponentA 的变量名其实是 ComponentA: ComponentA 的缩写
使用JS
可以通过一个普通的 JavaScript 对象来定义组件:
1 2 3
| var ComponentA = { } var ComponentB = { } var ComponentC = { }
|
然后在 components 选项中定义你想要使用的组件:
1 2 3 4 5 6 7
| new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
|
对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。
注意:局部注册的组件在其子组件中不可用。
分页组件示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| Vue.component('zj-page', { props: ["showpage", "currpage", "pages"], computed: { showPageArr: function () { var halfshow = parseInt(this.showpage / 2); var tempbegin = this.currpage - halfshow + 1; var tempend = this.currpage + halfshow; if (tempbegin < 1) { tempend = tempend - tempbegin + 1; tempbegin = 1; if (tempend > this.pages) { tempend = this.pages; } } else if (tempend > this.pages) { tempbegin = this.pages - this.showpage + 1; tempend = this.pages; if (tempbegin < 1) { tempbegin = 1; } }
var arr = []; for (var i = tempbegin; i <= tempend; i++) { arr.push(i); } return arr; } },
methods: { num_click: function (num) { this.$emit('gotopage', num, this.currpage); }, last_click: function () { if (this.currpage > 1) { this.$emit('gotopage', this.currpage - 1, this.currpage); } }, next_click: function () { if (this.currpage < this.pages) { this.$emit('gotopage', this.currpage + 1, this.currpage); } }, begin_click: function () { this.$emit('gotopage', 1, this.currpage); }, end_click: function () { this.$emit('gotopage', this.pages, this.currpage); } }, template: ` <div class="paging"> <span class="first_page" v-bind:class="{ unclickable: currpage == 1 }" v-on:click="begin_click()">首页</span> <span class="prev_page" v-bind:class="{ unclickable: currpage == 1 }" v-on:click="last_click()">上一页</span> <span v-bind:class="{ pitch_on: item == currpage }" class="pagination" v-for="(item,index) in showPageArr" v-on:click="num_click(item)">{{ item }} </span> <span class="next_page" v-bind:class="{ unclickable: currpage == pages||pages == 0 }" v-on:click="next_click()">下一页</span> <span class="last_page" v-bind:class="{ unclickable: currpage == pages||pages == 0}" v-on:click="end_click()">末页</span> </div> ` });
|
使用方法
1
| <zj-page v-bind:showpage="showpage" v-bind:currpage="currpage" v-bind:pages="pages" v-on:gotopage="gotopage"></zj-page>
|
JS代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| new Vue({ el: ".prepare_main", data: { showpage: 10, currpage: 1, pages: 20 }, methods: { gotopage: function (newpage, oldpage) { console.info("跳转到:" + newpage + " 原页:" + oldpage); } } });
|
注意:在分页组件内部,点击页数后 并没有直接在自定义组件内修改当前页数,而是发送了一个事件,让父组件来更新页数,
是因为Vue的属性传递是单向的