Vue基础语法【上】(完结)

目录

文章内容较多,可以结合Ctrl+F输入关键词搜索自己想要的内容,同时多多利用“返回顶部”按钮和顶部的目录提高文章阅读效率。

上期更新的时候,因为文章最初是由Typora编辑的,图片又是上传到图床的,所以粘贴过来的时候,有些图片没有正确上传,特在此更新重整。

文章目录

1.代码规范

缩进一般为4个空格,前端一般情况下缩进两个空格,不同的公司不一样,超过50%的公司的缩进规定是两个空格。故本文及之后的代码缩进都用2个空格。

其他地方可以参考CLI 中editconfig 也是2个空格。

2.WebStorm中Vue模板的设置

下图是模板的一个例子:

image-20210510160332146

image-20210510160355050

3.Vue初体验

3.1.声明式编程和命令式编程的比较

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
<!--    Vue中mustache语法-->
<!--    这样可以可以做到数据和界面完全分离-->
    <h2>{{message}}</h2>
    <h1>{{name}}</h1>
</div>
<div>{{message}}</div>
<script src="../js/vue.js"></script>
<script>
    // let(变量)/const(常量)ES6标准中
    // let name = 'why'
    // name = 'wei'
    // var 没有作用域存在一定的缺陷

    // 将message交给Vue实例管理

    // 编程范式:声明式编程
    const app = new Vue({
        el:'#app',  // 用于挂载要管理的元素
        data: {
            // 定义数据
            message: 'Hello, Mike!',
            name: 'sayHi'
        }
    })

    // JavaScript的做法(编程范式:命令式编程)
    // 1.先创建div元素,设置id属性
    // 2.定义一个变量叫做message
    // 3.将message变量放在前面的div元素中显示出来
    // 4.修改message数据:今天天气不错
    // 5.将修改后的数据再次替换到div元素
</script>
</body>
</html>

3.2.Vue的列表展示

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>

    <!-- 直接使用传参只会显示字符串 -->
    <!-- <div id="app">{{movies}}</div>
    <div id="app">
        <ul>
            <li>{{movies[0]}}</li>
            <li>{{movies[1]}}</li>
            <li>{{movies[2]}}</li>
            <li>{{movies[3]}}</li>
        </ul>
    </div> -->

    <div id="app">
        <ul>
            <!-- 相当于创建了movies容量个li,并逐个赋值 -->
            <li v-for="item in movies">{{item}}</li>
        </ul>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊',
                movies: ['星际穿越', '大话西游', '少年派的奇幻漂流', '到梦空间']
            }
        })
    </script>

</body>

</html>

image-20210514164305865

3.3.Vue计数器

跳回7.事件监听例子部分

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Vue计数器</title>
	</head>
	<body>
		<div id="app">
			<h2>当前计数:{{counter}}</h2>
			<!-- 添加监听事件v-on,选择事件类型,并定义事件响应的操作 -->
			<!-- <button v-on:click="counter++" type="button">+</button>
			<button v-on:click="counter--" type="button">-</button> -->
			<!-- 
				新指令 → @+事件名
				@ → 简写,又叫"语法糖"
				@+事件名:该指令用于监听某个元素的点击事件,并且需要指定当发生点击时,执行的方法(方法通常是methods中定义的方法)
			 -->
			<button v-on:click="increament" type="button">+</button>
			<button @click="subtraction" type="button">-</button>
		</div>
		<script src="../js/vue.js"></script>
		<script type="text/javascript">
			const obj = {
				counter: 0,
				message: 'abc'
			}
			const app = new Vue(
			// options
			{
				// 类型:string | HTMLElement
				// 作用:决定之后Vue实例会管理哪一个DOM
				el: '#app',
				// 类型:Object | Function (组件中data必须是一个函数function)
				// 作用:Vue实例对应的数据对象
				data: obj,
				// data: {
				// 	counter: 0
				// },

				// 类型:{[key:string]: function(){}}
				// 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
				// 定义了一些事件和方法
				// 新的属性 → methods属性:该属性用于在Vue对象中定义方法
				methods: {
					increament: function() {
						// 在方法里直接写变量会直接去寻找一个全局变量,因此是找不到的
						// counter++;
						// 所以,要确定作用域,即this
						this.counter++;
						console.log('计数+1');
					},
					subtraction: function() {
						this.counter--;
						console.log('计数-1');
					},
				},
				beforeCreated: function() {

				},
				
				// 下面是以后可能会用到的一些options
				// 常会在created里面做一些网络请求
				created: function() {
					console.log('created')
				},
				mounted: function() {
					console.log('mounted')
				}
			}
			)

			// javascript编程范式
			// 1. 建立button元素
			// 2.添加监听事件
		</script>
	</body>
</html>

4.mustache的简单语法

4.1.mustache语法可以实现简单的文字和数字呈现,如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{firstName+lastName}}</h2>
    // mustache语法语句长度过长时也可以用options中的computed来实现
    <h2>{{firstName+' '+lastName}}</h2>
    <h2>{{firstName}} {{lastName}}</h2>
    <h2>{{counter * 2}}</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!',
            firstName: 'Kobe',
        lastName: 'Bryant',
        counter: 100
        }
    })
  </script>
</body>
</html>

4.2.v-once指令

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>{{message}}</h2>
<!--    v-once指令用于应对这样需求:在服务器端更改数据或者控制台更改页面响应内容时,对应的DOM不会跟随改变,也就是非响应模式-->
    <h2 v-once>{{message}}</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!'
        }
    })
  </script>
</body>
</html>

对应效果图和console操作显示:

img

4.3.v-html指令

全部代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>{{url}}</h2>
    <h2 v-html = "url">{{}}</h2>
  </div>
  
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!',
            url: '<a href="http://www.baidu.com">百度一下</a>'
        }
    })
  </script>
</body>
</html>

对应效果图:

img

4.4.v-text指令

