vue3的props和defineProps

1. Props 声明

  • 一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute
  • props 需要使用 props 选项来定义

1.1 props用字符串数组来声明

看上去跟vue2没什么区别,就是先引入组件,然后注册组件,然后就使用组件。接着绑定属性。

Blog.vue
<template>
    <div>
        <h1>Blog组件</h1>
        <blog-post title="学习vue3"/>
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        }
    }

</script>

<style lang="scss"></style>
BlogPost.vue
<template>
    <h3>{{ title }}</h3>
</template>

<script>

    export default {
        name: 'BlogPost',
        props: ['title']
    }

</script>

<style lang="scss"></style>

1.2 props使用对象来声明

  • key 是 prop 的名称,而值则是该 prop 预期类型的构造函数
  • 如果在使用时,传递了错误的类型,会在浏览器控制台中抛出警告
Blog.vue
<template>
    <div>
        <h1>Blog组件</h1>
        <blog-post title="学习vue3" :likes="3"/>
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        }
    }

</script>

<style lang="scss"></style>
BlogPost.vue
<template>
    <h3>{{ title }}</h3>
    <span>{{ likes }}</span>
</template>

<script>

export default {
    name: 'BlogPost',
    props: {
        title: String,
        likes: Number
    }
}

</script>

<style lang="scss"></style>

2. 传递 prop 的细节

2.1 Prop 名字格式

  • 定义prop的名字时 ,建议使用 camelCase 形式(驼峰命名,首字母小写)
  • 向子组件传递 props 时,可以使用 camelCase 形式,但通常使用kebab-case形式(短横线形式)
  • 定义组件名时,建议使用PascalCase形式(驼峰命名,但首字母大写)

2.1 静态Prop & 动态 Prop

静态prop
<BlogPost title="My journey with Vue" />
动态prop

可以将组件中定义的属性值(响应式数据)传给子组件,当这些属性值发生变化时,子组件也会更新重新渲染

<!-- 根据一个变量的值动态传入 -->
<BlogPost :title="post.title" />

<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
示例

在这里插入图片描述

Blog.vue
<template>
    <div>
        <h1>Blog组件</h1>
        <button @click="addLike">addLike</button>
        <blog-post title="学习vue3" :likes="likes"/>
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        },
        data() {
            return {
                likes: 0
            }
        },
        methods: {
            addLike() {
                this.likes ++
            }
        }
    }

</script>

<style lang="scss">

</style>
BlogPost.vue
<template>
    <h3>{{ title }}</h3>
    <span>{{ likes }}</span>
</template>

<script>

export default {
    name: 'BlogPost',
    props: {
        title: String,
        likes: Number
    }
}

</script>

<style lang="scss"></style>

2.3 传递不同的值类型

  • 任何类型的值都可以作为 props 的值被传递。
  • 使用 v-bind绑定的值 将视为 js表达式
Number
<!-- 虽然 `42` 是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :likes="42" />

<!-- 根据一个变量的值动态传入 -->
<BlogPost :likes="post.likes" />
Boolean

注意会隐式转换,和 使用 v-bind绑定的值 将视为 js表达式

<!-- 仅写上 prop 但不传值,会隐式转换为 `true` -->
<BlogPost is-published />

<!-- 虽然 `false` 是静态的值,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :is-published="false" />

<!-- 根据一个变量的值动态传入 -->
<BlogPost :is-published="post.isPublished" />
Array
<!-- 虽然这个数组是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- 根据一个变量的值动态传入 -->
<BlogPost :comment-ids="post.commentIds" />
Object
<!-- 虽然这个对象字面量是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :author="{name: 'Veronica', company: 'Veridian Dynamics'}" />

<!-- 根据一个变量的值动态传入 -->
<BlogPost :author="post.author" />

2.4 使用一个对象绑定多个 prop

可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。这样就可以将一个对象的所有属性都当作 props 传入

在这里插入图片描述

Blog.vue
<template>
    <div>
    
        <h1>Blog组件</h1>
        
        <blog-post v-bind="post"/>
        
        <blog-post :title="post.title" :likes="post.likes"/>
        
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        },
        data() {
            return {
                post: {
                    likes: 3,
                    title: '学习vue3'
                }
            }
        }
    }

</script>

<style lang="scss"></style>
BlogPost.vue
<template>
    <h3>{{ title }}</h3>
    <span>{{ likes }}</span>
