likes
comments
collection
share

组件传值,数据双向绑定

作者站长头像
站长
· 阅读数 36

一、组件传值

组件间的传值方式

父子传值:props、emit、$parent、$chidren、ref兄弟之间传值:事件总线(on、emit)、Vuex

传值实例

(1)父组件传到子组件(props)一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。

// 父组件
<template>
    <div>
        <ceshi @test="test"></ceshi>
    </div>
</template>
<script>
export default {
    data() {
        test: '测试'
    }
}
</script>

// 子组件接收 ceshi
<template>
    <div>
        {{test}}
    </div>
</template>
<script>
export default {
    props: {
        test: String
    }
    data() {
        
    }
}
</script>

(2)子组件传到父组件(emit)子组件通过emit触发事件给父组件,父组件通过on去监听数据的变化。

// 父组件
<template>
    <div>
        <ceshi :test="test" @change="change"></ceshi>
    </div>
</template>
<script>
export default {
    data() {
        test: '测试'
    },
    methods: {
       change(val){
           console.log(val)
       }
    }
}
</script>

// 子组件接收 ceshi
<template>
    <div>
        {{test}}
        <button @click="getTitle"></button>
    </div>
</template>
<script>
export default {
    `props: {
        test: String
    }`
    data() {
        title: '子组件'
    },
    methods: {
       getTitle(){
           this.$emit('change', this.title) // 将值绑定到change上传递过去
       }
    }
}
</script>

二、prop

prop类型

以字符串形式列出:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

通常我们都需要每个 prop 都有指定的值类型,因此通过对象的形式列出、分别标明prop的名称和类型以及默认值

props: {
   test: String,
   likes: {
       type: Number,
       default: () => []
   },
   isPublished: {
       type: Boolean,
       default: false
   }
}

传递静态或动态 Prop

// 静态传值
<blog-post title="My journey with Vue"></blog-post>

// 动态赋予一个变量的值
<blog-post v-bind:title="post.title"></blog-post>

// 动态赋予一个复杂表达式的值
<blog-post
  v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

需要改变prop时,可以通过

(1)子组件内需要将prop作为本地数据进行使用,可以在data中将这个prop作为初始值

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

(2)这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。

三、自定义事件(v-model)

双向绑定v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

// 子组件
<template>
    <div>
        <el-input v-model="price" @input="updateVal"></el-input>
    </div>
</template>
<script>
export default {
   // model用于改变v-model绑定的属性和抛出事件(实时监听)
    model: {
        prop: 'price',
        event: 'change'
   },
    props: {
        price: Number
    },
    methods: {
       updateVal(val){
           this.$emit('change', val) // 监听price值变化,传递给父组件
       }
    }
}
</script>

在父组件使用v-model,通过该语句实现price变量与输入值双向绑定

<ceshi @change="change" v-model="price"></ceshi>


// 等同于
<ceshi @change="change" :value="price" @input="price=$event.target.value"></ceshi>

执行流程:1、子组件通过通过 prop 接收父组件的数据price2、当input进行输入时,会触发input事件3、通过 $emit 提交事件,将值传递给父组件4、父组件的input事件被触发,将会更新变量price的值5、父组件在通过props传值给子组件

v-bind 实现单向绑定v-model 实现双向绑定

.sync修饰符

真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源。因此推荐update:myPropName 的模式触发事件

vue事件名可以updata:prop去定义,用于处理prop传递的新值组件内部将通过$emit('update:dtitle', val)触发事件,修改title的值

// 子组件
<el-input v-model="title" @input="updateVal"></el-input>

props: ['title'],
updateVal(val) {
    this.$emit('update:dtitle', val)
}
// 父组件
<ceshi @update:dtitle="title = $event" :title.sync="title"></ceshi>

注意事项:1、 .sync 修饰符的 v-bind 不能和表达式一起使用 2、组件内部需要$emit触发的事件名,格式为update:prop

同时在使用sync的时候,简化了绑定prop和绑定update:prop事件

四、动态组件&异步组件

动态组件

通过 is 来控制不同组件的切换

<component v-bind:is="currentTabComponent"></component>

但是有时候切换组件的时候,组件都会重新渲染为了避免反复重新渲染这个问题,可以通过keep-alive来实现

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

注意这个 <keep-alive> 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。

异步组件

全局或局部引入组件时,一般通过import引入,然后在components注入。

import Header from './pages/header'

components: {
       Header
}

1、使用异步组件可以减少打包的结果,会将异步组件分开打包,会采用异步的方式加载组件。2、异步组件的核心可以给组件定义变成一个函数

components: {
    myComponent(resolve){
      // 这个特殊的 `require` 语法将会告诉 webpack
      // 自动将你的构建代码切割成多个包,这些包
      // 会通过 Ajax 请求加载
      require(['./my-component'], resolve)
    }
}

components: {
    'my-component': () => import('./my-component')
}

结语

流云秋风金黄 月落舟窗,一句我特别喜欢的句子,也希望这篇文章给到大家帮助,编写不易,也希望帮忙点个赞。