全部代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>{{message}} I am  Wei Chun.</h2>
<!--    div中的内容会被message所覆盖-->
    <h2 v-text="message"> I am  Wei Chun.</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!'
        }
    })
  </script>
</body>
</html>

img

4.5.v-pre指令

全部代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>{{message}}</h2>
<!--    原封不动的显示元素中的内容-->
    <h2 v-pre>{{message}}</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!'
        }
    })
  </script>
</body>
</html>

img

4.6.v-cloak指令

在没有style演示控制和v-cloak指令修饰之前,在页面显示之初,会呈现出mustache原始标签,在延时之后才会显示出要显示的内容

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    [v-cloak] {
      display: none;
    }
  </style>
</head>
<body>
<!--  -->
  <div id="app" v-cloak>
    <h2>{{message}}</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    // 在vue解析之前,div中有一个属性v-cloak
    // 在vue解析之后,div中没有一个属性v-cloak
    // 掩饰显示内容,但是不会显示原mustache语法标签,达到用户显示友好的目的,但几乎不常用
    setTimeout(function(){
      const app = new Vue({
        el: '#app',
        data: {
          message: 'Hello, world!'
        }
      })
    }, 1000)
  </script>
</body>
</html>

4.7.v-bind指令

前面学的指令主要作用是将值插入到我们模板的内容当中。

但是除了内容需要动态来决定外,某些属性我们也希望动态来绑定。比如:

① 动态绑定a元素的href属性;

② 动态绑定img元素的src属性。

这时,可以使用v-bind指令↓

作用:动态绑定属性

缩写::

预期:any(with argument) | Object(without argument)

参数:attrOrProp(optional)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
<!--    mustache语法是直接使用在标签内部的(content),不能放在图片源-->
<!--    <img src="{{message}}" alt="">-->
<!--    这个做法同上面mustache语法的错误用法没有本质区别-->
<!--    <img src="imgURL" alt="">-->
<!--    动态绑定图片源:当图片源改变的时候,浏览器端也会自动更改-->
    <img v-bind:src="imageURL" alt="">
    <img :src="imageURL" alt="">
    <a v-bind:href="aHref">百度一下</a>
<!--    v-bind的语法糖-->
    <a :href="aHref">百度两下</a>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!',
            imageURL: 'https://img14.360buyimg.com/img/s100x100_jfs/t1/165712/18/9083/56369/603f3fa7E94c2bc8e/063831e5edec7f4d.jpg!cc_100x100.webp',
        aHref: 'http://www.baidu.com',
        }
    })
  </script>
</body>
</html>

img

4.7.1.v-bind动态绑定(对象语法)

通过鼠标监听事件监听鼠标点击,并执行动作。

前端常会用到鼠标点击切换某一个按钮或者div的样式显示,例如:

某个元素标签有一个默认样式,当点击时切换到指定给的样式,再次点击时样式回复默认,如此反复切换。

绑定class有两种方式:① 对象语法;② 数组语法

对象语法:class后面跟的是一个对象。

对象语法有以下几种常见的用法:

用法①:直接通过{}绑定一个类

<h2 :class="{'active': isActive}">Hello World</h2>

用法②:也可以通过判断,传入多个值

<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法③:和普通的类同时存在,并不冲突

注:如果isActive和isLine都为true,那么会有title/active/line三个类

<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法④:如果过于复杂,可以放在一个methods或者computed中

注:classes是一个计算属性

<h2 class="title" :class="classes">Hello World</h2>

对象语法的综合使用:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .active{
      color: #ff0000;
    }
  </style>
</head>
<body>
  <div id="app">
<!--    <h2 class="active">{{message}}</h2>-->
<!--    <h2 :class="active">{{message}}</h2>-->
<!--    {}代表的是对象,不是mustache语法-->
<!--    通过控制true和false来控制标签的active属性的添加(true)和移除(false)-->
<!--    <h2 :class="{类名1: true, 类名2: false}">{{message}}</h2>-->
<!--    下面这种情况并不会将属性覆盖掉,而是合并共存-->
    <h2 class="title" :class="{active: isActive, line: isLine}">{{message}}</h2>
<!--    函数的调用实际上应该带小括号的,e.g.leftBtnClick()-->
    <h2 class="title" :class="getClasses()">{{message}}</h2>
    <button @click="leftBtnClick">change color</button>
    <button @click="rightBtnClick">change property</button>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!',
        isActive: 'true',
        isLine: 'true',
        },
      methods: {
        leftBtnClick: function () {
          this.isActive = !this.isActive
        },
        rightBtnClick: function () {
          this.isLine = !this.isLine
        },
        getClasses: function () {
          return {active: this.isActive, line: this.isLine}
        }
      }
    })
  </script>
</body>
</html>

img

img

img

4.7.2.v-bind绑定class(数组语法)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
<!--    单引号将会被当做字符串去解析,不带单引号则作为变量去解析-->
    <h2 class="title" :class="['active', 'line']">{{message}}</h2>
    <h2 class="title" :class="[active, line]">{{message}}</h2>
    <h2 class="title" :class="getClasses()">{{message}}</h2>
  </div>
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
    const app = new Vue({
        el: '#app',
        data: {
            message: 'Hello, world!',
        active: 'aaa',
        line: 'lll',
        },
        methods: {
        getClasses: function () {
          return [this.active, this.line]
        }
      },
    })
  </script>
</body>
</html>

img

4.7.3.v-bind和v-for集合(小案例练习)

实现无序列表,鼠标点钟哪一项,哪一项就高亮, 再次点击取消高亮

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .active {
      color: red;
    }
  </style>
</head>
<body>
<!--点击列表中任意一项,该项的文字颜色变成红色-->
<div id="app">
  <h2>{{message}}</h2>
  <ul>
    <!--    judge函数的作用:鼠标点击,调起标记事件,
    即把鼠标点击的li标签的index值传给currentIndex,
    借此标记选中的列表选项,与此同时每个li将会同时添加
    上为active的class,并返回active的Boolean值。
    其中Boolean值来自于当前选中的列表选项的index
    (也就是currentIndex)和列表选项的index的取并
    注意:这一取并会对每一个列表选项都进行-->
    <li @click="judge(index)" :class="getClasses(index)" v-for="(item, index) in movies">{{index+1}}. {{item}}</li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      movies: ['海王', '海尔兄弟', '黑猫警长', '舒克贝塔', '海贼王', '进击的巨人', '灵笼'],
      isActive: true,
      checkedIndex: -1,
    },
    methods: {
      judge: function (index) {
        this.checkedIndex = index
        this.isActive = !this.isActive
      },
      getClasses: function (index) {
        return {active: index == this.checkedIndex && this.isActive}
      },
    },
  })
</script>

</body>
</html>

效果:

image-20210510212155967

image-20210510212204297

4.7.4.v-bind动态绑定style

“用Vue开发,具有共性的可复用的东西常常单独写一个.vue文件,相当于一个现成的模块”

“在不同的页面为了实现同一模块的不同样式显示,常进行定制化”

京东首页和搜索页面的搜索框基本样式差不多,但是具体的内容和样式又有区别。

这里可以采用动态绑定的方法来实现。

但是按照传统的写法,直接在style里面写死了,再次修改很麻烦。

可以利用v-bind:style来绑定一些CSS内联样式。

在写CSS属性名的时候,比如font-size:

  1. 我们可以使用驼峰式 (camelCase) fontSize
  2. 或短横线分隔 (kebab-case,记得用单引号括起来) ‘font-size

动态绑定style有两种方式:

  1. 对象语法
    • style后面跟的是一个对象类型 :style="{key(属性名): value(属性值)}"
    • 对象的key是CSS属性名称
    • 对象的value是具体赋的值,值可以来自于data中的属性
  2. 数组语法 :style="[baseStyles, overridingStyles]"
    • style后面跟的是一个数组类型
    • 多个值以,分割即可
4.7.4.1.v-bind动态绑定style(对象语法)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
<!--    对象语法:{key(属性名): value(属性值)}
没有单引号的时候,vue将会自动将14px解析为一个变量,去data中寻找名为14px的变量,自然会报错
加上一对单引号时,vue就会将14px解析为一个字符串-->
<!--    数组语法:[]-->
<!--  	<h2 :style="{fontSize: finalSize}">{{message}}</h2>-->
    <h2 :style="{fontSize: responsiveSize + 'px',backgroundColor: responsiveBgColor, color:responsiveColor}">{{message}}</h2>
    <h2 :style="getStyle()"></h2>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
        // finalSize: '100px',
        // 不同的页面对应不同的值,即实现了动态的绑定style
        responsiveSize: 100,
        responsiveBgColor: 'red',
        responsiveColor: 'white',
  		},
  		methods: {
  		  getStyle: function () {
          return {fontSize: this.responsiveSize, backgroundColor: this.responsiveBgColor, color: this.responsiveColor}
        }
      }
  	})
  </script>

</body>
</html>

效果图:

image-20210510203209364

注意:在页面初加载的一瞬间,会出现mustache语法标签,这里可以使用v-cloak指令进行屏蔽

4.7.4.2.v-bind动态绑定style(数组语法)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
  	<h2 :style="[baseStyle, baseStyle1,baseStyle2]">{{message}}</h2>

  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			baseStyle: {backgroundColor: 'red'},
        baseStyle1: {fontSize: '100px'},
        baseStyle2: {color: 'white'},
  		}
  	})
  </script>

</body>
</html>

效果图:

image-20210510203237474

5.计算属性

我们都知道在模板中航可以通过mustache语法显示一些data中的数据。

但是在某些情况下,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示:

  • 比如我们有firstName和lastName两个变量,我们需要显示完整的名称。但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}和{{lastName}}

我们可以将上面的代码换成计算属性→是写在computed选项中的

5.1.计算属性的基本使用

小技巧:将常用的字词保存到字典中

将字词保存到字典中

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
<!--    过于繁琐,易读性不高-->
  	<h2>{{firstName + ' ' + lastName}}</h2>
    <h2>{{firstName}} {{lastName}}</h2>
<!--    可读性好,易于知道此处代码的含义
弊端在于mustache中一般放的是变量名称,将方法放入其中比较不好-->
    <h2>{{getFullName()}}</h2>
    <h2>{{fullName}}</h2>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			firstName: 'Lebron',
        lastName: 'James',
  		},
      // computed:计算属性()  其中的一般不写成动作形式,写成变量式,如写fullName而写getFullName
      computed: {
  		  fullName: function () {
          return this.firstName + ' ' + this.lastName
        }
      },
  		methods: {
  		  getFullName: function () {
          return this.firstName + ' ' + this.lastName
        }
      }
  	})
  </script>

</body>
</html>

效果:

image-20210510205020723

5.2.计算属性的复杂使用

计算属性和methods的区别之一:

计算属性在多次调用的时候只会调用一次,methods调用几次就会执行几次。

因此methods是没有缓存的,性能更低一点。

计算属性是有缓存的,性能更高一点。

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>总价格:{{totalPrice}}</h2>

</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      books: [
        {id: 1, name: 'Unix编程艺术', price: 89},
        {id: 2, name: '代码大全', price: 59},
        {id: 3, name: '现代操作系统', price: 79},
      ]
    },
    computed: {
      totalPrice: function () {
        let result = 0
        // 三种写法都可以,限于ES6语法
        // for (let i = 0; i < this.books.length; i++) {
        //   result += this.books[i].price
        // }
        // for(let i in this.books){
        //   result+=this.books[i].price
        // }
        for(let book of this.books){
          result+=book.price
        }
        return result
      }
    }
  })
</script>

</body>
</html>

效果:

image-20210510210931691

5.3.计算属性的setter和getter

每个计算属性都包含一个getter和一个setter。

  • 在上面的例子中,我们只是通过getter来获取
  • 在某些情况下,也可以提供一个setter方法(不常用)
  • 在需要写setter的时候,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{fullName}}</h2>

