Vue中少见但实用的技巧

     摘自:https://div.io/topic/1880,并加入一点自己实际练习的demo,记录下学习历程,并方便后续翻阅。


一、vue动态组件:is

     关于is的妙用,参考另一篇自己写的demo文章:https://juejin.im/editor/drafts/5c863924e51d4561a0778dd5

二、递归组件

     对于一些有规律的 dom 结构,我们可以通过递归方式来生成这个结构,在 vue 的模板中递归生成dom。

     官方介绍:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8

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-components

 准备数据

     首先为了使用递归组件需要准备一份数据,因为这次是生成一个菜单,所以准备一个菜单书数据,新建一个testdata.js 文件代码如下:

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.

另外一个需要注意就是递归组件时候,需要有一个条件来终止递归,在这里使用 v-for 隐形条件终止递归. 

三、自定义组件使用 v-model

我们知道,v-model是在表单类元素上进行双向绑定时使用的,比如:

<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了,比如:

<my-component v-model="data"></my-component>复制代码

在组件my-component中,通过this.$emit('input')就可以改变data的值了。

四、Vue 父子组件数据传递( inheritAttrs + $attrs + $listeners)

当我们在书写 vue 组件的时候,经常会用到数据传递;将父组件的数据传递给子组件,有时候也需要通过子组件去事件去触发父组件的事件;总结一下比较常用的三种解决办法:

  1. 通过 props 的方式向子组件传递(父子组件)

  2. vuex 进行状态管理(父子组件和非父子组件) 

  3. 父组件通过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>复制代码

后来在查看iview和element源码的时候发现还有第四种传递方式, inheritAttrs + $attrs + $listeners

基本是大部分的公司或者项目都是用前面两种,我也不例外......初次看到第四种写法时甚至有些惊讶,原来还有这种写法,然后去API看才发现其实很早就有,只是没有仔细看文档......故整理一下,如果有需求可以尝试用一用,官方api地址(英文看到很懵,转中文文档先看。。。):https://cn.vuejs.org/v2/api/index.html#inheritAttrs

1、场景介绍

vue中一个比较令人烦恼的事情是属性只能从父组件传递给子组件。这也就意味着当你想向嵌套层级比较深组件数据传递,只能由父组件传递给子组件,子组件再传递给孙子组件...像下面这样:

<parent-component :passdown="passdown">

<child-component :passdown="passdown">

<grand-child-component :passdown="passdown">

....

就这样一层一层的往下传递passdown这个变量,最后才会用{{passdown}}。复制代码

假如我们需要传递的属性只有1,2个还行,但是如果我们要传递的有几个或者10来个的情况,这会是什么样的场景,我们会在每个组件不停的props,每个必须写很多遍。有没有其它方便的写法?有,通过vuex的父子组件通信,的确这个是一个方法,但是还有其它的方法,这个就是我们要说的。通过inheritAttrs选项,以及实例属性$attrs

inheritAttrs + $attrs + $listeners

说实话,官方的解释开始看了几遍也是云里雾里的,忽略我的理解力。。。

2、实例:

父组件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并没有注册和使用,那么下浏览器渲染出来是什么样呢?如下图:



我们看到:子组件内未被注册的属性将作为普通html元素属性被渲染,如果想让属性能够向下传递,即使prop组件没有被使用,你也需要在组件上注册。这样做会使组件预期功能变得模糊不清,同时也难以维护组件,尤其是多层嵌套传递的场景。

在Vue2.4.0,可以在组件定义中添加inheritAttrs:false,组件将不会把未被注册的props呈现为普通的HTML属性。

$attrs

关于Props 的一个令人讨厌的事情是,他们只能从父母传给孩子。 这意味着如果您有深入的嵌套组件,您需要传递数据,则必须将数据作为Props 绑定到每个中间组件中:

对于一个或两个 Props 来说还好,但是在一个真正的项目中,你可能会有许多更多的东西要传下去。
您可以使用事件总线或Vuex来解决此问题,但Vue 2.4.0提供了另一种解决方案。 实际上,它是两个独立但相关的新功能的一部分:首先,一个称为 inheritAttrs的组件的标志,其次是一个实例属性 $attrs。 在组件里我们可以通过其$attrs可以获取到父组件传递给子组件,但子组件没有使用和注册的数据。

inheritAttrs

我们在子组件里设置 inheritAttrs: false  // 默认是true,  渲染效果如下,可以看到父组件传递给组件的参数,但子组件未注册和使用的,不会作为普通html元素被渲染


$listeners

我的理解就是:子组件可以触发父组件的事件(不需要用什么那些麻烦的vuex或者一个空的 Vue实例作为事件总线,或者又是什么vm.$on )



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值