去哪里学习建设网站,浏阳市商务局网站溪江农贸市场建设,红河学院网站建设,局机关网站建设各位看官#xff0c;点波关注和赞吧
组件允许我们将 UI 划分为独立的、可重用的部分#xff0c;并且可以对每个部分进行单独的思考。在实际应用中#xff0c;组件常常被组织成层层嵌套的树状结构#xff1a; 这和我们嵌套 HTML 元素的方式类似#xff0c;Vue 实现了自己的…各位看官点波关注和赞吧
组件允许我们将 UI 划分为独立的、可重用的部分并且可以对每个部分进行单独的思考。在实际应用中组件常常被组织成层层嵌套的树状结构 这和我们嵌套 HTML 元素的方式类似Vue 实现了自己的组件模型使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component网络组件。 定义一个组件
当使用构建步骤时我们一般会将 Vue 组件定义在一个单独的 .vue 文件中这被叫做单文件组件 (简称 SFC)
script setup
import { ref } from vueconst count ref(0)
/scripttemplatebutton clickcountYou clicked me {{ count }} times./button
/template 使用组件
要使用一个子组件我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 Counter.vue 的文件中这个组件将会以默认导出的形式被暴露给外部。
script setupimport { ref } from vueconst count ref(0)
/scripttemplatebutton clickcountYou clicked me {{ count }} times./button
/template
在App.vue中可以这样引用
script setupimport ButtonCounter from ./components/Counter.vue
/scripttemplateh2 下面是一个子组件/h2ButtonCounter/ButtonCounter
/template
组件可以被重用任意多次
h1这里是一个子组件!/h1
ButtonCounter /
ButtonCounter /
ButtonCounter /
你会注意到每当点击这些按钮时每一个组件都维护着自己的状态是不同的 count。这是因为每当你使用一个组件就创建了一个新的实例。
在单文件组件中推荐为子组件使用 驼峰命名法的标签名以此来和原生的 HTML 元素作区分。虽然原生 HTML 标签名是不区分大小写的但 Vue 单文件组件是可以在编译中区分大小写的。我们也可以使用 / 来关闭一个标签。 传递Props Props初体验
如果我们正在构建一个博客我们可能需要一个表示博客文章的组件。我们希望所有的博客文章分享相同的视觉布局但有不同的内容。要实现这样的效果自然必须向组件中传递数据例如每篇文章标题和内容这就会使用到 props。
Props 是一种特别的 attributes你可以在组件上声明注册。要传递给博客文章组件一个标题我们必须在组件的 props 列表上声明它。这里要用到 defineProps 宏
!-- BlogPost.vue --
script setupconst props defineProps([title])console.log(props.title)
/scripttemplateh4{{ title }}/h4
/template
defineProps 是一个仅 script setup 中可用的编译宏命令并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象其中包含了可以传递给组件的所有 props.
一个组件可以有任意多的 props默认情况下所有 prop 都接受任意类型的值。
当一个 prop 被注册后可以像这样以自定义 attribute 的形式传递数据给它
script setupimport BlogPost from ./components/BlogPost.vue
/scripttemplateBlogPost title山海经/BlogPost
/template
在实际应用中我们可能在父组件中会有如下的一个博客文章数组 script setupimport {ref} from vueimport BlogPost from ./components/BlogPost.vueconst posts ref([{id:1,title:钢铁是怎样练成的},{id:2,title:坏蛋是怎样练成的},{id:3,title:秦始皇是怎样练成的},])/scripttemplateBlogPost v-forpost in posts:idpost.id:titlepost.title/BlogPost
/template
!-- BlogPost.vue --
script setupconst props defineProps([id,title]) console.log(props.title)
/scripttemplateh4{{ id }}--《{{ title }}》/h4
/template 传递Prop的细节 传递不同的值类型
除了传递简单的字符串以外Prop还可以传递数组以及对象具体如下
script langts setupimport {reactive,markRaw, ref} from vueimport BlogPost from ./components/BlogPost.vue;const post reactive({likes:56,isDisabled:true,wifes:[苍老师,波多老师,陇泽老师]})const author {name:兰陵笑笑生,company:水浒分舵}/scripttemplate!-- 传入数字 --BlogPost :likes42/BlogPost!-- 根据一个变量的值动态传入 --BlogPost :likespost.likes/BlogPost!-- 传入boolean类型的值 --!-- 仅写上 prop 但不传值会隐式转换为 true --BlogPost disabled/BlogPost
!-- --BlogPost :disabledfalse/BlogPost
!-- 根据一个变量的值动态传入 --BlogPost :disabledpost.isDisabled/BlogPost!-- 传入一个数组Array --BlogPost :wifes[赵丽颖,刘亦菲,景甜]/BlogPost!-- 传入一个数组Array变量 --BlogPost :wifespost.wifes/BlogPost!--传入一个Object对象--BlogPost :authorauthor/BlogPost
/templatestyle /style子组件中代码
!-- BlogPost.vue --
script setupconst props defineProps([likes,disabled,wifes,author])/scripttemplateh4取到的值为{{ likes }}/h4button :disableddisabled Enlarge text/buttonh2 v-forwife in wifes{{ wife }}/h2h2 v-ifauthor作者{{ author.name }} - 公司{{ author.company }}/h2
/template 使用一个对象绑定多个prop
如果你想要将一个对象的所有属性都当作 props 传入你可以使用没有参数的v-bind即只使用 v-bind 而非 :prop-name。例如这里有一个 post 对象
script langts setupimport {reactive,markRaw, ref} from vueconst post {id: 1,title: 熊熊火焰}
/scripttemplateBlogPost v-bindpost /
/template
运行起来后 单向数据流
所有的 props 都遵循着单向绑定原则props 因父组件的更新而变化自然地将新的状态向下流往子组件而不会逆向传递。这避免了子组件意外修改父组件的状态的情况不然应用的数据流将很容易变得混乱而难以理解。
另外每次父组件更新后所有的子组件中的 props 都会被更新到最新值这意味着你不应该在子组件中去更改一个 prop。若你这么做了Vue 会在控制台上向你抛出警告
如下面案例
script langts setupimport {reactive,markRaw, ref} from vueimport BlogPost from ./components/BlogPost.vue;const post {id: 1,title: 熊熊火焰}
/scripttemplateBlogPost v-bindpost /BlogPost :likespost.id/BlogPost
/templatestyle /style在子组件中我们尝试去修改likes的值
!-- BlogPost.vue --
script setupconst props defineProps([likes,disabled,wifes,author])props.likes 3
/scripttemplateh4取到的值为{{ likes }}/h4/template
保存后浏览器会报 如果确实想要修改props中的值可以采取以下方式
prop 被用于传入初始值而子组件想在之后将其作为一个局部数据属性。在这种情况下最好是新定义一个局部数据属性从 props 上获取初始值即可
!-- BlogPost.vue --
script setupimport {ref} from vueconst props defineProps([likes,disabled,wifes,author])// 此处的likes只是将prop.likes作为初始值// 后续对这个likes的更改就和prop无关了// let 定义变量const定义常量let likes ref(props.likes)likes 1001
/scripttemplateh4取到的值为{{ likes }}/h4/template Prop 校验
Vue 组件可以更细致地声明对传入的 props 的校验要求。比如我们上面已经看到过的类型声明如果传入的值不满足类型要求Vue 会在浏览器控制台中抛出警告来提醒使用者。这在开发给其他开发者使用的组件时非常有用。
要声明对 props 的校验你可以向 defineProps() 宏提供一个带有 props 校验选项的对象例如
!-- BlogPost.vue --
script setupimport {ref} from vuedefineProps({// 基础类型检查// 给出 null 和 undefined 值则会跳过任何类型检查propA: Number,// 多种可能的类型propB: [String, Number],// 必传且为 String 类型propC: {type: String,required: true},// Number 类型的默认值propD: {type: Number,default: 100},// 对象类型的默认值propE: {type: Object,// 对象或数组的默认值// 必须从一个工厂函数返回。// 该函数接收组件所接收到的原始 prop 作为参数。default(rawProps) {return { message: hello }}},// 自定义类型校验函数propF: {validator(value) {// The value must match one of these stringsreturn [success, warning, danger].includes(value)}},// 函数类型的默认值propG: {type: Function,// 不像对象或数组的默认这不是一个// 工厂函数。这会是一个用来作为默认值的函数default() {return Default function}}
})
/scripttemplateh4取到的值为{{ propA }}/h4h4取到PropC的值为{{ propC }}/h4h4取到PropD的值为{{ propD }}/h4/template
script langts setupimport {reactive,markRaw, ref} from vueimport BlogPost from ./components/BlogPost.vue;const post {id: 1,title: 熊熊火焰}
/scripttemplateBlogPost :propApost.id/BlogPostBlogPost :propCpost.title/BlogPost
/templatestyle /style这里propA要求的值是Number当传递的值是字符串类型的时候就会报错 监听事件
让我们继续关注我们的 BlogPost 组件。我们会发现有时候它需要与父组件进行交互。例如要在此处实现无障碍访问的需求将博客文章的文字能够放大而页面的其余部分仍使用默认字号。
在父组件中我们可以添加一个 postFontSize ref 来实现这个效果
script setupimport {ref} from vueimport BlogPost from ./components/BlogPost.vueconst posts ref([{id:1,title:钢铁是怎样练成的},{id:2,title:坏蛋是怎样练成的},{id:3,title:秦始皇是怎样练成的},])const postFontSize ref(1)/script
这里我们定义了一个postFontSize这样一个值在模板中用它来控制所有博客文章的字体大小
div :style{ fontSize: postFontSize em }BlogPostv-forpost in posts:keypost.id:titlepost.title/
/div
然后给 BlogPost 组件添加一个按钮
!-- BlogPost.vue --
script setupconst props defineProps([id,title])console.log(props.title)/scripttemplateh4{{ id }}--《{{ title }}》/h4!--点击之后会抛出一个名为enlarge-text的事件这个事件名可以自定义 --button click$emit(enlarge-text) Enlarge text/button
/template
在这里我们使用了click 点击事件子组件可以通过调用内置的$emit方法通过传入事件名称来抛出一个事件。 抛出事件后父组件可以通过 v-on 或 来选择性地监听子组件上抛的事件就像监听原生 DOM 事件那样
templatediv :style{fontSize:postFontSize em}BlogPost v-forpost in posts:idpost.id:titlepost.title!-- 此处是子组件抛出来的enlarge-text 事件事件名和子组件抛出来名字要的对应 --enlarge-textpostFontSize 0.1/BlogPost
/div
/template
我们可以通过 defineEmits 宏来声明需要抛出的事件
!-- BlogPost.vue --
script setupconst props defineProps([id,title])console.log(props.title)// 可以定义出这个组件可能触发的所有的事件const emit defineEmits([enlarge-text,event2,event3])// 当点击 Enlarge text 的时候需要抛出的事件const large (){emit(enlarge-text)}
/scripttemplateh4{{ id }}--《{{ title }}》/h4button clicklarge Enlarge text/button
/template
这声明了一个组件可能触发的所有事件父组件中还是按照之前的方式监听子组件抛出的事件。
script setupimport {ref} from vueimport BlogPost from ./components/BlogPost.vueconst posts ref([{id:1,title:钢铁是怎样练成的},{id:2,title:坏蛋是怎样练成的},{id:3,title:秦始皇是怎样练成的},])const postFontSize ref(1)/scripttemplatediv :style{fontSize:postFontSize em}BlogPost v-forpost in posts:idpost.id:titlepost.titleenlarge-textpostFontSize 0.1/BlogPost
/div
/template 通过插槽来分配内容 插槽初体验
一些情况下我们会希望能和 HTML 元素一样向组件中传递内容
AlertBoxSomething bad happened.
/AlertBox
我们期望能渲染成这样 这可以通过 Vue 的自定义 slot 元素来实现
templatediv classalert-boxdiv老弟这里发生了一个状况/divdiv !-- slot 这是一个占位符父组件传过来的值会渲染在这个地方 --slot //div/div
/templatestyle .alert-box{width: 500px;height: 80px;border-radius: 10px;border: 1px solid red;background-color: antiquewhite;}.alert-box div{display: block;margin-top: 10px;margin-left: 20px;}
/style
如上所示我们使用 slot 作为一个占位符父组件传递进来的内容就会渲染在这里。在父组件中可以引入子组件并插入值。
AlertBox你的老婆跟别人跑了
/AlertBox 插槽的出口与内容
在上面的案例中
AlertBox你的老婆跟别人跑了
/AlertBox
父组件中提供了子组件中显示的内容这个内容成为插槽内容而在子组件中我们写了一个 div !-- slot 这是一个占位符父组件传过来的值会渲染在这个地方 --slot //div
这个地方的slot 元素我们称为插槽出口标识了父元素提供的插槽内容将在哪里被渲染。 动态组件
有些场景会需要在两个组件间来回切换比如 Tab 界面 动态组件是通过Vue 的 component 元素和特殊的 is attribute 实现的 templatediv classgz这里是关注页面/div
/templatestyle.gz{width: 100%;height: 800px;background-color: red;}
/style
templatediv classTJ这里是推荐页面/div
/templatestyle.TJ{width: 100%;height: 800px;background-color: blue;}
/style
templatediv classcp这里是测评页面/div
/templatestyle.cp{width: 100%;height: 800px;background-color: yellow;}
/style
然后在App.vue中导入所有的组件并使用component :is指定组件名来选择引用哪个组件
script setupimport {reactive,markRaw, ref} from vueimport TJ from ./components/TJ.vue;import GZ from ./components/GZ.vue;import CP from ./components/CP.vue;
/scripttemplatecomponent :isCP/component
/template
如果想要实现选项卡操作可以将App.vue修改成如下代码
script setupimport {reactive,markRaw, ref} from vueimport TJ from ./components/TJ.vue;import GZ from ./components/GZ.vue;import CP from ./components/CP.vue;const comId ref(GZ)const switchTab(tab,index){comId.value tab.com}const tabData reactive([{name:关注,com:GZ},{name:推荐,com:TJ},{name:测评,com:CP}])
/scripttemplate!-- 组成选项卡div --div classtabs-content clickswitchTab(tab) v-for(tab, index) in tabData :keyindex{{ tab.name }}/div!-- 动态选择组件 --component :iscomId/component
/templatestyle
.tabs-content {display: inline-block;width: 100px;border: 1px solid #ccc;background: rgb(175, 96, 96);color: white;
}
/style