</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      firstName: '韦',
      lastName: '春',

    },
    computed: {
      // 计算属性一般是没有set方法, 只读属性
      // 但是如果在控制台修改fullName,其实是会调用set方法的
      fullName: {
        set: function (newValue) {
          console.log('----', newValue)
          // 识别新赋进来的值,以空格作为分割线,将字符串分割为两个字符串数组,分别给firstName和lastName赋值
          const names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[1]
        },
        get: function () {
          return this.firstName + ' ' + this.lastName
        }
      },
      // 📌 这两种情况实际上是一样的
      // 📌 因为平时使用实际上是用不到set方法的,所以就可以省略掉set方法,也就变相地等于下面这种写法
      // 📌 这也就是为什么明明fullName看起来是一个函数,但是在mustache语法中用计算属性输出的时候,却不需要写成加()的形式
      // fullName: function () {
      //   return this.firstName + ' ' + this.lastName
      // }
    }
  })
</script>

</body>
</html>

image-20210513205840942

image-20210513205919855

5.4.计算属性和methods的对比(计算属性的缓存)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--  1.通过mustache语法直接进行拼接 → 过于繁琐-->
  <h2>{{firstName}} {{lastName}}</h2>

  <!--  2.通过定义methods → 反复调用函数并执行,耗内存,性能较低-->
  <h2>{{getFullName()}}</h2>
  <h2>{{getFullName()}}</h2>
  <h2>总价格: {{getTotalPrice()}}</h2>
  <h2>总价格: {{getTotalPrice()}}</h2>

  <!--  3.通过定义计算属性computed-->
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>总价格: {{totalPrice}}</h2>
  <h2>总价格: {{totalPrice}}</h2>


</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      firstName: 'Kobe',
      lastName: 'Bryant',
      books: [
        {id: 1, name: 'Unix编程艺术', price: 89},
        {id: 2, name: '代码大全', price: 59},
        {id: 3, name: '现代操作系统', price: 79},
      ]
    },
    methods: {
      getFullName: function () {
        console.log('getFullName()')
        return this.firstName + ' ' + this.lastName
      },
      getTotalPrice: function () {
        console.log('getTotalPrice()')
        let result = 0
        // 三种写法都可以,限于ES6语法
        // for (let i = 0; i < this.books.length; i++) {
        //   result += this.books[i].price
        // }
        // for(let i in this.books){
        //   result+=this.books[i].price
        // }
        for(let book of this.books){
          result+=book.price
        }
        return result
      }

    },
    computed: {
      fullName: function () {
        console.log('fullName')
        return this.firstName + ' ' + this.lastName
      },
      totalPrice: function () {
        console.log('totalPrice')
        let result = 0
        // 三种写法都可以,限于ES6语法
        // for (let i = 0; i < this.books.length; i++) {
        //   result += this.books[i].price
        // }
        // for(let i in this.books){
        //   result+=this.books[i].price
        // }
        for(let book of this.books){
          result+=book.price
        }
        return result
      }
  }
  })
</script>

</body>
</html>

image-20210513213018554

image-20210513212956481

从控制台输出可以看到,当更改数据的时候,methods方法将会反复执行,但是computed属性因为有缓存的机制,所以只有在数据发生更改的那一次才会重新执行,如果发现之后的数据并没有发生变化,computed属性并不会像methods那样,每次调用都会执行一次。而methods方法甚至当只修改一项数据的时候,也会每次调用都执行,即便该方法对应的数据并没有发生变化。

综上所述,在面对数据会更新以及提高页面加载性能等这样的需求的时候,选择computed属性进行输出和显示,是最好的办法。

tips:在WebStorm中的快捷操作之console.log()

image-20210514134941007

点击enter之后,会自动生成 console.log(‘fullName’);

image-20210514134947443

6.ES6的补充和复习

6.1.块级作用域——let和var

var声明的变量不可变,是定死的。事实上,var的设计可以看成是JavaScript语言设计上的错误。但是,这种错误多半不能修复和移除,因为需要向后兼容。

💡大概十年前,Brendan Eich决定修复这个问题,于是添加了一个新的关键字:let。

💥我们可以将let看成更完美的var

块级作用域

  • JS中使用var声明一个变量时,变量的作用域主要是和函数的定义有关
  • 针对于其他块定义来说是没有定义域的,比如if/for等,这在开发中常会引起一些问题

6.1.1.关于变量的作用域

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
  // 1.变量作用域:变量在什么范围内是可用的
  {
    var name = 'why'
    console.log(name);
  }
  console.log(name);
</script>
</body>
</html>

image-20210514140609892

如上所示,console.log()无论在块内还是在块外,都是可以输出的

6.1.2.var作用下的变量输出

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script>
  // 2.没有块级作用域可能会引起的问题:if块阶
  // 变量在输出之前就被修改了,输出不了自己想要的结果
  var func;
  if(true){
    var name = 'why'
    func = function () {
      console.log(name);
    }
    // func()
  }
  name = 'Kobe'
  func()

  console.log(name);
</script>
</body>
</html>

image-20210514143251521

没有块级作用域的影响下,变量在函数执行前,如果被修改了,那么函数就起不到输出想要的结果

6.1.3.函数闭包解决var导致的块级作用域问题

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
  var btns = document.getElementsByTagName('button')
  for (var i = 0; i < btns.length; i++) {
    (function (n) {
      btns[i].addEventListener('click', function () {
        console.log('第' + n + '个按钮被点击')
      })
    })(i)
  }
  // 上述的循环实际上是将下面的这部分循环了5次,但是点击事件只有在button按钮上发生点击事件才会执行
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }()	小括号表示立即执行
  // 也就是:
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }(0)
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }(1)
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }(2)
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }(3)
  // function (n){
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + n + '个按钮被点击');
  //   })
  // }(4)
  // 但是如果采用var,因为var没有作用域这一说,
  // 这也就导致了var实际上是在{}之外的,因此,循环结束之后,每一个console.log()输出的i都是i=4
