摘自:https://div.io/topic/1880,并加入一点本身实际练习的demo,记录下学习历程,并方便后续翻阅。html
关于is的妙用,参考另外一篇本身写的demo文章:https://juejin.im/editor/drafts/5c863924e51d4561a0778dd5vue
对于一些有规律的 dom 结构,咱们能够经过递归方式来生成这个结构,在 vue 的模板中递归生成dom。
git
官方介绍:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8github
demo详见:https://github.com/elainema/elaine/tree/master/VUE/vue-components/src/views/component-communication和https://github.com/elainema/elaine/tree/master/VUE/vue-components/src/views/recursive-componentsvuex
首先为了使用递归组件须要准备一份数据,由于此次是生成一个菜单,因此准备一个菜单书数据,新建一个testdata.js 文件代码以下:api
var demoData = [ { 'id': '1', 'menuName': '基础管理', 'menuCode': '10', 'children': [ { 'menuName': '用户管理', 'menuCode': '11' }, { 'menuName': '角色管理', 'menuCode': '12', 'children': [ { 'menuName': '管理员', 'menuCode': '121' }, { 'menuName': 'CEO', 'menuCode': '122' } ] }, { 'menuName': '权限管理', 'menuCode': '13' } ] }, { 'id': '2', 'menuName': '商品管理', 'menuCode': '' } ]; export default demoData;复制代码
如今创建树形组件,首先新建一个文件treeMenu,代码以下浏览器
<template><div> <li> <span @click="toggle"> <i v-if="hasChild" class="icon" v-bind:class="[open ? 'folder-open': 'folder' ]"></i> <i v-if="!hasChild" class="icon file-text"></i> {{model.menuName}} </span> <ul v-show="open" v-if="hasChild"> <tree-menu v-for="(item,index) in model.children" v-bind:model="item" v-bind:key="index"></tree-menu> </ul> </li></div></template><script> export default { name: "TreeMenu", inheritAttrs:false, props: ['model'], data(){ return { open:false } }, computed:{ hasChild(){ return this.model.children && this.model.children.length } }, methods:{ toggle(){ if(this.hasChild){ this.open = !this.open } } } }</script><style> ul { list-style: none; margin: 10px 0; } li { padding: 3px 0; } li > span { cursor: pointer; font-size: 14px; line-height: 20px; } i.icon { display: inline-block; width: 20px; height: 20px; margin-right: 5px; background-repeat: no-repeat; vertical-align: middle; } .icon.folder { background-image: url(/src/assets/folder.png); } .icon.folder-open { background-image: url(/src/assets/folder-open.png); } .icon.file-text { background-image: url(/src/assets/file-text.png); } .tree-menu li { line-height: 1.5; }</style>复制代码
上述代码中咱们须要注意,这个组件必须含有 name 这个属性,由于没有 name 这个属性会形成控件自身不能调用自身,自身调用的时候最好有绑定 key ,由于这个 key 是惟一的标识,对于 vue 更新控件比较好.除非控件很是简单就不用 key.bash
咱们知道,v-model
是在表单类元素上进行双向绑定时使用的,好比:
iview
<template>
<input type="text" v-model="data">
{{ data }}
</template>
<script>
export default {
data () {
return {
data: ''
}
}
}
</script>复制代码
这时
data
就是双向绑定的,输入的内容会实时显示在页面上。在 Vue 1.x 中,自定义组件可使用 props 的
.sync
双向绑定,好比:
<my-component :data.sync="data"></my-component>复制代码
在 Vue 2.x 中,能够直接在自定义组件上使用 v-model
了,好比:
dom
<my-component v-model="data"></my-component>复制代码
在组件my-component
中,经过this.$emit('input')
就能够改变data的值了。
当咱们在书写 vue
组件的时候,常常会用到数据传递;将父组件的数据传递给子组件,有时候也须要经过子组件去事件去触发父组件的事件;总结一下比较经常使用的三种解决办法:
经过 props
的方式向子组件传递(父子组件)
vuex
进行状态管理(父子组件和非父子组件)
父组件经过this.$refs[子组件ref] 可直接调用子组件的方法
// 父组件
<template> <child ref="child"></child></template><script>export default { name:'parent', mounted() { this.refs.child.childSubmit() }}</script>
// 子组件
<template> <div> 这是一个子组件 </div></template><script>export default { name:'parent', methods:{ childSubmit() { alert("trigger") } }}</script>复制代码
inheritAttrs
+
$attrs
+
$listeners
基本是大部分的公司或者项目都是用前面两种,我也不例外......初次看到第四种写法时甚至有些惊讶,原来还有这种写法,而后去API看才发现其实很早就有,只是没有仔细看文档......故整理一下,若是有需求能够尝试用一用,官方api地址(英文看到很懵,转中文文档先看。。。):https://cn.vuejs.org/v2/api/index.html#inheritAttrs
vue中一个比较使人烦恼的事情是属性只能从父组件传递给子组件。这也就意味着当你想向嵌套层级比较深组件数据传递,只能由父组件传递给子组件,子组件再传递给孙子组件...像下面这样:
<parent-component :passdown="passdown">
<child-component :passdown="passdown">
<grand-child-component :passdown="passdown">
....
就这样一层一层的往下传递passdown这个变量,最后才会用{{passdown}}。复制代码
说实话,官方的解释开始看了几遍也是云里雾里的,忽略个人理解力。。。
父组件ComponentCommunication.vue
<template> <div class=""> <MyTest :title="title" :massgae="massgae"></MyTest> </div></template><script>import MyTest from './MyTest.vue'export default { name:'componentCommunication', data () { return { title:'定义在父组件的title', massgae:'message111' } }, components:{ MyTest }, created:function(){ }}</script>复制代码
子组件MyTest.vue
<template><section> <div>这里是标题,父组件经过prop传递给子组件的:{{title}}</div> <div> 注意这里:this.$attrs{{$attrs}}</div></section></template><script>export default { props:['title'], data(){ return{ } }, created:function(){ console.log(this.$attrs)//注意这里 }}</script>复制代码
上边的代码,父组件传递了两个参数给子组件title和message,在子组件里只注册并使用了title,massgae并无注册和使用,那么下浏览器渲染出来是什么样呢?以下图:
在Vue2.4.0,能够在组件定义中添加inheritAttrs:false,组件将不会把未被注册的props呈现为普通的HTML属性。
关于Props
的一个使人讨厌的事情是,他们只能从父母传给孩子。 这意味着若是您有深刻的嵌套组件,您须要传递数据,则必须将数据做为Props
绑定到每一个中间组件中:
Props
来讲还好,可是在一个真正的项目中,你可能会有许多更多的东西要传下去。
inheritAttrs
的组件的标志,其次是一个实例属性
$attrs
。 在组件里咱们能够经过其$attrs能够获取到父组件传递给子组件,但子组件没有使用和注册的数据。
咱们在子组件里设置 inheritAttrs: false // 默认是true, 渲染效果以下,能够看到父组件传递给组件的参数,但子组件未注册和使用的,不会做为普通html元素被渲染
个人理解就是:子组件能够触发父组件的事件(不须要用什么那些麻烦的vuex或者一个空的 Vue
实例做为事件总线,或者又是什么vm.$on
)