Vue - 6 组件与属性
Vue - 6 组件与属性
1.Vue组件
在 Vue.js 中,组件是一种可复用的 Vue 实例,可以定义自己的模板、样式和逻辑,并且可以被其他 Vue 实例复用。使用组件可以将复杂的页面分解为多个独立的小组件,从而使代码更加简洁、可维护、可复用。
组件可以通过 Vue.component() 方法进行注册,也可以使用 .vue 文件进行定义。在注册组件时,需要指定组件的名称和组件选项。
组件选项包括组件的数据data【但是data需要写成函数的形式,并且将数据在return中返回】、模板template、方法methods、生命周期钩子函数等。定义好组件之后,就可以在其它 Vue 实例中使用该组件了。
(1)全局组件
通过Vue.component()方法进行注册的组件,被称之为全局组件
全局组件放在Vue实例【根组件】中
Vue.component('my-component', { data: function () { return { message: 'Hello Vue!' } }, template: '<div>{{ message }}</div>' })
(2)局部组件
在Vue实例中定义或者全局组件中定义的,以关键字components:{}
中注册的组件,称之为局部组件。
局部组件常用的方式:是用一个变量名接收后,通过注册在不同的【全局组件】中或【根组件】中重复使用。
- 案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- vue 文件 --> <script src="./js/vue.js"></script> </head> <body> <div class="app"> <my-component> </my-component> </div> </body> <script> // 定义全局组件 Vue.component('my-component', { // data需要以方法的形式出现,data中的数据可以重复使用 data() { return { name: 'duoduo', show: false, } }, // 在全局中设计局部组件 template: ` <div> <button @click="handleShow">展示名字</button> <sections></sections> <div v-if="show">名字{{ name }}</div> </div>`, methods: { handleShow() { this.show = !this.show } }, // 局部组件 components: { sections: { template: ` <div> <div>年龄{{ age }}</div> </div> `, data() { return { age: 19 } }, }, }}) let vm = new Vue({ el: '.app', data: {}, methods: {}, }) </script> </html>
2.组件注意
注意点:
(1)根组件
定义的组件(body中的位置)必须要放在Vue实例【这也是一个组件:根组件】中,new Vue()
管理的div
通常被称为根组件
(2)局部组件无法单独使用
必须放在全局组件/根组件中,无法单独使用
(3)定义的组件的位置
定义的组件必须放在Vue实例的上方,在渲染的时候,组件必须已经加载完毕
(4)组件中的配置项
- 相同点:都有 数组、模版、方法等
组件选项包括组件的数据data【但是data需要写成函数的形式,并且将数据在return中返回】、模板template、方法methods、生命周期钩子函数等
①②③④⑤⑥⑦⑧
- 不同点:
①data在组件中要写成函数的形式,数据在return中以返回值的形式返回
②在组件中this指代的不再是Vue实例,而是组件对象
③父子组件的data是无法共享的
(5)自定义组件需要有1个根标签
自定义组件需要有1个 root element
,一般包裹在 1个div
中
3.组件间通信
组件间的数据是相互隔离的,无法共享。但是对于不同关系的组件间通信,Vue给出了不同的方法。
1.父传子:自定义属性
通过在子组件中【自定义属性】,就可以在子组件中通过【插值语法】来获得父组件的数据
(1)props关键字来声明需要通信的数据
- props属性验证
通过给props传一个对象,键是变量,值是数据类型,则可以限制变量的类型
props:{age:Number}
(2)父传子:自定义属性
父传子使用自定义属性
<child :自定义属性="变量"></child>
1.自定义属性不可以使用驼峰体
2.父子组件之间,避免出现变量名冲突
- 案例
<body> <div class="app"> <child :name1="name1"></child> </div> </body> <script> // 定义子组件 let child = { data() { return { mymsg: '这里是子组件的msg' } }, template: ` <div style="background-color: cornflowerblue"> <h2>我是子组件</h2> <p>父传子:{{ name1 }}</p> </div> `, // 通过props配置项来声明 父组件传给子组件的值,并且可以限定数据的类型 props:{name1:String}, } // vue实例 let vm = new Vue({ el: '.app', data: { name1:'我是根组件', }, // 在父组件中注册子组件 components:{ child } }) </script>
2.子传父:自定义事件
(1)在子组件中自定义事件,但是该自定义事件绑定的函数写在父组件的methods方法
<父组件> <child @自定义事件="函数"></child> </父组件>
(2)在子组件中,通过$emit方法触发一个自定义事件,会执行父组件自定义事件绑定的函数
// 定义局部组件 var child = { ... // 子组件中的方法,通过$emit方法触发一个自定义事件,并传递数据。 methods: { mySend(){ this.$emit('myroot',this.msg) } }, }
(3)在父组件的methods方法中,将子组件传过来的数据值赋值给父组件中对应的值
var vm = new Vue({ el: '.app', data: { name:'根组件', // 一定要定义一个变量来接受子组件传递过来的值 msg:'' }, methods: { // 自定义事件,触发后,将子组件传过来的数据值赋值给父组件中对应的值 // 即可完成子组件给父组件传值 getMsg(msg){ this.msg = msg console.log(msg) } }, components: { child } })
- 案例
<body> <div class="app"> <h4>这里是根组件:{{msg}}</h4> <h4>根组件:{{name}}</h4> // 在子组件中自定义事件,通过触发事件来传递数据 <child @myroot="getMsg"></child> </div> </body> <script> // 定义局部组件 var child = { // 在模版中定义方法,通过点击事件来使用$emit方法触发一个自定义事件,并传递数据 template: ` <div> <h3>##-- {{ msg }} --##</h3> <button @click="mySend">点击发送</button> </div> `, // 定义在子组件中需要传递的数据,必须要return出去数据 data() { return { msg: '来自子组件' } }, // 子组件中的方法,通过$emit方法触发一个自定义事件,并传递数据。 methods: { mySend(){ this.$emit('myroot',this.msg) } }, } var vm = new Vue({ el: '.app', data: { name:'根组件', msg:'' }, methods: { // 子定义事件,触发后,将子组件传过来的数据值赋值给父组件中对应的值 // 即可完成子组件给父组件传值 getMsg(msg){ this.msg = msg console.log(msg) } }, components: { child } }) </script>
3.ref属性
自定义属性和自定义事件可以实现父子传值,但是通过vue提供的ref属性,可以更方便的事项父子组件间的通信
因此不需要关注是子传父,还是父传子,直接通过ref属性获取组件对象即可
(1)ref属性放在普通标签上,拿到标签的dom对象
ref属性放在普通标签上,通过this.$refs
可以拿到所有写了ref属性
的标签
vue
将其封装成了对象,key
为ref属性
对应的变量名,value
为原生dom对象
,则可以通过ref属性
直接修改原生dom对象
的属性
(2)ref属性放在组件上,拿到组件对象
通过this.$refs.组件名,拿到组件对象,既可以拿到组件的属性,也可以拿到组件的方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- vue 文件 --> <script src="./js/vue.js"></script> </head> <body> <div class="app"> <h3>根组件---{{msg}}</h3> 根组件中的input框:<input type="text" ref="myinput"> <button @click="mySend">【父传子】发送消息</button> <hr> <h3>根组件中的子组件</h3> <child ref="mychild"></child> <hr> </div> </body> <script> // 定义局部组件 var child = { template: ` <div> <p ref="childp">{{msg}}</p> <h3>子组件中的信息##-- {{ msg }} --##</h3> <button @click="mySend">【子传父】发送消息</button> </div> `, data() { return { msg:"一条来自子组件的信息", name:'子组件的信息' } }, methods: { mySend(){ // alert(this.name) this.$emit('fu',this.msg) // this.msg=this.$refs..msg }, }, } var vm = new Vue({ el: '.app', data: { msg: '来自根组件' }, methods: { mySend() { console.log(this.$refs) }, fu(msg){ console.log(msg) } }, // 在跟组件中注册子组件 components: { child } }) </script> </html>
4.动态组件
(1)原始
绑定点击事件,并且通过 v-if判断来显示不同的页面
<body> <div class="app"> <span @click="handleClick('home')">首页</span>| <span @click="handleClick('order')">订单</span> | <span @click="handleClick('goods')">商品</span> <home v-if="chooseType=='home'"></home> <order v-else-if="chooseType=='order'"></order> <goods v-else></goods> </div> </body> <script> var home = { template: ` <div> <h1>home页面</h1> </div>`, } var order = { template: ` <div> <h1>order页面</h1> </div>`, } var goods = { template: ` <div> <h1>商品页面</h1> </div>`, } var vm = new Vue({ el: '.app', data: { chooseType: 'home' }, methods: { handleClick(type) { this.chooseType = type } }, components: { home, order, goods } }) </script>
(2)动态组件:component标签
通过component标签,绑定对应的数据值,根据组件对应的值不同来显示不同的组件
<body> <div class="app"> <span @click="handleClick('home')">首页</span>| <span @click="handleClick('order')">订单</span> | <span @click="handleClick('goods')">商品</span> <!-- 通过component标签,绑定is属性来动态判断--> <component :is="who"></component> </div> </body> <script> var home = { template: ` <div> <h1>home页面</h1> </div>`, } var order = { template: ` <div> <h1>order页面</h1> </div>`, } var goods = { template: ` <div> <h1>商品页面</h1> </div>`, } var vm = new Vue({ el: '.app', data: { // data中绑定 component标签对应的value值,来通过value值的不同来显示不同页面 who: 'home' }, methods: { handleClick(type) { this.who = type } }, components: { home, order, goods } }) </script> </html>
(3)keep-alive:防止销毁组件
当组件部分的input框中,我们有输入的内容时,切换组件后,该内容会被销毁。而通过keep-alive
标签,将component
标签包裹起来后,key保持组件活跃状态不被销毁
<body> <div class="app"> <span @click="handleClick('home')">首页</span>| <span @click="handleClick('order')">订单</span> | <span @click="handleClick('goods')">商品</span> <!-- 用keep-alive来包裹,保持组件活跃状态不被销毁--> <keep-alive> <component :is="who"></component> </keep-alive> </div> </body> <script> var home = { template: ` <div> <h1>home页面</h1> </div>`, } var order = { template: ` <div> <h1>order页面</h1> <input type="text">输入的内容 </div>`, } var goods = { template: ` <div> <h1>商品页面</h1> </div>`, } var vm = new Vue({ el: '.app', data: { // data中绑定 component标签对应的value值,来通过value值的不同来显示不同页面 who: 'home' }, methods: { handleClick(type) { this.who = type } }, components: { home, order, goods } }) </script>
5.插槽
通过在组件内添加<slot></slot>
标签,就可以在组件标签内添加不同的内容,并插在<slot></slot>
标签的位置,增加组件的拓展性
(1)匿名插槽
<body> <div class="app"> <home> <div> <h2>我是插槽!!!</h2> </div> </home> </div> </body> <script> let home = { template: ` <div> <h1>home页面开始</h1> <slot></slot> <slot></slot> <h1>home页面结束</h1> </div>`, } let vm = new Vue({ el:'.app', data:{}, components:{ home } }) </script>
(2)具名插槽
通过在<slot name='xxx'></slot>
标签中的name属性给插槽起名字,并在想要添加在插槽内的标签添加 slot="xxx"
,指定插入的标签,当存在多个插入的标签时,可以通过名字来管理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- vue 文件 --> <script src="js/vue.js"></script> </head> <body> <div class="app"> <home> <div slot="a"> <h2>我是插槽AAA</h2> </div> <div slot="b"> <h2>我是插槽BBB</h2> </div> </home> </div> </body> <script> let home = { template: ` <div> <h1>home页面开始</h1> <slot name="a"></slot> <slot name="a"></slot> <h1>home页面结束</h1> </div>`, } let vm = new Vue({ el:'.app', data:{}, components:{ home } }) </script> </html>
6.计算属性
计算属性是一种特殊的属性,计算属性的值是基于现有的状态计算得出的,并且只有在其依赖的状态发生变化时才会重新计算
(1)特性
1.延缓计算,只有发生依赖关系也就是只有使用的变量发生变化时候,才重新计算
2.可以将方法当做属性来用
(2)定义方式
通过配置项的关键字computed来定义
computed: { // 计算属性的名称 propertyName () { // 计算属性的计算逻辑 // 可以使用 this 来访问组件的状态 return computedValue; } }
计算属性的值可以通过在模板中使用该属性来获取:
<p>{{ propertyName }}</p>
- 案例
<body> <div class="app"> <h1>过滤案例</h1> <p>请输入要搜索的内容:<input type="text" v-model="searchText"></p> <ul> <li v-for="item in newList">{{item}}</li> </ul> </div> </body> <script> let vm = new Vue({ el: '.app', data: { searchText: '', dataList: ['a', 'at', 'atom', 'be', 'beyond', 'cs', 'csrf'] }, methods: {}, // 通过计算属性 computed 将newList方法包装成一个属性,每当使用的变量发生变化的时候,才会重新计算 computed: { newList() { return this.dataList.filter(item => item.indexOf(this.searchText) >= 0) } } }) </script>
7.监听属性
在data中定义的变量,只要变量发生变化,Vue实例就会自动调用指定的回调函数
定义
通过配置项的关键字watch来定义
watch: { // 被监听的状态 propertyName(newValue, oldValue) { // 回调函数,newValue表示新的值,oldValue表示旧的值 // 在这里可以进行一些响应式的逻辑处理 } }
注意
watch
选项监听的状态必须是响应式的。也就是说,它必须是通过data
选项或props
属性定义的watch
选项不支持使用计算属性或方法
- 案例
<body> <div> <div class="app"> <div @click="handleVar=1"><h3>变量111</h3></div> <div @click="handleVar=2"><h3>变量222</h3></div> </div> </div> </body> <script> var vm = new Vue({ el: '.app', data: { handleVar: '0' }, created() { this.getData() }, methods: { getData() { // 可以发送ajax,与后端交互,获取数据值,重新渲染页面 console.log('与后端交互') }, }, watch: { handleVar() { console.log('点击了不同的标签,变量发生了变化') this.getData() } } }) </script>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY