Vue 3.0 快速上手组件篇(二)

103 阅读1分钟

前言

Vue目前是一款流行的前端框架,已更新到3.0版本,她是一套用于构建用户界面的渐进式框架,易于上手并且有强大的社区,和友好的开发文档。上一篇简单整理了vue的基础,本章继续学习vue的组件,代码还是一定要自己敲一遍两遍三遍。

组件基础

一、组件基础

使用cdn,示例:

<div id="app">
    {{text}}
    <button-counter />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                text: 111
            }
        }
    })
    
    // 创建一个组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        template: `<div>我是一个组件</div>`
    })

    app.mount('#app')
</script>

使用npm

新建一个component.vue文件,然后在父组件index.vue中,使用import component from 'component.vue'引入子组件,最后在index.vue中使用,示例:

// index.vue 父组件
<template>
  <div>我是父组件</div>
  <component />
</template>

<script setup>
import component from './component.vue'
</script>

// component.vue 子组件
<template>
  <div>我是子组件</div>
</template>

二、通过prop向子组件传递数据

使用cdn,示例:

<div id="app">
    {{text}}
    <button-counter v-model:title="title" />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                text: 111,
                title: '父组件向子组件传值'
            }
        }
    })
    
    // 创建一个组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        props: ['title'],
        template: `
          <div>我是一个组件</div>
          <div>{{title}}</div>
        `
    })

    app.mount('#app')
</script>

使用npm

我这里使用的是vue3setup语法糖,子组件拿到父组件的值需要用到defineProps这个函数。如果不想使用setup语法糖也可以使用cdn的写法。

// index.vue 父组件
<template>
  <div>我是父组件</div>
  <component title="父组件向子组件传值" />
</template>

<script setup>
import component from './component.vue'
</script>

// component.vue 子组件
<template>
  <div>我是子组件</div>
  <div>{{props.title}}</div>
</template>

<script setup>
import { defineProps } from 'vue'

const props = defineProps({
  title: {
    required: true
  }
})
</script>

三、监听子组件事件

当我们在开发组件的时候,它有些功能是需要和父组件进行沟通的,这时候我们就可以用这个方法。

使用cdn,示例:

<div id="app">
    <button-counter @event-child="handlerEvent" />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
            }
        },
        methods: {
            handlerEvent () {
                console.log('我被子组件触发了')
            }
        }
    })
    
    // 创建一个组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        template: `
              <div @click="handlerBtn">点击</div>
        `,
        methods: {
          handlerBtn () {
            this.$emit('eventChild')
          }
        }
    })

    app.mount('#app')
</script>

使用npm

使用vue3setup语法糖,子组件向父组件传值需要用到defineEmits这个函数

// index.vue 父组件
<template>
  <div>我是父组件</div>
  <component @eventChild="handlerEvent" />
</template>

<script setup>
import component from './component.vue'

const handlerEvent = (val) => {
  console.log('我被子组件触发了')
  console.log(val)
}
</script>

// component.vue 子组件
<template>
  <div>我是子组件</div>
  <div @click="handlerBtn">点击</div>
</template>

<script setup>
import { defineEmits } from 'vue'

const emit = defineEmits(['eventChild'])

const handlerBtn = () => {
  emit('defineEmits', '我可以传值给父组件')
}
</script>

特殊,可以使用$emit('update:title', '修改的值')直接修改父组件传来的执行,而不需要通过事件去修改。示例:

<div id="app">
    <button-counter v-model:title="title" />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                title: '父组件向子组件传值'
            }
        },
        methods: {
        }
    })
    
    // 创建一个组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        props: ['title'],
        template: `
              <div @click="handlerBtn">点击:{{title}}</div>
        `,
        methods: {
          handlerBtn () {
            this.$emit('update:title', '直接被子组件修改了')
          }
        }
    })

    app.mount('#app')
</script>

四、插槽

向一个组件传递内容,比如:文本或者html元素。这时就可以使用插槽(slot)来实现。示例:

使用cdn,npm也是一样的

<div id="app">
    <button-counter>
        <div>通过插槽来传递内容</div>
        <div>传递内容到子组件</div>
    </button-counter>
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
            }
        },
        methods: {
        }
    })
    
    // 创建一个组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        template: `
          <div>我是一个子组件</div>
          <slot></slot>
        `
    })

    app.mount('#app')
</script>

五、Provide / Inject

深度嵌套。如果父组件向子组件传值,如果只有一层使用props即可,如果嵌套了多层就需要使用Provide / Inject来实现。

使用cdn,示例:

<div id="app">
    <div @click="handlerProvide">点击:{{text}}</div>
    <button-counter />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    // 父组件
    const app = Vue.createApp({
        data() {
            return {
                text: 111,
                title: '父组件传递的值'
            }
        },
        provide() {
            return {
                parentText: Vue.computed(() => this.title)
            }
        },
        methods: {
            handlerProvide() {
                this.title = '改变了,父组件传递的值'
            }
        }
    })
    
    // 第一层子组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        template: `
          <div>我是一个一层嵌套组件</div>
          <button-counter-child />
        `
    })
    
    // 第二层子组件
    app.component('button-counter-child', {
        data() {
          return {
          }
        },
        inject: ['parentText'],
        template: `
          <div>我是一个二层嵌套组件</div>
          <div>{{parentText.value}}</div>
        `
    })

    app.mount('#app')
</script>

注意:如果要想实现响应式,通过Vue.computed来实现,如父组件中的parentText属性。

使用npm,示例:

// 父组件
<template>
    <div @click="handlerProvide">点击改变值</div>
    <test1-component />
</template>

<script setup>
import test1Component from './test1-component.vue'
import { ref, provide } from 'vue'

const title = ref('父组件传递的值')
provide('parentText', title)

const handlerProvide = () => {
  title.value = '修改了父组件传递的值'
}
</script>

// 第一层子组件
<template>
  <div>我是一个一层嵌套组件</div>
  <test2-component />
</template>

<script setup>
import test2Component from './test2-component.vue'
</script>

// 第二层子组件
<template>
  <div>我是一个二层嵌套组件</div>
  <div>{{title}}</div>
</template>

<script setup>
import { inject } from 'vue'
const title = inject('parentText')
</script>

六、模板引用

虽然已经存在了props 和 事件,基本可以解决与子组件的通信,但有时候我们会在JavaScript中直接访问子组件。那么我们可以使用ref来实现。示例:

使用cdn,示例:

<div id="app">
    <button-counter ref="userInput" />
</div>

<script src="https://unpkg.com/vue@next"></script>
<script>
    // 父组件
    const app = Vue.createApp({
        data() {
            return {
            }
        },
        mounted() {
            this.$refs.userInput.focusInput()
        }
    })
    
    // 子组件
    app.component('button-counter', {
        data() {
          return {
          }
        },
        template: `
          <input ref="input" placeholder="请输入" />
        `,
        methods: 
          focusInput() {
            this.$refs.input.focus()
          }
        }
    })

    app.mount('#app')
</script>

使用npm,示例:

// index.vue 父组件
<template>
  <component ref="userInput" />
</template>

<script setup>
import component from './component.vue'
import { ref, onMounted } from 'vue'

const userInput = ref(null)

onMounted(() => {
  userInput.value.$refs.input.focus()
})
</script>

// component.vue 子组件
<template>
  <input ref="input" placeholder="请输入" />
</template>

结语:本章主要介绍了vue的组件的一些内容,后面还有更新的会再更新。