Vue组件
文章目录
一.概念
在vue里,一个组件的本质上是一个拥有预定义选项的一个Vue实例
二.组件的使用
- 定义(注册)组件
- 调用(使用)组件
将组件就看成是一个函数
1.组件的注册
-
全局组件注册
当前Vue项目中,任何组件中都可以去使用
/**
* componentName 组件的名字
* componentOptions 组件的选项配置
*/
Vue.component(componentName,componentOptions);
//com1 全局组件
Vue.component('com1', {
template: `<div>
我是hello组件
</div>`
});
-
局部组件注册
在哪个组件中注册的,就只能使用在哪个组件中
/**
* 局部组件注册
* 在组件的components配置选项中去定义
* new Vue 构造函数 ,生成的实例,一般我们叫它根组件
* componentName1 局部组件名字
* componentOptions1 局部组件对应的选项
* */
new Vue({
components:{
componentName1:componentOptions1
componentName2:componentOptions2
}
});
在组件的components配置选项中去定义
new Vue,生成的实例,一般我们叫它根组件
compontentOptions就是一个对象,里面有配置与new Vue时类似,但有一些例外:
- 组件没有el选项,因为后续调用组件在哪里,这个组件的挂载点就是哪里
- 必须有template或者render选项,用来规定组件的模版内容
- data选项必须是一个函数返回对象的形式
2.组件的调用
将组件名当做自定义的html标签使用即可
//全局组件注册
Vue.component('hello', {
// 注意:必须有template和render选项,用来规定组件的模版内容
template: `<div>我是全局组件hello </div>`
});
//根组件
var vm = new Vue({
el: '#app'
});
//调用组件 这个div可以看成是根组件的template
<div id="app">
<hello></hello>
</div>
调用时,必须要在根组件里面,我这里的根组件的挂载点是 #app, 这个hello 组件放在了根组件的div中,实际上就是把hello组件的template渲染到了根组件上,那么这里就是 hello组件是 根组件Root的子组件
//com1 全局组件
Vue.component('com1', {
// 这里,com3是不能用的,会报错,因为com3是com2的子组件,只能在com2中使用
template: `<div>
我是hello组件
</div>`
});
//com2和com1是全局组件,任何组件都可以使用
Vue.component('com2', {
//com3和com4只能在com2中使用,局部组件,只属于com3和com4的子组件
template:
`
<div>
我是com2组件
<com3></com3>
</div>`
,
components: {
'com3': {
template: `<div>我是com3组件,只能在com2中使用</div>`
},
'com4': {
template: `<div>我是com4组件,只能在com2中使用</div>`
}
}
});
//根组件
var vm = new Vue({
el: '#app'
});
3.组件的复用
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script>
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">你点击了 {{ count }} times.</button>'
})
var vm = new Vue({
el: '#app'
});
</script>
定义的全局组件可以在根组件下任意使用多个,进行组件的复用
注意
data必须是一个函数
为什么组件的data选项要写成函数形式呢?
原因是组件是可以进行复用的,如果data直接写成对象,那么复用的时候,会造成数据污染(多个实例对象共用一个变量)
当我们点击上面按钮的时候,因为使用了函数形式的返回data数据,所以每个组件都会各自独立维护它的变量count,y因为每用一个组件,就会有一个新的实例被创建.
4.组件名
组件名不能是现有的html标签名,也不能是之前已经注册过的组件(实际上是可以,只不过会覆盖之前的)
Vue.component('nav', {
template: '<button v-on:click="count++">你点击了 {{ count }} times.</button>'
})
var vm = new Vue({
el: '#app'
});
nav是html5的标签名,这里不能使用,会报错.也不能是 a,button,div之类,总之不能使用现有的html标签名.
组件名的规则:
可以使用短横线写法:
- hello-world
可以使用驼峰写法
- HelloWorld
调用组件时,需要使用短横线写法 上述两种,都可以使用hello-world
但是也有例外,以下三种情况可以无视这种规则
1.写在template选项中
//写在template选项中,可以无视这种规则
Vue.component('com1', {
template:
'<div>
COM1
<HelloWorld></HelloWorld>
</div>'
})
Vue.component('HelloWorld', {
template: '<div>HelloWorld</div>'
})
//这里又template选项,会覆盖页面上的#app
//驼峰命名的组件,这里使用驼峰和短横线都可以使用
var vm = new Vue({
el: '#app',
template:
`<div id='app'>
<HelloWorld></HelloWorld>
<hello-world></hello-world>
<com1></com1>
</div> `
});
2.写在里面的
我们写了组件这么多代码,特别是在写template选项中的写模版内容代码时,发现非常的不方便,没有语法高亮,也没有代码提示,这时可以将模版内容,放置在一个script标签里面
要注意:
- 需要设置script 的type属性为text/x-template
- 给这个script设置一个id
调用时,直接使用template:’#xxxx’即可
<script id="appTemplate" type="text/x-template">
<div id='app'>
<HelloWorld></HelloWorld>
<hello-world></hello-world>
<com1></com1>
</div>
</script>
<script>
Vue.component('com1', {
template: '<div>COM1<HelloWorld></HelloWorld></div>'
})
Vue.component('HelloWorld', {
template: '<div>HelloWorld</div>'
})
//调用模版内容
var vm = new Vue({
template: `#appTemplate`
});
</script>
这种情况时,使用template选项中调用组件,可以无视上面的组件调用规则
3.vue后缀的单文件组件时
5.组件的注意事项
-
全局组件注册时,必须要放置在new Vue之前
Vue.component('com1', { template: '<button v-on:click="count++">你点击了 {{ count }} times.</button>' }) //上面定义的全局组件,必须写在new Vue之前,否则会报错 var vm = new Vue({ el: '#app' });
//这是错误的写法,报错 var vm = new Vue({ el: '#app' }); Vue.component('com1', { template: '<button v-on:click="count++">你点击了 {{ count }} times.</button>' })
2.组件的template模版内容,必须只能有一个根元素.
<script id="appTemplate" type="text/x-template"> //调用组件时,必须只能有一个根组件 <div id='app'> <HelloWorld></HelloWorld> <hello-world></hello-world> <com1></com1> </div> </script>
<script id="appTemplate" type="text/x-template"> //这种情况会报错,因为必须只能有一个根元素 <HelloWorld></HelloWorld> <hello-world></hello-world> <com1></com1> </script>
三.Props选项
将组件看成是一个函数,props就是这个函数接收到的参数集合
prop就是props中具体的一个参数
//这样理解props选项
//定义一个函数,接收props形参
function hello(props){
}
//调用hello函数 传递一个对象,给到参数
hello({name:liuqiao,age:18,sex:'男'});
//props指的就是这些个参数集合
props=>{name:liuqiao,age:18,sex:'男'}
name =>prop 这都是其中的一个prop
age =>prop
sex =>prop
1.props命名规则
HTML 中的 属性 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符,
如果是在html中写prop时,如果props设置时是使用驼峰命名法,则需要改用短横线
```js
Vue.component('hello', {
props: ['postTitle'],
template: '<div> 我的姓名是:{{ postTitle }} </div>'
})
<div id="app">
<hello post-title='liuqiao'></hello>
</div>
```
注意,这里的props命名规则与组件名命名的规则一样,也有三种情况可以无视这种规则
- 写在template选项中
- 写在里面的
- vue后缀的单文件组件时
官网的说明是:使用字符串模板,那么这个限制就不存在了。
2.设置props
组件定义时,如何设置形参?就是设置props选项
最简单的方式就是:写成数组形式,数组中每一项就是一个prop
定义的每一个prop,可以直接通过实例对象去访问到,就像访问data数据和computed数据一样
Vue.component('hello', {
//props选项:写成数组形式,数组中每一项就是一个prop
props: ['name', 'age', 'sex'],
template: `
<div>
我的姓名是:{{ name }},我的年龄:{{age}}
<button @click="hi('abc')">点我</button>
</div>
`,
methods:{
hi(a){
console.log(this.name+this.age+a);
console.log(this.sex);//不传递sex参数时,打印结果是undefined
}
}
})
3.传递参数
调用组件时,在组件上的标签上写属性即可
注意:调用时:不传递参数是没有问题的
//调用模版内容
var vm = new Vue({
el:'#app',
template: `
<div id="app">
<hello name='liuqiao' age='19'></hello>
</div>
`
});
如:
hello组件上name就是liuqiao
hello组件上age就是19
hello组件上sex没有传递,是undefined
4.props的默认值
如果调用组件时,没有传递某个prop下来,那么组织中的这个prop的值是undefined
设置props默认值:也就是调用时如果没有传递某prop,那我就使用默认值
类似函数中es6的设置默认值
es6中函数设置默认值
funciton hello(name='liuqiao'){
}
hello();
在组件中给props设置默认值>关键点是:将数组格式的props改成对象格式
props:{
key1:{
type:String,
default:'liuqiao'
},
key2:{
type:Number,
default:18
}
}
key1,key2就是props的名字
type:是接收这个参数的数据类型
default:是这个prop的默认值
Vue.component('hello', {
props: {
'name':{
type:String,
default:'zhangsan'
},
'age':{
type:Number,
default:18
}
},
template: `
<div>
我的姓名是:{{ name }},我的年龄:{{age}}
</div>
`
})
var vm = new Vue({
el: '#app',
template: `
<div id="app">
<hello></hello>
</div>
`
});
5.props的校验
如果调用组件时,我希望可以保证你传过来的prop是有效的,所以需要设置 正确的数据类型
比如我在组件中规定,你需要传递string类型的参数,你就必须传递string类型,否则报错
这叫做props的类型校验
官网的例子是这样的:
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
我这边自己也就是总结了4中写法:
1.直接写需要的类型
2.写成对象形式
3.写成数组形式(意思是string和number都可以接收)
props:{
key:String //第一种
key:{
type:String //第二种
}
key:[String,Number],//第三种
//第四种,自定义验证函数
key:{
abc:function(){
}
}
}
其中type类型可以有以下几种:
String
Number
Boolean
Array
Object
Date
Function
- Symbol
另外,传递prop的时候,默认都是字符串格式,需要转化的话,只需加上 v-bind 即可
Vue.component('hello', {
props: {
'name':String,
'age':{
type:Number
},
'sex':[String,Number]
},
template: `
<div>
我的姓名是:{{ name }},我的年龄:{{age}}
</div>
`
})
//调用模版内容
var vm = new Vue({
el: '#app',
template: `
<div id="app">
<hello name='liuqiao'></hello>
<hello :age='18'></hello>
<hello sex='男'></hello>
</div>
`
});
v-bind可以省略,直接使用冒号 : 即可
6.传递动态的props
传入一个对象所有需要的属性
Vue.component('hello', {
props: {
name: String,
age: Number
},
template: `
<div>
我的姓名是:{{ name }},我的年龄:{{age}}
</div>
`
})
//调用模版内容
var vm = new Vue({
el: '#app',
data: {
userinfo: {
name: 'liuqiao',
age: 28
}
},
template: `<hello v-bind='userinfo'></hello>`
});
调用组件时,直接属性上写v-bind=
<hello v-bind='userinfo'></hello>
实际上,这种写法等价于
<hello v-bind:name='userinfo.name' v-bind:age='username.age'></hello>
7.Props单向数据流
单向数据流:props传递参数时,都是单向下行绑定的.父组件的prop会向下传递到子组件,反过来不行
所有的prop都使得其父子prop之间形成了一个单向下行绑定.父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
上面一段是官网的原话.总结一句话就是:不要去直接修改prop传过来的数据
一般有两种情况改变prop
-
父组件传过来的初始值 通过props传过来的,子组件想作为自己本地存储
props:{ msg:{ type:String, required:true } }, data(){ return{ msgInfo:this.msg; } }
这种方式,就是在本地声明一个属性,然后初始化去接收,作为本地存储,就不会修改prop了
-
父组件传过来的初始值,通过props传过来,然后需要自己做转换
props:{ msg:{ type:String, required:true } }, computed:{ newMsg:function(){ return this.msg.trim().toLowerCase() } }
这种方式,就是通过一个计算属性去处理