Vue2 第二天学习
2017-05-02 23:25 龙恩0707 阅读(1349) 评论(0) 编辑 收藏 举报阅读目录
1.vue属性和方法
每个Vue实例都会代理其 data对象里所有的属性。
如下代码:
var data = { a: 1 }; var vm = new Vue({ data: data }); console.log(vm); console.log(vm.a === data.a); // true // 设置属性也会影响到原始数据 vm.a = 2; console.log(data.a); // 2 // 反之 data.a = 3; console.log(vm.a); // 3 //除了data属性,Vue实例还暴露了一些有用的实例属性与方法。这些属性与方法都有前缀$, 以便与代理的data属性区分。 var data = { a: 1 } var vm = new Vue({ el: '#container1', data: data }) console.log(vm.$data === data) // true console.log(vm.$el === document.getElementById('container1')) // true data.a = 5; // $watch 是一个实例方法 vm.$watch('a', function (newVal, oldVal) { // 这个回调将在 `vm.a` 改变后调用 console.log(newVal); console.log(oldVal); })
1-1. data 必须是函数
通过Vue构造器传入的各种选项大多数都可以在组件里用。 data 是一个例外,它必须是函数。 如下代码Vue 会停止,并在控制台会报错。
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var data = { counter: 0 }; // 全局注册 Vue.component('component1', { template: '<span>{{ message }}</span>', data: { message: 'hello' } }); new Vue({ el: '#container1' }) </script> </html>
data是函数解决该方案
代码如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> <component1></component1> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var data = { counter: 0 }; // 全局注册 Vue.component('component1', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // data是一个函数,vue不会报错,但是我们返回给每个组件的实列引用了同一个data对象 data: function() { return data } }); new Vue({ el: '#container1' }) </script> </html>
由于这三个组件共享了同一个 data , 因此增加一个 counter 会影响所有组件!这不对。我们可以通过为每个组件返回全新的 data 对象来解决这个问题:
代码如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <component1></component1> <component1></component1> <component1></component1> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全局注册 Vue.component('component1', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // data是一个函数,vue不会报错,但是我们返回给每个组件的实列引用了同一个data对象 data: function() { return { counter: 0 } } }); new Vue({ el: '#container1' }) </script> </html>
现在每个 counter 都有它自己内部的状态了.
2.理解组件的通信。
一般情况父子组件是这样的关系,组件A在它的模板中使用了组件B,他们之间必然需要相互通信,父组件要给子组件传递数据,子组件需要将它内部发生的事情告知父组件,为了保证父子组件的解耦,可维护性及可重用性。在vue.js中,父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。
2-1 使用props传递数据
不能在子组件的模板内直接引用父组件的数据,要让子组件使用父组件的数据,我们需要通过子组件的props选项。
如下代码:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <child message="hello!"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全局注册 Vue.component('child', { // 声明props props: ['message'], template: '<span>{{ message }}</span>' }); new Vue({ el: '#container1' }) </script> </html>
结果在页面上会打印 hello。
注意: HTML特性是不区分大小写的,所以当使用的不是字符串模板,camelCased(驼峰式) 命名的prop需要转换为相对应的 kebab-case(短横线隔开式)命名:
如下代码:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <!-- kebab-case in HTML--> <child my-message="hello!"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 全局注册 Vue.component('child', { // 声明props props: ['myMessage'], template: '<span>{{ myMessage }}</span>' }); new Vue({ el: '#container1' }) </script> </html>
2-2 理解动态prop
在模板中,要动态地绑定父组件的数据到子模板的props,使用v-bind,每当父组件的数据变化时,该变化会传递给子组件。
<div id="container1"> <input v-model='parentMsg' /> <br /> <!-- kebab-case in HTML--> <child v-bind:my-message="parentMsg"></child> </div>
使用 v-bind 的缩写语法通常更简单:
<child :my-message="parentMsg"></child>
代码如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <input v-model='parentMsg' /> <br /> <!-- kebab-case in HTML--> <child v-bind:my-message="parentMsg"></child> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> new Vue({ el: '#container1', data: { parentMsg: 'Message' }, components: { child: { props: ['myMessage'], template: '<span>{{myMessage}}</span>' } } }) </script> </html>
3.理解自定义事件
父组件使用props传递数据给子组件,但是如果子组件需要把数据传回去的话,就需要自定义事件了;
3-1 使用v-on绑定自定义事件
每个vue实例都实现了事件接口,即:
1. 使用 $on(eventName) 监听事件
2. 使用 $emit(eventName) 触发事件
注意: $on 和 $emit 不是 addEventListener 和 dispatchEvent的别名。且 父组件可以在使用组件的地方直接用 v-on 来监听子组件触发的事件。
不能用$on侦听子组件抛出的事件,而必须在模板里直接用v-on绑定,就像以下的例子:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <p> {{ total }} </p> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('button-counter', { template: '<button v-on:click="increment">{{ counter }}</button>', data: function() { return { counter: 0 } }, methods: { increment: function() { this.counter += 1; this.$emit('increment'); } }, }) new Vue({ el: '#container1', data: { total: 0 }, methods: { incrementTotal: function() { this.total += 1; } } }) </script> </html>
上面代码: 初始化时候 实例化设置 data: {total: 0}, 设置total为0, 子组件button-counter 默认为0, 当点击子组件的时候调用 increment方法,当前的counter自增1, 然后在子组件触发 $emit('increment')事件,当使用 v-on:increment 会监听到事件后,会调用父组件的incrementTotal方法,因此父组件也自增1.
上面代码中 子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件,至于父组件是否关心则与它无关。
4.理解使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件,使用v-modal来进行数据双向绑定。比如如下代码:
<input v-modal="something" />
上面的代码是下面的语法糖;如下代码:
<input v-bind:value="something" v-on:input="something=$event.target.value" />
因此在创建组件中时,相当于下面的简写;如下代码:
<custom-input v-bind:value="something" v-on:input="something=arguments[0]"></custom-input>
所以要让组件的v-model 生效,必须满足下面的条件:
1. 接受一个value属性。
2. 在有新的value时触发input事件。
如下测试代码:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <currency-input v-model="price"></currency-input> </div> </body> <script src="./vue.js"></script> <script> </script> <script type="text/javascript"> Vue.component('currency-input', { template: '\ <span>\ $\ <input\ ref="input"\ v-bind:value="value"\ v-on:input="updateValue($event.target.value)"\ >\ </span>\ ', props: ['value'], methods: { // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制 updateValue: function (value) { var formattedValue = value // 删除两侧的空格符 .trim() // 保留 2 小数位 .slice(0, value.indexOf('.') + 3) // 如果值不统一,手动覆盖以保持一致 if (formattedValue !== value) { this.$refs.input.value = formattedValue } // 通过 input 事件发出数值 this.$emit('input', Number(formattedValue)) } } }); new Vue({ el: '#container1', data: { price: 0 } }) </script> </html>
5.单个slot
<slot>标签中的任何内容都被视为 备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有插入的内容时才显示备用内容。
如果<slot>标签中有内容的话,就显示该内容。
比如 my-component 组件有如下代码:
<div class="content">
<h2>this is a component</h2>
<slot>如果没有分发内容,则显示slot中的内容</slot>
<p>asdsadsdad</p>
</div>
父组件有如下代码:
<div id="container1"> <my-component> <h1>Hello Vue.js</h1> </my-component> <my-component></my-component> </div>
渲染后的结果为:
<div id="container1"> <div class="content"> <h2>this is a component</h2> <h1>Hello Vue.js</h1> <p>asdsadsdad</p> </div> <div class="content"> <h2>this is a component</h2> 如果没有分发内容,则显示slot中的内容 <p>asdsadsdad</p> </div> </div>
所有测试实例代码如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <my-component> <h1>Hello Vue.js</h1> </my-component> <my-component></my-component> </div> <template id="myComponent"> <div class="content"> <h2>this is a component</h2> <slot>如果没有分发内容,则显示slot中的内容</slot> <p>asdsadsdad</p> </div> </template> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('my-component', { template: '#myComponent' }) new Vue({ el: '#container1' }) </script> </html>
6.具名slot
<slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。
比如:假如有一个 my-component 组件,它的模板为:
<template id="myComponent"> <div class='content'> <header> <slot name='header'></slot> </header> <main> <slot></slot> </main> <footer> <slot name='footer'></slot> </footer> </div> </template>
父组件的模板如下:
<div id="container1"> <h1 slot="header">这里可能是一个页面标题</h1> <p>主要内容的一个段落</p> <p>另一个主要段落</p> <p slot='footer'>这里是底部信息</p> </div>
页面渲染的结果如下:
<div id="container1"> <h1>这里可能是一个页面标题</h1> <p>主要内容的一个段落</p> <p>另一个主要段落</p> <p>这里是底部信息</p> </div>
所有的代码如下:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <h1 slot="header">这里可能是一个页面标题</h1> <p>主要内容的一个段落</p> <p>另一个主要段落</p> <p slot='footer'>这里是底部信息</p> </div> <template id="myComponent"> <div class='content'> <header> <slot name='header'></slot> </header> <main> <slot></slot> </main> <footer> <slot name='footer'></slot> </footer> </div> </template> </body> <script src="./vue.js"></script> <script type="text/javascript"> Vue.component('my-component', { template: '#myComponent' }) new Vue({ el: '#container1' }) </script> </html>
7.理解作用域插槽(2.1.0新增的)
在slot分发中,无论是单分发还是具名分发,都是父组件替换子组件的数据,或者没有替换,用子组件默认的数据。 但是通过设置作用域槽,就可以改变这种状况,让子组件可以在父组件进行分发时获取自己的数据,至于是什么数据,由子组件决定,这样就能解耦了。
作用域槽通过slot的一个自定义的属性,官方给出的DEMO是text,但也可以是其他,值为暴露的数据。 这个自定义属性已经存放在子组件的prop对象里了。等待着被父组件获取。
怎么获取呢? 在父组件的模板里,使用一个Vue自带的特殊组件<template> ,并在该组件上使用scope属性,值是一个临时的变量,存着的是由子组件传过来的prop对象,获得由子传过来的prop对象。这时候,父组件就可以访问子组件在自定义属性上暴露的数据了。
如下代码:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <div id="container1"> <parent-component></parent-component> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> // 子组件 Vue.component('child-component', { template: '<ul><slot name="child-ul" v-for="item in rets" v-bind:text="item.name"></slot></ul>', data: function() { return { rets: [ {name: '我是苹果'}, {name: '我是香蕉'}, {name: '我是橘子'} ] } } }); // 父组件 Vue.component('parent-component', { template: '<child-component><template scope="props" slot="child-ul"><li>{{props.text}}</li></template></child-component>' }) new Vue({ el: '#container1' }) </script> </html>
页面渲染后的代码如下:
<div id="container1"> <ul> <li>我是苹果</li> <li>我是香蕉</li> <li>我是橘子</li> </ul> </div>
8.理解动态组件
通过使用保留的<component>元素,动态地绑定到它的 is 特性,我们可以让多个组件使用同一个挂载点,并动态的切换。
keep-alive: 如果把切换出去的组件留在内存中,可以保留它的状态或避免重新渲染,为此我们可以添加一个 keep-alive指令参数。
如下实现的tab切换代码:
<!DOCTYPE html> <html> <body> <head> <title>演示Vue</title> </head> <h3>动态组件</h3> <template id="tab-01"> <div>this is tab01</div> </template> <template id='tab-02'> <div>this is tab02</div> </template> <template id="tab-03"> <div>this is tab03</div> </template> <div id="container1"> <!-- 导航栏 --> <ul> <li> <a href="javascript:void(0)" @click="toggleTabs(tab01Text);">{{ tab01Text }}</a> </li> <li> <a href="javascript:void(0)" @click="toggleTabs(tab02Text);">{{ tab02Text }}</a> </li> <li> <a href="javascript:void(0)" @click="toggleTabs(tab03Text);">{{ tab03Text }}</a> </li> </ul> <!-- 点击导航后要切换的内容 --> <div class='content' style='height: 200px'> <!-- 如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数 --> <keep-alive> <component :is="currentView"></component> </keep-alive> </div> </div> </body> <script src="./vue.js"></script> <script type="text/javascript"> var tab01 = Vue.extend({ template: '#tab-01' }); var tab02 = Vue.extend({ template: '#tab-02' }); var tab03 = Vue.extend({ template: '#tab-03' }); // 新建vue实例 var newVue = new Vue({ el: '#container1', data: { tab01Text: "tab01", // 菜单一 tab02Text: "tab02", // 菜单二 tab03Text: "tab03", // 菜单三 currentView: "tab01" // 默认选中的导航栏 }, // 局部注册组件 components: { tab01: tab01, tab02: tab02, tab03: tab03, }, methods: { // 绑定tab的切换事件 toggleTabs: function(tabText) { this.currentView = tabText; } } }) </script> </html>