Vue组件间数据传递常用方法

目录

 1,props

2,$emit

3,$refs

 4,$attrs

5,EventBus

6,路由传参

7,vuex

8,依赖注入(provide / inject)


 1,props

父传子,在子组件中利用props属性来接受数据,

第一步,在子组件中定义需要传递的props数据,定义props应该按照验证方式,为 props 中的值提供一个带有验证需求的对象。

// 子组件 menu-tree
props: {
    className: {
      type: String, // props的类型,这是props的基本类型检查
      required: true, // 是否必填
      default: 'chart' // 默认值
    },
    treeDate: {
      type: Object,
      required: true,
      default: function () { // 对象或数组默认值必须从一个工厂函数获取
        return {}
      }
    }
  },

第二步,在父组件中传值,在父组件中的子组件标签上进行传值

<!-- 父组件 -->
<menu-tree
  :treeDate="treeDate"
  :className="className">
</menu-tree>

这时,子组件就可以获取到父组件中的数据了,当父组件的数据发生改变,子组件同时改变。

2,$emit

子传父,$emit可以触发当前实例上的事件,并携带参数

第一步,在子组件内,可以是click,change,input等事件函数中调用$emit触发当前组件实例上的事件,如下:

<!-- 子组件el-tree  click事件 -->
<el-row style="text-align:center">
   <el-button type="primary" @click="capture(form)" size="mini"></el-button>
</el-row>
// 触发当前实例的capture事件,并携带参数
capture(form) {
    this.$emit('capture', form)
},

第二步,因为在子组件中capture事件被激活,所以在父组件中,当前组件capture方法绑定的事件会被激活,并携带参数

<!-- 父组件中的子组件标签 -->
<el-tree
  style="height: 300px; overflow: auto"
  @capture="handleCapture">
</el-tree>

父组件中的handleCapture事件被激活,获取参数:

// 父组件中被激活的事件
handleCapture(form) {
  this.listQuery.name= form.name
  this.listQuery.sex= form.sex
},

3,$refs

$refs属性可以获取持有注册过 ref 属性的所有 DOM 元素和组件实例,可以调用组件实例的方法,获取数据,传递数据

第一步,在子组件中定义 数据 及 方法,

<!-- 子组件menu-tree -->
<template>
  <el-tree empty-text="加载中" show-checkbox></el-tree>
</template>
// 子组件menu-tree数据
<script>
export default {
  data: () => ({
    str: '我是子组件的数据!'
  }),
  methods: {
    methodsname() {
      console.log('我是子组件的方法!')
    }
  }
}
</script>

第二步,在父组件中为子组件添加 ref 属性,调用子组件中的方法,获取子组件中的数据,

<!-- 父组件中的子组件标签,定义 ref 属性 -->
<menu-tree ref="menuTree" style="height: 300px; overflow: auto"></menu-tree>
// 在父组件事件中获取数据
<script>
import MenuTree from './coms/MenuTree'
export default {
  data() {
    return {}
  },
  components: {
    MenuTree
  },
  methods: {
    onclick() {
      let menuTree= this.$refs.menuTree // 父组件可以通过$refs拿到子组件的对象
      // 然后直接调用子组件的 methods里的方法和data里的数据
      console.log(menuTree) //子组件对象
      console.log(menuTree.str) // 我是子组件的数据
      console.log(menuTree.methodsname()) // 我是子组件的方法
    }
  }
}
</script>

 向子组件传递数据:

// 父组件
<template>
    <div>
        <resetPassword
          ref="resetPassword"
          @succ="successPass"
        />
    </div>
</template>
<script>
export default {
    methods: {
        handleClick() {
            this.$refs.resetPassword.init('忘记支付密码', 2)
        },
    }
}
</script>

// 子组件代碼
<script>
export default {
    methods: {
        init(title, type) {
            // ...
        },
    }
}
</script>

 4,$attrs

 官方解释:

包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

个人理解:

$attrs 包含了在父组件中为子组件 html 上绑定的,且在子组件 props 中不能有定义的属性(class 和 style 除外),如果在子组件中的 props 中定义了,那么将以 props 的方式传递,不会出现在 $attrs 中。

其实 $attrs 获取的就是通过类似v-bind 方式为子组件传递的,子组件却没有用 props 方式接收的数据集合。

// 父组件
<template>
	<div class="father_content">
		<div style="margin-top: 200px; background: yellow; padding: 20px">
			<p>父组件页面</p>
			<son 
              :fatherName="fatherName" 
              :fatherAge="fatherAge" 
              :fatherOper="fatherOper" 
              :transitionShow="transitionShow"
            ></son>
		</div>
	</div>
</template>
<script>
import son from './attrMode/index.vue'
export default {
	components: { son },
	data() {
		return {
			transitionShow: false,
			fatherName: 'fatherName',
			fatherAge: 345,
			fatherOper: '打开'
		}
	}
}
</script>
// 子组件
<template>
	<div style="padding: 40px; background: #eee; text-align: center">
		<p style="cursor: pointer" @click="sonClickFn">儿子组件</p>
        <!-- 通过v-bind="$attrs将父组件数据传向孙子组件" -->
		<groundson :sonName="sonName" :sonAge="sonAge" v-bind="$attrs"></groundson>
	</div>