</script>
</body>
</html>

image-20210514151512126

6.1.4.带参函数

带参函数拥有自己的作用域,这也就是为什么闭包可以解决var导致的会计作用域问题

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script>
  var name = 'why'
  function f(Name) {
    console.log(Name);
  }
  f(name)
  name = 'Kobe'
  f(name)
</script>
</body>
</html>

6.1.5.ES6之后let的引进(闭包与let)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
  // 5.ES6中let的使用
  // ES6之前,只有var的时候,for循环会把循环里的监听事件执行五次,
  // 但是无论点击的是哪一个按钮,for循环始终是循环到头的,
  // 但是这执行的五次监听事件所用的i都是同一个i
  // 这也就导致无论点击的是哪一个按钮,每一个监听事件使用的i都是for循环执行到最后的i
  // 而ES6之后,引进了let,for循环同样的还是没执行一次循环,就执行一次监听事件,
  // 但是这时候的每个i是独立的,每一次执行的监听事件所用的i都有着自己的作用域,
  // 第一次循环的i就是属于第一个监听事件的,不会被第二个循环中的监听事件所引用
  const btns = document.getElementsByTagName('button')
  for(let i = 0; i< btns.length; i++){
    btns[i].addEventListener('click',function () {
      console.log('第' + i + '个按钮被点击');
    })
  }
  // 上述的循环如果采用var,则实际上是将下面的这部分循环了5次,
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // 也就是:
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   var i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // 但是如果采用var,因为var没有作用域这一说,
  // 这也就导致了var实际上是在{}之外的,因此,循环结束之后,点击按钮开始执行点击事件,每一个console.log()输出的i都是i=4
    
  // ES6中的let,let偶自己的作用域
  // i = 2
  // {
  //   i = 0
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   i = 1
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
  // {
  //   i = 2
  //   btns[i].addEventListener('click',function () {
  //     console.log('第' + i + '个按钮被点击');
  //   })
  // }
</script>
</body>
</html>

image-20210514151439860

💥💥💥

  1. ES5中的var是没有块级作用域的(if/for)
  2. ES6中的let是有块级作用域的(if/for)
  3. ES6之前,因为if和for都没有块级作用域的概念,所以导致在很多时候,我们都必须借助function的作用域来结局应该用外面变量的问题
  4. ES6中,加入let,let是有if和for的块级作用域

6.2.const使用

const关键字

  • 在很多语言汇总已经存在,比如C/C++中,主要的作用是将某个变量修饰为常量
  • 在JavaScript中也是如此,使用const修饰的标识符为常量,不可以再次赋值

→→那么什么时候使用const?

  • 当需要修饰的标识符不被再次赋值,就可以使用const来保证数据的安全性

const使用注意:

  1. const a = 20;
    a = 30;	// 错误,不可以修改
    
  2. const name;	// 错误,const修饰的标识符必须予以赋值
    

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
  // 1.一旦给const修饰的标识符被赋值之后,不能修改
  // const name = 'why';
  // name = 'Kobe';

  // 2. 在使用const定义标识符时,必须进行赋值
  // const name;

  const obj = {
    name: 'r7',
    age: 21,
    height: 182
  }

  console.log(obj);

  // 3.常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
  obj.name = 'Kobe';
  obj.age = 22;
  obj.height = 188;

  console.log(obj);

  // 可见不能改变const的指向,但是其属性是可以修改的
  // 而修改属性,仅仅是修改了属性的值,并没有直接修改obj的值,也就是没有改变obj的内存地址
  // 如果写成obj = obj1,这个时候,就相当于将obj的内存地址指向了obj1,这是不可以的
</script>
</body>
</html>

效果:

image-20210514161110697

6.3.对象字面量的增强写法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script>
  // const obj = new  Object();
  //对象的字面量(传统的写法)
  // const obj = {
  //   name: 'r7',
  //   age: 21,
  //   run: function () {
  //     console.log('run');
  //   },
  //   eat: function () {
  //     console.log('eat');
  //   }
  // }

  // 1.属性的增强写法
  const name = 'r7';
  const age = 18;
  const height = 182;

  // ES5的写法
  // const obj = {
  //   name: name,
  //   age: age,
  //   height: height
  // }

  // ES6的写法
  // const obj = {
  //   name,
  //   age,
  //   height
  // }

  // console.log(obj);

  // 2.函数的增强写法
  // ES5的写法
  const obj = {
    run: function () {

    },
    eat: function () {

    }
  }
  // ES6的写法
  const obj = {
    run() {

    },
    eat() {

    }
  }
</script>

</body>
</html>

7.事件监听

7.1.v-on的基本使用和其语法糖

前端开发中,我们经常有用户交互的需求,来简单查看v-on的使用:

  • 3.3.Vue计数器示范了一个小案例,案例里使用v-on:click = “counter++”

  • 除此之外,还可以将事件指向一个methods中定义的函数

<button v-on:click="increament" type="button">+</button>
<button v-on:click="subtraction" type="button">-</button>
methods: {
	increament: function() {
    // 在方法里直接写变量会直接去寻找一个全局变量,因此是找不到的
    // counter++;
    // 所以,要确定作用域,即this
    this.counter++;
    console.log('计数+1');
	},	
    subtraction: function() {
    this.counter--;
    console.log('计数-1');
    },
},

注:v-on也有对应的语法糖:

比如:v-on:click可以写成@click

<button @click="increament" type="button">+</button>
<button @click="subtraction" type="button">-</button>

计数器例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
  	<h2>{{counter}}</h2>
      <!--    v-on语法糖写法-->
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			counter: 0
  		},
      methods: {
          // 字面量增强写法
  		  increment(){
  		    this.counter++
        },
        decrement(){
  		    this.counter--
        }
      }
  	})
  </script>

</body>
</html>

在日常的开发中,指令一般都用语法糖的写法,同时也一般都采用字面量增强的写法.

7.2.v-on的参数传递问题

