vue应用构建基石-组件
1)如何封装展示内容和控制逻辑
2)组件之间层层嵌套为树状结构
3)配合使用原生Web Component

一、定义vue组件

构建型vue和非构建型各有不同的定义语法

1. SFC - 构建型vue的组件定义-标准方式

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

const count = ref(0)
</script>

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

2. vue特定选项的javascript对象 - 非构建型vue的组件定义

是一个包含 Vue 特定选项的 JavaScript 对象来定义

import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return { count }
  },
  // 模板是一个内联的 JavaScript 字符串,Vue 将会在运行时编译它
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
  // 模板也可以是一个ID选择器,针对一个 DOM 内联模板:
  // template: '#my-template-element'
}

二、使用vue组件

1. 导入, 全局导入和局部导入

2. 在SFC中 和 在html DOM中使用组件的名称格式注意点

1)SFC中使用驼峰PascalCase 标签名

// .vue文件中
<h1>Here is a child component!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />

2)若直接DOM中,使用kebab-case 形式,并显示关闭标签

<!-- 如果是在 DOM 中书写该模板 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

三、向(子)组件传递props

1. 声明组件的props

1).<script setup>中可用defineProps(编译宏命令)

<!-- BlogPost.vue -->
<script setup>
const props = defineProps(['title'])
console.log(props.title)
</script>

<template>
  <h4>{{ title }}</h4>
</template>

2).export + setup函数

export default {
  props: ['title'],
  setup(props) {
    console.log(props.title)
  }
}

2. 传递静态props

<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />

3. 传递动态props

const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])
...

<BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />

四、抛出/监听事件

1. 子组件声明要抛出事件名

1),<script setup>中声明

<!-- BlogPost.vue -->
<script setup>
    defineProps(['title'])
    const emits = defineEmits(['enlarge-text'])
    ...
    // 抛出事件
    emits('enlarge-text')
</script>

2),export + setup()中声明

export default {
  emits: ['enlarge-text'],
  setup(props, ctx) {
    // 抛出事件
    ctx.emit('enlarge-text')
  }
}

2. 父组件监听事件

和监听原生 DOM 事件一样

<BlogPost
  ...
  @enlarge-text="postFontSize += 0.1"
 />

3. 第三种抛出事件方式:内置的 $emit 方法

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

五、插槽占位符<slot>

1. 问题:如何传入如下:Something to sub component.

<AlertBox>
  Something to sub component.
</AlertBox>

2. 解决, 插槽占位符<slot>

<template>
  <div class="alert-box">
    <strong>This is an Error for Demo Purposes</strong>
    <slot />
  </div>
</template>

六、动态组件

1. <component>标签实现

<!-- currentTab 改变时组件也改变 -->
<component :is="tabs[currentTab]"></component>

2. 存活被切换出的组件

在上述<component>外套一个 <KeepAlive> 组件

七、vue模板来源方式

1. 四种来源方式

1)单文件组件
2)内联模板字符串 (例如 template: '...')
3)<script type="text/x-template">
4)DOM内直接编写(受浏览器解析DOM行为的限制)

2. 浏览器解析DOM的行为限制对上述第4)中方式的影响

1)名称方面注意点包括:组件名称、prop名称、v-on事件名称的。需要将PascalCase 形式、camelCase 形式的转换成kebab-case (短横线连字符)
2)闭合标签

<my-component /> <!-- 这个关闭标签无效,需要显式闭合 -->
<span>hello</span>

3)特定子元素类型限制
例如 <ul>,<ol>,<table> 和 <select>,相应的只允许特定的子元素,即<li>,<tr> 和 <option>。

如下使用子组件<blog-post-row>,将被忽略

<table>
  <blog-post-row></blog-post-row>
</table>

改用特殊属性 is, 注意子组件名使用 vue:开头

<table>
  <tr is="vue:blog-post-row"></tr>
</table>