自定义组件(一)

自定义一个customButton组件

在components目录下创建customButton.vue文件

<template>
   <button @click="count++">You clicked me {{ count }} times.</button>
</template>

<script setup>
  import {ref} from "vue";

  const count = ref(0)
</script>

使用组件

引入组件有两种方法,一个是全局注册组件,另一个是局部引入组件。

全局注册组件

在main.js中创建 app.component() 方法,让组件在当前 Vue 应用中全局可用。

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
import customButton from './components/customButton.vue'
app.component('customButton',customButton)
app.mount('#app')

使用

<custom-button/>

app.component() 方法可以被链式调用:

app
  .component('ComponentA', ComponentA)
  .component('ComponentB', ComponentB)
  .component('ComponentC', ComponentC)
局部注册

全局注册虽然很方便,但有以下几个问题:

  • 全局注册,但并没有被使用的组件无法在生产打包时被自动移除,如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
  • 全局注册在大型项目中使项目的依赖关系变得不那么明确,影响应用长期的可维护性。

在使用<script setup>的单文件组件中,导入的组件可以直接在模板中使用,无需注册:

<template>
  <div>
   <custom-button></custom-button>
  </div>
</template>
<script setup>
import CustomButton from "@/components/customButton";
</script>

如果没有使用 <script setup>,则需要使用 components 选项来显式注册:

<template>
  <div>
   <custom-button></custom-button>
  </div>
</template>

<script>
import CustomButton from "@/components/customButton";
export default {
  components: {
    CustomButton
  },
  setup() {

  }
}
</script>

传递 props

props 声明

在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:

<script setup>
  import {ref,defineProps} from "vue";
  const count = ref(0)
  const props = defineProps(['title','likes'])
  console.log(props)
</script>

在没有使用<script setup>的组件中,prop 可以使用 props 选项来声明:

<script>
  import {ref} from "vue";
  export default {
    props: ['title','likes'],
    setup(props) {
      console.log(props)
      const count = ref(0)
      return {
        count
      }
    }
  }
</script>

除了使用字符串数组来声明 prop 外,还可以使用对象的形式:

<script setup>
  import {ref,defineProps} from "vue";
  const count = ref(0)
 // const props = defineProps(['title','likes'])
  const props = defineProps({
    titleString,
    likesNumber
  })
   console.log(props)
</script>
<script>
  import {ref} from "vue";
  export default {
   // props: ['title','likes'],
    props: {
      titleString,
      likesNumber
    },
    setup(props) {
      console.log(props)
      const count = ref(0)
      return {
        count
      }
    }

  }
</script>

传递 prop

自定义组件

<button @click="count++">{{title}}:{{likes}}</button>

向子组件传递 props

<custom-button title="点赞数" likes="5"/>

静态 vs. 动态 Prop

<custom-button title="点赞数" likes="5"/>

以上就是静态值形式的 props。

相应地,还有使用 v-bind 或缩写 : 来进行动态绑定的 props:

<template>
  <div>
    <!-- 根据一个变量的值动态传入 -->
   <custom-button title="点赞数" :likes="likes"/>
    <br>
    <!-- 根据一个更复杂表达式的值动态传入 -->
    <custom-button :title="userName+`点赞数`" :likes="likes+10"/>
  </div>
</template>

<script setup>
import {ref} from "vue";
const userName = ref('该用户获得')
const likes = ref(10)
</script>

传递不同的值类型

Number
<!-- 虽然 `42` 是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<custom-button :likes="42" />

<!-- 根据一个变量的值动态传入 -->
<custom-button :likes="post.likes" />
Boolean
<!-- 仅写上 prop 但不传值,会隐式转换为 `true` -->
<custom-button is-liked />

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

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

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

 />


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

Prop 校验

要声明对 props 的校验,你可以向 defineProps() 宏提供一个带有 props 校验选项的对象,比如

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

触发与监听事件

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件 (例如:在 v-on 的处理函数中):

<template>
   <button @click="$emit('addEvent')">{{title}}:{{likes}}</button>
</template>

<script setup>
  import {defineProps} from "vue";
  const props = defineProps({
    likesNumber,
    titleString,
  })
   console.log(props)
</script>

父组件可以通过 v-on (缩写为 @) 来监听事件:

<template>
    <div>
      <custom-button @addEvent="callback" title="点赞数" :likes="likes"></custom-button>
    </div>
</template>

<script setup>
import {ref} from "vue";
import CustomButton from "@/components/customButton";
const likes = ref(10)
const callback = () => {
  likes.value++
}
</script>

组件的事件监听器也支持.once修饰符:

 <custom-button @addEvent.once="callback" title="点赞数" :likes="likes"></custom-button>
事件参数

$emit 提供一个额外的参数:

 <button @click="$emit('addEvent',2)">{{title}}:{{likes}}</button>

父组件

<template>
    <div>
      <custom-button @addEvent="callback" title="点赞数" :likes="likes"></custom-button>
    </div>
</template>

<script setup>
import {ref} from "vue";
import CustomButton from "@/components/customButton";
const likes = ref(10)
const callback = (n) => {
   likes.value +=  n
}
</script>

所有传入 $emit() 的额外参数都会被直接传向监听器。举例来说,$emit('foo', 1, 2, 3) 触发后,监听器函数将会收到这三个参数值。

声明触发的事件

组件可以显式地通过 defineEmits() 宏来声明它要触发的事件:

<template>
   <button @click="addClick">{{title}}:{{likes}}</button>
</template>

<script setup>
  import {defineProps,defineEmits} from "vue";
  defineProps({
    likesNumber,
    titleString,
  })
  const emit = defineEmits(['addEvent'])
  const addClick = () => {
  //  emit('addEvent')
    emit('addEvent',2)//传参数
  }
</script>

如果你显式地使用了 setup 函数而不是 <script setup>,则事件需要通过 emits 选项来定义,emit 函数也被暴露在 setup() 的上下文对象上:

<template>
   <button @click="addClick">{{title}}:{{likes}}</button>
</template>
<script>
  export default {
    props: {
      titleString,
      likesNumber
    },
    emits: ['addEvent'],
    setup(props,ctx) {
      const addClick = () => {
       // ctx.emit('addEvent')
         ctx.emit('addEvent',2)
      }
      return {
        addClick
      }
    }
  }
</script>
事件校验

要为事件添加校验,那么事件可以被赋值为一个函数,接受的参数就是抛出事件时传入 emit 的内容,返回一个布尔值来表明事件是否合法。

比如:

<script setup>
const emit = defineEmits({
  // 没有校验
  clicknull,

  // 校验 submit 事件
  submit({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password{
  emit('submit', { email, password })
}
</script>

组件 v-model

v-model 可以在组件上使用以实现双向绑定。自定一个input组件customInput

  • 将内部原生 <input> 元素的 value 属性 绑定到 modelValue prop。
  • 当原生的 input 事件触发时,触发一个携带了新值的 update:modelValue 自定义事件。
<template>
  <input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)">
</template>

<script setup>
  import {defineProps,defineEmits} from "vue";
  defineProps(['modelValue'])
  defineEmits(['update:modelValue'])
</script>

现在 v-model 可以在这个组件上正常工作了:

<custom-input v-model="searchText"></custom-input>

另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 gettersettercomputed 属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件:

<template>
<!--  <input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)">-->
  <input v-model="value" />
</template>

<script setup>
import {defineProps, defineEmits, computed} from "vue";
  // defineProps(['modelValue'])
  // defineEmits(['update:modelValue'])
  const props = defineProps(['modelValue'])
  const emit = defineEmits(['update:modelValue'])
  const value = computed({
    get() {
      return props.modelValue
    },
    set(value) {
      emit('update:modelValue',value)
    }
  })
</script>

v-model 的参数

默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。我们可以通过给v-model指定一个参数来更改这些名字。

子组件应声明一个 title prop,并通过触发 update:title 事件更新父组件值:

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

<script setup>
  import {defineProps,defineEmits} from "vue";
  defineProps(['title'])
  defineEmits(['update:title'])
</script>

父组件

 <custom-input @input="input" v-model:title="searchText"></custom-input>
多个 v-model 绑定

在组件实例上创建多个 v-model 双向绑定。 创建一个UserName组件

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

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

</template>

<script setup>
  import {defineProps,defineEmits} from "vue";
  defineProps({
    firstNameString,
    lastNameString
  })
  defineEmits(['update:firstName','update:lastName'])
</script>

父组件

...
 <user-name :first-name="firstName" :last-name="lastName"></user-name>
 ...
 
<script setup>
import {ref} from "vue";
const firstName = ref('Lo')
const lastName = ref('Hedy')
</script>
处理 v-model 修饰符

v-model有一些内置的修饰符,例如 .trim.number .lazy。在某些场景下,你可能想要一个自定义组件的 v-model 支持自定义的修饰符。

我们来创建一个自定义的修饰符 capitalize,它会自动将 v-model 绑定输入的字符串值第一个字母转为大写:

<MyComponent v-model.capitalize="myText" />

组件的 v-model 上所添加的修饰符,可以通过 modelModifiers prop 在组件内访问到。在下面的组件中,我们声明了 modelModifiers 这个 prop,它的默认值是一个空对象:

<script setup>
const props = defineProps({
  modelValueString,
  modelModifiers: { default() => ({}) }
})

defineEmits(['update:modelValue'])

console.log(props.modelModifiers) // { capitalize: true }
</script>

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

</template>

注意这里组件的 modelModifiers prop 包含了 capitalize 且其值为 true,因为它在模板中的 v-model 绑定 v-model.capitalize="myText" 上被使用了。

有了这个 prop,我们就可以检查 modelModifiers 对象的键,并编写一个处理函数来改变抛出的值。在下面的代码里,我们就是在每次 <input /> 元素触发 input 事件时将值的首字母大写:

<script setup>
const props = defineProps({
  modelValueString,
  modelModifiers: { default() => ({}) }
})

const emit = defineEmits(['update:modelValue'])

function emitValue(e{
  let value = e.target.value
  if (props.modelModifiers.capitalize) {
    value = value.charAt(0).toUpperCase() + value.slice(1)
  }
  emit('update:modelValue', value)
}
</script>

<template>
  <input type="text" :value="modelValue" @input="emitValue" />
</template>

对于又有参数又有修饰符的 v-model 绑定,生成的 prop 名将是 arg + “Modifiers”。举例来说:

<MyComponent v-model:title.capitalize="myText">

相应的声明应该是:

const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])

console.log(props.titleModifiers) // { capitalize: true }

观看更多相关文章请关注公众号:

自定义组件(一)
前端编程.jpg


原文始发于微信公众号(大前端编程教学):自定义组件(一)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/224278.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!