温故知新 Vue 3: Lesson 8
Components Basics
Base Example
Components are reusable instances with a name.
组件是带名字的可复用的实例.
We can use this component as a custom element inside a root instance:
我们可以将其用在 root 实例中, 作为一个 custom element 自定义元素.
Components can be reused as many times as you want. each time you use a component, a new instance of it is created.
组件可以使用很多次, 你使用一次, 一个新的组件实例就会被创建.
// Create a Vue application
const app = Vue.createApp({});
// Define a new global component called button-counter
app.component("button-counter", {
data() {
return {
count: 0,
};
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`,
});
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
Organizing Components
It's common for an app to be organized into a tree of nested components
通常一个应用的结构是以组件树的形式存在的.
Globally registered components can be used in the template of app instance created afterwards - and even inside all subcomponents of that root instance's component tree.
全局注册的组件不仅可以在 app instance 中使用, 在所有 app instance 的 subcomponents 中也可以使用.
app.component("my-component-name", {
// ... options ...
});
Passing Data to Child Components with Props
Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomes a property on that component instance.
props 是你放在组件上的 custom attribute 自定义属性.
当一个值传入这个 prop attribute, component instance 组件实例上便有了这么一个 property.
app.component("blog-post", {
props: ["title"],
template: `<h4>{{ title }}</h4>`,
});
<div id="blog-post-demo" class="demo">
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
</div>
we can use v-bind to dynamically pass prop
我们可以用 v-bind 来动态传值
<div id="blog-posts-demo">
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
></blog-post>
</div>
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
}
Listening to Child Components Events
some features may require communicating back up to the parent.
一些功能需要 child 向上给 parent 通信
component instances provide a custom events system. The parent can choose to listen to any event on the child component instance with v-on or @, just as we would with a native DOM event
组件实例提供了一个自定义事件系统.
parent 可以选择用 v-on 来监听 child component 实例上的事件.
Then the child component can emit an event on itself by calling the built-in emit's 2nd parameter to provide this value.
child component 可以通过$emit 方法发送一个事件, 第一个参数传入 event 事件的名字.
$emit
的第二个参数, 可以提供一个值
the parent will receive the event and update postFontSize value. we can access the emitted event's value with $event
在 parent 中. 通过 v-on:[event-name]
监听到事件后, 在 v-on 指令的值中指定要执行的操作. 我们可以通过 $event
来获取事件的值.
如果 v-on:[event-name]
绑定的值是一个 method name. 那么 method 的第一个参数会用来接收 $event
. 比如 <blog-post ... @enlarge-text="onEnlargeText"></blog-post>
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += $event"
></blog-post>
</div>
const app = Vue.createApp({
data() {
return {
posts: [
{ id: 1, title: "My journey with Vue" },
{ id: 2, title: "Blogging with Vue" },
{ id: 3, title: "Why Vue is so fun" },
],
postFontSize: 1,
};
},
});
app.component("blog-post", {
props: ["title"],
emits: ["enlarge-text"],
template: `
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
</div>
`,
});
Using v-model on Components
Custom events can also be used to create custom inputs that work with v-model.
Custom events 自定义事件可以被用来创建 custom input. 与 v-model 一起配合使用. 来实现双向绑定.
1. input example
vue 中 v-model 默认会自动绑定 v-bind:value 和 v-on:input="value = $event"
<input v-model="searchText" />
<!-- does the same thing as: -->
<input :value="searchText" @input="searchText = $event.target.value" />
2. component example
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
<!-- does the same thing as: -->
<custom-input v-model="searchText"></custom-input>
app.component("custom-input", {
props: ["modelValue"],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`,
});
For this to actually work though, the <input>
inside the component must:
为了让这个组件能正常工作, 组件里面的 input 需要:
- Bind the value attribute to a modelValue prop
将 value 绑定到 modelValue prop 2. On input, emit an update:modelValue event with the new value
监听到 input event 时, 发送 'update:modelValue' 事件到 parent.
Another way of creating the v-model capability within a custom component is to use the ability of computed properties' to define a getter and setter.
也可以使用 computed property 的 getter 和 setter 来构建 v-model 的能力
the get method should return the modelValue property, or whichever property is being using for binding, and the set method should fire off the corresponding $emit for that property.
getter 中返回 modelValue. setter 中发送 'update:modelValue' 事件.
app.component("custom-input", {
props: ["modelValue"],
template: `
<input v-model="value">
`,
computed: {
value: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
},
},
},
});
Content Distribution with Slots
it's often useful to be able to pass content to a component. Fortunately, this task is made very simple by Vue's custom <slot>
element.
有些时候, 我们需要直接传递一些内容到 child component 中特定的地方. 可以使用 <slot />
<alert-box> Something bad happened. </alert-box>
app.component("alert-box", {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`,
});
Dynamic Components
Sometimes, it's useful to dynamically switch between components. The above is made possible by Vue's <component>
element with the is special attribute
有些时候, 我们希望能动态切换组件. 我们可以使用 is
属性.
<!-- Component changes when currentTabComponent changes -->
<component :is="currentTabComponent"></component>
In the example above, currentTabComponent can contain either:
currentTabComponent 需要包含:
- the name of a registered component, or 注册组件的名字
- a component's options object 或组件的 options object 对象.
Keep in mind that this attribute can be used with regular HTML elements, however they will be treated as components, which means all attributes will be bound as DOM attributes.
is
这个 attribute 可以和正常的 html 元素一起使用. 这些元素也会被当成组件. 他们的所有 attribute 都会被绑定成 DOM attribute.
DOM Template Parsing Caveats
Some HTML elements, such as <ul>, <ol>, <table> and <select>
have restrictions on what elements can appear inside them, and some elements such as <li>, <tr>, and <option>
can only appear inside certain other elements.
有些 html 元素里面限制了里面的子元素类型. 应该使用 v-is
attribute
<!-- wrong -->
<table>
<blog-post-row></blog-post-row>
</table>
<!-- right -->
<table>
<tr v-is="'blog-post-row'"></tr>
</table>
Also, HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase.
原生 html 里面是不区分大小写的, 会把所有大写转成小写, 因此应该使用 kebabe-case. 避免使用 camelCase
It should be noted that these limitations do not apply if you are using string templates from one of the following sources:
大小写的限制不适用于以下情形:
String templates (e.g. template: '...') 组件定义中的 template
Single-file (.vue) components vue 单文件组件
<script type="text/x-template">
template script 元素
本文使用 mdnice 排版