</template>
<script>
import groundson from './components/add.vue'
export default {
	data() {
		return {
			sonName: 'sonName',
			sonAge: 1000
		}
	},
	components: { groundson },
	props: {
        // 如果父组件传递过来的数据没有通过props接受,则可以在$attrs中访问到
		fatherName: String,
		fatherAge: Number
	},
	methods: {
		sonClickFn() {
			console.log(this.fatherName, this.fatherAge) // fatherName 345
			console.log(this.$attrs) // {fatherOper: '打开', transitionShow: false}
		}
	}
}
</script>
// 孙子组件
<template>
	<div class="grandson">
		<p @click="grandsonClickFn">孙子组件</p>
	</div>
</template>

<script>
export default {
	props: {
        // $attrs 方式传递
		fatherName: String,
		fatherAge: Number,
        fatherOper: String,
        // props 方式传递
		sonName: String,
		sonAge: Number
	},
	methods: {
		grandsonClickFn() {
			console.log(this.fatherName, this.fatherAge, this.fatherOper) // undefined undefined '打开'
			console.log(this.sonName, this.sonAge) // sonName 1000
			console.log(this.$attrs) // {transitionShow: false}
		}
	}
}
</script>

在三级嵌套的组件关系中(父子孙),可以通过 $attrs 将数据从父组件直接传递到孙子组件中,孙子组件通过props 来获取数据,无需通过子组件传递。

具体的使用方法请看:

vue中$attrs你会用吗?https://juejin.cn/post/6844903784989081607

和 $attrs 属性使用相关的属性 inheritAttrs

官方解释:

默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。

个人理解:

默认情况下为子组件绑定的所有属性,哪些不被认为是 props 的属性绑定会被挂载到子组件的根元素上,通过设置 inheritAttrs 属性值为 false,这些默认行为将会被去掉。

同时可以在子组件中使用 $attrs 来接收这些属性,将属性绑定到子组件中的某一个元素上,如下(通过儿子和孙子组件lai):

// 儿子组件
<template>
	<div style="padding: 40px; background: #eee; text-align: center">
		<p style="cursor: pointer">儿子组件</p>
		<groundson 
          type="text" 
          placeholder="请输入" 
          :sonName="sonName" 
          :sonAge="sonAge" 
          v-bind="$attrs"
        ></groundson>
	</div>
</template>
<script>
import groundson from './components/add.vue'
export default {
	name: 'son',
	data() {
		return {
			sonName: 'sonName',
			sonAge: 1000
		}
	},
	components: {
		groundson
	},
}
</script>
// 子组件
<template>
	<div class="grandson">
		<p @click="grandsonClickFn">孙子组件</p>
        <!-- 通过 $attrs 来接收子组件传过来的不属于 props 绑定的属性 -->
        <!-- 这些属性将绑定到 input 元素上 -->
		<input v-bind="$attrs" />
	</div>
</template>
<script>
export default {
	inheritAttrs: false, // ***
	name: 'grandson',
	data() {
		return {}
	},
	props: {
		fatherName: String,
		fatherAge: Number,
		sonName: String,
		// 没有通过props 接收sonAge,所以sonAge不作为props接收的属性,会绑定到input元素上
		fatherOper: String
	},
	components: {},
	methods: {
		grandsonClickFn() {
			console.log(this.sonName, this.sonAge) // sonName undefined
			console.log(this.$attrs) 
            // {type: 'text', placeholder: '请输入', sonAge: 1000, transitionShow: false}
		}
	}
}
</script>

 不设置 inheritAttrs 属性值,并且没有为 input 绑定 $attrs 属性,DOM结构如图:

 设置 inheritAttrs 属性 false,并使用 $attrs 接收属性,DOM结构如图:

5,EventBus

  • 事件总线,原理就是声明一个全局的vue实例对象,然后把需要传递的方法数据都储存到这个实例上面,兄弟组件都从这个实例上面拿数据,有点像vuex,但是是适用于比较小的项目中使用,大型项目一般不用。
  • 第一步,先创建eventbus,即vue实例,当前实例作为总线,用来储存通信数据和事件监听,并向外导出
  • 创建一个单独的js文件,eventbus.js文件
import Vue from 'vue'
export default new Vue

假如,当前有三个组件,父组件及兄弟组件,如下:

<!-- 父组件 -->
<template>
   <components-a></components-a>
   <components-b></components-b>
</template>

当前需要将子组件a中的数据传递到子组件b

第二步,先在子组件a,b中分别引入vue实例

import eventBus from '@/api/js/event.js'

第三步,在子组件a中,利用当前引入实例的$emit方法,触发当前实例(eventBus)上的事件,并携带参数,

// a组件中
methods:{
  clickbtn(){
    eventBus.$emit("gettarget",this.msg)   //$emit这个方法会触发gettarget事件
  }
}

