一、组件的定义
组件: 封装了自定义内容与逻辑,独立的、可重用的显示组合体,它包括逻辑 (Script)、模板 (template)或样式 (style)。
(一)组件的两种形态
组件的书写有两种形态,JavaScript对象形态与vue单文件形态
1、 JavaScript对象形态
JavaScript对象形态:组件用js对象的形式表达,内容与逻辑等都是以对象的属性表示 { /* 选项 */ }
。
组件的对象形态非常的自由,对象的属性除了用于表达组件内容与逻辑的组件内属性外,还可放置非组件属性,可以像操作普通对象一样自由操作对象内的组件属性也非组件属性。
对象形态的组件在components属性或component函数注册时,能利用组件属性编译为组件,非组件属性将被舍弃,编译后的组件与原js对象属性结构会有不同。
对象形态组件定义如下:
const ttt="我是外部数据"
const XXX={
props: ['ccc'], //接收传参
data(){return{count:0,ttt}}, //定义响应对象
methods:{tt:function (){this.count++}},//定义函数
mounted(){tt()},//周期勾子
template: `<button @click="tt">{{ttt}}点了 {{ count }} 次!</button><app dd="子组件"/>`,//模版
components: {'app':{props: ['dd'],template: `<button>我是{{dd}}</button>`}}//定义子组件
}
a、对象形态组件书写规范
对象形态组件只有组件属性才能在注册时编译为组件,组件属性有规范的属性名及规范的属性值。
- 模板属性: 模板属性名为template,值为<html标签>。注:如果是多个html标签,要用反单引号括起。
- 组件属性: 按功能不同,有如下属性。
放置响应数据的data(){}函数
放置自定义函数的methods:{}属性
各种生命周期函数如 mounted(){}函数
放置父参的 props:[]属性
放置子组件的components:{}属性。
组件属性可以引用对象里非组件属性。
组件属性也能引用对象外的数据。 - 样式部属性: 没有单独的样式部属性,样式是放在模板部的html标签里,且只能使用内联样式和外部样式表,不能使用内部样式表(template:不能放
<style>
标签)。
b、js文件导入
对象形态组件可以存在于其它的js文件中,可通过完整引入或模块化引入(副本引入)(详见:vue3个人心得---概论(HTML、JS 和 VUE初解)。如果是import(副本引入),js文件要用export导出对象形态组件。
c、对象组件的使用环境
对象组件一般在非构建环境使用,在vite构建的开发环境,需要开启template属性支持。
开启支持的方法:根目录vite.config.js文件加入如下语句(Vue3+vite架构)
import { defineConfig } from 'vite'
import path from "path";
export default defineConfig({
resolve: {
alias: {'vue': path.resolve(__dirname,"node_modules/vue/dist/vue.esm-bundler.js")},}})
注意地址的准确性,否则不生效。
非构建开发环境不能从node_modules包中的vue导入ref、reactive、computed、watch等函数,如:import {ref} from 'vue'。但导入vue.js文件会导入一个名为Vue的对象,可用解构的方法解构出,如:const {ref} = Vue。注意Vue对象首字母大写。
2、vue单文件形态组件
在vite构建的开发环节,有一种后缀为.vue的组件文件,这种组件采用html标签的形式表达。在<template>
标签里直接书html标签、在<script>
标签的export default里书写组件逻辑属性、还允许使用</style>
样式标签。import导入vue单文件时vite会将其转换为js组件对象。
vue组件只能在vite构建环境下使用,在非构建环境导入运行会跳出(禁止运行非js模块)警告。vue单文件形态组件如下:
<script>
import { ref } from 'vue'
export default {
// 放置响应数据的data(){}函数
// 放置自定义函数的methods:{}属性
// 各种生命周期函数如 mounted(){}函数
// 放置父参的 props:[]属性
// 放置子组件的components:{}属性。
data() {const tr=ref(0);function pt(){tr.value++};return {count:0,tr,pt}},
components:{} ,
mounted(){},
methods:{increment(){this.count++}},
props:[]}
</script>
<template>
<button @click="increment">{{ count }}</button>
<button @click="pt">{{tr}}</button>
</template>
<style scoped>.logo {height: 6em}</style>
a、vue组件书写规范
vue组件通过标签来描述组件,vite规范了vue组件可用的标签及标签内容的书写形式,必须遵循vite书写规范才能被解释为组件内容。
- template标签: 所有的html标签都可放在template标签内(除
<script>、<style>
标签外),import导入时vite会自动将其编译为组件对象的render函数。
在一个vue文件中只能有一个<template>
标签,非<template>
标签内的html标签不会被解释为html显示。 - script标签: script标签内可放置副作用代码和export default代码,export default内可放置组件属性与非组件属性,组件属性会被template标签调用,import导入时vite会将所有export default内的属性并入到组件对象内。
在一个vue文件中只能使用一个<script>
标签,除非有一个<script setup>
语法糖才可以使用第二个<script>
。 - 组件属性: 与对象组件的组件属性一样,包括data(){}、methods:{}、生命周期函数(如mounted(){})、props:[]、components:{}等。
组件属性须经export default暴露才能被template标签使用。
组件属性可以引用export default里非组件属性。
组件属性也能引用副作用代码的数据 <style>
标签: 在一个vue文件中可以有多个<style>
标签,标签加上scoped属性表明样式只作用于本vue或子vue,防止污染上家。
b、vue文件导入
vue文件只能在vite构建环境下使用。js文件需手动用export原样导出组件对象,而vue文件不需要export,vite会自动将template标签编译为组件对象的render函数,并将script标签export default内的属性并入到编译的组件对象里一并导出。
(二)setup函数及<script setup>
语法糖
1、setup函数
从vue3开始,无论是对象组件还是单文件组件,都可以使用setup函数,使用了setup函数的组件称为组合式 API,未采用setup函数的组件称为选项式 API。
- 组合式API使用setup将多个属性选项(data、computed、methods、watch、beforeCreate、created)合并成一个选项。详见setup函数在vue中的使用。
- setup(a,b)函数有两个形参,第一个形参是父传过来的props,第二个形参是父传过来的attrs及emit。
- 并不是所有的属性选项都并入到setup,比如components:{}属性、props:[]属性、template属性(对象组件)、生命函数(除beforeCreate、created外)。
- setup函数是个生命函数,位于beforeCreate、created之间,在组件挂载前需执行的代码(beforeCreate、created)都可以放在setup函数内。
setup函数是个返回函数,组合进来的属性、只有通过return返回才能被template使用,通过return能实现。
对象组件里使用setup函数如下:。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <script src="https://cdn.staticfile.net/vue/3.2.36/vue.global.min.js"></script> </head> <body> <div id="hello-vue" class="demo"></div> <script > const {ref} = Vue const HelloVueApp = { setup(a,b){ console.log(a,b) const count=ref(1) tt() function tt() {count.value++} return{count,tt}}, template: `<button @click="tt">点了 {{ count }} 次!</button><app ccc="子组件"/>`, components: {'app':{props: ['ccc'],template: `<button>我是{{ccc}}</button>`}} } Vue.createApp(HelloVueApp).mount('#hello-vue') </script> </body> </html>
2、<script setup>
语法糖
setup函数在vue组件上有一种语法糖的形式——即在<script>
标签内加上setup。
- 采用语法糖的组件,不需要手动用return返回setup函数内的属性值,不需要手动用export default暴露setup函数及其它属性值,就能供
<template>
使用。 - 采用语法糖的组件,子组件不需要components属性定义就能直接供
<template>
使用。 <script setup>
内的代码(非生命函数内的代码)会在每次组件实例被刷新的时候执行,而setup函数及普通<script>
内的代码只在组件初始化时执行一次。- 有些配置项在script setup中不适用(如:在vue3.3版以下,禁止自动接收透传inheritAttrs: false不能写在script setup中,需另开一个
<script>
书写),有些代码只希望初始化时执行一次,也可以结合普通script实现。 当script setup与普通script标签在一起时,script代码会并入script setup代码且script代码排在前面。因此在普通script标签定义的对象不需要再使用export default暴露(如是属性,还是需要export default暴露)。
采用语法糖如下:<script setup> import { ref } from 'vue' const Eee={template:`<button>我是子组件</button>`} const count = ref(0) function increment(){count.value++} </script> <template> <button @click="increment">{{count}}</button> <Eee /><br> <component :is="Eee" /> </template> <style scoped>.logo {height: 6em}</style>
二、全局组件、局部子组件、动态子组件
在app创建阶段可以通过app的component函数创建全局组件。
在当前组件,可以通过components:{}属性注册局部子组件。
1、全局组件
定义的全局子组件,在所有的组件中可以直接使用,不必import引入,也不需在当前组件的components:{}属性里再注册为子组件。
a、component函数书写规范
app.component('组件名', 组件)
组件名: 组件在模版上使用的标签名,组件名要加引号。
组件: 组件可以是外部的组件对象名(js组件对象名或vue组件对象名),也可以是js组件对象实体。
实例如下:
import App from './App.vue'
import zivue from './zivue.vue'
const ziapp={setup(){},template:'<h1>对象子组件</h1>',}
const app=createApp(App)
app.component('zivue', zivue)
app.component('ziapp', ziapp)
app.component('aHoo', {setup(){},template: '<h1>对象子组件</h1>',})
app.mount("#app")
b、component函数链式调用
多次调用component()函数时,可以被链式调用:
app.component('ComponentA', ComponentA)
.component('ComponentB', ComponentB)
.component('ComponentC', ComponentC)
c、全局组件注册时机
全局组件注册必须在挂载之前,即
app.component(‘a’,b)函数须在app.mount("#app")之前。
如果采用的是vite构建,参数2只能使用vue组件名,对象组件名和对象组件实体必须开启对象组件规范才能使用。
2、局部子组件
组件外的组件要在当前组件使用,需在当前组件的components:{}属性里注册为局部子组件才能被当前组件使用(全局子组件不用)。如:
export default{components:{子组件名:组件}}
注意:全局子组件注册是调用函数,用的是调用函数的()小括号,子组件名是参数,要用引号。局部子组件的注册是属性的方式,用的是属性的:{}帽号加大括号,子组件名为属性名,不需要加引号。
如果用了<script setup>
语法糖,组件可以直接作为子组件使用,不须在当前组件的components:{}属性里注册。
3、动态子组件
组件只有经全局注册或局部注册才可以在<template>
作为组件标签使用。
动态子组件:组件不须注册,用绑定<component/>标签is属性的方式将<component/>标签渲染为组件标签。如:<component :is="组件" />
动态子组件标签。
三、非组件标签
vue默认会将任何非原生的 HTML 标签优先当作 Vue组件标签处理,如果是个自定义标签(非组件标签),会在开发时导致 Vue 抛出一个“解析组件失败”的警告。
可通过判定函数赋给isCustomElement来对自定义标签进行标注。
- 非构建开发时:app.config.compilerOptions.isCustomElement属性用于自定义标签标注,如:app.config.compilerOptions.isCustomElement = (tag){return tag.includes('rrr')},须在.mount('#app')挂载函数前对isCustomElement属性赋值。
Vite 构件开发时:在根目录下找到配置文件vite.config.js,在里面添加如下语句:
import vue from '@vitejs/plugin-vue' export default { plugins: [ vue({ template: { compilerOptions: {isCustomElement: (tag){return tag.includes('-')}}}})]}
四、标签内的内容处理
1、挂载点标签内的内容处理
挂载点标签内有内容时,如果子组件的template属性有内容时会覆盖挂载点标签内的内容。如下:
<div id="hello-vue">
<button >我是挂载1会被覆盖吗</button>
</div>
<div id="hello">
<button >我是挂载2</button>
</div>
<script>
const App = {template: `<button @click="">你消失了</button>` }
const HelloVueApp = {template:``}
Vue.createApp(App).mount('#hello-vue')
Vue.createApp(HelloVueApp).mount('#hello')
</script>
当子组件是单文件组件时 :无论组件有没有<template>
标签都会清空挂载标签内的内容。
特例:以上限制只适用于 createApp 方法创建的app。如果是createSSRApp创建的app,不会清空挂载点内的标签。详见SSR
2、子组件标签内的内容处理
子组件标签内的内容vue默认会将其归为 隐式具名插槽 ,其显示与否取决于子组件是否有插槽出口标签<slot></slot>
递归组件???命名空间组件????顶层 await????
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。