Vue组件-组件组合

时间:2023-03-09 03:36:23
Vue组件-组件组合

组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。

 <html>
<head>
<title>Vue组件 A 在它的模板中使用了组件 B的例子</title>
<script src="vue.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="app">
<my-item :list="list"></my-item>
</div> <script>
new Vue({
el:"#app",
data:{
list:["第一项","第二项","第三项"]
},
components:{
// 组件A
"my-item":{
template : `
<div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
<my-list :list="list"></my-list>
</div>
`,
props:['list'], components:{
// 组件B
"my-list":{
template : `
<div>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
`,
props:['list']
} } },
}
})
</script>
</body>
</html>

在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。

Vue组件-组件组合

Prop

使用 Prop 传递数据

组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中。

我们来看一个例子:

 <html>
<head>
<title>Vue子组件的模板内直接引用父组件的数据</title>
<script src="vue.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="app">
<child></child>
</div> <script>
new Vue({
el:"#app",
data:{
message:"Hello" },
components:{
"child":{
template:"<p>{{ message }}</p>"
} } })
</script>
</body>
</html>

这样子组件直接引用父组件的数据会出错!

Vue组件-组件组合

子组件要显式地用 props 选项声明它预期的数据:

 <html>
<head>
<title>Vue props父子之间通信</title>
<script src="vue.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="app">
<child message="Hello Vue!!!"></child>
</div> <script>
new Vue({
el:"#app",
components:{
"child":{
template:"<p>{{ message }}</p>" ,
props:['message']
},
} })
</script>
</body>
</html>

这样就可以正常显示我们想要的结果

Vue组件-组件组合

模板的要求

注意:组件的模板只能有一个根元素。下面的情况是不允许的。

template: `<div>这是一个局部的自定义组件,只能在当前Vue实例中使用</div>
<button>hello</button>`,

camelCase vs. kebab-case

HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名):

  <div id="example">
<child myMessage="Hello Vue!!!"></child>
</div> <script>
new Vue({
el:"#example",
components:{
"child":{
template:"<p>{{ myMessage }}</p>",
props:["myMessage"]
}
}
})
</script>

这样就会报错

应改为

 <div id="example">
<child my-Message="Hello Vue!!!"></child>
</div>

动态prop:

与绑定到任何普通的 HTML 特性相类似,我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:

 <div id="example">
<input v-model="parentMsg"><br>
<child :message="parentMsg"></child>
</div>
<script>
Vue.component("child",{
template:"<span>{{ message }}</span>",
props:["message"] })
new Vue({
el:"#example",
data:{
parentMsg:""
}
})
</script>

浏览器打开显示

Vue组件-组件组合

如果你想把一个对象的所有属性作为 prop 进行传递,可以使用不带任何参数的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。

这句话怎么理解呢?我们看一个例子

 <div id="app">
<my-component v-bind="todo"></my-component> <!--重点就是这-->
<!--不用将对象的属性写成 v-bind:first="todo.first" v-bind:second="todo.second"-->
</div>
Vue.component("my-component", {
template: "<div><span>第一个值是:{{first}},第二个值是:{{ second }}</span></div>",
props:["first","second"]
})
new Vue({
el:"#app",
data:{
todo:{
first:"Hello world",
second:"Hello Vue"
}
}
})

字面量语法 vs 动态语法

初学者常犯的一个错误是使用字面量语法传递数值:

<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>

因为它是一个字面量 prop,它的值是字符串 "1" 而不是一个数值。如果想传递一个真正的 JavaScript 数值,则需要使用 v-bind,从而让它的值被当作 JavaScript 表达式计算:

<!-- 传递真正的数值 -->
<comp v-bind:some-prop="1"></comp>

例子如下:

 <div id="example">
<child num1="4" v-bind:num2="4"></child>
</div>
<script>
Vue.component("child", {
template:"<p>使用了字面量传递数值+2得出结果:{{num1 + 2}}<br>使用了动态语法传递数值+2的结果:{{num2 + 2}}</p>",
props:["num1","num2"]
})
new Vue({
el:"#example",
})
</script>

浏览器打开显示:

Vue组件-组件组合

单向数据流

Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。

