一、vue的起步
创建实例化对象:
<div id='app'> <h3>{{xx}}</h3> </div> <script src="./js/vue.js"></script> <script> // 使用vue第一步,创建vue实例对象 var app = new Vue({ el:'#app', // 当前实例绑定的元素 data:{ // 所有的数据都放在数据属性中 xx:'Hello World!' } }) // {{}}: 模板语法插值 // {{变量}} // {{1+1}} // {{'hello'}} // {{函数的调用}} // {{1==1?'true':'false'}} </script>
二、指令系统
// 常用 v-text v-html v-if v-show v-for v-bind v-on
v-text v-html {{}} //对页面的dom进行赋值运算,相当与js中innerText innerHTML
// 只在可信内容上使用v-html
,永不用在用户提交的内容上(防止xss攻击)!
<div id="app"> <div>{{ msg }}</div> <!-- <p>嘻嘻嘻嘻嘻</p> --> <div v-html="msg"></div> <!-- 嘻嘻嘻嘻嘻 --> </div> <script> var vm = new Vue({ el: '#app', data: { msg: '<p>嘻嘻嘻嘻嘻</p>' } }) </script>
v-if 和 v-show
①v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 ②v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 ③相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 ④一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使 用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
v-for
// 数组或者对象 格式 v-for="(item, index) in items" v-for="(value, key, index) in object"
注意:
不推荐同时使用 v-if 和 v-for,为什么?;
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
v-bind 和 v-on
v-on:click //对当前DOM绑定click事件;注意:所有的原生js的事件使用v-on都可以绑定 v-if和v-on //来对页面中DOM进行操作 v-bind:class和v-on //对页面中DOM的样式切换 v-bind:id 等价于 :id v-on:click="方法名" 等价于 @click="方法名"
v-model及其内部实现原理
v-model 指令用在表单 <input>、<textarea> 及<select> 元素上创建双向数据绑定。v-model只适用在表单控件中,比如:表单文本、多行文本,复选框,单选框、多选框、选择框......
<div id="app"> <h4>v-model</h4> <input type="text" v-model="msg"> <p>{{ msg }}</p> <h4>v-model内部实现原理</h4> <input type="text" v-bind:value="getValue" v-on:input="changeMsg"> <p>{{ getValue }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { msg: 'Hello World!', }, methods: { changeMsg: function(e) { this.getValue = e.target.value; } }, computed: { getValue: { set: function(newValue) { this.msg = newValue; }, get: function() { return this.msg; } } }, }) </script>
注意:v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。所以应该在 data 选项中声明初始值。
对于复选框和多选select,v-model 应该绑定到同一个数组。
练习
<div id="app"> <button @click="count+=1">点击</button> <p>点击了{{ count }}次......</p> </div> <script> var vm = new Vue({ el: '#app', data: { count: 0 }, }) </script>
<div id="app"> <button v-on:click="imgToggle">切换</button> <div v-if="show"><img v-bind:src="imgSrc"></div> </div> <script> var vm = new Vue({ el: '#app', data: { show: true, imgSrc: "./img/1.jpg" }, methods: { imgToggle: function () { this.show = !this.show } }, }) </script>
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <script src="./js/vue.js"></script> <title>Document</title> <style> .box1 { width: 200px; height: 200px; background-color: coral } .box2 { background-color: hotpink } </style> </head> <body> <div id="app"> <button @click="changeColor">切换颜色</button> <div class="box1" :class="{box2: isHotpink}"></div> </div> <script> var vm = new Vue({ el: '#app', data: { isHotpink: false }, methods: { changeColor: function () { this.isHotpink = !this.isHotpink } }, }) </script> </body> </html> </body> </html>
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <script src="./js/vue.js"></script> <title>Document</title> <style> div a { display: block; text-decoration: none; float: left; width: 30px; height: 30px; line-height: 30px; background-color: #4682B4; color: white; margin-right: 10px; text-align: center; } div a:hover { color: blue; } #img-page { overflow: hidden } </style> </head> <body> <div id="app"> <h4>轮播图</h4> <img v-bind:src="imgSrc"> <div id="img-page"> <a href="#" @click="previousImg"><</a> <a href="#" v-for="(item, index) in imgArray" v-on:click="currentImg(item)">{{ index+1 }}</a> <a href="#" @click="nextImg">></a> </div> </div> <script> var vm = new Vue({ el: '#app', data: { imgSrc: './img/1.jpg', imgArray: [ {id: 1, src: './img/1.jpg'}, {id: 2, src: './img/2.jpg'}, {id: 3, src: './img/3.jpg'}, {id: 4, src: './img/4.jpg'}, ], imgIndex: 0, }, methods: { currentImg: function (item) { this.imgSrc = item.src; }, nextImg: function() { if (this.imgIndex === this.imgArray.length - 1) { this.imgIndex = -1; } this.imgIndex = this.imgIndex + 1; this.imgSrc = this.imgArray[this.imgIndex].src; }, previousImg() { if (this.imgIndex === 0) { this.imgIndex = this.imgArray.length; } this.imgIndex = this.imgIndex - 1; this.imgSrc = this.imgArray[this.imgIndex].src; } }, }) </script> </body> </html> </body> </html>
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <script src="./js/vue.js"></script> <title>Document</title> <style> div a { display: block; text-decoration: none; float: left; width: 30px; height: 30px; line-height: 30px; background-color: #4682B4; color: white; margin-right: 10px; text-align: center; } div a:hover { color: blue; } #img-page { overflow: hidden } </style> </head> <body> <div id="app"> <h4>轮播图</h4> <img v-bind:src="imgSrc" @mouseenter="closeTimer" @mouseleave="openTimer"> </div> <script> var vm = new Vue({ el: '#app', data: { imgSrc: './img/1.jpg', imgArray: [ {id: 1, src: './img/1.jpg'}, {id: 2, src: './img/2.jpg'}, {id: 3, src: './img/3.jpg'}, {id: 4, src: './img/4.jpg'}, ], imgIndex: 0, timer: "", }, created() { this.timer = setInterval(this.nextImg, 1500) }, methods: { nextImg: function() { if (this.imgIndex === this.imgArray.length - 1) { this.imgIndex = -1; } this.imgIndex = this.imgIndex + 1; this.imgSrc = this.imgArray[this.imgIndex].src; }, closeTimer: function() { clearInterval(this.timer) }, openTimer: function() { this.timer = setInterval(this.nextImg, 1500) } }, }) </script> </body> </html> </body> </html>
三、计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
<div id="app"> <p>{{ msg.split('').reverse().join('') }}</p> <!-- 过多逻辑运算,不好 --> <p>{{ reversedMsg }}</p> <!-- 对于任何复杂逻辑,使用计算属性 --> <button @click="changeMsg">修改msg</button> </div> <script> var vm = new Vue({ el: '#app', data: { msg: 'Hello World!' }, methods: { changeMsg: function() { // 一旦msg被修改,reversedMsg随时改变,计算属性具有监听功能 this.msg = "Hello PD!" } }, computed: { // 计算属性 reversedMsg: function() { // 计算数据属性,默认使用getter方法 return this.msg.split('').reverse().join('') } }, }) </script>
如果我们要使用计算属性的 setter 方法,修改如下(实现的效果与上面一模一样):
methods: { changeMsg: function() { this.reversedMsg = "Hello PD!" } }, computed: { // 计算属性,默认只有getter方法 reversedMsg: { // 要使用set方法,计算数据属性变成了一个对象 set: function(newValue) { // 接收一个参数,一般为newValue,此时 newValue="Hello PD!" this.msg = newValue; }, get: function() { return this.msg.split('').reverse().join('') } } },
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。这就意味着只要 msg 还没有发生改变,多次访问 reversedMsg 计算属性会立即返回之前的计算结果,而不必再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
四、父子组件传值
父组件:
<template> <div id="app"> <h2>{{ msg }}</h2> <Xheader></Xheader> <!-- 父组件向子组件传递数据 --> <!-- 父->子1: 绑定自定义属性 --> <Xcontent v-bind:citysArray="citys"></Xcontent> <!-- 自定义事件 add-city --> <!-- 推荐始终使用 kebab-case 的事件名 --> <Xfooter v-on:add-city="add2"></Xfooter> <!-- 子->父2:在子组件中绑定 自定义事件 --> </div> </template> <script> import Xheader from './components/header' import Xcontent from './components/content' import Xfooter from './components/footer' export default { name: 'app', data () { return { msg: 'App.vue', citys: ['北京', '上海', '广州'], } }, components: { Xheader, Xcontent, Xfooter, }, methods: { add2 (arg) { // add2 ('深圳') this.citys.push(arg); } }, } </script> <style scoped> /deep/ * { padding: 0; margin: 0; } #app { background-color: grey; width: 200px; text-align: center; } h2 { color: black; } </style>
子组件:
<template> <div id="content"> <h3>{{ msg }}</h3> <a href="javascript:;" v-for="city in citysArray" :key="city">{{ city }}</a> </div> </template> <script> export default { name: 'content', data () { return { msg: 'content.vue' } }, props: { // 父->子2:props用来对父组件传过来的值进行类型检查 citysArray: Array, // String、Number、Boolean、Function、Object、Array、Symbol } } </script> <style scoped> #content { background-color: tan } a { text-decoration: none; margin-right: 5px; } a:hover { color: tomato; } </style>
<template> <footer id="foot"> <h3>{{ msg }}</h3> <!-- 通过事件向父级组件发送消息 --> <!-- 子->父1:点击button触发父组件的 add-city 事件 --> <button v-on:click='add1'>新增city</button> </footer> </template> <script> export default { name: 'footer', data () { return { msg: 'footer.vue' } }, methods: { add1 () { // 通过 this.$emit() 触发自定义事件 this.$emit('add-city', '深圳'); // 参数1:自定义事件名字;参数2:从前端拿到的数据 } }, } </script> <style scoped> #foot { background-color: orangered; } </style>
实现效果:
父组件App.vue的citys数据传到子组件content.vue,子组件footer.vue通过事件修改父组件citys数据。
通过 Prop 向子组件传递数据 和 通过事件向父级组件发送消息
如看不清,请右键图片在新页面打开。
总结:父向子传值,使用props;子向父传值,使用自定义事件。