Vue 3教程 - 属性props


使用组件属性的优势

  • 可重用性:使得组件变得可被定制,进而能被重用
  • 数据一致性:保证single source of true,只有一个唯一的数据源。

使用属性

在组件中定义属性

<template>
  <div>{{ prop1 }}</div>
</template>

<script>
export default {
  props: ['prop1', 'prop2']
}
</script>

传递属性给组件

<MyComponent prop1="prop1 value"/>

数据绑定data binding

也可以绑定当前组件的属性进而动态获得值

<template>
  <MyComponent :prop1="p1"/>
</template>

<script>
    // ...
    data() {
      p1: 'test value'
    }
</script>  

如果想要传递非字符串类型的属性值时,可以使用数据绑定(data binding):

更多的时候是在script中定义/获取数据,然后通过数据绑定将这些数据传递给子组件:

<template>
  <MyComponent :header="header" />
</template>  

<script>
import MyComponent from './components/MyComponent.vue'

export default {
  components: {MyComponent},
  data() {
    return {
      header: 'This is the header'
    }
  }
}
</script>  

通过数据绑定实现不同主题

在调用组件时:

<MyComponent theme="dark"/>

在组件中:

<template>
<div class="message_box" :class="theme === 'dark' ? 'dark' : 'light'">
</template>

<script>
export default {
  props: ["theme"],
};
</script>

<style>
.dark {
  background: black;
}
.light {
  background: lightgrey;
}
</style>  

使用对象类型属性

像前面例子一样,我们可以使用数组来定义组件的props。但同时也可以使用对象来定义这些props,这样就能在定义prop的同时声明其属性:

export default {
  name: 'Student',
  props: {
    name: String,
    score: Number,
    registered: Boolean,
  }
}  

这样在调用这个组件的时候,Vue就会检查传递过来的属性类型,因此这时需要使用 :score 来声明这个属性不是字符串类型的。

<Student name="Paul" :score="90" :registered="true" />

验证属性

必填属性及默认值

props: {
  name: {
    type: String,
    required: true,
    default: 'YOUR NAME'
  }
}

非prop属性

针对非prop属性,Vue的默认行为

Vue的一个默认行为就是,对于任何非prop的属性,会被自动添加到该组件的唯一根元素上。如果根元素不唯一,则不会添加该属性到任何元素上。

比如:在Student组件中没有定义id属性,但如果传递了id属性的话:

<Student id="9901"/>

则选然后的Studenet组件中,根元素上会被添加一个id属性:

<div data-v-xxxxxxxx id="9901">

自定义非prop属性的绑定行为

在组件中,可以通过$atts来自己指定想要把传递过来的非prop属性绑定到哪个元素上:

<template>
  <div>title</div>
  <div v-bind="$atts">{{ name }}</div>
</template>  

还可以禁止Vue的默认行为,这样就不会自动将非prop属性绑定到根元素上。

export default {
  // ...
  inheritAttrs: false
}

Provide / Inject API

在一个多级组件中,可以通过props来从上到下传递信息。但这就涉及到很多不必要的属性传递。通过Provide/Inject API,就可以实现在Vue组件中传递属性,而不需在组件层级结构中显式传递。这就有些点像React中的Context API。

其涉及到的操作包括:

  • 在上级组件中,比如App中提供 (Provide) 相应值
  • 在下级组件中注入 (Inject) 对应的值

In App.vue:

export default {
  name: 'Student',
  data() {
    //
  },
  provide: {
    authenticated: true
  }
}

In sub component, e.g. Header.vue:

export default {
  inject: ['authenticated']
}

但目前存在一个小问题,在App中无法直接访问authenticated的值。如果要解决的话:

export default {
  name: 'Student',
  data() {
    return {
      authenticated: true
    }
  },
  provide() {
    return {
      authenticated: this.authenticated,
    }
  }
}

通过自定义事件从子组件传递值到父组件

我们已经知道可以通过props将值从父组件传递到子组件。那么如何从子组件传递信息到父组件呢?在Vue中可以通过自定义事件来实现。

自定义事件

比如在子组件Dialog中:

<template>
  <button @click="$emit('close')">关闭对话框</button>
</template>
<script>
  export default {
    name: 'Dialog',
    emits: ['close']
  }
</script>  

在父组件中:

<Dialog v-show="showDialog" @close="showDialog = false" />

在自定义事件中传递参数

子组件中:

<button @click="$emit('close', 'lcoding')">关闭对话框</button>

父组件中:

<Dialog v-show="showDialog" @close="closeDialog" />

<script>
  // ...
  methods: {
    closeDialog(tag) {
      this.showDialog = false;
      console.log(tag);
    }
  }
</script>  

定义自定义事件参数的验证规则

子组件中:

<template>
  <input type="text" v-model="tag" />
  <button @click="$emit('close', tag)">关闭对话框</button>
</template>

<script>
  emits: {
    close: (tag) => {
      if (tag) {
        return true;
      } else {
        return false;
      }
    }
  },
  data() {
    return {
      tag: '';
    }
  }
</script>  

注意,上面的代码中,无论验证是否通过,都会发出close事件。关于这点,可以进行改进。比如:单击关闭按钮时,进行验证,如果验证通过,发出close事件,否则什么都不做。

methods: {
  closeDialog() {
    if (this.tag) {
      this.$emit("close");
    }
  },
},

如何通过v-model将属性和自定义组件关联

问题描述

在默认情况下,如果通过v-model绑定了一个一般属性,则会实现UI和属性的双向绑定。但如果使用v-model绑定一个属性到自定义组件时,Vue不会实现这种双向绑定。比如:

有一个简单的Input组件:

<template>
  <input type="text" />
</template>

在父组件中调用这个组件:

<Input v-model="name">
...
<script>
  // ...
  data() {
    return name: '';
  }
</script>  

当输入框中的值发生变化时,App的name属性并不会随之发生变化。

解决方法

在子组件Input中:

<template>
  <input
    type="text"
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<script>
export default {
  name: "MyInput",
  props: {
    modelValue: String,
  },
};
</script>

示例-模式对话框

创建ModalBox.vue

添加文件components/ModalBox.vue:

<template>
  <div class="background">
    <div class="modal">
      <p>test content</p>
    </div>
  </div>
</template>

<style>
.background {
  position: fixed;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.8);
  width: 100%;
  height: 100%;
}
.modal {
  margin: 150px auto;
  padding: 100px;
  width: 600px;
  background: white;
  border-radius: 5px;
}
</style>

在父组件中调用ModalBox

在父组件App.vue中:

<template>
  <ModalBox />
</template>

<script>
import ModalBox from "./components/ModalBox.vue";

export default {
  name: "App",
  components: {
    ModalBox,
  },
};
</script>

文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !
  目录