另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。

 <html>
<head>
<title>Vue单向数据流</title>
<script src="vue.js"></script>
</head>
<body>
<div id="example">
<my-button :count="count"></my-button>
</div>
<script>
Vue.component("my-button", {
props:["count"],
template:"<button @click='changeMessage'>{{ count }}</button>",
methods:{
changeMessage:function(){
this.count++
}
}
})
new Vue({
el:"#example",
data:{
count:0
}
})
</script> </body>
</html>

Vue组件-组件组合

虽然想要做的功能实现了但vue报了个错,你不应该在子组件内部改变 prop

在两种情况下,我们很容易忍不住想去修改 prop 中数据:

  1. Prop 作为初始值传入后,子组件想把它当作局部数据来用;

  2. Prop 作为原始数据传入,由子组件处理成其它数据输出。

上面的例子就是第一种情况子组件怎么把它当局部数据用?

定义一个局部变量,并用 prop 的值初始化它:

 data:function(){
return {
initCount: this.count
}
}
 <html>
<head>
<title>Vue单向数据流</title>
<script src="vue.js"></script>
</head>
<body>
<div id="example">
<my-button :count="count"></my-button>
</div>
<script>
Vue.component("my-button", {
props:["count"],
template:"<button @click='changeMessage'>{{ initCount }}</button>",//别忘了这里也需要修改
data:function(){
return {
initCount: this.count
}
},
methods:{
changeMessage:function(){
this.initCount++
}
}
})
new Vue({
el:"#example",
data:{
count:0
}
})
</script> </body>
</html>

这样就ok了!

第二种情况请看下面例子:

 <html>
<head>
<title>Vue单向数据流</title>
<script src="vue.js"></script>
</head>
<body>
<div id="example">
<input v-model="message">
<child :message="message"></child>
</div>
<script>
Vue.component("child", {
props:["message"],
template:"<p>{{ message }}</p>"
})
new Vue({
el:"#example",
data:{
message:"test"
}
})
</script>
</body>
</html>

Vue组件-组件组合

这是一个双向绑定的例子,我们如何把输入的值将它用p标签转化成大写显示出来!

computed:{
    changeMessage:function(){
   return this.message.trim().toUpperCase();
}
}

 <html>
<head>
<title>Vue单向数据流</title>
<script src="vue.js"></script>
</head>
<body>
<div id="example">
<input v-model="message">
<child :message="message"></child>
</div>
<script>
Vue.component("child", {
props:["message"],
template:"<p>{{ changeMessage }}</p>",
computed:{
changeMessage:function(){
return this.message.trim().toUpperCase();
}
}
})
new Vue({
el:"#example",
data:{
message:"test"
}
})
</script>
</body>
</html>

Vue组件-组件组合

props验证

我们可以为组件的 prop 指定验证规则。如果传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件非常有用。

<html>
<head>
<title>Vueprops验证</title>
<script src="vue.js"></script>
</head>
<body>
<div id="example">
<child
:name="name"
:age="age"
></child>
</div>
<script>
Vue.component("child", {
props:{
name:{
type:String,
default:"Vue",
required:true
},
age:Number
},
template:"<p>同学叫{{ name }},年龄:{{ age }}</p>",
}) new Vue({
el:"#example",
data:{
name:"胡小明",
age:18,
}
})
</script>
</body>
</html>

就从这个简单的例子分析一下用法:

type:可以使用type来声明这个参数可以接受的数据的类型,当检查规则只有一个的时候type可以略写如

Vue组件-组件组合

当参数可以是多种类型的其中一个的时候,使用数组来表示

message:[String,Number]

type可以是以下原生类型:

String

Number

Boolean

Function

Object

Array

Symbol

required:可以使用required选项来声明这个参数是否必须传入。如果是true则为必填项,如不填则报出警告

defaule:使用default选项来指定当父组件未传入参数时props变量的默认值:

validator:当校验规则很复杂,默认提供的校验规则无法满足的时候可以使用自定义函数来校验。(上面代码没有写这个例子)

举例当小明可以去网吧上网的时候必定年龄是>=18的

 props:{
age:{
validator:function(value){
return value >= 18
}
} }