15-不得不学的Vue.js基础之Prop
前端 · 2020-07-22 ·
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Prop</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--组件是vue.js最强大的功能,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。那么组件间如何通信,也就成了 vue 中永恒的话题。prop 、 $emit 这2个属性就是常常用来通信的主角。"props down , events up" 就是实现父子组件之间的双向传值口令。-->
<!--1.通过prop实现通信:子组件的props选项能够接收来自父组件数据,仅仅只能接收,props是单向绑定的,既只能父组件向子组件传递,不能反向。传递一般分为2类。-->
<!--1.1静态传递:子组件通过props选项来声明一个自定义的属性,父组件可以通过此属性往子组件传递数据-->
<p>
<sub-unit message="我是要传递给子组件的数据"></sub-unit>
</p>
<!--1.2动态传递:通过v-bind 绑定props的自定义的属性,传递过去的就不是静态的字符串了,它可以是一个表达式、布尔值、对象等任何类型的值-->
<p>
<sub-unit message="我是要传递给子组件的数据"></sub-unit>
<sub-unit v-bind:message="a+b"></sub-unit>
<sub-unit v-bind:message="msg"></sub-unit>
</p>
<!--2.通过$ref 实现通信:ref 是被用来给元素或者子组件注册引用信息的。引用信息将会注册在父组件的 $refs 对象上。-->
<!--· 如果ref用在子组件上,指向的是组件实例,可以理解为对子组件的索引,通过$ref可获取到在子组件里定义的属性和方法-->
<!--· 如何ref用在普通的DOM元素上,引用指向的就是该DOM元素,通过$ref可能获取到该DOM的属性集合,轻松访问到DOM元素,作用与JQ选择器类似。-->
<!--以下通过$rel,实现prop实现的功能-->
<p>
<sub-unit2 ref="msg2"></sub-unit2>
</p>
<!--· prop 着重于数据的传递,它并不能调用子组件的属性和方法。比如创建文章组件时,自定义标题和内容这样的场景,最适合使用prop-->
<!--· $ref 着重于索引/引用,主要用来调用子组件的属性和方法,不善于数据的传递。并且ref 用在DOM元素的时候,能起到选择器的作用,这个功能比作为索引更时常用到-->
<!--3.通过$emit实现通信:通过$emit 实现子组件向父组件通信。vm.$emit(event,arg) $emit 绑定一个自定义事件event,当这个语句被执行到的时候,就会将参数arg传递给父组件,父组件通过v-on:event监听并接收参数-->
<p>
<h1>{{title}}</h1>
<sub-unit3 v-on:get-message="showTitle"></sub-unit3>
</p>
<!--4 prop 类型-->
<!--
4.1 以字符串数组形式列出 prop:
props:['title','likes','isPublished','commentIds','author']
4.2 指定每个prop的值类型:
props:{
title:String,
likes:Number.
isPublished:Boolean,
commentIds:Array,
author:Object,
callback:Function,
ContactsPromise:Promise //其他的构造函数
}
这不仅为你的组件提供了文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。
4.3 传递静态或动态prop
<blog-post title="My journey with Vue"></blog-post> //传递静态的值
<blog-post v-bind:title="post.title"></blog-post> // 动态赋予一个变量的值
<blog-post v-bind:title="post.title + ' by ' + post.author.name"></blog-post> //动态赋予一个表达式的值
4.3.1 传入一个数字
// 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue,这是一个 JavaScript 表达式而不是一个字符串。
<blog-post v-bind:likes="42"></blog-post>
// 用一个变量进行动态赋值
<blog-post v-bind:likes="post.likes"></blog-post>
4.3.2 传入一个布尔值
// 包含该 prop 没有值的情况在内,都意味着 `true`。
<blog-post is-published></blog-post>
// 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue,这是一个 JavaScript 表达式而不是一个字符串。
<blog-post v-bind:is-published="false"></blog-post>
// 用一个变量进行动态赋值
<blog-post v-bind:is-published="post.isPublished"></blog-post>
4.3.3 传入一个数组
// 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue,这是一个 JavaScript 表达式而不是一个字符串。
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
// 用一个变量进行动态赋值
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
4.3.4 传入一个对象
// 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue,这是一个 JavaScript 表达式而不是一个字符串。
<blog-post
v-bind:author="{
name: 'Veronica',
company: 'Apple'
}"
></blog-post>
// 用一个变量进行动态赋值
<blog-post v-bind:author="post.author"></blog-post>
4.3.5 传入一个对象的所有 property
//如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>
等价于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
-->
<p>
<blog-post v-bind="post"></blog-post>
</p>
<!--5.单向数据流:所有的 prop 都使得其父子 prop 之间形成一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。为了防止子组件意外变更父级组件的状态,导致你的应用的数据流向难以理解。-->
<!--额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这就意味着你不应该在一个子组件内部改变 prop。如果你这样做,Vue会给你发出警告。-->
<!--注意:在JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 而言,在子组件中改变这个对象或数组将会影响到父组件的状态。-->
<!--6.prop 验证:可以为prop 指定验证要求,如果有一个需求没有通过,则Vue 会在浏览器控制台中警告-->
<p>
<my-component v-bind:propa='20' v-bind:propb='20' v-bind:propc='propc'></my-component>
</p>
<!--7.非 Prop 的 Attribute:一个非 prop 的attribue 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute-->
<!--显式定义的prop适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的attribute,而这些attribute会被添加到这个组件的根元素上。-->
<!--例如:想象一下你通过一个Bootstrap 插件使用了一个第三方的 <bootstrap-date-input> 组件,这个插件需要在其内部 <input> 上用到一个 data-date-picker attribute。我们可以将这个attribute添加到你的组件实例上-->
<p>
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>
</p>
<!--然后这个 data-date-picker="activated" attribute 就会自动添加到 <bootstrap-date-input> 的根元素 input 上。-->
<!--7.1替换/合并已有的 Attribute:想象一下 <bootstrap-date-input> 的模板是这样的:
<input type="date" class="form-control">
为了给我们的日期选择器插件定制一个主题,我们可能需要像这样添加一个特别的类名:
-->
<p>
<bootstrap-date-input data-date-picker="activated" class="date-picker-theme-dark"></bootstrap-date-input>
</p>
<!--
这种情况下,我们定义了2个不同的 class 的值:
· form-control ,这是在组件的模板内设置好的
· date-picker-theme-dark,这是从父级组件传入的
对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值,所以如果传入 type="text" 就会替换掉 type="date" 并把它破坏!庆幸的是,class 和 style attribute 会稍微智能一些,两边的值会合并起来。
-->
<p>
<bootstrap-date-input data-date-picker="activated" class="date-picker-theme-dark" type="text"></bootstrap-date-input>
</p>
<!--8.禁用 Attribute 继承
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false。
这尤其适合配合实例 $attrs property 使用,该property 包含了传递给一个组件的 attribute 名和attribute 值。
有了 inheritAttrs: false 和 $attrs,你就可以手动决定这些 attribute 会被赋予哪个元素。
注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。
-->
<p>
<base-input v-model="username" label="username" required placeholder="Enter your username"></base-input>
</p>
</div>
<script>
// 全局对象
// 1.注册全局组件sub-unit!
Vue.component('sub-unit', {
props: ['message'],
template: `
<h3>{{message}}</h3>
`
});
// 2.注册全局组件sub-unit2!
Vue.component('sub-unit2', {
data(){
return{
message:'sub-unit2'
}
},
template: `
<h3>{{message}}</h3>
`,
methods:{
getMessage(val){
this.message= val
}
}
});
//3.注册全局组件sub-unit3!
Vue.component('sub-unit3', {
mounted:function(){
this.$emit('get-message',"我是子组件的title数据");
},
template: `
<h3>这是个子组件传数据给父组件的例子</h3>
`,
});
//4.注册全局组件blog-post!
Vue.component('blog-post', {
props:['id','title'],
template:`
<h3>{{id}}-{{title}}</h3>
`
});
//6.注册全局组件my-component!
Vue.component('my-component',{
props:{
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propa:Number,
// 多个可能的类型
propb:[String,Number],
// 必填的字符串
propc:{
type:String,
require: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
}
}
},
template:`
<span>{{propa}}{{propb}}{{propc}}{{propd}}</span>
`
});
//7.注册全局组件bootstrap-date-input!
Vue.component('bootstrap-date-input', {
template:`
<input type="date" class="form-control">
`
});
//8.注册全局组件base-input!
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind:value="value"
v-bind="$attrs"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
});
var vm = new Vue({
el: "#app",
data:{
a:"我是传递给子组件的数据a",
b:"我是传递给子组件的数据b",
msg:"我是传递给子组件的数据msg",
title:"我是父组件的title",
post:{
id:88,
title: 'My Journey with Vue'
},
propc:'',
username:''
},
mounted:function(){
console.log(this.$refs.msg2);
this.$refs.msg2.getMessage("我要传递数据给子组件rel=msg2");
},
methods:{
showTitle(val){
this.title=val;
}
}
});
</script>
</body>
</html>
以下是测试数据:
{{title}}