赞
踩
组件是vue中一个强大的功能,通过组件,开发者可以封装出复用性强、扩展性强的HTML元素,并且通过组件的组合可以将复杂的页面元素拆分成多个独立的内部组件,方便代码的逻辑分离和管理。
组件系统的核心是将大型应用拆分成多个可以独立使用且可复用的小组件,之后通过组件树的方式将这些小组件构建完成完整的应用程序
vue框架将常规的网页页面开发以面向对象的形式进行了抽象,一个网页甚至一个网站在vue里面被抽象为一个应用程序。一个应用程序里面可以定义多个组件,但是需要配置一个根组件,当应用程序被挂载渲染到页面,根组件会作为起点元素进行渲染
vue里面的createApp方法可以创建一个vue应用实例。vue应用里有许多方法和配置供开发者来使用
创建一个vue应用,只需要调用createApp方法就行,这个方法会返回一个vue应用实例,在创建应用实例的时候,我们可以传入一个js对象来提供应用创建时数据相关的配置项,例如data和methods的选项。
data本身需要配置为一个js函数,次函数需要提供应用所需的全局数据
- const appData={
- count:{}
- }
- const App=Vue.creatApp({
- data(){
- return appData
- }
- })
props选项用于接收父组件传递的数据
computed选项用来配置组件的计算属性,可以在其中实现getter和setter方法
- computed:{
- countString:{
- get(){
- return this.count+"次"
- }
- }
- }
methods选项用来配置组件中需要使用到的方法,不用使用箭头函数来定义methods里的方法,这样子会影响this关键字的指向
watch配置项可以对组件属性的变化添加监听函数
当监听的组件属性发生变化的时候,监听函数会将变化前后的值作为参数传递进来。如果要使用的监听函数本身定义在组件的methods选项中,也可以使用字符串的方式来指定要执行的监听方法
watch选项还可以配置很多高级的功能,例如深度嵌套监听、多重监听处理等。
创建好vue应用实例后,使用mount方法可以将其绑定在指定的html元素上。应用实例可以使用component方法来定义组件,定义好组件后,可以直接在HTML文档中进行使用。
例如创建一个测试文件,
- <script>
- const App=Vue.createApp({})
- const alertComponent={
- data() {
- return {
- msg:"警告框提示",
- count:0
- }
- },
- methods: {
- click(){
- alert(this.msg+this.count++)
- }
- },
- template:`<div><button @click="click">按钮</button></div>`
- }
- App.component("my-alert",alertComponent)
- App.mount("Application")
- </script>
在vue应用里定义组件的时候使用component方法,这个方法的第一参数用来设置组件名,第二个参数进行组件的配置,组件的配置选项和应用的配置选项基本一致。data选项配置了组件必要的数据,methods配置了组件所需的方法。定义组件最重要的是template选项,这个选项设置组件的html模板,上面就是创建一个简单的按钮,当用户单击此按钮的时候会弹出警告框。
- <div id="Application">
- <my-alert></my-alert>
- <my-alert></my-alert>
- </div>
运行代码,尝试单击页面的按键,可以看到程序已经能够按照预期正常运行
上面的my-alert定义在Application应用实例里面的,在组织HTML框架结构的时候,my-alert组件也只能在Application挂载的标签内使用,在外部使用无法正常工作。
例如在id为application的div外面使用此组件,是无法正常工作的。
使用vue里面的组件可以使得HTML代码的复用性大大增强,同样在日常开发里,我们也可以将一些通用的页面元素封装成可定制化的组件,在开发新的网站应用的时候,可以使用日常积累的组件进行快速的搭建。组件在定义时的配置选项和vue应用实例在创建时的配置选项是一致的。都有data、methods、watch、computed等的配置选项,这是因为我们在创建应用的时候传入的参数实际上就是根组件。
当组件进行复用的时候,每个标签其实就是一个独立的组件实例,其内部的数据是独立维护的,例如上面代码里的my-alert组件内部维护了一个名为count的属性,单击按钮后其会计数,不同的俺就将会分别进行计数
组件是具有复用性的,因此要使得组件能够在不同的应用场景里得到最大程度的复用和最少的内部改动,就需要组件有一定的灵活度,即可配置性。可配置性归根结底是通过数据的传递来使组件的交互行为、渲染样式有略微的差别,所以就需要通过数据和事件的传递使得vue组件更具灵活性。
使用原生的HTML标签元素的时候,我们可以通过属性来控制元素的一些渲染行为,例如style属性可以设置元素的样式风格,class属性用来设置元素的类。自定义组件的使用方法和原生html标签一样,也是可以通过属性来控制他的内部行为
以上一节的测试代码为例,my-alert组件会在页面中渲染出一个按钮元素,此按钮的标题为字符串按钮,这个标题文案是写死在template模板字符串中的,也就是我们无论创建多少的my-alert组件,其渲染出的按钮的标题都是一样的,如果需要在使用此组件时灵活设置其按钮显示的标题,就需要使用组件里面的props配置
props是properties的缩写,意思为属性,其定义的属性是提供给外部进行设置使用的,也可以将其称为外部属性。修改my-alert组件加入props:title即可
props选项用来定义自定义组件内的外部输血,组件可以定义任意多个外部输血,在template模板里,可以用访问内部data属性一样的方式来访问定义的外部属性。在使用my-alert组件时,可以直接设置title属性来设置按钮的标题
- <my-alert title="anniu1"></my-alert>
- <my-alert title="anniu2"></my-alert>
props也可以进行许多复杂的配置,例如类型检查,默认值等,后面也会介绍
在开发自定义组件的时候,需要进行事件传递的场景也很多。例如my-alert组件,在使用组件的时候,当用户单击按钮是会提出自动弹出系统的警告框,但更多时候不同项目的警告框风格可能不一样,弹出警告框的逻辑也可能相差很远,但是这样来看,这个组件的复用性就会显得非常差,不能满足各自自定义的需求
如果要对my-alert组件进行改造,我们可以将其中按钮单击的时间传递给父组件处理,即传递给使用此组件的业务方处理。在vue中,可以使用内建的$emit方法来传递事件
- <div id="Application">
- <my-alert @myclick="appfunc" title="anniu1"></my-alert>
- <my-alert title="anniu2"></my-alert>
- </div>
- <script>
- const App=Vue.createApp({
- methods:{
- appfunc(){
- console.log('点击了自定义组件');
- }
- }
- })
- const alertComponent={
-
- props:["title"],
- template:`<div><button @click="$emit('myclick')">{{title}}</button></div>`
- }
- App.component("my-alert",alertComponent)
- App.mount("#Application")
- </script>
修改后的代码将my-alert组件内的按钮的点击事件定义为myclick事假宁晋县传递,在使用这个组件的时候,可以直接使用myclick事件名进行监听,$emit方法在传递时间的时候也可以传递一些参数,很多自定义组件都有状态,这时候我们就可以将状态作为参数进行传递,代码如下
- <div id="Application">
- <my-alert @myclick="appfunc" title="anniu1"></my-alert>
- <my-alert @myclick="appfunc" title="anniu2"></my-alert>
- </div>
- <script>
- const App=Vue.createApp({
- methods:{
- appfunc(param){
- console.log('点击了自定义组件-'+param);
- }
- }
- })
- const alertComponent={
-
- props:["title"],
- template:`<div><button @click="$emit('myclick',title)">{{title}}</button></div>`
- }
- App.component("my-alert",alertComponent)
- App.mount("#Application")
- </script>
运行代码,打印的就是按钮加标题了,标题数据也就是子组件传递事件的时候带给父组件的事件参数。如果在传递事件之前,子组件还有一些内部的逻辑需要处理,也可以在子组件里包装一个方法,在方法内调用$emit进行事件传递,例子如下
- <div id="Application">
- <my-alert @myclick="appfunc" title="anniu1"></my-alert>
- <my-alert @myclick="appfunc" title="anniu2"></my-alert>
- </div>
- <script>
- const App=Vue.createApp({
- methods:{
- appfunc(param){
- console.log('点击了自定义组件-'+param);
- }
- }
- })
- const alertComponent={
- methods:{
- click(){
- console.log("组件内部逻辑");
- this.$emit('myclick',this.title)
- }
- },
- props:["title"],
- template:`<div><button @click="click">{{title}}</button></div>`
- }
- App.component("my-alert",alertComponent)
- App.mount("#Application")
- </script>
选择,可以灵活得通过事件的传递来使自定义组件的功能更加纯粹,好的开发模式是将组件内部的逻辑在组件内部处理掉,需要调用方处理的业务逻辑属于组件外部的逻辑,将其传递给调用方处理。
v-model是vue的双向绑定指令,即对于可交互用户输入的相关元素,使用这个指令可以将数据的变化同步到元素上,同样当元素输入的信息变化时,也会同步到对应的数据属性上,在编写自定义组件的时候,难免会使用到可进行用户输入的相关元素,所以需要对输入的内容进行双向绑定。
- <div id="Application">
- <div>
- <input type="text" v-model="inputText">
- <div>{{inputText}}</div>
- <button @click="this.inputText=''">清空</button>
- </div>
- </div>
- <script>
- const App=Vue.createApp({
- data() {
- return {
- inputText:""
- }
- },
- })
- App.mount("#Application")
- </script>
在页面的输入框输入文案,对应div标签里面的文案也会发生变化,当点击清空的时候,输入框和div都会清空,如果不使用v-model指令,要实现相同也是可以的
- <div id="Application">
- <div>
- <input type="text" :value="inputText" @input="action">
- <div>{{inputText}}</div>
- <button @click="this.inputText=''">清空</button>
- </div>
- </div>
- <script>
- const App=Vue.createApp({
- data() {
- return {
- inputText:""
- }
- },
- methods: {
- action(event){
- this.inputText=event.target.value
- }
- },
- })
- App.mount("#Application")
- </script>
修改后的代码运行效果一样,代码先使用v-bind指令控制输入的内容,如何属性改变的时候,v-bind指令会将其同步更新到输入框中,之后使用v-on:input指令来监听输入框的输入事件,输入框的输入内容改变的时候,手动通过action函数来更新inputText属性,这样就实现了双向绑定的效果,这也是v-model指令的基本工作原理。
- <div id="Application">
- <div>
- <input type="text" v-model="inputText" >
- <div>{{inputText}}</div>
- <button @click="this.inputText=''">清空</button>
- </div>
- </div>
- <script>
- const App=Vue.createApp({
- data() {
- return {
- inputText:""
- }
- },
- })
- const inputComponent={
- props:["modelValue"],
- methods: {
- action(event){
- this.$emit('update:modelValue',event.target.value)
- }
- },
- template:`<div><span>输入框:</span><input :value="modelValue" @input="action"/></div>`
- }
- App.component("my-input",inputComponent)
- App.mount("#Application")
- </script>
上面的代码v-model也可以正常工作。所有支持v-model指令的组件默认都会提供一个modelValue的属性,而组件内部的内容变化后,向外传递的时间为update:modelValue,并且在时间传递会将组件内容作为参数传递
插槽是指HTML起止标签和结束标签中间的部分,通常在使用div标签时,内部的插槽位置既可以放置要显示的文案,也可以嵌套放置其他标签例如div标签里可以写字,也可以放置button按钮
插槽的核心功能是将组件内部的元素抽离到外部进行实现,在进行自定义组件的设计的时候,良好的插槽逻辑可以使组件的使用更加灵活。对于在开发容器类型的自定义组件,插槽更重要,在定义容器类的组件时,开发者只需要将容器本身编写好,内部的内容都可以通过插槽来实现
- <div id="Application">
- <my-container></my-container>
- </div>
- <script>
- const App=Vue.createApp({
-
- })
- const containerComponent={
- template:`<div style="border-style:solid;border-color:red;border-width:10px">1111</div>`
- }
- App.component("my-container",containerComponent)
- App.mount("#Application")
- </script>
定义了一个容器组件,组件内部有红色边框,在容器组件内部添加子元素添加子元素是不可以的,例如
<my-container>111111</my-container>
运行代码,组件里并没有任何文本被渲染,如果需要自定义组件支持插槽,就需要使用slot标签来指定插槽的位置,修改组件模板
- const containerComponent={
- template:`<div style="border-style:solid;border-color:red;border-width:10px"><slot></slot></div>`
- }
此时,标签内部的内容可以被添加到自定义组件的插槽位置
虽然上面的代码只是将文本作为插槽的内容,但实际上插槽中也支持任意的标签内容或其他组件
对于支持插槽的组件,我们也可以为插槽添加默认的内容,这样的话组件在使用的时候没有设置插槽内容,则会自动渲染默认的内容
- <div id="Application">
- <my-container></my-container>
- </div>
- <script>
- const App=Vue.createApp({
-
- })
- const containerComponent={
- template:`<div style="border-style:solid;border-color:red;border-width:10px"><slot>默认内容</slot></div>`
- }
- App.component("my-container",containerComponent)
- App.mount("#Application")
- </script>
一旦设置了插槽的内容,那么默认的内容就不会被渲染
具名插槽是指为插槽设置一个具体的名称,在使用组件的时候,可以通过插槽的名称来设置插槽的内容。由于具名插槽可以非常明确的指定插槽内容的位置,因此当一个组件要支持多个插槽的时候,通常需要具名插槽
例如编写一个容器组件,此组件由头部元素、主元素和尾部元素组成,此组件需要有三个插槽,具名插槽的用法如下
- <div id="Application">
- <my-container2>
- <template v-slot:header>
- <h1>
- 头部元素
- </h1>
- </template >
- <template v-slot:main>
- <p>
- 内容元素
- </p>
- </template>
- <template v-slot:footer>
- <p>
- 尾部元素
- </p>
- </template>
- </my-container2>
- </div>
- <script>
- const App=Vue.createApp({
-
- })
- const container2Component={
- template:`<div>
- <slot name="header">111</slot>
- <hr/>
- <slot name="main"></slot>
- <hr/>
- <slot name="footer"></slot>
- </div>`
- }
- App.component("my-container2",container2Component)
- App.mount("#Application")
- </script>
在组件内部定义slot插槽的时候,可以使用name属性来为其设置具体的名称,在使用组件的时候,要使用template来包裹插槽的内容,对于template标签,通过v-slot来指定与其对应的插槽位置
在vue中,很多指令都有缩写形式,具名插槽也有缩写,可以用#代替v-slot,修改上面的代码
- <body>
- <div id="Application">
- <my-container2>
- <template #header>
- <h1>
- 头部元素
- </h1>
- </template >
- <template #main>
- <p>
- 内容元素
- </p>
- </template>
- <template #footer>
- <p>
- 尾部元素
- </p>
- </template>
- </my-container2>
- </div>
- <script>
- const App=Vue.createApp({
-
- })
- const container2Component={
- template:`<div>
- <slot name="header">111</slot>
- <hr/>
- <slot name="main"></slot>
- <hr/>
- <slot name="footer"></slot>
- </div>`
- }
- App.component("my-container2",container2Component)
- App.mount("#Application")
- </script>
- </body>
正常运行
动态组件是vue开发中经常会使用到的一种高级功能,有时候页面的某个位置需要渲染的组件不是固定的,有可能会根据用户的操作而渲染不同的组件,这时候就需要用到动态组件
实现一个用户选择不同的选项后,切换页面渲染是组件
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- <script src="https://unpkg.com/vue@next"></script>
- </head>
- <body>
- <div id="Application">
- <input type="radio" value="page1" v-model="page">页面1
- <input type="radio" value="page2" v-model="page">页面2
- <div>{{page}}</div>
- </div>
- <script>
- const App=Vue.createApp({
- data() {
- return {
- page:"page1"
- }
- },
- })
- App.mount("#Application")
- </script>
- </body>
- </html>
运行上面的代码,将会在页面渲染出一组单选框,用户切换选项后,其div标签中渲染的文案对应修改,在实际应用里并不只是修改div标签里面的文本,更多时候是更换组件来实现内容的切换。
定义两个vue组件page1和page2并将页面的div元素替换为动态组件
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- <script src="https://unpkg.com/vue@next"></script>
- </head>
- <body>
- <div id="Application">
- <input type="radio" value="page1" v-model="page">页面1
- <input type="radio" value="page2" v-model="page">页面2
- <component :is="page"></component>
- </div>
- <script>
- const App=Vue.createApp({
- data() {
- return {
- page:"page1"
- }
- },
- })
- const page1={
- template:`<div style="color:red">页面组件1</div>`
- }
- const page2={
- template:`<div style="color:green">页面组件2</div>`
- }
- App.component("page1",page1)
- App.component("page2",page2)
- App.mount("#Application")
- </script>
- </body>
- </html>
component是一个特殊的标签,其通过is属性来指定渲染吗的组件名称,随着vue应用的page属性的变化,component所渲染的组件也是动态变化的
目前我们所使用的的component定义的组件都是全局组件,对于小型的项目来说,是非常方便的,且这种开发方式也非常便捷,但对于大项目来说,缺点也很多。首先全局定义的模板命名不能重复,大项目里面会使用非常多的组件,维护是非常困难的,在定义全局组件的时候,组件内容是通过字符串式的HTML模板来定义的,在编写的时候对开发者来说不太友好
开关组件需要满足一定的定制化需求,例如开关的样式,背景色,边框颜色等。当用户对开关组件的开关状态进行切换的时候,需要将事件同步传递到父组件里。
基础的文档结构:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- <script src="https://unpkg.com/vue@next"></script>
- </head>
- <body>
-
- </body>
- </html>
根据需求,我们将按钮颜色、开关风格、边框颜色、背景色这些属性设置为外部属性。此开关组件也是可以交互的,因此需要使用一个内部状态属性来控制开关的状态
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- <script src="https://unpkg.com/vue@next"></script>
- </head>
- <body>
- <div id="Application">
- <my-switch @switch-change="change1" switch-style="mini" background-color="green" border-color="green" color="blue"></my-switch>
- <div>开关状态:{{state1}}</div>
- <my-switch @switch-change="change2" switch-style="mini" background-color="red" border-color="red" color="green"></my-switch>
- <div>开关状态:{{state2}}</div>
- </div>
- <script>
- const switchComponent={
- props:["swicthStyle","borderColor","backgroundCokor","color"],
- data() {
- return {
- isOpen:false,
- left:'0px'
- }
- },
- computed:{
- cssStyleBG:{
- get(){
- if(this.switchComponent=="mini"){
- return `position:relative;border:${this.borderColor};border-width:2px;border-style:solid;width:55px;height:30px;border-radius:30px;background-color:${this.isOpen? this.backgroudColor:'white'};`
- }else{
- return `position:relative;border:${this.borderColor};border-width:2px;border-style:solid;width:55px;height:30px;border-radius:10px;background-color:${this.isOpen? this.backgroudColor:'white'};`
- }
- }
- },
- cssStyleBtn:{
- get(){
- if(this.switchComponent=="mini"){
- return `position:absolute;width:30px;height:30px;left:${this.left};border-radius:50%;background-color:${this.color};`
- }else{
- return `position:absolute;width:30px;height:30px;left:${this.left};border-radius:8px;background-color:${this.color};`
- }
- }
- },
-
- },
- methods: {
- click(){
- this.isOpen=!this.isOpen
- this.left=this.isOpen? '25px':'0px'
- this.$emit('switchChange',this.isOpen)
- }
- },
- template:`
- <div :style="cssStyleBG" @click="click">
- <div :style="cssStyleBtn"></div>
- </div>
- `
- }
- const App=Vue.createApp({
- data() {
- return {
- state1:"关",
- state2:"关"
- }
- },
- methods:{
- change1(isOpen){
- this.state1=isOpen ?"开":"关"
- },
- change2(isOpen){
- this.state2=isOpen ?"开":"关"
- }
- }
- })
- App.component("my-switch",switchComponent)
- App.mount("#Application")
- </script>
- </body>
- </html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。