组件中props属性的使用
组件中props属性的作用: props是用来给组件传递响应静态数据或者是动态数据的, 其实就是给组件设置属性的
- 那么组件有了属性之后使用组件的时候就可以通过向组件中的属性赋值而将数据传递到组件中来了
- 我们既可以是直接静态的向组件的属性中传值, 也可以是给组件的对应参数绑定一个v-bind指令, 那么就可以动态的传递数据了
- 静态传递数据就是直接在使用组件的时候通过参数传递一个具体的值而传递过来一个数据, 我们就称之为: 静态传递数据
- 动态传递数据就是使用组件的时候使用v-bind指令给我们的参数绑定一个数据模型, 那么只要这个数据模型的值发生改变之后对应的参数值就会发生改变, 我们就称之为: 动态传递数据
- 我们既可以是直接静态的向组件的属性中传值, 也可以是给组件的对应参数绑定一个v-bind指令, 那么就可以动态的传递数据了
(一)通过在组件上声明静态数据传递给组件内部
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>通过在组件上声明静态数据传递给组件内部</title>
<!-- 导入vue.js文件 -->
<script src="../../js/Vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id = "app">
<!-- 这里我们一定要注意: 我们使用自定义组件的时候组件一定是使用到Vue实例作用范围之内,比如这里就是作用于id属性值为app的div标签 -->
<login user-name = "小陈" age = "23"></login>
</div>
<script type="text/javascript">
//1. 声明组件的模板配置对象
let login = {
template : "<div><h1>欢迎: {{userName}} 年龄: {{age}}</h1></div>",
props : ["userName","age"]
}
// 2. 注册组件
const vue = new Vue({
el: "#app",
data:{
},
methods:{
},
components:{
login
}
})
</script>
</body>
</html>
小结:
- 使用组件时可以在组件上定义多个属性以及对应数据
- 在组件的内部可以使用props属性声明多个定义在组件上的属性, 之后可以在组件中通过{{属性名}}方式获取组件中的属性值
这里有一个关于prop大小写的问题: (camelCase VS kebab-case)
HTML中的attribute(属性名)是大小写不敏感的, 所以浏览器会把所有大写字符解释为小写字符, 这意味着当你使用DOM中的模板时camelCase(驼峰命名法)的prop名需要使用其等价的kebab-case(短横线分隔命名)命名
-
因为我们在JS中创建组件的时候对于组件名和组件的属性都是使用表示符的命名规范命名的, 但是到了HTML中的时候对应的我们组件的组件名和组件的属性的命名都是要符合kebab-case(短横线分隔命名规范的)
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试驼峰改短横线命名规范</title>
<!-- 导入vue.js文件 -->
<script src="../../js/Vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id = "app">
<blog-first post-title = "哈哈哈"></blog-first>
</div>
<script type="text/javascript">
// 注册全局组件
Vue.component("blogFirst",{
// 注意,JS对象中多属性之间一定要使用,(逗号)分割
template:"<h3>{{postTitle}}</h3>",
props:["postTitle"]
})
const vue = new Vue({
el : "#app",
data:{
},
methods:{
}
})
</script>
</body>
</html>
- 我们可以发现在JS中声明为blogFist的组件名和声明为postTitle的属性名在HTML中使用的时候就是标签名为: blog-first, 属性名就是: post-title
如果你使用的是字符串模板, 那么这个限制就不存在了
(二)通过在组件上声明动态数据传递给组件内部
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>通过在组件上声明动态数据传递给组件内部</title>
<!-- 导入vue.js文件 -->
<script src="../../js/Vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id = "app">
<login :name = "name" :age = "age"></login>
</div>
<!-- 定义组件模板 -->
<template id = "template_1">
<h1>{{name}},{{age}}</h1>
</template>
<script type="text/javascript">
// 创建组件模板对象
let login = {
template:"#template_1",
props:["name","age"]
}
const vue = new Vue({
el : "#app",
data: {
name : "张三",
age : 23
},
methods:{
},
components:{
login:login
}
})
</script>
</body>
</html>
- 这里就是使用了v-bind指令将数据绑定到Vue的实例中的data属性上, 然后data属性中的数据模型值一旦发生改变之后组件内部数据也会跟着变化
最后这里我们再补充一个重要的问题: prop的单向数据流
prop的单向数据流:
所有的prop都会使得父子prop之间形成了一个"单向下行绑定", 父级的prop的值的更新就会向下流动到子组件中, 但是反过来则不行
- 这样能防止子组件中意外改变父级组件的状态, 从而导致你的应用的数据流向难以理解
- 额外的, 每次父组件中发生更新时, 子组件中的所有prop都会刷新为最新值, 这意味着你不应该在一个子组件的内部改变prop值, 如果你这样做了, Vue就会在浏览器的控制台发出一个警告
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>prop的单向数据流</title>
<!-- 导入vue.js文件 -->
<script src="../../../js/Vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<login :name = "username">
</login>
</div>
<script type="text/javascript">
//创建局部组件对象
const login = {
template : "<div><h1>欢迎:{{name}}</div></h1>",
data(){ //组件中自己的data数据
return{
name:"小陈"
}
},
props:["name"]
}
const vue = new Vue({
el: "#app",
data:{
username : "小黑"
},
methods:{
},
// 注册局部组件对象
components:{
login:login
}
})
</script>
</body>
</html>
-
这个时候我们使用的login组件的时候name属性肯定就是内部组件的参数( 内部组件就是login组件 ),而这个参数和Vue中的data中的username数据模型进行了绑定, 而我们的Vue此时就是外部组件, 那么如果我们的username值发生了改变之后, login组件的name参数就会发生改变, 而此时我们可以发现在内部组件( 也就是login组件)中我们也声明了data属性, 在data属性中声明了一个name数据, 那么当我们的username值发生改变之后内部组件中的name属性的值也会发生对应的改变 —> 因为此时data中的name和props中定义的参数name是同名的, 所以这个时候当参数的值发生改变时name的值也会发生改变 ----> 但是一旦username的值改变就会引起name值的改变, 如果这个时候name值和username值不相同, 这个时候可能name的值会反过来影响username值, 所以我们就要么就让内部的参数值和data中的数据值不相同
-
简而言之: 我们其实就是Vue核心对象中的data中的username数据模型和组件中的name参数进行了一个绑定, 这个时候如果我们的组件中data属性中如果声明了一个为name的本地数据的时候这个name的值就会反过来影响name属性的值, 那么就有可能会在刷新prop值的时候就会由name(data中name)影响到name属性值, 由name属性值又会影响到我们的和此组件绑定数据模型的值, 所以为了避免这种问题的发生, 我们就要求我们的自组件的内部一定不可以改变prop(也就是属性)的值
-
eg:
-
//内部文件的data属性中 return { uname : this.username }
- 所以我们如果是设置为uname = this.username的时候这个时候其实就是将我们的传入的参数值保存到了组件的内部, 这个时候就是保存到了组件的内部的uname数据中
-
最后我们还有一个问题: 我们的自定义组件中的data必须是一个函数,而不能是一个对象, 为什么?
如果data是一个对象, 我们知道对象是object类型的, 而object类型是引用类型, data就是一个指针, 指向对象在堆内存中存储的地址
- 那么由于我们的data是一个对象, 我们在进行组件复用的时候, 相当于这些组件是共用的同一个data对象, 一个组件中的data数据发生了改变之后, 那么其他复用组件的数据也会跟着发生改变, 这就降低了组件的复用性和灵活性
那么如果我们将data定义为函数之后, 我们知道函数是具有作用域的, 组件中的data设置为一个函数之后, 相当于每个组件实例都有自己的作用域, 那么局部作用域之内的数据的改变并不会影响其他的作用域内的data数据, 这样就能保证组件的复用性和灵活性
- 我们就可以理解为和递归一样, 开辟了很多的栈, 不会相互之间形成干扰
小结:
- 根实例对象(Vue)data可以是对象, 也可以是函数(根实例是单例), 不会产生数据污染的情况
- 组件实例对象data必须为函数, 目的是为了防止多个组件实例对象之间共用一个data对象, 就会产生数据污染, 采用函数的形式, initData时(初始化data时)会将其作为工厂函数都会返回全新的data对象