当通过methods中定义方法,一共@click进行调用的时候,需要注意参数问题:

  1. 情况一:如果该方法不需要额外参数,那么方法后的()可以不添加;
    • 但是要注意的是:如果方法本身有一个参数,那么会默认将原生时间event参数传递进去.
  2. 情况二:如果需要同时传入某个参数,同时需要event的时候,可以通过$event​传入事件.

image-20210515182323067

image-20210515182336644

全部示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
   <h2>{{message}}</h2>
<!--1.事件调用的方法没有参数-->
<!--没有参数的时候,加不加小括号没有结果上的影响-->
    <button @click="btn1Click()">无参函数,带括号</button>
    <button @click="btn1Click">无参函数,不带括号</button>

<!--2.事件调用的时候有参数-->
<!--在写事件定义的时候,写函数省略了小括号,但是方法本身其实是需要一个参数的,这个时候,Vue会默认将浏览器生产的event事件作为参数传入到方法-->

<!--传入123作为实参,则Vue将会把123作为事件输出-->
    <button @click="btn2Click(123)">有event参数,且传入</button>
<!--在写了小括号,但是没有传入实参,则函数的形参将会被定义为undefined-->
    <button @click="btn2Click()">有event参数,未传入</button>
<!--省略小括号时,Vue将会把默认的鼠标事件作为事件对象传入函数-->
    <button @click="btn2Click">有event参数,未加括号</button>

<!--3.方法定义时,我们需要event对象,同时又需要其他参数-->
<!--Vue会自动把event事件传给第一个参数-->
    <button @click="btn3Click">双参,不加括号</button>
<!--在直接写event的时候,Vue会将调用里的event识别为变量或者函数,但是如果data中没有定义event变量或者methods里面有没定义函数的时候就会报错-->
    <button @click="btn3Click(123,event)">双参,event</button>
<!--在调用方式时,如果手动地获取到浏览器参数的event对象:$event-->
<!--$event将会把浏览器产生的event对象传进调用的函数里-->
    <button @click="btn3Click(123,$event)">双参,数字作为其他参数直接输入</button>
<!--如果传入的是一串数字,那么Vue将会把数字作为基本数据类型进行传入和输出,但是如果写的不是加单引号的字符,那么Vue将会把这串字符当做一个data中的变量,但是如果用户没有定义,这时候就会报错-->
    <button @click="btn3Click(abc,$event)">双参,字母作为其他参数直接输入</button>
    <button @click="btn3Click('我是abc字符串',$event)">双参,字符串作为其他参数直接输入</button>


  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
   const app = new Vue({
    el: '#app',
    data: {
     message: 'Hello, world!',
        abc: '我是abc变量'
    },
      methods:{
      btn1Click(){
          console.log("无参函数");
        },
        btn2Click(event){
          console.log('event参数',event);
        },
        btn3Click(abc,event){
          console.log('双参含event', abc,event)
        }
      }
   })
    // 如果函数需要参数,但是没有传入,那么函数的形参为undefined
    function f(name) {  //name即为形参
      console.log(name);
    }
    f()

  </script>

</body>
</html>

示例的显示结果:image-20210516151548361

7.3.v-on的修饰符

在某些情况下,我们之所以需要拿到event的目的可能是进行一些事件的处理.

在Vue中提供了修饰符来帮助方便的处理一些事件:

  1. .stop → 调用event.stopPropagation()
  2. .prevent → 调用event.preventDefault()
  3. .{keyCode | keyAlias} → 只有当事件是由特定键出发时才出发回调
  4. .native → 间厅组建根元素的原生事件
  5. .once → 只触发一次回调
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
<!--    1..stop修饰符的使用-->
  	<div @click="divClick">
      <button @click.stop="btnClick">按钮</button>
    </div>
    
<!--    2..prevent修饰符的使用-->
    <form action="Baidu">
      <input type="submit" value="提交" @click.prevent="submitClick">
    </form>

<!--    3.监听某个键位的点击-->
    <input type="text" @keyup="keyUp">
    <input type="text" @keyup.enter="keyUp">

<!--    自定义组件-->
<!--    <cpn @click="cpnClick"></cpn>-->
<!--    自定义组件添加监听事件必须加上native才能监听到-->
<!--    <cpn @click.native="cpnClick"></cpn>-->

<!--    4.once修饰符的使用-->
<!--    可以用在点赞按钮上-->
    <button @click.once="onceClick">onceClick</button>

  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',

  		},
      methods:{
        divClick(){
          console.log('divClick');
        },
        btnClick(){
          console.log('btnClick');
        },
        submitClick(){
          console.log('submitClick');
        },
        keyUp(){
          console.log('keyUp');
        },
        onceClick(){
          console.log('onceClick');
        }
      }
  	})
  </script>

</body>
</html>

image-20210516160403072

8.条件判断

8.1.v-if/v-else-if和v-else的使用

v-if、v-else-if、v-else

  • 这三个指令和JavaScript的条件语句if、else、else if类似
  • Vue的条件指令可以根据表达式的值在DOM中选择性的渲染元素或组件

v-if的原理:

v-if后面的条件为false时,对应的元素及其子元素不会被渲染,也就是说根本不会有对应的标签出现在DOM中

8.1.1.v-if的使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
<!--    条件渲染 → v-if-->
  	<h2 v-if="isShow">{{message}}</h2>

  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
        isShow: false,
  		},
  	})
  </script>

</body>
</html>

image-20210516161256721

image-20210516161301938

8.1.2.v-if和v-else的结合使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--    条件渲染 → v-if-->
  <h2 v-if="isShow">{{message}}</h2>
  <h1 v-else>当isShowWiefalse的时候显示我</h1>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      isShow: false,
    },
  })
</script>

</body>
</html>

image-20210516161811140

image-20210516161817298

8.1.3.v-if v-else-if和v-else的结合使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
  	<h2>{{message}}</h2>
