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>