第十五篇 vue - 基础 - 组件基础
组件基础
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑
Vue 同样也能很好地配合原生 Web Component
补充 Web Component
1、Web Component 组件化已经成为目前主流的前端开发模式,其可复用性这一大特点是一众复制粘贴工程师的福音。目前我们实现组件化主要是依托于各大框架如 Vue, React, Angular 。这些框架基本都是在遵从浏览器的规则下制定出自己的一套开发规则和书写语法使开发者的项目获得组件化的能力。随着近年来组件化框架的盛行,官方也推行了一套组件化的解决方案和原生API上的支持 —— Web Component 使用 Web Component 编写的组件是脱离框架的,换言之,也就是说使用 Web Component 开发的组件库,是适配所有框架的,不会像 Antd 这样需要对 Vue、React等框架出不同的版本 //引入自定义的组件 <script src="/build/myCom.js"></script> //组件的使用 <my-com></my-com> 这里的 my-com 标签是原生的哦,不需要经过框架复杂的逻辑和算法去编译成 dom 挂载,或者说是浏览器本身会帮你做一些编译,但肯定会比框架的实现更加具有性能优势
2、完成一个简单的 Web Component 1、定义自定组件 class MyButton extends HTMLElement { constructor () { super(); const template = document.getElementById('mybutton'); const content = template.content.cloneNode(true); this.appendChild(content); } } 2、定义组件模板: <template id="mybutton"> <button>Add</button> </template> 3、注册组件: window.customElements.define('my-button', MyButton); 4、使用组件: <body> <my-button></my-button> </body>
定义一个组件
当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件 (简称 SFC)
<script> export default { data() { return { count: 0 } } } </script> <template> <button @click="count++">You clicked me {{ count }} times.</button> </template>
当不使用构建步骤时,一个 Vue 组件以一个包含 Vue 特定选项的 JavaScript 对象来定义
export default { data() { return { count: 0 } }, template: ` <button @click="count++"> You clicked me {{ count }} times. </button>` } 这里的模板是一个内联的 JavaScript 字符串,Vue 将会在运行时编译它。你也可以使用 ID 选择器来指向一个元素 (通常是原生的 <template> 元素),Vue 将会使用其内容作为模板来源。 上面的例子中定义了一个组件,并在一个 .js 文件里默认导出了它自己,但你也可以通过具名导出在一个文件中导出多个组件。
使用组件
要使用一个子组件,我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 ButtonCounter.vue 的文件中,这个组件将会以默认导出的形式被暴露给外部
<script> import ButtonCounter from './ButtonCounter.vue' export default { components: { ButtonCounter } } </script> <template> <h1>Here is a child component!</h1> <ButtonCounter /> </template> 若要将导入的组件暴露给模板,我们需要在 components 选项上注册它。这个组件将会以其注册时的名字作为模板中的标签名。 当然,你也可以全局地注册一个组件,使得它在当前应用中的任何组件上都可以使用,而不需要额外再导入。
<h1>Here is a child component!</h1> <ButtonCounter /> <ButtonCounter /> <ButtonCounter /> 你会注意到,每当点击这些按钮时,每一个组件都维护着自己的状态,是不同的 count。这是因为每当你使用一个组件,就创建了一个新的实例。 在单文件组件中,推荐为子组件使用 PascalCase 的标签名,以此来和原生的 HTML 元素作区分。虽然原生 HTML 标签名是不区分大小写的,但 Vue 单文件组件是可以在编译中区分大小写的。我们也可以使用 /> 来关闭一个标签
传递 props
Props 是一种特别的 attributes,你可以在组件上声明注册。要传递给博客文章组件一个标题,我们必须在组件的 props 列表上声明它。这里要用到 props 选项
当一个值被传递给 prop 时,它将成为该组件实例上的一个属性。该属性的值可以像其他组件属性一样,在模板和组件的 this 上下文中访问。
一个组件可以有任意多的 props,默认情况下,所有 prop 都接受任意类型的值
<!-- BlogPost.vue --> <script> export default { props: ['title'] } </script> <template> <h4>{{ title }}</h4> </template>
当一个 prop 被注册后,可以像这样以自定义 attribute 的形式传递数据给它:
<BlogPost title="My journey with Vue" /> <BlogPost title="Blogging with Vue" /> <BlogPost title="Why Vue is so fun" />
在实际应用中,我们可能在父组件中会有如下的一个博客文章数组: export default { // ... data() { return { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } } } 这种情况下,我们可以使用 v-for 来渲染它们 <BlogPost v-for="post in posts" :key="post.id" :title="post.title" />
监听事件
子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出一个事件
父组件可以通过 v-on 或 @ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样
<BlogPost ... @enlarge-text="postFontSize += 0.1" /> <!-- BlogPost.vue, 省略了 <script> --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">Enlarge text</button> </div> </template>
我们可以通过 emits 选项来声明需要抛出的事件
<!-- BlogPost.vue --> <script> export default { props: ['title'], emits: ['enlarge-text'] } </script> 这声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。同时,这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素
通过插槽来分配内容 slot
一些情况下我们会希望能和 HTML 元素一样向组件中传递内容 <AlertBox> Something bad happened. </AlertBox> 我们期望能渲染成这样: This is an Error for Demo Purposes Something bad happened.
这可以通过 Vue 的自定义 <slot> 元素来实现
<template> <div class="alert-box"> <strong>This is an Error for Demo Purposes</strong> <slot /> </div> </template> <style scoped> .alert-box { /* ... */ } </style> 如上所示,我们使用 <slot> 作为一个占位符,父组件传递进来的内容就会渲染在这里
动态组件 component
有些场景会需要在两个组件间来回切换,比如 Tab 界面 <!-- currentTab 改变时组件也改变 --> <component :is="currentTab"></component> 在上面的例子中,被传给 :is 的值可以是以下几种: 被注册的组件名 导入的组件对象 你也可以使用 is attribute 来创建一般的 HTML 元素 当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。 我们可以通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态。
DOM 模板解析注意事项
如果你想在 DOM 中直接书写 Vue 模板,Vue 则必须从 DOM 中获取模板字符串。由于浏览器的原生 HTML 解析行为限制,有一些需要注意的事项
大小写区分 闭合标签 元素位置限制 我们可以使用特殊的 is attribute 作为一种解决方案: 当使用在原生 HTML 元素上时,is 的值必须加上前缀 vue: 才可以被解析为一个 Vue 组件。这一点是必要的,为了避免和原生的自定义内置元素相混淆。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现