目录
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