</template>

<script>

export default {
    name: 'BlogPost',
    props: {
        title: String,
        likes: Number
    }
}

</script>

<style lang="scss"></style>

3. 单向数据流(重要)

  • 所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

  • 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告

    export default {
      props: ['foo'],
      created() {
        // ❌ 警告!prop 是只读的!
        this.foo = 'bar'
      }
    }
    
  • 子组件想修改prop,可以使用 “变通的手段来更改prop”(不是真正的修改)

    • prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:

      export default {
        props: ['initialCounter'],
        data() {
          return {
            // 计数器只是将 this.initialCounter 作为初始值
            // 像下面这样做就使 prop 和后续更新无关了
            counter: this.initialCounter
          }
        }
      }
      
    • 需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性

      export default {
        props: ['size'],
        computed: {
          // 该 prop 变更时计算属性也会自动更新
          normalizedSize() {
            return this.size.trim().toLowerCase()
          }
        }
      }
      

3.1 更改对象 / 数组类型的 props

对 象数组 作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值

  • 在最佳实践中,你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合

  • 在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变

在这里插入图片描述

Blog.vue
<template>
    <div>
        <h1>Blog组件</h1>
        <blog-post :post="post" @addLiking="addLiking"/>
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        },
        data() {
            return {
                post: {
                    likes: 3,
                    title: '学习vue3',
                    tags: ['java','spring','vue','ps']
                },
                
            }
        },
        methods: {
            addLiking(){
                this.post.likes += 2
            }
        }
    }

</script>

<style lang="scss"></style>
BlogPost.vue
  • 父组件通过props传递过来post
  • 子组件中直接修改通过props传过来的post(对象)里面内部的属性
  • 子组件中直接修改通过props传过来的post.tags(数组)里面内部的属性
<template>
    <h3>{{ post.title }}</h3>
    <span>{{ post.likes }}</span> 

    <br/>

    <span v-for="tag,idx in post.tags" :key="idx">{{ tag }}、</span>
    
    <br/>

    <button @click="addLike">addLike + 1</button>
    <button @click="emitLike">emitLike + 2</button>

    <br/>

    <button @click="post.tags[3] = 'redis'">changeArr</button>
    
</template>

<script>

export default {
    name: 'BlogPost',
    props: {
        post: Object
    },
    methods: {
        addLike() {
            this.post.likes++
        },
        emitLike() {
            this.$emit('addLiking')
        }
    }
}

</script>

<style lang="scss"></style>

4. Prop 校验

可以向 props 选项提供一个带有 props 校验选项的对象。

  • 所有 prop 默认都是可选的,除非声明了 required: true
  • 除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
    • Boolean 类型的未传递 prop 将被转换为 false!!!(但是,可以通过default来修改,比如:default:undefined。)。
  • 如果声明了 default 值,那么在 prop 的值被解析为 undefined 时,无论 prop 是未被传递还是显式指明的 undefined,都会改为 default 值。