<!--    1.标签内逻辑判断-->
    <h2 v-if="score>=90">优秀</h2>
    <h2 v-else-if="score>=80">良好</h2>
    <h2 v-else-if="score>=70">一般</h2>
    <h2 v-else-if="score>=60">及格</h2>
    <h2 v-else>不及格</h2>
<!--    2.计算属性内逻辑判断-->
    <h1>{{result}}</h1>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			score: 99,
  		},
      computed: {
  		  result(){
          let showMessage = '';
          if (this.score>=90){
            showMessage = '优秀'
          }else if(this.score>=80){
            showMessage = '良好'
          }else if(this.score>=70){
            showMessage = '一般'
          }else if(this.score>=60){
            showMessage  ='及格'
          }else{
            showMessage = '不及格'
          }
          return showMessage
        }
      }
  	})
  </script>

</body>
</html>

image-20210516163129490

8.1.4.条件渲染的案例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
  	<h2>{{message}}</h2>
  	<span v-if="isUser">
<!--      for表示当鼠标点击label的时候,光标将会聚焦于id为username的标签上-->
      <label for="username">账号登录</label>
<!--      placeholder表示当文本输入框没有文字输入时,默认显示的底字-->
      <input type="text" id="username" placeholder="请输入您的用户账号">
    </span>
    <span v-else>
      <label for="email">邮箱登录</label>
      <input type="text" id="email" placeholder="请输入您的用户邮箱">
    </span>
    <button @click="isUser = !isUser">切换登录类型</button>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			isUser: true
  		},
  	})
  </script>

</body>
</html>

image-20210516164902130

image-20210516164941213

8.1.4.1.案例的小问题

可能存在的一些用户需求上的小问题:

如果在输入框已经有输入内容的情况下,切换了类型,就会发现代码在这样写的情况下,输入框内已经输入的内容在切换登录类型之后,仍然没有发生变化

但是按道理来讲,点击切换类型之后,应该是切换到另外一个input元素中了,输入框文字应该会变的。

为什么会有这种情况发生?

  • 这是由于Vue的特性,Vue在进行DOM渲染的时候,处于性能的考虑,会尽可能的复用已经存在的元素,而不是去重新创建新的元素。因此上面的案例中,再点击切换后,Vue内部会发现原来的input元素不再使用,会把它直接拿来作为else中的input来使用,同时输入框内的文字也不会进行处理

怎样避免这种情况?or怎么实现切换登录类型时清空输入框的需求?

  • 给对应的input添加key属性并复制,同时要注意保证key值的不同,如下:

    <input type="text" id="username" placeholder="请输入您的用户账号" key="username">
    
    <input type="text" id="email" placeholder="请输入您的用户邮箱" key="email">
    

8.2.v-show

v-show的用法和v-if很类似也用于决定一个元素是否渲染。

  • v-if和v-show对比

    1. v-if当条件为false时,所对应的元素压根就不会出现在DOM中;
    2. v-show当条件为false时,仅仅是将元素的display属性设置为none而已。
  • 既然两种指令都可以决定一个元素是否渲染,那么在开发中应该怎么选呢?

    1. 当需要在显示和隐藏两种状态之间切换特别频繁的时候,就是用v-show;
    2. 当切换次数很少时,甚至只有1次切换时,通常使用v-if。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  
  <div id="app">
<!--    v-if:当条件为false时,包含v-if指令的元素根本就不会存在DOM中-->
  	<h2 v-if="isShow" id="aaa">{{message}}</h2>
<!--    v-show:当条件为false时,v-show仅仅是给该元素添加了一个样式:display: none;-->
    <h2 v-show="isShow" id="bbb">{{message}}</h2>

  </div>
  
  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
  			isShow: true,
  		},
  	})
  </script>
  
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABakBQCT-1621174011040)(https://i.loli.net/2021/05/16/Edkg8MpbYxiaUFH.png)]

通过控制台的修改操作和显示效果,我们可以清楚的看到,使用v-show指令的h2标签并没有从DOM中小时,仅仅是添加了一个display样式,将h2标签给隐藏掉了。

9.循环遍历

  1. 通常当我们需要对一组数据进行排列处理的时候,可以使用v-for。
    • v-for语法类似于JavaScript中的for循环。
    • 一般格式为:v-for = “item in objs”
  2. 来看几个简单的案例:
    1. 在遍历中不需要使用索引值(遍历影片列表中所有影片并输出)
      • 语法格式示例:v-for = “movie in movies”
      • 依次从movies中取出movie,并且在元素的内容中,使用mustache语法,来显示movie
    2. 在遍历的过程中,需要拿到元素在数组中的索引值
      • 语法格式示例:v-for = “(movie, index) in movies”
      • 其中index就相当于取出的item在原数组的索引值

9.1.v-for遍历数组

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{message}}</h2>
<!--1.在遍历的过程中,没有使用索引值(下标)-->
  <ul>
    <li v-for="movie in movies">{{movie}}</li>
  </ul>
<!--2.在遍历的过程中获取索引值-->
  <ul>
    <li v-for="(movie, index) in movies">{{index+1}}. {{movie}}</li>
  </ul>

</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      movies: ['灵笼', '雾山五行', '三体', '岁城璃心']
    },
  })
</script>

</body>
</html>

显示效果:

image-20210516193536049

9.2.v-for遍历对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{message}}</h2>
  <ul>
    <li>{{info.name}}</li>
    <li>{{info.age}}</li>
    <li>{{info.height}}</li>

  </ul>
  <!--1.在遍历对象的过程中,如果只是获取一个值,那么默认获取到的是value值-->
  <ul>
    <li v-for="item in info">{{item}}</li>
  </ul>
  <!--2.获取key和value 格式:(value, key)-->
  <ul>
    <li v-for="(value, key) in info">{{value}}: {{key}}</li>
  </ul>
<!--3.获取key、value和index 格式:(value, key, index)-->
  <ul>
    <li v-for="(value, key, index) in info">{{value}} - {{key}} - {{index}}</li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      info: {
        name: '阿柒',
        age: 21,
        height: 183

      }
    },
  })
