vue组件三要素

  1. props参数
  2. slot定制插槽
  3. event自定义事件

基本用法
在使用 vue-cli 创建的项目中,组件的创建非常方便,只需要新建一个 .vue 文件,然后在 template 中写好 HTML 代码,一个简单的组件就完成了 一个完整的组件,除了 template 以外,还有 script和 style

<template>
  <div class="headComponent">
    子组件{{{myData}}
  </div>
</template>

<script>
export default {
    props:['data','type'],
    inheritAttrs: false,
    data(){
        return{
            myData:'',
        }
    },
    mounted(){

    },
    methods:{
       
    }
}
</script>
<style scoped>

</style>

然后在其他文件的 js 里面引入并注册,就能直接使用这个组件了

import list from '../components/headComponent.vue'


components: {list },

一、props:数据父组件传入子组件

父对子传参,就需要用到 props,通常的 props 是这样的:

props:['data','type']

但是通用组件的的应用场景比较复杂,对 props 传递的参数应该添加一些验证规则,常用格式如下:

 props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }

对于通过 props 传入的参数,不建议对其进行操作,因为会同时修改父组件里面的数据 // vue2.5已经针对 props 做出优化,这个问题已经不存在了 如果一定需要有这样的操作,可以这么写:

let copyData = JSON.parse(JSON.stringify(this.data))

为什么不直接写 let myData = this.data 呢? 因为直接赋值,对于对象和数组而言只是浅拷贝,指向的是同一个内存地址,其中一个改变另一个也会改变。而通过 JSON颠倒转换之后,实现了深拷贝,则可以互不影响。

二、子组件触发父组件事件

在通用组件中,通常会需要有各种事件,比如复选框的 change 事件,或者组件中某个按钮的 click 事件,有时子组件需要触发一个事件,并传递给父组件

// 子组件方法:触发父组件方法,并传递参数data到父组件
handleSubmit(data){
    this.$emit('submitToParent', data)
}
// 父组件调用子组件
<child-component @submitToParent="parentSubmit"></child-component>
... ...
// 父组件中被触发的方法,接受到子组件传来的参数
parentSubmit(data){
    // 父组件的逻辑处理
}

父组件中的逻辑要放在父组件处理,子组件基于父组件的数据做的逻辑放在子组件中处理; 这样既降低了耦合性,也保证了各自的数据不被污染。

三、记得留一个 slot

一个通用组件,往往不能够完美的适应所有应用场景 所以在封装组件的时候,只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决

在这里插入图片描述
上面是一个通用组件,在某些场景中,右侧的按钮是 “处理” 和 “委托”。在另外的场景中,按钮需要换成 “查看” 或者 “删除” 在封装组件的时候,就不用写按钮,只需要在合适的位置留一个 slot,将按钮的位置留出来,然后在父组件写入按钮

子组件
<div class="child-btn">
    <!-- 具名插槽 -->
    <slot name="button"></slot>
    <!-- 匿名插槽(每个组件只能有一个) -->
    <slot><slot>
</div>

父组件
<child>
    <!-- 对应子组件中button的插槽 -->
    <button slot="button">slot按钮</button>
</child>

开发通用组件的时候,只要不是独立性很高的组件,建议都留一个 slot,即使还没想好用来干什么。

再来个例子:

开发过程中,常常需要在子组件内添加新的内容,这时候可以在子组件内部留一个或者多个插口
在这里插入图片描述
然后在调用这个子组件的时候加入内容

添加的内容就会分发到对应的 slot 中

在这里插入图片描述
slot 中还可以作为一个作用域,在子组件中定义变量,然后在父组件中自定义渲染的方式

子组件:
在这里插入图片描述
父组件:
在这里插入图片描述
这个示例中,首先在子组件中添加 slot,并在子组件中定义了数组变量 navs
然后在父组件中以作用域 template 添加内容,其中 scope 是固有属性,它的值对应一个临时变量 props
而 props 将接收从父组件传递给子组件的参数 navs

四、Vuex:巧用但不要滥用

子组件向子组件传递数据

Vue 没有直接子对子传参的方法,建议将需要传递数据的子组件,都合并为一个组件。如果一定需要子对子传参,可以先从传到父组件,再传到子组件。
父子组件之间是通过 props 和 自定义事件来传参,非父子组件通常会采用 Vuex 传参
Vuex设计初衷是用来管理组件状态,也可以用于复杂组件的参数传递
但是 Vuex 的虽然可以用来传参,但并不推荐;因为 Vuex 类似于一个全局变量,会一直占用内存
在写入数据庞大的 state 的时候,就会产生内存泄露(人是活的,不能因为事物有弊端就不使用,合理利用即可)。
Tips:
当页面刷新的时候,Vuex 会初始化,同时也会 丢失编辑过的数据
如果刷新页面时需要保留数据,可以通过 url 或者 sessionstorage 保存 id,然后 ajax 请求数据

五、组件的css:合理运用 scoped 控制样式作用域

在编写组件的时候,可以在 style 标签中添加 scoped,让标签中的样式只对当前组件生效
但是一味的使用 scoped,肯定会产生大量的重复代码
所以在开发的时候,应该避免在组件中写样式
当全局样式写好之后,再针对每个组件,通过 scoped 属性添加组件样式

六、动态组件

Vue 还可以将多个子组件,都挂载在同一个位置,通过变量来切换组件,实现 tab 菜单这样的效果

在这里插入图片描述
这样的功能可以通过路由 vue-router 实现,但路由更适合较大的组件,而且 url 会有相应的改变 Vue 自身保留的 元素,可以将组件动态绑定到 is 特性上,从而很方便的实现动态组件切换
在这里插入图片描述
上例中,当 tabView 的值改变,component 就会渲染对应的组件,和路由的效果十分类似,但是地址栏并没有发生改变,但这样一来,每次切换组件都会重新渲染,无法保留组件上的数据。这时可以使用 ** keep-alive ** 将组件保留在内存中,避免重新渲染

七、递归组件

当组件拥有 name 属性的时候,就可以在它的模板内递归的调用自己,这在开发树形组件的时候十分有效
在这里插入图片描述
上面是一个子组件,定义了 name 为 simple03,然后在模板中调用自身,结合 v-for 实现递归 为了防止出现死循环,在调用自身的时候,加入了 v-if 作为判定条件 父组件中调用的时候,需要通过 props 传入一个 tree:
在这里插入图片描述
在这里插入图片描述

最终渲染结果:
在这里插入图片描述

其他情况

子组件改变父组件的数据

在vue2.0之后的版本中,不允许子组件直接改变父组件的数据,在1.0的版本中可以这样操作的,但是往往项目需求需要改变父组件的数据,那我们如何变通呢?
当我们把父元素的数据给子组件时,要传一个非基础类型,即传递对象or数组,子组件通过访问对象中的属性操作数据,因为对象和数组是传引用,所以在子组件中修改的时候,父组件也会同步改变,如下:

// 父组件要props传递给子组件的数据
myData:{
    info:'父组件信息'
}

// 子组件
 <template id="tpl">
    <div>
        <button @click="change">change</button>
        <p>{{data.info}}</p>
    </div>
</template>
... 省略部分无关代码 ...
props:['data'],
methods:{
    change(){
        this.data.info = 'change info'
    }
}

当子组件点击change按钮改变数据的时候,父组件也会同步改变
如果不想影响父组件的值,我们也可以用文章开头讲的JSON颠倒转换的方式,在mounted方法中进行深拷贝,在data中初始化数据时用另一个变量作为承载,这样才承载变量改变,就不会影响父组件该变量的值了。。

Logo

前往低代码交流专区

更多推荐