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}}

%