export default {

  props: {
  
    // 基础类型检查
    //(给出 `null` 和 `undefined` 值则会跳过任何类型检查)
    propA: Number,
    
    // 多种可能的类型
    propB: [String, Number],
    
    // 必传,且为 String 类型
    propC: {
      type: String,
      required: true
    },
    
    // Number 类型的默认值
    propD: {
      type: Number,
      default: 100
    },
    
    // 对象类型的默认值
    propE: {
      type: Object,
      // 对象或者数组应当用工厂函数返回。
      // 工厂函数会收到组件所接收的原始 props
      // 作为参数
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    
    // 自定义类型校验函数
    propF: {
      validator(value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    
    // 函数类型的默认值
    propG: {
      type: Function,
      // 不像对象或数组的默认,这不是一个
      // 工厂函数。这会是一个用来作为默认值的函数
      default() {
        return 'Default function'
      }
    }
    
  }
}

4.1 运行时类型检查

校验选项中的 type 可以是下列这些原生构造函数,也可以是自定义的类或构造函数,Vue 将会通过 instanceof 来检查类型是否匹配。

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

5. Boolean 类型转换

声明为 Boolean 类型的 props 有特别的类型转换规则

在这里插入图片描述

Blog.vue

  • 虽然diabled属性被声明为了Boolean类型,但是,如果传一个字符串过去,仍然就是字符串。
<template>
    <div>
        <h1>Blog组件</h1>
        <blog-post disabled/> <br/>
        <blog-post disabled/> <br/>
        <blog-post disabled="false"/> <br/>
        <blog-post disabled="false1"/> <br/>
        <blog-post :disabled="false"/>
    </div>
</template>

<script >
    import BlogPost from './BlogPost.vue'
    export default {
        name: 'Blog',
        components:{
            BlogPost
        },
        data() {
            return {}
        }
    }

</script>

<style lang="scss"></style>

BlogPost.vue

<template>
    
    {{ disabled }}

</template>

<script>

export default {
    name: 'BlogPost',
    props: {
        disabled:Boolean
    }
}

</script>

<style lang="scss"></style>
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Vue3中,当一个页面引入多个echarts时,由于每个echart实例都具有自己的Dom元素,并且每个实例都需要根据父容器的大小进行自适应,可能会出现自适应失灵的问题。 为了解决这个问题,可以采取以下几个步骤: 1.确保每个echart实例的Dom元素都有唯一的id,在页面的template中设置不同的id。 2.在Vue3中,可以通过使用refs来获取echart实例,然后对每个实例进行相应的操作。 3.在生命周期的mounted钩子函数中,对每个echart实例进行初始化,并设置其自适应。可以使用window的resize事件监听父容器的大小变化,然后调用echart实例的resize方法。 4.在数据更新后,可以通过watch来监听数据的改变,并调用相应的echart实例的setOption方法进行更新。 综上所述,通过以上步骤,可以使得多个echarts实例在同一个页面中进行自适应,并且随着父容器的大小变化而更新。这样就可以解决多个echarts实例自适应失灵的问题。 ### 回答2: 当一个页面引入了多个echarts图表时,使用Vue3进行自适应可能会导致失灵的情况发生。这是因为Vue3中的组件渲染和数据响应机制发生了变化,可能会导致echarts图表无法正确地根据父容器的大小进行自适应。 要解决这个问题,我们可以采取以下步骤: 1. 在Vue3中,使用`nextTick`方法来确保DOM已经完全渲染后再进行echarts图表的初始化。在组件的`mounted`钩子函数中,使用`nextTick`来确保初始化的echarts图表能够正确获取父容器的大小。 2. 在需要自适应的echarts图表中,使用`window.onresize`事件来监听窗口大小的变化,并在事件回调函数中重新绘制图表。这样可以确保图表能够根据父容器的大小进行自适应。 3. 如果有多个echarts图表需要进行自适应,可以为每个图表添加一个专属的`resize`事件,并在事件回调函数中根据当前图表的父容器大小重新绘制图表。 总结来说,要解决Vue3中多个echarts图表自适应失灵的问题,我们需要确保图表的初始化在DOM渲染完成后进行,并通过监听窗口大小变化的事件来实现图表的自适应。这样可以确保多个图表能够正确地根据父容器的大小进行自适应。 ### 回答3: 在Vue3中,当一个页面引入多个echarts图表并且要实现自适应的时候,可能会出现自适应失灵的情况。这是因为echarts图表默认是根据其容器的大小来自适应的,而在Vue3中,由于组件的加载顺序以及页面的渲染顺序可能存在差异,导致echarts在计算容器大小时获取到的是不准确的数值。 解决这个问题的方法有以下几种: 首先,可以使用Vue3的生命周期钩子函数来手动触发echarts的自适应计算。在mounted钩子函数中,使用ref获取到页面上echarts容器的DOM元素,然后调用echarts的resize()方法,强制图表重新计算自适应大小。这样即使组件加载顺序有变化,也能确保每个图表都能正确地计算自适应大小。 其次,可以使用Vue3的watch特性来监听页面容器的大小变化。通过监听容器的width和height属性,当属性发生变化时,调用echarts的resize()方法进行自适应计算。这样无论页面中有多少个echarts图表,只要有容器大小变化,都能触发echarts的自适应计算。 最后,可以考虑使用Vue3提供的第三方插件或者封装自定义指令来处理echarts的自适应。这些插件或者指令会在页面渲染完毕后自动触发echarts的resize()方法,保证图表大小的正确计算。通过使用这些插件或者指令,可以简化代码,提高开发效率。 综上所述,通过合理运用生命周期钩子函数、watch特性以及第三方插件或自定义指令,可以解决在Vue3中多个echarts图表自适应失灵的问题。这样能够确保页面中的每个图表都能正确计算适应大小,提供良好的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值