</script>

</body>
</html>

显示效果:
image-20210516193224710

9.3.组件的key属性

Vue官方推荐在使用v-for时,给对应的元素或组件添加上一个key属性。

❓为什么要添加key属性呢

💡这个其实和Vue的虚拟DOM的Diff算法有关,这里借用React的Diff algorithm中的一张图来解释说明一下:

  • 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
    1. 大多数情况下:我们希望可以在B和C之间插入一个F,Diff算法默认执行起来是这样的:
      • 把C更新成F,D更新成C,E更新成D,最后再插入E,如此看来,效率十分低下
    2. 依照上方Diff默认执行的过程来看,我们需要使用key来给每个节点u走一个唯一标识。如此一来,Diff 算法就可以正确的识别此节点,从而直接招到正确的位置插入新的节点。

所以,一言以概述:key的作用是为了高效的更新虚拟DOM,从而提升页面性能。

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

  <div id="app">
  	<h2>{{message}}</h2>
<ul>
  <li v-for="item in letters" :key="item">{{item}}</li>
</ul>
  </div>

  <script src="../js/vue.js"></script>
  <script type="text/javascript">
  	const app = new Vue({
  		el: '#app',
  		data: {
  			message: 'Hello, world!',
        letters: ['A','B','C','D','E']
  		},
  	})
  </script>

</body>
</html>

初始:

image-20210516203737429

删除下标为2之后及自身的数组元素:

image-20210516203731344

在下标为2的位置上添加一个F作为元素插入letters数组:

image-20210516203742270

📌tips:app.letters.push(‘需要新增的元素’)可以在数组尾端新增元素

代码理解和Diff算法利用key进行性能优化:

image-20210516204305312

在插入数组中插入一个新的元素时,DOM的渲染首先从Vue的虚拟DOM开始,虚拟DOM中利用Diff算法,将插入位置之后及自身的元素每一个都进行后移操作,之后再行虚拟DOM到实际DOM的渲染。

就像下图,对新插入的的F节点,Diff算法默认的做法是将C节点的位置给F,再把D节点位置给C,再把E节点的位置给D,最后再在尾部新增一个节点E。

image-20210516204431146

下为由没有key属性绑定和有key属性绑定的简单比较图:

有了key属性进行绑定之后,数组内每个元素和其原来的位置的绑定关系依旧不变,变的只是在插入位置处新增了一个节点,这样在数组元素很多的情况下就极大的节省了系统开销,进而提高性能。

image-20210516204719619

❗❓❗:特别注意一点:为什么key的绑定不采用index呢

💡:很容易就可以看出来,index对于一些元素来讲,在增删的过程中是改变的,比如上方的C,原本的index为2,但是当插入F之后,C的index就变成了3。如此看来,绑定index压根就没什么意义。

9.4.检测数组更新(数组中有哪些方法是响应的)

❓为什么在控制台中操作数据,浏览器窗口会自动更新

💡Vue内部会监听数据的变化,当数据发生变化,Vue会重新渲染虚拟DOM,然后根据虚拟DOM对真是DOM进行重渲染

这里我们可以重新回忆一下Vue官方给出的Vue生命周期:

img

示例代码(并没有涉及到原理和复杂的逻辑,所以单纯的手动操作一下代码就可以明白了):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{message}}</h2>
  <ul>
    <li v-for="item in letters" :key="item">{{item}}</li>
  </ul>
  <button @click="btnClick">function</button>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, world!',
      letters: ['A', 'B', 'C', 'D', 'E']
    },
    methods: {
      btnClick() {
        // 1.push方法(此方法是响应式的),该方法可以新增多个元素
        // this.letters.push('数组尾部新增元素')
        // this.letters.push('数组尾部新增元素1', '数组尾部新增元素2')

        //  2.通过索引值来修改数组中的元素(此方法并不能做到响应式)
        //   this.letters[0] = 'bbbbbbb'

        //  3.pop()方法
        // this.letters.pop()

        //  4.shift()
        //   this.letters.shift()

        //  5.在数组最前面添加元素(此方法是响应式的),该方法可以新增多个元素
        // this.letters.unshift('数组头部新增元素')
        // this.letters.unshift('数组头部新增元素1', '数组头部新增元素2')

        //  6.splice()
        //  splice作用:删除元素、插入元素、替换元素
        //  格式:splice(start: number, deleteCount?: number)
        //  如果要删除元素,第一个参数填写要删除的位置初始处,第二个参数写入要删除的个数
        // this.letters.splice(1,2)
        //  如果之传入第一个参数,那就会默认把这个下标以及这个下标之后的所有元素都删除
        // this.letters.splice(1)
        // 将下标为1之后的3个元素(包括下标为1的元素)替换成 m 和 n
        // this.letters.splice(1, 3, 'm','n')
        // 插入元素(第二个元素为0,后面跟上需要插入的元素(不限个数))
        // this.letters.splice(1, 0, 'x','y')

        //  7.sort()排序函数
        //   this.letters.sort()
        //   8.reverse()反转函数
        //   this.letters.reverse()

      //  那么既然letter[]式的天幻方式做不到响应式,恰好有需求怎么办?
      //   利用splice函数进行替换
      //   this.letters.splice(0, 1, 'bbb')

        // 利用Vue内部提供的函数对数组显示进行响应式修改
        // Vue.set(array, index, string)
        Vue.set(this.letters, 0, 'bbb')
      }
    }
  })

  // ...num 👉 可变参数
  // 函数会自动将传入的若干个数变成一个数组
  // function sum(...num) {
  //   console.log(num);
  // }
  // sum(10,20,30,40,50,60,70,80,90)
</script>

</body>
</html>

都是一些比较简单的效果,可以自行实现,不想动手的,那就🧠脑补一下运行效果。就不放展示图了。
下接阶段性案例——简单购物车界面

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柒柒星球

(๑•̀ㅂ•́)و✧给点儿赏赐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值