1. 认识组件
1.1
基础示例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><script>/*1. 组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>; 我们可以在一个通过new Vue创建的Vue根实例中,把这个组件作为自定义标签来使用; 2. 因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等; 3. 你可以将组件进行任意次数的复用;注意当点击按钮时,每个组件都会各自独立维护它的 count;因为你每用一次组件,就会有一个它的新实例被创建;4. data 必须是一个函数;当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:data: {count: 0}取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:data: function() {return {count: 0}}如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例:也就是说,此时这几个组件共同使用count这个属性, 数据共享, 一改全改!*/</script>
</head><body><div id="app"><button-counter></button-counter><button-counter></button-counter><button-counter></button-counter></div><script type="text/javascript">Vue.component('button-counter', {data: function () {return {count: 0}},template: `<button v-on:click="count++">you clicked me {{count}} times!</button>`}); var vm = new Vue({el: '#app'});</script>
</body></html>
1.2
全局注册和局部注册
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><script>/*0. 局部组件:在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件;var ComponentA = { ... };var ComponentB = { ... };var ComponentC = { ... };然后在 components 选项中定义你想要使用的组件:new Vue({el: '#app',components: {'component-a': ComponentA,'component-b': ComponentB} 属性名就是组件的名字!!!}); 1. 组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用; 仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树; 2. 所有实例都能用全局组件; 也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用。比如在app的Vue实例中分别注册了全局组件和局部组件,此时,我们又创建了另一个APP的Vue实例,在APP的Vue实例和app的Vue实例中我们都可以使用全局组件global-item,但是不能在APP的Vue实例中使用 在app的Vue实例中注册局部组件local-item!*/</script>
</head><body><!-- app的Vue实例 --><div id="app">{{message}}<global-item></global-item> <local-item></local-item> </div><hr><!-- APP的Vue实例 --><div id="APP">{{message}}<global-item></global-item><local-item></local-item> <!-- 在App实例下使用app实例下的局部组件, 默默失败(之前我记得是会报错的, 现在不报错了) --></div><script>// 全局组件Vue.component('global-item', {template: '<h4>全局组件</h4>'});// 局部组件(挂载到app实例下)var item = {template: `<h4>挂载在app实例下的局部组件</h4>`};// appvar app = new Vue({el: '#app',data: {message: 'app实例',},components: {'local-item': item}});// Appvar APP = new Vue({el: "#APP",data: {message: 'APP实例'}}); </script>
</body></html>
1.3
template模板抽离
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 通过template标签注册局部组件(id选择器);这样就把原来写在template中的一坨代码给抽离了出来,并且此时可以语法高亮,而且更易于维护!!!强烈建议以后写在组件中的template中的代码单独将其抽离出来写!!!(通过id选择器)-->
</head><body><!-- [1].以下这样也可以注册组件内容:但是自身也会占有一个标签 --><!-- <div id="loginTemplate"><h1>登录</h1></div> --><!-- [2].通过template标签注册组件内容: template标签起包裹作用; --><template id="loginTemplate"><h1>登录</h1></template><div id="app"><login-item></login-item></div><script type="text/javascript">var vm = new Vue({el: '#app',components: {'login-item': {template: `#loginTemplate`}}});</script>
</body></html>
1.4
根组件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 实际上这个Vue实例也可以看做是一个根组件, 既然是组件,那么必然是可以有template属性了!-->
</head><body><div id="app"></div><script type="text/javascript">var vm = new Vue({el: '#app', data: { },methods: {},template: `<h1>HelloWorld</h1>` /* template属性 */});</script>
</body>
</html>
1.5
单向数据流
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。--><!-- 这里有两种常见的试图变更一个 prop 的情形:1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:props: ['initialCounter'],data: function() {return {counter: this.initialCounter}}2. 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:props:['size'],computed: {normalizedSize: function() {return this.size.trim().toLowerCase();}}--><!-- 注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。-->
</head><body><div id="app"><login-item :attr='father'></login-item></div><script type="text/javascript">/*我们通过props-attr属性确实拿到了父组件中的father属性, 与此同时我们又定义了自己的数据属性attr, 从而发生了错误!!!这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue会在浏览器的控制台中发出警告; 可以通过this.name来保存来自父组中的father数据: data() {return { data: this.attr,}}*/const item = {template: `<div>{{attr}} <***> {{}}</div>`,props: ['attr'],data() {return {attr: 'i am son', // data: this.attr,}}}; var vm = new Vue({el: '#app',data: {father: 'i am father'},components: {'login-item': item}});</script>
</body></html>
2. 组件注册
2.1
全局组件
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js"></script>
</head><body><div id="app"><button-counter></button-counter><button-counter></button-counter><button-counter></button-counter></div><script type="text/javascript">// 组件注册:Vue.component('button-counter', {// data必须是一个函数 data: function () {return {count: 0}},// 组件模板:如下所示, 如果不存在div, 那么测试按钮不能被渲染出来(之前我记得是:如果不包含div, 那么直接报错, 现在倒是不报错了) // 也即是如果template中有多个元素, 必须使用一个根元素来包裹其内容, 这个根元素通常是div; template: `<div><button @click="handle">点击了{{count}}次</button><button>测试</button></div>`,// 组件方法methods: {handle: function () {this.count += 2;}}});// var vm = new Vue({el: '#app'});</script>
</body></html>
2.2
局部组件
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js"></script><!-- 可以通过一个普通的 JavaScript 对象来定义组件; 然后在 components 选项中定义你想要使用的组件; var ComponentA = { ... };var ComponentB = { ... };new Vue({el: '#app',components: {'component-a': ComponentA,'component-b': ComponentB}}); 局部组件只能在注册它的父组件中使用; 别的地方把不可以使用;-->
</head><body><div id="app"><tom-com></tom-com><jerry-com></jerry-com></div><script type="text/javascript">// 局部组件var tom = {data: function () {return {msg: 'helloTom'}},template: '<div>{{msg}}</div>'};var jerry = {data: function () {return {msg: 'helloJerry'}},template: '<div>{{msg}}</div>'};// var vm = new Vue({el: '#app',data: {},methods: {},components: {'tom-com': tom,'jerry-com': jerry/* tom 和 jerry不要加引号,否则无法无法正常显示, tom 和 jerry是变量 *//* tom-com 和 jerry-com是组件的名字 */}});</script>
</body></html>
2.3
组件命名规则
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js"></script><!-- 1. 短横线: 当用短横线(字母全小写且必须包含一个连字符)定义组件时, 那么引用该组件时必须使用短横线形式; (<my-component-name>)2. 驼峰: 当用驼峰(大驼峰)定义组件时, 那么引用该组件时两种命名法都可以;(<my-component-name> 或 <MyComponentName>)但是如果直接在DOM元素中使用驼峰命名的组件时, 必须以短横线形式来进行引用;如下所示:a. HelloWorld组件是大驼峰形式命名的; 此时在button-counter组件中使用HelloWorld组件, 因为是在template模板字符串中包裹, 此时用HelloWorld 和 hello-world两种形式都可以;但是如果是在<div id="app">中使用, 那么此时对于HelloWorld组件来说, 必须使用短横线形式, 否则无效; b. 综上所述, 后续为了避免这种麻烦事, 组件命名一律采用短横线形式; -->
</head><body><div id="app"><button-counter></button-counter><HelloWorld></HelloWorld> <!-- 在DOM中使用驼峰名, 无效 --><hello-world></hello-world> <!-- 必须使用短横线形式 --></div><script type="text/javascript">Vue.component('HelloWorld', {data: function() {return {msg: 'HelloWorld'}}, template: `<div>{{msg}}</div>`});Vue.component('button-counter', {data: function () {return {count: 0}},template: `<div><button @click="handle">点击了{{count}}次</button><button>测试</button><HelloWorld></HelloWorld><hello-world></hello-world></div>`,methods: {handle: function () {this.count += 2;}}});// var vm = new Vue({el: '#app'});</script>
</body></html>
2.4
单个根元素
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 单个根元素:当构建一个 <blog-post> 组件时,你的模板最终会包含的东西远不止一个标题:template: `<h3> {{ title }} </h3>`最起码,你会包含这篇博文的正文:template: `<h3> {{ title }} </h3><div v-html="content"></div>`然而如果你在模板中尝试这样写,Vue会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。解决方案:你可以将模板的内容包裹在一个父元素内,来修复这个问题,例如:template: `<div class="content"><h3> {{title}} </h3><div v-html="content"></div></div>`--><!-- 之前控制台会报错提示, 现在没有提示了, 只是默默的失败; -->
</head><body><div id="app"><global-item></global-item></div><script type="text/javascript">Vue.component('global-item', {template: `<div><h4>HelloWorld</h4><h4>WorldHello</h4></div>`});var vm = new Vue({el: '#app'});</script>
</body></html>
3. props属性
3.1
props属性
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js"></script><!-- 《摘自官网》Prop 是你可以在组件上注册的一些自定义 attribute; 当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property; 一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop; Vue.component('blog-post', {props: ['title'],template: '<h3>{{ title }}</h3>'});在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样; <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>如上所示,你会发现我们可以使用 v-bind 来 ```动态传递 prop ```; -->
</head><body><div id="app"><!-- 在这里我们给自定义组件的属性todo绑定了一个对象item,然后在自定义组件中通过props这个属性进行获取这个绑定的对象,然后对这个获取的对象进行操控; ``````此时我们就在子组件中拿取到了父组件中的值; ``````--><self-com v-for="item in shopList" :todo="item" v-bind:key="item.id"></self-com></div><script>/* props就是一个存放组件中自定义属性的数组; *//* 通过此属性,我们将父作用域中的数据传到了子组件中; */Vue.component('self-com', {props: ['todo'],template: ` <li> {{ todo.text + '---id:'+ todo.id }}</li> `});// var app = new Vue({el: '#app',data: {shopList: [{id: 0,text: '蔬菜'}, {id: 1,text: '奶酪'}, {id: 2,text: '酒'}]}});</script>
</body></html>
3.2
props类型
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js"></script>
</head><body><!-- props属性值类型:String, Number, Boolean, Array, Object说白了,这个props里面存储的就是它这个自定义组件本身的属性名,然后在template里面将这属些性值打印出来!!!--><div id="app"><self-item v-bind:str='string' v-bind:num='12' :bol='false' :arr='array' :obj='object'></self-item></div><script type="text/javascript">Vue.component('self-item',{props: ['str','num','bol','arr','obj'],template: `<div><div>{{str}}</div><div>{{num + 24}}</div><div>{{bol}}</div><ul><li :key="index" v-for="(item,index) in arr">{{item}}</li></ul><div><span>{{obj.name + " " + obj.age}}</span> </div></div>`});var vm = new Vue({el: '#app', data: { string: '我是String',array: ['apple','pear','banana'],object: {name: 'liSi',age: 23} }}); </script>
</body>
</html>
3.3
props类型校验
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script> <!-- 1. 到这里,我们只看到了以字符串数组形式列出的 prop:props: ['title', 'likes', 'isPublished', 'commentIds', 'author']2. 但是,通常你希望每个 prop 都有指定的值类型,这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:props: {title: String,likes: Number,isPublished: Boolean,commentIds: Array,author: Object,callback: Function,contactsPromise: Promise // or any other constructor}这不仅为你的组件提供了文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。3. 注意:在下面的例子中,因为props中的属性全部是驼峰, 所以标签中要使用小写短横线形式, 否则ERROR!(ps: 在此处浪费诸多时间)-->
</head><body><div id="app"><self-com:prop-a="100" :prop-b="12":prop-c="'Xing'":prop-d="":prop-e="{name: 'Wei'}":prop-f="'success1'":prop-g="12"></self-com></div><script type="text/javascript">Vue.component('self-com',{props: {propA: Number,propB: [String, Number], /* propB: 多种类型 */propC: { /* propC: 必传且必须是字符串 */type: String,required: true},propD: { /* propD: 有默认值的数字 */type: Number,default: 100},propE: {type: Object,default: function() {return {message: 'HelloWorld'}}},propF: { /* 自定义验证函数 */validator: function(value) { return ['success','warning','danger'].indexOf(value) !== -1;}},propG: {validator: function(value) {return value < 100;}}},template: `<div><p>{{propA}}</p><p>{{propB}}</p><p>{{propC}}</p><p>{{propD}}</p><p>{{propE}}</p><p>{{propF}}</p><p>{{propG}}</p></div>`})var vm = new Vue({el: '#app'});</script>
</body>
</html>
3.4
props属性名规则1
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- props属性名规则: (1)在DOM中, 如果在props中使用的是属性名的驼峰形式, 那么必须在DOM中的标签中使用该属性的短横线形式, 否则默默的失败!(2)当然, 如果是在字符串模板中使用,则没有这个限制;=> 这和组件命名规则是相同的; => 为了省去不必要的麻烦, 后续一律采用短横线形式; -->
</head><body><div id="app"><!-- 1. props中的属性MenuTitle为驼峰形式, 此时在DOM的标签中该属性必须使用短横线形式, 否则默默的失败!!! --><menu-item MenuTitle="HelloWorld"></menu-item><!-- 2. props中的属性MenuTitle为驼峰形式, 此时在DOM,,,,,,使用了短横线形式, 正确!!! --><menu-item menu-title="HelloWorld"></menu-item></div><script type="text/javascript">Vue.component('menu-item', {props: ['MenuTitle'],template: `<div> <h1> {{ MenuTitle }} </h1></div>`});var vm = new Vue({el: '#app'}); </script>
</body></html>
3.5
props属性名规则2
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script>
</head><body><!-- 当然, 如果是在字符串模板中使用,则没有这个限制;--><div id="app"><menu-item menu-title="HelloWorld"></menu-item></div><script type="text/javascript">Vue.component('third-com',{props: ['testTitle'],template:'<div>{{ testTitle }}</div>'})Vue.component('menu-item', {props: ['menuTitle'],template: `<div> {{ menuTitle }}<third-com testTitle="hello"></third-com></div>`});// var vm = new Vue({el: '#app'}); </script>
</body></html>
3.6
传递静态或动态props
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../vue.js" type="text/javascript"></script><script src="../vue-router.js"></script> <!-- 1. 像这样,你已经知道了可以像这样给 prop 传入一个静态的值:<blog-post title="My journey with Vue"></blog-post>2. 你也知道 prop 可以通过 v-bind 动态赋值,例如:// 动态赋予一个变量的值<blog-post v-bind:title="post.title"></blog-post>// 动态赋予一个复杂表达式的值<blog-post v-bind:title="post.title + 'by' + post.author.name"></blog-post>在上述两个示例中,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 prop。--> <!-- 传入一个数字: <blog-post v-bind:likes="42"></blog-post>传入一个布尔值: (静)<blog-post is-published></blog-post>(静)<blog-post v-bind:is-published="false"></blog-post>(动态传递)<blog-post v-bind:is-published="post.isPublished"></blog-post>--><!-- 传入一个数组: (静态传递)<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>(动态传递)<blog-post v-bind:comment-ids="post.commentIds"></blog-post>--><!-- 传入一个对象:(静)<blog-post v-bind:author="{name: 'Veronica',company: 'Veridian Dynamics'}"></blog-post>(动)<blog-post v-bind:author="post.author"></blog-post>--><!-- 传入一个对象的所有 property:如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:post: {id: 1,title: 'My Journey with Vue'}下面的模板:<blog-post v-bind="post"></blog-post>等价于:<blog-postv-bind:id="post.id"v-bind:title="post.title"></blog-post>-->
</head><body><div id="app"></div><script type="text/javascript">var vm = new Vue({el: '#app', data: { },methods: {}});</script>
</body>
</html>
4. 事件发射与监听
4.1
监听子组件事件1
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../vue.js" type="text/javascript"></script><!-- 父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递出去,就需要使用自定义事件;我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:使用 $on(eventName) 监听事件;使用 $emit(eventName) 触发事件;另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件;以下实例中子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。--><!-- 以下案例中:当点击子组件时, 将会执行incrementHandler函数, 而在该事件处理函数中会发射add事件, 在根组件中, 我们对add事件进行了监听, 一旦截获到add事件, 那么就去执行sum处理函数; -->
</head><body><div id="app"><p> {{ total }}</p><button-counter v-on:add="sum"></button-counter> </div><script type="text/javascript">Vue.component('button-counter', {data: function () {return {counter: 0}},template: `<button v-on:click="incrementHandler">{{ counter }}</button>`,methods: {incrementHandler: function () {this.counter += 1;this.$emit('add'); /* 触发add事件 */}}});// var vm = new Vue({el: '#app',data: {total: 0,},methods: {sum: function () {this.total += 1;}}});</script>
</body></html>
4.2
监听子组件事件2
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 监听子组件事件: 子组件中使用$emit('事件名称', args...)触发事件; 在父组件中通过v-on:事件名称 来监听子组件触发的事件;-->
</head><body><div id="app"><div :style='{fontSize: size + "px"}'>{{ str }}</div><hr><!-- 1. 通过v-on来监听component-item组件的enlarge-text事件, 一旦监听到, 就立即执行 handle函数进行处理; --><component-item :attr='Attr' v-on:enlarge-text='handle'></component-item><!-- 2. 传递参数时: 通过用```$event```来截获子组件emit的事件所传递过来的参数值; --><component-item :attr='Attr' v-on:enlarge-text='handle($event)'></component-item></div><script type="text/javascript">/* 点击此组件时, 将会触发enlarge-text这个事件,并且传递了一个参数5,在父组件(#app)中通过v-on来监听该事件; */Vue.component('component-item', {props: ['attr'],template: `<button @click='$emit("enlarge-text", 5)'>自定义组件/button> `});//var vm = new Vue({el: '#app',data: {Attr: 'love',str: 'HelloWorld',size: 10},methods: {/* val就是子组件传递过来的参数 */handle: function (val) {console.log(val);this.size += val;}}});</script>
</body></html>
4.3
兄弟组件之间的交互
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 兄弟组件的交互:1. 通过事件中心进行交互var eventHub = new Vue();2. 监听事件与移除事件监听eventHub.$on('事件名称',处理函数)eventHub.$off('事件名称')3. 触发事件eventHub.$emit('事件名称',id)-->
</head><body><div id="app"><bother1-com></bother1-com><hr><bother2-com></bother2-com><hr><button @click="destroy">移除事件监听</button></div><script type="text/javascript">// 定义事件中心var hub = new Vue();// 兄弟组件1Vue.component('bother1-com', {data: function () {return {num: 0}},template: `<div><div>bother1: {{num}}</div> <button @click='handle'>点击</button></div></div>`,methods: {handle: function () {hub.$emit('bother2-event', 1); /* 给兄弟组件发射事件信号 */}},mounted() { // 生命周期钩子函数//我的事件处理函数hub.$on('bother1-event', (val) => {this.num += val; /*val是兄弟组件传过来的值 */});}});// 兄弟组件2Vue.component('bother2-com', {data: function () {return {num: 0}},template: `<div><div>bother2: {{num}}</div> <div><button @click='handle'>点击</button></div></div>`,methods: {handle: function () {hub.$emit('bother1-event', 2);}},mounted() {hub.$on('bother2-event', (val) => {this.num += val;})}});//var vm = new Vue({el: '#app',data: {},methods: {destroy: function () {hub.$off('bother1-event');hub.$off('bother2-event');}}}); </script>
</body></html>
5. 插槽
5.1 初识插槽1
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 插槽: 用来插文本的槽!一个预留的占位符! <slot></slot>-->
</head><body><div id="app"><!-- 1. 如果<alert-box>组件的 template中没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃; 2. 组件中的内容交由slot处理渲染:比如 <alert-box>存在问题</alert-box>,那么由于在组件template中存在插槽slot, 所以```存在问题```将会被slot给渲染; 否则(没有slot), 那么诸如```存在问题```等内容不能被渲染出来; --><alert-box>存在问题</alert-box><alert-box>存在警告</alert-box><alert-box></alert-box><alert-box><span>HelloWorld</span>I know your name!</alert-box></div><script type="text/javascript">Vue.component('alert-box', {template: `<div><strong>ERROR:</strong><slot>default</slot></div>`}); var vm = new Vue({el: '#app'});</script>
</body></html>
5.2 初始插槽2
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><style>.current {color: red;}</style><!-- 如下所示:在<child>组件中的内容 <h2>好吧</h2>等 实际上并不会被渲染出来; <child><h2>好吧</h2></child>也就是说:在组件<child></child>中的内容并不会被渲染出来, 它只会渲染<child>组件中template中的内容;要想<child>组件中的内容也被渲染出来, 除非在组件<child>中的template中预留一个<slot></slot>占位符;<div><p>HelloWorld</p><slot></slot></div>--><!-- <child><h2>好吧</h2><h2>好吧</h2><h2>好吧</h2></child>template: `<div><strong>HelloWorld</strong><slot>default</slot></div>`此时好比将<child>组件中template中的<slot>标签给替换成了<child>的内容(3个h2标签); --><!-- 插槽可以提供一个默认内容,如果如果<child>组件没有为这个插槽提供了内容,会显示默认的内容; 如果<child>组件为这个插槽提供了内容,则默认的内容会被替换掉; <child></child> // child组件没有提供插槽内容<child> <h1>Ren Min</h1> </child> // child组件提供插槽内容-->
</head><body><div id="ok"><child><h2>好吧</h2><h2>好吧</h2><h2>好吧</h2></child><child></child></div><script type="text/javascript">Vue.component('child', {template: `<div><strong>HelloWorld</strong><slot>default</slot></div>`});var vm = new Vue({el: '#ok'});</script>
</body></html>
5.3 具名插槽1
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><!-- 具名插槽:就是有名字的插槽!当需要多个插槽时,可以使用<slot>标签的属性:name; 这个特性可以用来定义额外的插槽使用方法; --><!-- <p slot='head'>头部内容</p>也即是表达向名为head的插槽传递<p>这个标签数据;-->
</head><body><div id="app"><h1>--------第一种引用方式:--------</h1><self-com><p slot='head'>头部内容</p><p>中间内容1</p><p>中间内容2</p><p slot="foot">底部内容</p></self-com><h1>------第二种引用方式:----------</h1><self-com><template slot="head"><p>头部内容</p></template><p>中间内容1</p><p>中间内容2</p><template slot="foot"><p>底部内容</p></template></self-com></div><script type="text/javascript">Vue.component('self-com', {template: `<div><header><div style="color: red"><slot name='head'></slot></h1></header> <center><slot></slot> </center> <footer><div style="color: green"><slot name="foot"></slot></div></footer> </div>`});// var vm = new Vue({el: '#app'}); </script>
</body></html>
5.4 具名插槽2
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><script>/*slot和v-slot的结合使用:1. <slot>元素有一个特殊的属性name, 这个属性可以用来定义额外的插槽; 一个不带name的<slot>出口会带有隐含的名字"default"; (<slot name="default"></slot>)在向具名插槽提供内容的时候,我们可以在一个<template>元素上使用v-slot指令,并以v-slot的参数的形式提供其名称; 2. 在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以v-slot的参数的形式提供其名称; 现在<template>元素中的所有内容都将会被传入相应的插槽; 任何没有被包裹在带有v-slot的 <template>中的内容都会被视为默认插槽的内容; 3. <template v-slot:header><h1>标题系列</h1></template>也即是表达向名为header的插槽传递<h1></h1>这个标签数据;实际上插槽就是一个空的占位符, 等待外物的到来, 然后将其渲染出来;4. 注意 v-slot 只能添加在 <template> 上;*/</script>
</head><body><div id="app"><base-layout><template v-slot:header><h1>标题系列</h1></template><template v-slot:default> <!-- 等价于<template> <p>文章的主要内容</p> </template> --><p>文章的主要内容</p></template><template v-slot:footer><h1>文章结尾部分</h1></template></base-layout></div><script type="text/javascript">Vue.component('base-layout', {template:`<div><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer></div>`})var vm = new Vue({el: '#app'});</script>
</body>
</html>
5.5 作用域插槽
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../../vue.js" type="text/javascript"></script><style>.current {color: red;}</style><!-- 应用场景:父组件对子组件的内容进行加工处理; --><!-- 作用域插槽(slot-scope属性):首先我们给插槽<slot :info='item'>绑定了一个属性info; <self-com>在渲染时, 会执行template中的内容, 实际上此时会生成3个li标签, 而3个li中又存在3个插槽<slot>, 并且该插槽绑绑定了一个动态属性info, 它的值是数组的项; 那么我们如何在外面拿取到info属性的值呢???<template slot-scope="scope"><h1>{{ scope.info }}</h1></template>-->
</head><body><div id="ok"><self-com :Array="array"><template slot-scope='_scope'><!-- <strong>{{_scope.info}}</strong> --><strong v-if='_scope.info.id == 2' class="current">{{_scope.info}}</strong><i v-else style="font-weight: bold;">{{_scope.info}}</i></template></self-com></div><script type="text/javascript">Vue.component('self-com', {props: ['Array'],template: `<div><li v-for='item in Array' :key='item.id'><slot :info='item'>{{item.name}}</slot></li> </div>`});// var vm = new Vue({el: '#ok',data: {array: [{id: 1,name: 'apple'}, {id: 2,name: 'orange'}, {id: 3,name: 'banana'}]}});</script>
</body></html>