当前a组件触发的事件及传递的参数是携带在我们自己定义的eventBus总线中,所以,同样在b组件中引入eventBus,就可以获取a组件的数据,

第四步,b组件中我们在created钩子函数中利用vue $on  API监听当前实例(即eventBus)上的事件,回调函数会接收所有传入事件触发函数的额外参数

// b组件中
created:{
  eventBus.$on("gettarget",target=> {
    console.log(target) // 获取到a组件中的数据
  })
}

至此,数据传递完成,其实,相比于$emit方法还是有很多相似之处的。

6,路由传参

vue中路由传参总共有三种方式

<li v-for="article in articles" @click="getDescribe(article.id)">

如上,当在父组件中点击元素,跳转到子组件中,并携带参数,共有如下实现方式:

第一种:

父组件中利用动态路由的方式,通过路径的携带参数

getDescribe(id) { // 直接调用$router.push 实现携带参数的跳转
   this.$router.push({path: `/describe/${id}`,
})

对应路由配置如下:

{
   path: '/describe/:id',
   name: 'Describe',
   component: Describe
}

在子组件中通过如下方法获取传递的参数:

var id = this.$route.params.id

以上这种方法就是,vue动态路由的使用方式。根据传递的不同的参数来获取渲染不同的内容。

第二种:

父组件中,通过路由的name属性来确定匹配的路由,通过params来传递参数。
 

// 父组件中
this.$router.push({
    name: 'Describe',
    params: {
        id: id
    }
})

对应路由配置

// 这里可以添加/:id 也可以不添加,添加数据会在url后面显示,不添加数据就不会显示
{
     path: '/describe/:id',
     name: 'Describe',
     component: Describe
}

但是如果不添加,刷新时会出现id丢失的现象,但是我们可以通过在当前页面保存id参数,来解决这个问题。

// 子组件中获取参数
var id = this.$route.params.id

第三种:

父组件:使用path来匹配路由,然后通过query来传递参数,query传递的参数会显示在url后面,就像这样:?id=?

// 父组件中路由跳转,参数传递
this.$router.push({
    path: '/describe',
    query: {
        id: id
    }
})

对应路由配置:

{
     path: '/describe',
     name: 'Describe',
     component: Describe
}
// 子组件中这样获取参数
var id= this.$route.query.id

query 和 params的不同点:

         params传参是直接带在路径上面的,类似这样

http://localhost:8080/tenderingdetailZBGL/1329000571788230658 // 后面数字为携带id

        而query传参是拼接在 ? 后面的,类似这样 

http://localhost:8080/tenderingdetailZBGL/1329000571788230658?name=%E6%8B%9B%E6%A0%87%E5%85%AC%E5%91%8Asdd%E6%B5%8B%E8%AF%95&state=7

7,vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。----引自vue官方网站

通俗来讲,vuex可以当成一个数据库,是用来存储、管理、设置项目数据的地方(功能模块)。即数据即可以代表项目的状态,如登录状态等,项目中信息的存储即是状态的存储,所以也叫状态管理模式 ----个人理解

而使用vuex非常重要的原因是因为:

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

而vuex又是怎么使用的,又包含些什么东西呐:

  • state     唯一数据源 ,储存项目状态数据。
  • getters – state的计算属性 (getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。)
  • mutations – 更改状态的地方,同步操作
  • actions – 提交mutation,异步操作 (Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。)
  • mudules – 数据模块化管理

详细的vuex的使用,请看我的另一篇文章:vuex是什么?怎么用?

或者查看vue官网文档。 

8,依赖注入(provide / inject)

 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。

inject 选项可以是一个字符串数组 或 一个对象

一个对象,对象的 key 是本地的绑定名,value 是:

  • 在可用的注入内容中搜索用的 key (字符串或 Symbol),或
  • 一个对象,该对象的:
    • from property 是在可用的注入内容中搜索用的 key (字符串或 Symbol)
    • default property 是降级情况下使用的 value

提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。 

一种最常见的用法是刷新vue组件 

父组件 

<template>
  <div
    id="app"
  >
    <router-view
      v-if="isRouterAlive"
    />
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      isRouterAlive: true
  },

// 父组件中返回要传给下级的数据
  provide () {
    return {
      reload: this.reload
    }
  },
  methods: {
    reload () {
      this.isRouterAlive = false
      this.$nextTick(() => {
        this.isRouterAlive = true
      })
    }
  }
}
</script>

 子组件

<template>
  <div class="content" @click="sonClickFn">
      调用父组件reload方法
    </div>
</template>
<script>
export default {
  //引用vue reload方法
  inject: ['reload'],
  methods: {
    async sonClickFn() {
      this.reload()
    }
  }
}
</script>

也可以将父组件的数据向下传递到子组件中使用

// 父组件
<script>
    export default {
        provide() {
            return {
              source: this.activeTabStatus
            }
        },
        data() {
            return {
                // 对象属性可响应
                activeTabStatus: {
                    tabStatus: 'buyer'
                },
            }
        },
    }
</script>

// 子组件
<script>
    export default {
        inject: ['source']
    }
</script>

更多详情请查阅vue官方文档 provide / inject

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值