13-不得不学的Vue.js基础之组件基础

前端 · 2020-07-22 ·
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>组件基础</title>
	<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
	<style>
	  .tab-button {
	    padding: 6px 10px;
	    border-top-left-radius: 3px;
	    border-top-right-radius: 3px;
	    border: 1px solid #ccc;
	    cursor: pointer;
	    background: #f0f0f0;
	    margin-bottom: -1px;
	    margin-right: -1px;
	  }
	  .tab-button:hover {
	    background: #e0e0e0;
	  }
	  .tab-button.active {
	    background: #e0e0e0;
	  }
	  .tab {
	    border: 1px solid #ccc;
	    padding: 10px;
	  }
	</style>
</head>
<body>
	<div id="app">
		<!--1.基本示例:组件是可复用的Vue 实例,它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等,仅有的例外是 el 选项。-->
		<p><button-counter></button-counter></p>

		<!--2.组件的复用:当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。
		data 必须是一个函数:因此每个实例可以维护一份被返回对象的独立的拷贝。如果你不这么做,Vue 也会警告你。
		-->
		<p><button-counter></button-counter> <button-counter></button-counter></p>

		<!--3.通过 Props 向子组件传递数据:比如博文组件,如果不能向该组件传递一篇博文的标的或内容之类的我们想要展示的数据的话,它是没有办法使用的,这也是Prop 的意义所在。-->
		<!--3.1.Prop 是你可以在组件上注册一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个property。为了给博文组件传递一个标题和日期,可以用一个props选项,将其包含在该组件可接受的prop列表中。-->
		<blog-post title="I am the title" date="2020-06-20"></blog-post>

		<!--
		3.2.使用v-bind动态传递prop。这样的场景会经常使用到,比如从一个API获取博文列表。	
		-->
		<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" v-bind:date="post.date"></blog-post>

		<!--我们的博文不只需要标题和日期,还有内容、作者、评论、点击等等,为每个相关信息定义一个 prop 变得很麻烦:
			<blog-post 
				v-for="post in posts" 
				v-bind:key="post.id" 
				v-bind:title="post.title" 
				v-bind:date="post.date"
				v-bind:content="post.content"
				v-bind:author="post.author"
				v-bind:comments="post.comments"
				v-bind:click="post.click">
			</blog-post>
		-->
		<blog-post2 v-for="post in posts" v-bind:key="'b-2'+post.id" v-bind:post="post"></blog-post2>
		
		<!--4.监听子组件事件:在<blog-post> 组件中,假设它的一些功能需要和父组件进行沟通,比如放大博文字号。-->
		<div :style="{ fontSize: postFontSize + 'em' }">
			<blog-post4 v-for="post in posts" v-bind:key="'b-4'+post.id" v-bind:post="post"  v-on:enlarge-text="postFontSize+=0.1" v-on:lessen-text="postFontSize-=0.1"></blog-post4>
		</div>

		<!--5.使用事件抛出一个值:有的时候抛出一个值时非常有用的-->
		<div :style="{ fontSize: postFontSize + 'em' }">
			<blog-post5 v-for="post in posts" v-bind:key="'b-5'+post.id" v-bind:post="post"  v-on:enlarge-text="onEnlargeText"></blog-post5>
		</div>

		<!--6.在组件上使用 v-model:自定义事件也可用于创建支持 v-model 的自定义输入组件。v-model相当于语法糖。-->
		<hr />
		<input v-model="searchText">
		双向数据绑定展示:你输入的是 <span style="color:red">{{searchText}}</span>
		<hr />
		<input
		  v-bind:value="searchText"
		  v-on:input="searchText = $event.target.value"
		>
		双向数据绑定展示:你输入的是 <span style="color:red">{{searchText}}</span>
		<hr />
		<!--当用在组件上是,v-model 则是这样的-->
		<custom-input
		  v-bind:value="searchText"
		  v-on:input="searchText = $event"
		></custom-input>
		双向数据绑定展示:你输入的是 <span style="color:red">{{searchText}}</span>

		<!--7.通过插槽分发内容:和HTML元素一样,我们经常需要向一个组件传递内容-->
		<alert-info>
		  Something bad happened.
		</alert-info>

		<!--8.动态组件:
		<component v-bind:is="currentTabComponent"></component>
		如果出现v-bind,那么加载这个组件会去创建的Vue实例对象的data或者computed里找值:
		var vm = new Vue({
			el: "#app",
			...
			computed: {
			  currentTabComponent: function() {
			    return "tab-" + this.currentTab.toLowerCase();
			  }
			}
		});
		假设当前的 currentTab 的值为Home,则
		<component is="tab-home"></component>
		注意这里的:is转变为了is,因为我们已经成功从vm里面渠道了tab-home,剩下的事情就去找这个叫做tab-home的component:
		Vue.component("tab-home", {
		  template: "<div>Home component</div>"
		});

		最后这里就会变为
		<div>Home component</div>

		对于:is='currentTabComponent'来说,总的流程如下
		1.去vue实例的data或者computed里找currentTabComponent对应的值(假设值为 tab-home)
		2.用这个值再去查找component里对应的组件。
		对于is='tab-home'来说,总的流程如下
		1.直接用tab-home去所有的component里找对应的组件。
		-->
		<div id="dynamic-component-demo" class="demo">
		  <button
		    v-for="tab in tabs"
		    v-bind:key="tab"
		    v-bind:class="['tab-button', { active: currentTab === tab }]"
		    v-on:click="currentTab = tab"
		  >
		    {{ tab }}
		  </button>

		  <component v-bind:is="currentTabComponent" class="tab"></component>
		</div>
		<div style="color:red">
			<component is="tab-home" class="tab"></component>
		</div>
	</div>
	<script>
		// 1.注册全局组件button-counter!
		Vue.component('button-counter', {
		  data: function () {
		    return {
		      count: 0
		    }
		  },
		  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
		});

		// 2.注册全局组件blog-post!
		Vue.component('blog-post', {
		  props:['title', 'date'],
		  template: `<div class="blog-post">
			<h3>{{title}}</h3>{{date}}
			<hr/>
		  </div>`
		});

		// 3.注册全局组件blog-post2!
		Vue.component('blog-post2', {
		  props:['post'],
		  template: `<div class="blog-post2">
			<h3>{{post.title}}</h3>{{post.date}}
			<hr/>
		  </div>`
		});
		// 4.注册全局组件blog-post4!	
		Vue.component('blog-post4', {
		  props: ['post'],
		  template: `
		    <div class="blog-post">
		      <h3>{{ post.title }}</h3>
		      <button v-on:click="$emit('enlarge-text')">
		        Enlarge text
		      </button>
		      <button v-on:click="$emit('lessen-text')">
		        Lessen text
		      </button>
		      <div v-html="post.content"></div>
		    </div>
		  `
		});
		// 5.注册全局组件blog-post5!	
		Vue.component('blog-post5', {
		  props: ['post'],
		  template: `
		    <div class="blog-post">
		      <h3>{{ post.title }}</h3>
		      <button v-on:click="$emit('enlarge-text', 2)">
		        Enlarge text
		      </button>
		      <div v-html="post.content"></div>
		    </div>
		  `
		});
		// 6.注册全局组件blog-post5!	
		Vue.component('custom-input', {
		  props:['value'],
		  template: `
		    <input
			    v-bind:value="value"
			    v-on:input="$emit('input', $event.target.value)"
			>
		  `
		});
		// 7.注册全局组件blog-post5!	
		Vue.component('alert-info', {
		  template: `
		    <div class="demo-alert-box">
	        	<strong>Error!</strong>
	        	<slot></slot>
	        </div>
		  `
		});
		// 8.注册全局组件 tab-home、tab-posts、tab-archive
		Vue.component("tab-home", {
		  template: "<div>Home component</div>"
		});
		Vue.component("tab-posts", {
		  template: "<div>Posts component</div>"
		});
		Vue.component("tab-archive", {
		  template: "<div>Archive component</div>"
		});
		// 全局对象
		var vm = new Vue({
			el: "#app",
			data:{
				posts:[
					{ id: 1, title: 'My journey with Vue' ,date:'20200620',content:"My journey with Vue"},
					{ id: 2, title: 'Blogging with Vue' ,date:'20200621',content:"Blogging with Vue"},
					{ id: 3, title: 'Why Vue is so fun',date:'20200622',content:"Why Vue is so fun"}
				],
				postFontSize:1,
				searchText:"搜索词",
				currentTab: "Home",
				tabs: ["Home", "Posts", "Archive"]
			},
			methods:{
				onEnlargeText:function(enlargeAmount){
					this.postFontSize+=enlargeAmount
				}
			},
			computed: {
			  currentTabComponent: function() {
			    return "tab-" + this.currentTab.toLowerCase();
			  }
			}
		});
	</script>
</body>
</html>

以下是测试数据:


双向数据绑定展示:你输入的是 {{searchText}}
双向数据绑定展示:你输入的是 {{searchText}}
双向数据绑定展示:你输入的是 {{searchText}} Something bad happened.
%