一、Vue入门

1、概述

​ 在众多的语言排名中,JavaScript 已经非常靠前,它是前端的核心编程语言,我们可以利用 js 开发 动态 效果的网页,也可以开发 app,为了简化 JavaScript 的使用,在其基础上,发展出两个门派:

  1. 真实 DOM 操作的 jQuery 门派,例如:bootstrap、layui、easyui 都使用了 jQuery 技术。

  2. 虚拟 DOM 操作门派,复杂的如 angular.js ,简单的国产框架

    我们以虚拟 DOM 操作的 vue.js 为例它有以下优点:

  • 它的核心库文件只有 jQuery 的 1/3,这非常有利于用于移动端 ;
  • 采用虚拟 DOM,只有在必须的时候才去操作真实 DOM,让显示性
  • 用数据 来驱动 呈现,在数据和表现控件 之间 建立一个适时互通 通道,任一方的改变,另一方也直 接改变,在通道构建完毕后,程序员进行业务逻辑处理时,只考虑数据,呈现完全自动化。
1.1、Vue到底是什么?

​ 官方解读:Vue (读音 /vju:/,类似于 view) 是一套用于构建用户界面的"渐进式框架"。与其它大型框架 不同的是,Vue 被设计为可以"自底向上逐层应用"。Vue 的核心库只关注"视图层",易于上手,便于与第三方库 或既有项目整合。

​ 简单来说,Vue 就是一个用于搭建表单项繁多且内容需要根据用户的操作进行修改的网页应用的框架。

1.2、为什么要使用Vue?

使用 JQuery 也可以完成相应的功能啊,为什么还要使用 Vue?

​ jQuery 是使用选择器($)选取 DOM 对象,对其进行赋值、取值、事件绑定等操作,其实和原生的 HTML 的 区别只在于可以更方便的选取和操作 DOM 对象,而数据和界面是在一起的。比如需要获取 label 标签的内容: $(“lable”).val();,它还是依赖 DOM 元素的值。

​ Vue 则是通过 Vue 对象将数据和 View 完全分离开来了。对数据进行操作不再需要引用相应的 DOM 对象,可 以说数据和 View 是分离的,他们通过 Vue 对象这个 vm 实现相互的绑定。这就是传说中的 MVVM。

​ 让我们投向 Vue.js 的主要原因在于:它能让团队书写用 js 更容易并且简化了 js。上手 Vue.js 是相当容易 的。它的源码有着很高的可读性,如果你需要仅用他的文档便可入门。你不必使用任何额外的库。如果需要可以 和 jQuery 协同工作。他有许多的插件,但并非必须。

1.3、Vue的使用
  1. 在 html 中,使用 script 标签引入 vue.js 文件:

    <script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
    
  2. 在 html 标签内添加一个 dom 元素作为 Vue

    <body>
        <div id="app" class="app"></div>
        <script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
    </body>
    
  3. 创建 Vue 对象的实例:

<body>
		
    <div id="app">

    </div>

    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {}
        })
    </script>

</body>

​ 我们已经成功创建了第一个 Vue 应用!现在数据 data 和 DOM(id=“app”的 div)已经被建立了关联,所有 东西都是响应式的。(所谓响应式即是 vue 实现的双向数据绑定,接下来会详细介绍)

2、 Vue 插值及表达式绑定数据

1、插值
  • 任务一: 使用双大括号将数据编译成普通文本,并输出到插值的位置。

    <div id="app">
        {{msg}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:"hello vue"
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

      ​ 插值标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变, 插值处的内容都会更新。

    Vue 的数据绑定是数据驱动。即:当数据发生变化时会触发 html 页面更新,所有相关联绑定的值也会随之 一起变化。

2、表达式
  • 任务二: vue 的数据绑定不仅限于简单的属性键值,VUE 对所有的绑定,都支持 JavaScript 表达式绑定, 如下例:

    <div id="app">
        <!-- 绑定三元运算符 -->
        {{count > 0?'是的':'不是'}}<br />
        <!-- 绑定一个计算 -->
        {{count+1}}<br />
        <!-- 绑定函数 -->
        {{msg.toUpperCase()}}
    
    
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:"hello vue",
                count:0
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

    这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。

3、基础指令

​ 指令 (Directives) 是带有v-前缀的特殊特性,绑定在 DOM 标签上。指令的职责是,当表达式的值改变 时,将其产生的连带影响,响应式地作用于 DOM。

1、v-text

​ v-text 的效果完全等价于使用双大括号绑定的效果。例如:

  • 任务三: 使用 v-text 基础指令实现在网页上显示指定内容。
<div id="app">
    <!-- 等同于 <span>{{msg}}</span> -->
    <span v-text="msg"></span>
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            msg:"hello vue"
        }
    })
</script>
  • 结果:

    在这里插入图片描述

使用 v-text 指令绑定数据

此时的效果完全等价于使用双大括号绑定的效果,那么二者有没有什么区别呢?

​ 答案是有。区别:v-text 是指令,只能绑定在 DOM 标签上,{{}}绑定 可以放置在 vue 挂在目标节点内的任 意位置,而不需要额外的 DOM 标签。

2、v-show

​ v-show 指令将根据表达式 isShow 的值的真假来显示/隐藏元素。

v-show 所绑定的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。

  • 任务四: 使用 v-show 指令实现指定内容的显示与否。

    <div id="app">
        <span v-show="isShow">我是span标签,能看到我不</span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                //当这个变量为true时,span标签显示,当为false时,隐藏
                isShow:true
            }
        })
    </script>
    

4、条件判断指令

​ vue 条件指令包括 v-if,v-else,v-else-if 用法与 js中的条件语句相同

1、v-if/v-else指令
  • 任务五: 使用 v-if/v-else 指令实现字符的大小写切换功能。

    <div id="app">
        <!-- 当lowerCase为true时,直接输出 -->
        <span v-if="lowerCase" v-text="msg"></span>
        <!-- 当lowerCase为false时,转换为大写再输出 -->
        <span v-else v-text="msg.toUpperCase()"></span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:"hello vue",
                lowerCase:false
            }
        })
    </script>
    

    v-else 使用限制: 上一个兄弟节点必须包含 v-if 或 v-else-if 指令之一

2、v-else-if指令

v-else-if 相当于 js 中的 if-else-if 语句:

  • 任务六: 使用 v-else-if 指令实现性别选择实时显示选择结果的功能。

    <div id="app">
        <!-- v-model是双向绑定,下面会讲,讲的时候可以来这参考案例 -->
        <label>
            <input type="radio" v-model="sex" value="0" /></label>
        <label>
            <input type="radio" v-model="sex" value="1" /></label>
        <label>
            <input type="radio" v-model="sex" value="-1" />保密
        </label>
    
        性别:
        <span v-if="sex==0"></span>
        <span v-else-if="sex==1"></span>
        <span v-else>保密</span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                sex:-1
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

      解释:当选中男的时候,sex改为0,v-if语句判断结果为男;当选中女的时候,sex改为1,v-else-if语句判断结果为女;当选中保密的时候,sex改为-1,v-else判断结果为保密,初始默认为-1,保密

5、循环指令v-for

​ v-for 循环数组或对象,表达式是 obj in objs 形式的特殊语法。objs 是一个数组或对象,obj 是元素迭代 的别名。

也可以使用 (obj,index) in objs 形式的表达式。

  • 当循环目标是数组时,第二个参数 index是数组的下标
  • 当循环目标是对象时,第二个参数 index是对象属性名

语法:

<!-- obj in objs 形式 -->
<ul>
    <li v-for="n in arr">
   	 {{n}}
    </li>
</ul>

<!-- v-for 循环时的索引 -->
<ul>
    <li v-for="(n,i) in arr">
    	index:{{i}} / value:{{n}}
    </li>
</ul>

案例:

  • 任务七: 使用 v-for 指令分别实现循环数组和对象的功能。

    <div id="app">
        <h4>遍历lessons数组</h4>
        <ul>
            <li v-for="l in lessons">
                {{l}}
            </li>
        </ul>
    
        <h4>带下标的遍历数组</h4>
        <ul>
            <li v-for="(l,i) in lessons">{{i}}:{{l}}</li>
        </ul>
    
        <h4>遍历对象</h4>
        <ul>
            <li v-for="s in student">{{s}}</li>
        </ul>
    
        <h4>带属性名遍历对象</h4>
        <ul>
            <li v-for="(s,i) in student">{{i}}:{{s}}</li>
        </ul>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                //student对象
                student:{
                    id:1,
                    name:"张三",
                    age:20,
                    sex:"男"
                },
                //lessons数组
                lessons:["h5","css","js","vue"]
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

6、 数据绑定指令 v-model

1、v-model指令

​ 基础指令及双大括号绑定只能解决单向数据绑定的问题,即当 data 中的数据发生改变时,页面的数据会随 之变化,而 v-model 指令(它负责监听用户的输入事件以更新数据)在表单 及 元素上创建 双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。

  • 任务八: 使用 v-model 指令实现如下的数据双向绑定功能,即实时显示选择结果。

    <div id="app">
        <!-- isShow变量默认为true,默认勾选框,span标签显示,
    当取消勾选的时候,isShow变量改为false,span标签不显示
    -->
        <input type="checkbox" v-model="isShow" />显示/隐藏
        <br />
        <span v-show="isShow">能看到我span标签不</span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                isShow:true
            }
        })
    </script>
    
    • 默认勾选结果:

      在这里插入图片描述

    • 不勾选结果:

      在这里插入图片描述

当单选框被选中时上面一行字体显示,再次点击时候单选框处于不被选中的状态此时上面一行字体不显示, 不需要刷新就能实现数据的实时传送,状态实时改变,这就是数据的双向绑定。

2、指令修饰符

修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指定应该以特殊方式绑定。

常见的指令修饰符有.number .trim 和 .lazy,下面对这些指令修饰符进行一一介绍。

2.1、 .number 修饰符
  • 任务九:体会.number 修饰符作用为自动将用户的输入值转为数值类型。

    <div id="app">
        <!-- 如果不加.number,当中输入的内容会默认认定为字符串
            然后赋值给n变量,进行双向绑定
            假如输入3,下面n+1结果为 31,当做字符串来拼接
        -->
        <!-- 
            <input v-model="n" />
            {{n+1}} 
        -->
    
        <!--
            .number自动转换为数值类型进行加减,
            当输入3,下面n+1结果为 4。
            当输入的内容开头为数值类型,后面为字符串类型,
            当标签失去焦点的时候自动删除后面的字符串类型,
            当输入的内容开头为字符串类型,则不进行自动转化,
            n+1作为字符串类型进行拼接
        -->
        <input v-model.number="n" />
        {{n+1}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:1
            }
        })
    </script>
    

    .number 修饰符可以将输入的值转化为 Number 类型 ,否则虽然你输入的是数字但它的类型其实是字符 串类型,在数字输入框中比较有用。

2.2、。trim修饰符
  • 任务十: 练习.trim 修饰符:自动过滤用户输入的首尾空白字符。

    <div id="app">
        <!-- 如果不加.trim,文本框里前后可以输入空格,
       		 假如为账号,密码,姓名什么的,不符合业务逻辑
        -->
        <!-- 
            <input v-model="n" />
            {{n}} 
        -->
    
        <!--
            .trim,当文本框失去焦点的时候自动去除文本框内前后空格,
            符合业务逻辑
        -->
        <input v-model.trim="n" />
        {{n}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:1
            }
        })
    </script>
    
2.3、.lazy修饰符

​ 在输入框中,v-model 默认是同步数据,使用 .lazy 会转变为在 change 事件中同步,也就是在失去焦点 或者按下回车键时才更新。

  • 任务十一: 使用.lazy 修饰符观察当输入数字时对应的内容会不会实时显示。

    <div id="app">
        <!-- 如果不加.lazy,当在文本框中输入的东西会马上进行双向绑定,
        	进行赋值
        -->
        <!-- 
            <input v-model="n" />
            {{n}} 
        -->
    
        <!--
            .lazy,当文本框失去焦点的时候,才进行双向绑定,
            才会进行赋值
        -->
        <input v-model.lazy="n" />
        {{n}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:1
            }
        })
    </script>
    

    在输入框中输入一串数字,有.lazy 修饰符修饰,并不会实时的同步数据,而是输入完成后点击空白处才能 同步 。

7、动态绑定指令v-bind

它是一个 vue 指令,用于绑定 html 属性,v-bind 指令可以被简写为":"

  • 测试

    <div id="app">
        <!-- 绑定属性 -->
        <img v-bind:src="imgSrc"/>
        <!-- 简写 -->
        <img :src="imgSrc"/>
        <!-- 字符串拼接 -->
        <img :src="'img/'+img"/> <!-- 相当于src="img/1.jpg" -->
        <!-- 绑定标签状态 -->
        <!-- 当isDis为true时,按钮被禁用,为false则没有 -->
        <button :disabled="isDis">确定</button> 
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                imgSrc:"http://via.placeholder.com/100x100",
                img:"1.jpg",
                isDis:true
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

1、 使用 v-bind 动态绑定 class属性

v-bind 通常用来绑定属性的,格式是 v-bind:属性名 = "值",简写:属性名 = "值"

v-bind动态更新class,分为三种方法:对象语法和数组语法,还有就是变量语法。

  • 变量语法: v-bind:class = “变量”,变量形式 ,这里的变量的值,通常是在 css 定义好的类名;
  • 数组语法: v-bind:class= “[变量 1,变量 2]” ,数组形式,其实跟上面差不多,只不过可以同时绑定多 个 class 名;
  • 对象语法: 法:v-bind:class = {classname1:boolean,classname2:boolean},对象形式,这里的 classname1 、2,其实就是样式表中的类名,这里的 boolean 通常是一个变量,也可以是常量、计算属性等,这种方法也是绑定 class 最常用的方式

使用 v-bind:class,v-bind:style 绑定多个值,vue 对 class 和 style 这两个 html 属性进行了一定程度 的增强。

只有 v-bind:class 设置的属性会追加原有属性,而 v-bind 绑定的其他属性是会覆盖原有属性。

1.1、 基于对象绑定针对 class 的增强
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
		<style type="text/css">
			.isActive{
				width: 100px;
				height: 100px;
				border: 3px solid #096;
			}
			.hasError{
				background-color: red;
			}
		</style>
	</head>
	<body>
		
		<div id="app">
			<p :class="cssObj">这是一个P标签</p>
		</div>
		
		<script type="text/javascript">
			var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
                    /* 基于对象的 */
					cssObj:{
/* 当isActive变量为true时,在标签的class属性里自动拼接这个类名,为false时不拼接,下面那个也一样 */
						isActive:true,
						hasError:true
					}
				}
			})
		</script>
		
	</body>
</html>
  • 页面渲染结果:

    <p class="isActive hasError">
        这是一个P标签
    </p>
    

    在这里插入图片描述

1.2、基于数组绑定针对 class 的增强
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
		<style type="text/css">
			.isActive{
				width: 100px;
				height: 100px;
				border: 3px solid #096;
			}
			.hasError{
				background-color: red;
			}
		</style>
	</head>
	<body>
		
		<div id="app">
			<p :class="cssArr">这是一个P标签</p>
		</div>
		
		<script type="text/javascript">
			var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
					/* 数组是没有办法单独设置类是否拼接的,但是可以进行优化 */
					cssArr:["isActive","hasError"]
				}
			})
		</script>
		
	</body>
</html>

使用数组的话是没有办法单独设置类是否拼接的,但是可以优化:

<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
		<style type="text/css">
			.isActive{
				width: 100px;
				height: 100px;
				border: 3px solid #096;
			}
			.hasError{
				background-color: red;
			}
		</style>
	</head>
	<body>
		
		<div id="app">
		<!-- 在数组里运用了三元运算符,如果jihuo为true,则拼接类,如果为false,则拼接空字符串 -->
			<p :class="['hasError',jihuo?'isActive':'']">这是一个P标签</p>
		</div>
		
		<script type="text/javascript">
			var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
					/* 数组是没有办法单独设置类是否拼接的,但是可以进行优化 */
					jihuo:true
				}
			})
		</script>
		
	</body>
</html>

当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:

<div id="app">
    <!-- 在数组里运用了三元运算符,如果jihuo为true,则拼接类,如果为false,则拼接空字符串 -->
    <p :class="['hasError',{isActive:jihuo}]">这是一个P标签</p>
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            /* 数组是没有办法单独设置类是否拼接的,但是可以进行优化 */
            jihuo:false
        }
    })
</script>

class 属性的值仅在 jihuo的值为 true 时包含样式 isActive

2、 使用 v-bind动态绑定 style属性
1.3、基于对象绑定针对style的增强

v-bind:style的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名

这样写是把css写到js里面,增加了代码的耦合度,不推荐使用

代码:

<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
	</head>
	<body>
		
		<div id="app">
		<!-- 在数组里运用了三元运算符,如果jihuo为true,则拼接类,如果为false,则拼接空字符串 -->
			<p :style="styObj">这是一个P标签</p>
		</div>
		
		<script type="text/javascript">
			var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
					/* 里面不能写-,比如background-color,会报错 */
					styObj:{
						width:"100px",
						height:"100px",
						background:"red"
					}
				}
			})
		</script>
		
	</body>
</html>
  • 页面渲染结果:

    <p style="width:100px;height:100px;background:red;">
        这是一个P标签
    </p>
    

注意:里面不能写-,比如background-color,会报错

1.4、基于数组绑定针对style的增强
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcss.com/vue/2.5.15/vue.js"></script>
	</head>
	<body>
		
		<div id="app">
		<!-- 在数组里运用了三元运算符,如果jihuo为true,则拼接类,如果为false,则拼接空字符串 -->
			<p :style="styArr">这是一个P标签</p>
		</div>
		
		<script type="text/javascript">
			var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
					/* 里面不能写-,比如background-color,会报错 */
					styArr:[{
						width:"100px"
					},{
						height:"100px"
					},{
						background:"#096"
					}]
				}
			})
		</script>
		
	</body>
</html>
  • 页面渲染结果:

    <p style="width:100px;height:100px;background:#096;">
        这是一个P标签
    </p>
    

    在这里插入图片描述

style 绑定数组时,数组中的每个有效的 style 对象,都将被输出到 html 标签的 style 属性中。

二、Vue生命周期

1、Vue生命周期

​ 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂 载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期的钩子函数,这给了用户在不同阶段添加自己代码的机会。

首先每个 Vue 应用都是通过 Vue 函数创建一个新的 Vue 实例开始的。

  • 如下代码

    var app = new Vue({
    	//选项
    })
    

    Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 Dom、渲染→更新→渲 染、销毁等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。

每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化运行中销毁

在这里插入图片描述

Vue生命周期图示:

在这里插入图片描述

2、生命周期钩子使用示例

​ 我们已经知道 Vue 实例对象从创建到销毁的过程中会发生各种事件,每种事件发生时,都可以有对应的 function 被调用,在不同的 function 中,我们访问对象的方式略有不同,下边的示例,你不需要完全掌握,但 以后开发中当你有需要时,可以参考这些示例,迅速完成代码构建。

方法的完整版:

var app = new Vue({
				el: "#app",//可以是 css 选择器.例如: #app 或.app
				data: {
					msg:"hello vue!"
				},
				beforeCreate: function(){
					console.log("--------------beforeCreate创建之前--------------");
					console.log(this.$el);// undefined
					console.log(this.$data);// undefined
					console.log(this.msg);// undefined
				}
			})

ES6简写版:

beforeCreate() {
    console.log("----------------beforeCreate创建之前----------------");
    console.log(this.$el);//undefined
    console.log(this.$data);//undefined
    console.log(this.msg);//undefined
}
2.1、beforeCreate

​ Vue 实例对象或组建实例被创建,Vue 对象数据观测 data 数据对象和 event 事件开始初始化但尚未初始化完成时。

  • 任务一:使用 beforeCreate 钩子函数,输出 e l 、 el、 eldata 对象,观看结果。
<div id="app">
    {{msg}}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            msg:"hello vue!"
        },
        beforeCreate: function(){
            console.log("--------------beforeCreate创建之前--------------");
            console.log(this.$el);// undefined
            console.log(this.$data);// undefined
            console.log(this.msg);// undefined
        }
    })
</script>
  • 结果:

    在这里插入图片描述

此时,Vue 实例内的对象均是 undefined 不可访问。

2.2、created

​ 在实例创建完成后被立即调用。在这一步,数据观测data、event均已被创建 -

  • 任务 2:使用 created钩子函数,输出 e l 、 el、 eldata对象,观看结果

    <div id="app">
        {{msg}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:"hello vue!"
            },
            created: function(){
                console.log("--------------created创建之后--------------");
                console.log(this.$el);// undefined
                console.log(this.$data);// object
                console.log(this.msg);// hello vue!
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

此时的 DOM 对象还未被创建完毕。

2.3、beforeMount

​ 将 Vue 实例挂载到 DOM 对象之前。vm.$el 对象已创建,但未挂载到页面上。此时获取到的DOM 对象中的指 令尚未被计算。

  • 任务 3:使用 beforeMount钩子函数,输出 e l 、 el、 eldata 对象,观看结果:
<div id="app">
    {{msg}}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            msg:"hello vue!"
        },
        beforeMount: function(){
            console.log("--------------beforeMount挂载之前--------------");
            console.log(this.$el);// <div id="app">{{msg}}</div>
            console.log(this.$data);// object
            console.log(this.msg);// hello vue!
        }

    })
</script>
  • 结果:

    在这里插入图片描述

注意:此时产生的是 Virtual DOM 对象。

2.4、mounted(常用)

​ 在原 DOM 对象被 Vue 创建的 vm.$el 替换到页面上之后。此时页面上才会存在相对于的 DOM 节点。一般理解 为 DOM 对象创建完毕。

  • 任务 4:使用 mounted 钩子函数,输出 e l 、 el、 eldata 对象,观看结果。

    <div id="app">
        {{msg}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:"hello vue!"
            },
            mounted: function(){
                console.log("--------------mounted挂载之后--------------");
                console.log(this.$el);// <div id="app">hello vue!</div>
                console.log(this.$data);// object
                console.log(this.msg);// hello vue!
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

注意:此时的 DOM 是真实 DOM!

2.5、beforeUpdate

​ 当数据发送变化时,组件更新之前。

调用时机:数据更新时调用,vm.$el 替换到页面上之前。

  • 任务 5:使用 beforeUpdate 钩子函数,并在观看函数的运行时机和结果。

    <div id="app">
        <!-- 当点击按钮进行修改的时候触发beforeUpdate事件 -->
        <input type="checkbox" v-model="isCheck" />切换<br />
        isCheck的值为:<span v-text="isCheck" id="span"></span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                isCheck:true
            },
            beforeUpdate: function(){
                console.log("--------------beforeUpdate修改之前--------------");
                console.log(this.isCheck);
                console.log("真实DOM的值为:" + document.getElementById("span").innerHTML)
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

2.6、updated

​ 组件更新之后。

调用时机:新的数据更新到 DOM 节点上之后。

  • 任务 6:使用updated 钩子函数,观察执行时机和与 beforeUpdate的不同。

    <div id="app">
        <!-- 当点击按钮进行修改的时候触发beforeUpdate事件 -->
        <input type="checkbox" v-model="isCheck" />切换<br />
        isCheck的值为:<span v-text="isCheck" id="span"></span>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                isCheck:true
            },
            updated: function(){
                console.log("--------------updated修改之后--------------");
                console.log(this.isCheck);
                console.log("真实DOM的值为:" + document.getElementById("span").innerHTML)
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

2.7、beforeDestroy

​ 实例销毁之前调用。在这一步,实例仍然完全可用。

  • 任务 7:使用 beforeDestory钩子函数,并观看运行时机。
<div id="app">
    <!-- 当点击按钮进行修改的时候触发beforeUpdate事件 -->
    <button type="button" @click="add">+1</button>
    <button type="button" @click="destroy">销毁</button><br />
    {{n}}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            n:1
        },
        methods:{
            add:function(){
                this.n++;
            },
            destroy:function(){
                this.$destroy();
            }
        },//销毁之前,通常用于善后操作,比如清除缓存等。
        beforeDestroy:function(){
            console.log("---------beforeDestroy销毁之前----------");
            console.log("销毁之前");
        }
    })
</script>
  • 结果:

    在这里插入图片描述

点击销毁按钮后,+1按钮将不能进行改值操作。

2.8、destroyed

​ Vue 实例销毁后调用

调用后 Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

  • 任务 8:使用 destroyed 钩子函数,观察与 beforeDestroy 函数运行时机的区别。

    <div id="app">
        <!-- 当点击按钮进行修改的时候触发beforeUpdate事件 -->
        <button type="button" @click="add">+1</button>
        <button type="button" @click="destroy">销毁</button><br />
        {{n}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:1
            },
            methods:{
                add:function(){
                    this.n++;
                },
                destroy:function(){
                    this.$destroy();
                }
            },//销毁之后,基本不用
            destroyed:function(){
                console.log("---------destroyed销毁之后----------");
                console.log("销毁之后");
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

三、使用ajax

​ 在 jQuery 中,直接提供了 ajax 函数(例如:$.post),可以和服务器端进行数据交互,但 vue.js 本身不包 含 ajax 相关函数,我们从 jQuery 中,提取了 ajax。

  • 任务 9:发送 ajax 请求到 stuapi,请求所有的角色信息,并将信息以li的形式显示到页面中。

    <div id="app">
        <ul>
            <li v-for="m in msg">
                ID:{{m.Id}},Name:{{m.Name}}
            </li>
        </ul>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                msg:""
            },
            mounted:function(){
                $.post("http://stuapi.ysdjypt.com/api/GetRolesAll",{
                    token:'eef6233e-32fb-43c1-b572-aa74b2aa7375'
                },function(res){
                    console.log(res);
                    app.msg=res;
                },"json");
    
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

其实使用Vue是不用ajax的,通常都是使用axios发送请求

axios中文说明: 使用说明 · Axios 中文说明 · 看云 (kancloud.cn)

四、Vue数据状态

4.1、methods对象

​ methods 是一个包含了{key:function}的对象。通过实例可以直接访问这些函数,也可以在指令的表达式中 调用。

​ methods 将被混入到 Vue 实例中,可以直接通过 Vue 实例访问这些方法,或者在指令表达式中使用。方法中 的 this 自动绑定为 Vue 实例。

定义方法全写:

<button type="button" v-on:click="add()"></button>

简写:

<button type="button" @click="add()"></button>
4.1.1、无参
  • 任务 1-1:简单使用 methods,点击按钮时,改变原有标签中的值。

    <div id="app">
        {{n}}<br />
        <!-- 当点击按钮的时候,触发click()方法,n+1 -->
        <button type="button" @click="add()">+1</button>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:0
            },
            methods:{
                //定义方法
                add:function(){
                    this.n++;
                }
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

4.1.2、带参
  • 方法中传递参数。
<div id="app">
    <!-- 
        当输入一个数字的时候,因为双向绑定,所以会改变按钮中方法的传参。
        当点击按钮的时候,就会触发click(n)方法,参数就会被穿进去。
        a = a+n
        但是v-model默认值是字符串,要转为数字类型才能正常加减
    -->
    <input type="text" v-model.number="n" /><br />
    {{a}}<br />
    <button type="button" @click="add(n)">+{{n}}</button>
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            a:0,
            n:0
        },
        methods:{
            //定义方法
            add:function(n){
                this.a = this.a + n;
            }
        }
    })
</script>
  • 结果:

    在这里插入图片描述

4.2、computed对象

​ computed 在 Vue 中被称作“计算属性”。我们之前所学面向对象语言中,大家都知道类中可以有属性和方法:

  • 访问方法时,需要对象名.方法名并加括号(括号中放参数)。例如:vm.getSum(a,b,c);
  • 访问属性时,需要对象名.属性名 即可。例如:obj.name=“张三”;

对于 Vue 而言,我们可以在 data 中,定义属性,但有些属性不需要存储(比如:可以由已知属性推导出来), 这时候定义到 computed 中是最合适的,定义到 computed 中的被称为计算属性,如果计算属性是可读可写的,这 时候需要使用 getter 和 setter(C#和 Java 中都有类似的概念),如果计算属性是只读的,我们既可以使用 getter, 也可以直接通过定义方法(这样更简单);

  • 任务 2-1:阅读下边的代码 ,学习在 Vue 中如何使用 computed 计算属性

    <div id="app">
        商品名:{{product.name}}<br />
        单价:{{product.price}}<br />
        数量:{{product.num}}<br />
        <!-- 当点击按钮的时候,执行对应的方法 -->
        <button type="button" @click="add()">+</button>
        <button type="button" @click="sub()">-</button><br />
        <!-- 调用的属性后面不需要加() -->
        总价方法一:{{totalMoney}}<br />
        总价方法二:{{totalMoney2}}<br />
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                product:{
                    name:"铅笔",
                    price:3,
                    num:2
                }
            },
            methods:{
                //执行方法,返回pronum这个属性对应的加1和减1
                add:function(){
                    return this.pronum++;
                },
                sub:function(){
                    return this.pronum--;
                }
            },
            computed:{
                //可以省略get方法,但是这样写就不可以写set方法了,只能得到,不能获取
                totalMoney:function(){
                    return this.product.price * this.product.num
                },
                // 属性有get、set方法
                totalMoney2:{
                    get:function(){
                        return this.product.price * this.product.num
                    }
                },
                /*
    					*	get方法用于获取值赋值给属性,
    					* 当属性发生改变的时候,执行set方法。
    					* newVal形参是属性发生改变后的值,进行判断是否小于0
    					* 如果小于0则提示不正确,进行提示 ,如果大于等于0则将新值赋值给商品数量。
    					*/
                pronum:{
                    get:function(){
                        return this.product.num;
                    },
                    set:function(newVal){
                        if(newVal<0){
                            alert("商品数量不能小于0");
                        }else{
                            return this.product.num = newVal;
                        }
                    }
                }
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

不断点击按钮“减少数量”,当数量减少到0一下的时候,就会提示了。

小结:

  • computed:计算属性是基于它们的依赖进行缓存的,只有相关依赖会发生改变时才会重新求值。只要相关依赖未改变,只会返回之前的结果,不再执行函数。
  • computed 中的对象可以是 key:function,也可以是 key:{get:function, set:function}

4.3、watch对象

​ watch 用来观察某个属性,当被观察的属性的值发生变化时,对应的 function 得以执行,此 function 有两 个参数,新值和老值,如果我们不干预的话,新值会被采纳,当然我们也可以重新使用老值来恢复。

​ watch 是一个对象{key:String | Function | Object | Array},键是需要观察的表达式,值是对应的回调 函数。值也可以是方法名,或者包含选项的对象。

Vue 实例将会在实例化时调用$watch()方法,遍历 watch 对象的每一个属性。

  • 任务 3-1:简单使用 watch 来观察文本框的变化。

    <div id="app">
        <!-- 当文本框输入内容时,就会修改ming的值,就会触发watch里ming的方法 -->
        名:<input type="text" v-model="ming"/><br />
        姓名:{{quanming}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                xing:'张',
                ming:'',
                quanming:''
            },
            watch:{
                //newVal:新值	oldVal:旧值
                ming:function(newVal,oldVal){
                    console.log(newVal);
                    console.log(oldVal);
                    this.quanming = this.xing + newVal;
                }
            }
    
        })
    </script>
    
    • 结果:

      在这里插入图片描述

任务讲解:我们在watch 中观看或者叫“监听”了ming这个属性,当这个属性发生变化时就触发里边的函数。

五、Vue事件处理

​ 事件是面向对象中 1 个非常重要的概念,它存在的原因是各种运行平台(PC 平台、手机平台)本身支持事 件(例如:所有的交互,键盘输入、鼠标点击、手指触碰等),作为运行在平台上的应用程序,只要涉及到交互, 自然都需要支持事件。

事件机制允许我们提前预制一些方法,当事件发生时,这些预制的方法就会被执行,事件和方法之间的关系 包括:

  • 建立绑定关系;(on)
  • 用户通过平台系统触发事件,从而引起事件执行;
  • 内部业务触发事件,从而引起事件执行;(emit)
  • 取消绑定关系;(off)

5.1、v-on

​ 可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。v-on 等价于@+事件名,例如:v-on:click=function等价于@click=function …

  • 任务1:设置一个按钮,点击时让数字加1。

    <div id="app">
        <!-- 当点击按钮的时候,触发点击事件,执行n++ -->
        <button type="button" @click="n++">点我</button><br />
        您一共点击了{{n}}次按钮
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
                n:0
            }
    
        })
    </script>
    

其它事件如:blur、change、focus、submit 等标签或 form 事件都可以通过 v-on 指令绑定

5.2、app.$on

使用这个可以自定义事件。

语法结构:

vm.$on(event,callback)

参数结构入下:

  • event: {String | Array} 事件名或包含事件名的数组。
  • callback: function回调函数。

通过 vm. o n 监 听 当 前 实 例 上 的 自 定 义 事 件 。 事 件 可 以 由 v m . on 监听当前实例上的自定义事件。事件可以由 vm. onvm.emit 触发。回调函数会接受所有传入事件触发函数的额外参数。(结合下面vm.$emit一起举例)

5.3、app.$emit

语法结构:

vm.$emit(event, [...args])

触发当前实例上的事件。附加参数都会传给监听器回调。

  • 任务 2:通过 vm. o n 自 定 义 事 件 并 绑 定 执 行 方 法 , 通 过 v m . on 自定义事件并绑定执行方法,通过 vm. onvm.emit 触发自己定义的事件。

    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
    
            },
            created: function(){
                //自定义test方法,方法接收msg参数
                this.$on('test',function(msg){
                    //控制台打印msg
                    console.log(msg);
                })
            }
    
        })
    
        //执行test自定义方法,参数为hi
        app.$emit('test','hi');
    </script>
    
    • 结果:

      在这里插入图片描述

5.4、app.$off

语法结构:

vm.$off(event,callback)

作用:移除自定义监听事件,作用和 vm.$on 相反。

参数解析如下:

  • event:{String | Array} 事件名或包含事件名的数组。
  • callback:function 回调函数

用法3条

  • 如果没有提供参数,则移除所有的事件监听器。

  • 如果只提供了事件,则移除该事件所有的监听器。

  • 如果同时提供了事件与回调,则只移除这个回调的监听器。

  • 任务 3:编写实验代码,验证以上“用法3条”用法。

<div id="app">
    <!-- 当点击按钮的时候,触发cli事件,cli方法执行自定义的test方法,
    	test方法又执行了fA,fB两个方法。
    -->
    <button type="button" @click="cli()">点我有惊喜</button><br />
    <!-- 点击就会fA的绑定事件,test方法里只有fB的方法了。只执行方法fB -->
    <button type="button" @click="removefA()">点击移除fA</button><br />
    <!-- 点击就会删除test所绑定的所有事件了。 -->
    <button type="button" @click="removetest()">点击删除test的所有事件绑定方法</button><br />
    <!-- 点击就会删除app内所有的事件绑定方法 -->
    <button type="button" @click="removeAll()">点击删除app内所有事件绑定方法</button><br />
    <!-- v-test和v-html的区别就是,v-html能识别里面的标签。v-test全部打印 -->
    <span v-html="msg"></span>
</div>

<script type="text/javascript">
    function fA(){
        app.msg += 'fA<br/>';
    }

    function fB(){
        app.msg += 'fB<br/>';
    }

    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            msg:''
        },
        methods:{
            cli: function(){
                this.$emit('test');
            },
            removefA: function(){
                this.$off('test',fA);
            },
            removetest: function(){
                this.$off('test')
            },
            removeAll: function(){
                this.$off();
            }
        },
        created: function(){
            this.$on('test',fA);
            this.$on('test',fB);
        }

    })

</script>
  • 结果:

    在这里插入图片描述

5.5、自定义事件综合案例

完成一个购物车案例。

执行效果如下:点击后边的+和-时,数量会变,同时共计也会变。

在这里插入图片描述

<div id="app">
    <h4>我的购物车</h4>
    <ul>
        <li v-for="c in carts">
            名称:{{c.name}},价格:{{c.price}},数量:{{c.num}}
            <!-- 点击按钮,执行add方法,必须要传入当前对象c才能对当前对象进行加减
                 执行add方法,当前对象的num++
            -->
            <button type="button" @click="add(c)">+</button>
            <!-- 点击按钮,执行subtract方法,必须要传入当前对象c才能对当前对象进行加减
                执行subtract方法,当前对象的num--
                这里在方法里添加了条件,当商品的数量大于0的时候才能执行方法,这是后面要讲的
                在这里使用了。并且两个条件不能相反,否则无效。
            -->
            <button type="button" @click="c.num>0 && subtract(c)">-</button>
        </li>
    </ul>
    总计:¥{{totalMoney}}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {
            carts:[
                {name:'香蕉',price:2,num:1},
                {name:'苹果',price:1.2,num:2},
                {name:'西瓜',price:8,num:3},
            ]
        },
        methods:{
            add: function(c){
                c.num++;
            },
            subtract: function(c){
                c.num--;
            }
        },
        computed:{
            //计算所有商品的总价
            totalMoney: function(){
                var sum = 0;
                for (var i = 0; i < this.carts.length; i++) {
                    sum += this.carts[i].price * this.carts[i].num
                }
                //保留小数点后两位
                return sum.toFixed(2);
            }
        }

    })

</script>

5.6、$event

触发事件时可以传入参数。事件本身的 event 对象可以使用$event 被当做参数传入。

<div id="app">
    <button type="button" @click="getEle($event)">点我</button>
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app",//可以是 css 选择器.例如: #app 或.app
        data: {

        },
        methods:{
            getEle: function(e){
                console.log(e);
                //打印e的初始目标
                console.log(e.currentTarget);
            }
        }

    })

</script>
  • 结果:

    在这里插入图片描述

5.7、事件条件

条件事件是常用的一种应用场景。既:当表达式满足条件时,才会触发相应的事件

具体示例第5.5章节里有具体举例与说明。

需要注意的是:我们平时在直接绑定函数时是不需要加“()”的,但是当 v-on 中有表达式时,就必须 带上“()”,否则 Vue 无法判断后边的内容是表达式还是方法名。

5.8、事件修饰符(重要)

dom 元素在浏览器内存中是以树状结构(父子)存在的,当孩子元素被点击时,会发生哪些情况呢?

  • 只有自己的 click 事件被触发;
  • 所有长辈的 click 事件也被触发,先触发自己的再向上冒泡到父亲元素的,爷爷元素的……(注意:这 是默认的行为方式);
  • 所有长辈的 click 事件也被触发,先触发最顶层爷爷元素的,……最后到自己元素的;

这些情况,我们原来都没怎么关注,因为我们写前端代码的量并不多,仅仅做表单元素处理等,但是纯粹的 前端开发者都需要考虑上边所述的情况,Vue 提供了几个修饰符,来帮助我们控制 事件冒泡 及一些更为细化的事件触发。

概念:事件冒泡,就是元素自身的事件被触发后,如果父元素有相同的事件,如 onclick 事件,那么元素本身的触发状态就会传递,也就是冒到父元素,父元素的相同事件也会一级一级根据嵌套关系向外触发,直到 document/window,冒泡过程结束。

5.8.1、.stop修饰符

在 Vue 中如何解决冒泡事件呢?我们使用“.stop”来阻止事件向上级传递。

  • 任务 6:阅读下边的代码,验证.stop 阻止事件的向上传递。

    <html>
    	<head>
    		<meta charset="utf-8" />
    		<title></title>
    		<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    		<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    		<style type="text/css">
    			div{
    				border: 1px solid red;
    			}
    			span{
    				border: 2px solid blue;
    			}
    		</style>
    	</head>
    	<body>
    		
    		<div id="app">
    			<div @click="wai()">
    				外层div
    				<!-- 
    					外层div和内层div都绑定了方法,那么我们点击内层的时候,
    					是触发的外层方法,还是内层方法,请查看下面结果
    				-->
    				<span @click="nei()">内层span</span>
    			</div>
    		</div>
    		
    		<script type="text/javascript">
    			var app = new Vue({
    				el: "#app",//可以是 css 选择器.例如: #app 或.app
    				data: {
    					
    				},
    				methods:{
    					wai: function(){
    						console.log("wai");
    					},
    					nei: function(){
    						console.log("nei");
    					}
    				}
    				
    			})
    			
    		</script>
    		
    	</body>
    </html>
    
    • 结果:

      在这里插入图片描述

5.8.2、.prevent修饰符

.prevent 修饰符的作用是取消事件的默认动作。用于拥有默认动作的 html 标签,例如:、标签等。

  • 任务 7:验证通过 prevent 取消默认动作的功能。

    <div id="app">
        <!-- 当加上.prevent以后,点击按钮就不会跳转页面了。-->
        <a href="https://www.baidu.com" @click.prevent="">跳转</a>
    </div>
    
5.8.3、.capture修饰符

事件冒泡的默认顺序是由内到外,使用“.capture”修饰符可以使事件的触发顺序变为由外到内。

  • 任务 8:验证通过 capture 修饰符来改变事件冒泡的顺序。

    <div id="app">
        <!-- 加上.capture修饰符后,就是先执行外层方法,再执行内层方法 -->
        <div @click.capture="wai()">
            外层div
            <!-- 
                外层div和内层div都绑定了方法,那么我们点击内层的时候,
                是触发的外层方法,还是内层方法,请查看下面结果
            -->
            <span @click="nei()">内层span</span>
        </div>
    </div>
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
    
            },
            methods:{
                wai: function(){
                    console.log("wai");
                },
                nei: function(){
                    console.log("nei");
                }
            }
    
        })
    
    </script>
    
    • 结果:

      在这里插入图片描述

5.8.4、.self修饰符

.self 修饰符只会触发元素本身的事件,事件不从内部的子孙元素触发。 即:event.target 是当前元素本身时才会触发事件。

  • 任务 9:自己编写代码验证 self 修饰符,阻断事件冒泡。

    <div id="app">
        <!--
    当我们加入.self修饰符后,只有点击外层div才能调用他的方法
    -->
        <div @click.self="wai()">
            外层div
            <!-- 
    外层div和内层div都绑定了方法,那么我们点击内层的时候,
    是触发的外层方法,还是内层方法,请查看下面结果
    -->
            <span @click="nei()">内层span</span>
        </div>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
    
            },
            methods:{
                wai: function(){
                    console.log("wai");
                },
                nei: function(){
                    console.log("nei");
                }
            }
    
        })
    
    </script>
    
    • 结果:

      在这里插入图片描述

5.8.5、.once修饰符

.once 修饰符:元素所绑定的事件仅触发 1 次。

  • 任务 10:验证.once 修饰符的功能。

    <div id="app">
        <div @click="wai()">
            外层div
            <!-- 
    外层div和内层div都绑定了方法,那么我们点击内层的时候,
    是触发的外层方法,还是内层方法,请查看下面结果
    -->
            <!--
    当我们添加.once修饰符后,该方法只能触发一次。
    -->
            <span @click.once="nei()">内层span</span>
        </div>
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",//可以是 css 选择器.例如: #app 或.app
            data: {
    
            },
            methods:{
                wai: function(){
                    console.log("wai");
                },
                nei: function(){
                    console.log("nei");
                }
            }
    
        })
    
    </script>
    
    • 结果:

      在这里插入图片描述

5.9.6、链式编程

事件修饰符可以叠加使用。例如:

  • 任务 11:下边的代码,实现了修饰符的叠加,运行测试一下吧。

    <!-- 修饰符可以串联	阻止了向上传递,并且使自己的跳转作用失效 -->
    <a v-on:click.stop.prevent="doSome"></a>
    

六、Vue组件

6.1、什么是vue组件

​ **组件(可复用的代码段)**是 vue 最强大的功能之一,是可扩展的 html 元素,是封装可重用的代码,同 时也是一个可以放重复使用的 Vue 实例。

​ 定义和使用组件,通常情况下我们会:使用 Vue.component方法,来定义(注册)组件,然后就可以在 Vue 的 环境中使用了。

  • 语法:

    Vue.compontent("标签名",{})
    
  • 任务1:实现1个自己记录自己被点击次数的组件按钮。

<div id="app">
    <!-- 定义好的组件可以直接用标签调用 -->
    <cli-btn></cli-btn>
</div>

<!-- 
	可以使用 template 模版,来定义组件的 html 内容,
	这样做的优点是非常直观,但缺点是不能将组件提到1个单独的js文件中。
-->
<template id="temp">
    <button @click="cli()">{{msg}}</button>
</template>

<script type="text/javascript">
    //标签名字随便写,但是不能包含大写字母,必须都为小写,否则会报错。
    Vue.component("cli-btn",{
        template:"#temp",
        //模板里的data必须定义成方法,固定格式
        data:function(){
            return{
                n:0,
                msg:"请点击"
            }
        },
        //方法还和原来一样。
        methods:{
            cli(){
                this.msg = "您已经点击了"+this.n+++"下";
            }
        }
    })

    //最后需要创建Vue实例来进行渲染,必须放到最后才行。
    var app = new Vue({
        el:"#app"
    })
</script>
  • 结果:
    在这里插入图片描述

标签名字随便写,但是不能包含大写字母,必须都为小写,否则会报错。

  • 任务 2: 去掉 id="temp"的 template,将其内容,以字符串的方式,放到 Vue.component 函数的 template 属性中,并最终将组件封到1个 js 文件中。

    1. 创建js文件,把组件代码剪切进去,然后在主页面上引用js文件,js文件:

      //标签名字随便写,但是-后面第一个字母不能为大写,像cli-Btn,会报错。
      Vue.component("clibtn",{
      	template:`<button @click="cli()">{{msg}}</button>`,
      	//模板里的data必须定义成方法,固定格式
      	data:function(){
      		return{
      			n:0,
      			msg:"请点击"
      		}
      	},
      	//方法还和原来一样。
      	methods:{
      		cli(){
      			this.msg = "您已经点击了"+this.n+++"下";
      		}
      	}
      })
      
    2. 主页面引用js文件进行测试

      <!DOCTYPE html>
      <html>
      	<head>
      		<meta charset="utf-8" />
      		<title></title>
      		<script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>
              <!-- 引用装有组件的js文件 -->
      		<script src="js/compontent.js" type="text/javascript" charset="utf-8"></script>
      	</head>
      	<body>
      		<div id="app">
      			<!-- 使用js文件里组件定义的标签 -->
      			<clibtn></clibtn>
      		</div>
      		
      		<script type="text/javascript">
      			var app = new Vue({
      				el:"#app"
      			})
      		</script>
      	</body>
      </html>
      
      • 结果:

        在这里插入图片描述

6.2、Vue组件注册

​ 通常情况下,我们可以将多个组件写到 1 个 js 文件中,作为基础组件库,在注册组件的时候,都需要一个 名字(以便后期使用)。

​ 调用 Vue 的内置函数 component 来注册一个全局组件:Vue.component(‘component-name’,{…}),其中 component 函数的第一个参数就是组件的名字,依据 W3C 规范自定义组建的名字应当避免与现有标签或未来会出现的标签名字重复。推荐使用全小写字母且必须包含一个’-'连接符

6.2.1、全局注册

全局注册在所有的 Vue 实例中都可以使用,全局注册使用 Vue.component 函数注册,我们在入门的例子中, 已经给大家展示过。

6.2.2、局部注册(基本不用)

局部注册的组件只能在 vue 实例内部使用,这里只给大家点到为止,因为在日常开发中,大多数都是使用全 局注册。

6.3、组件中的data函数

每个组件在被创建时,调用 data 函数时将返回 data 对象的一个新的实例。如果 data 是 JavaScript 对象, 则会返回该对象的引用(当 data 对象中的属性发生变化时,会影响到所有的该 Vue 组件的实例)。

上面代码中组件的data函数,自行理解。

6.4、向组件传数据

​ 在注册组件时,通过 props 可以定义参数,这些参数是组件内部 和 使用组件者 沟通的桥梁,就像 js 中 方法的参数一样(调用者可以传递参数到方法中,方法内部可以用这些参数)。

​ 可以理解为传递给组件的参数。使用 props 定义组件可接收的参数名,props 可定义参数类型和参数有效值等。

  • 任务5: 阅读下面案例,思考参数的传递。

    <div id="app">
        <news-item :news_title="news.title" :news_content="news.content"></news-item>
    </div>
    
    <template id="temp">
        <div>
            <h3 v-text="news_title"></h3>
            <p v-text="news_content"></p>
        </div>
    
    </template>
    
    <script type="text/javascript">
        Vue.component('news-item',{
            template:"#temp",
            //定义桥梁,用来在标签中引用vue实例中data的数据,有两种写法,下面会讲另一种
            props:['news_title','news_content']
        })
    
    
        var app = new Vue({
            el:"#app",
            data:{
                news: { 
                    title: "新闻标题", 
                    content: "就在美国对中国发动贸易战几个小时后,美国贸易代表处却在今日北京时间凌晨三点宣布说,那些会被贸易战影响的从中国进口产品的美国企业,可以有90天的时间向美国政府申请有效期为1年的“关税豁免”"
                }
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

    • props里也可以这样写

      props:['news-title','news-content']
      
    • 这样写的话,div里要这样写:news-title,在这里用newsTitle调用。

      <news-title :newsTitle="news.title" :newsTontent="news.content"></news-title>
      

6.5、prop的参数类型和拼接

​ prop 的类型可以是 Number 、Boolean 、Function、Object。

  • 任务6:点击a标签,跳转页面的路径中要出现正确的路径拼接

    <div id="app">
        <news-item :news_id="news.id" :news_title="news.title" :news_content="news.content"></news-item>
    </div>
    
    <template id="temp">
        <div>
            <a :href="'/home/'+news_id">
                <h3 v-text="news_title"></h3>
            </a>
            <p v-text="news_content"></p>
        </div>
    
    </template>
    
    <script type="text/javascript">
        Vue.component('news-item',{
            template:"#temp",
            //props定义数据的数据类型
            props:{
                "news_id":Number,
                "news_content":String,
                "news_title":String
            }
        })
    
    
        var app = new Vue({
            el:"#app",
            data:{
                news: { 
                    id:"123",
                    title: "新闻标题", 
                    content: "就在美国对中国发动贸易战几个小时后,美国贸易代表处却在今日北京时间凌晨三点宣布说,那些会被贸易战影响的从中国进口产品的美国企业,可以有90天的时间向美国政府申请有效期为1年的“关税豁免”"
                }
            }
        })
    </script>
    
    • 结果:

      在这里插入图片描述

  • 要注意标蓝色的超链接代码 的参数拼接,双引号中套单引号,要理解透彻;

  • 标红的参数类型,虽然可以指定,但无法真正起到限制作用,当我们给 1 个字符串时(比如:将 10 变 为 abc),只会在控制台中给你1个错误提示,如:

在这里插入图片描述

6.6、prop验证

​ 我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会 在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。 为了定制 prop 的验证方式,你可以为 props中的值提供:

  • 默认值

  • 验证

  • 语法示例:

    props: {
        //基础的类型检查 (null 匹配任何类型)
        propA: Number,
        // 多个可能的类型
        propB: [String, Number],
        // 必填的字符串
        propC: {
            type: String,
            required: true
        },
        // 带有默认值的数字
        propD: {
            type: Number,
            default: 100
        },
        // 带有默认值的对象
        propE: {
            type: Object,
            // 对象或数组且一定会从一个工厂函数返回默认值
            default: function() {
                return {
                    message: 'hello'
                }
            }
        },
        // 自定义验证函数
        propF: {
            validator: function(value) {
            // 这个值必须匹配下列字符串中的一个
                return [
                        'success',
                        'warning',
                        'danger'
                ].indexOf(value) !== -1
            }
        }
    }
    
    

    当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

注意:那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性(如 data、computed 等)在 default 或 validator 函数中是不可用的。

  • 任务 7:阅读下边代码,体会默认值和验证的使用。

    <div id="app">
        <news-item :news_id="news.id" :news_title="news.title" :news_content="news.content"></news-item>
    </div>
    
    <template id="temp">
        <div>
            <a :href="'/home/'+news_id">
                <h3 v-text="news_title"></h3>
            </a>
            <p v-text="news_content"></p>
        </div>
    
    </template>
    
    <script type="text/javascript">
        Vue.component('news-item',{
            template:"#temp",
            //props定义数据的数据类型
            props:{
                "news_id":{
                    //默认值
                    default:222
                },
                //没有任何限制约束
                "news_content":{},
                //自定义验证、标题字数不能小于4个字
                "news_title":{
                    validator: function(val){
                        return val.length >= 4;
                    }
                }
            }
        })
    
    
        var app = new Vue({
            el:"#app",
            data:{
                news: { 
                    title: "新闻标题", 
                    content: "就在美国对中国发动贸易战几个小时后,美国贸易代表处却在今日北京时间凌晨三点宣布说,那些会被贸易战影响的从中国进口产品的美国企业,可以有90天的时间向美国政府申请有效期为1年的“关税豁免”"
                }
            }
        })
    </script>
    

6.7、传递动态或静态prop

组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据。

​ 要让子组件使用父组件的数据,我们需要通过子组件的 props 选项。 prop 是单向绑定的:当父组件的属性 变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态。 每次父组件更新 时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变prop。

  • 任务 8:阅读下边的代码,注意从运行效果中观察动态传参和静态(固定值)传参。
<div id="app">
    <news-item v-bind:news_id="news.id" v-bind:news_title="news.title" v-bind:news_content="news.content"></news-item>
    <!--不加 v-bind 表示传入固定 字符串常量,加上 v-bind 表示 传入的 js 对象/表达式 -->
    <news-item v-bind:news_id="news.id" news_title="干翻老美" v-bind:news_content="news.content"></news-item>
    <news-item v-bind:news_id="news.id" v-bind:news_title="news.title" v-bind:news_content="news.content.substring(0,20)+'...'"></news-item>
    <news-item v-bind:news_id="120+10" v-bind:news_title="news.title" v-bind:news_content="news.content"></news-item>
</div>

<template id="temp">
    <div>
        <a :href="'/home/'+news_id">
            <h3 v-text="news_title"></h3>
        </a>
        <p v-text="news_content"></p>
    </div>

</template>

<script type="text/javascript">
    Vue.component('news-item', {
        template: "#temp",
        //props定义数据的数据类型
        props: {
            "news_id": {
                //默认值
                default: 222
            },
            //没有任何限制约束
            "news_content": {},
            //自定义验证、标题字数不能小于4个字
            "news_title": {
                validator: function(val) {
                    return val.length >= 4;
                }
            }
        }
    })


    var app = new Vue({
        el: "#app",
        data: {
            news: {
                title: "新闻标题",
                content: "就在美国对中国发动贸易战几个小时后,美国贸易代表处却在今日北京时间凌晨三点宣布说,那些会被贸易战影响的从中国进口产品的美国企业,可以有90天的时间向美国政府申请有效期为1年的“关税豁免”"
            }
        }
    })
</script>
  • 结果:

    在这里插入图片描述

6.8、组件插槽

父组件如何向子组件传递模板内容,大家有没有什么解决方法?

​ 对于这个需求,Vue 提供一种快捷便利的操作—slot 插槽标签。slot 插槽可以在组件中实现预留位置,方 便在父组件调用时向子组件指定位置传递不同内容,显示不同效果。

  • 基本语法如下:

    template:`
        <div>
        	<slot>插槽默认内容</slot>
        </div>
    `
    
  • 在使用时,需要在组件标签内部编写要传递的内容。父组件的内容会替换 slot 的默认值,如果不传递内容, 会显示子组件 slot 插槽默认内容。

    <组件名>内容</组件名>
    <组件名>内容</组件名>
    <组件名></组件名>
    
  • 任务 1:根据插槽语法,编写一个程序员必读书籍的插槽效果。

    <div id="app">
        <test-slot>《MySql从入库到跑路》</test-slot>
    </div>
    
    <script type="text/javascript">
        Vue.component('test-slot',{
            template:`
    <div>
    程序员必读书籍:<slot>《Java从入门到入土》</slot>
        </div>
    `
        })
    
        new Vue({
            el:"#app"
        })
    </script>
    
    • 结果:

      • 如果标签里没有输入内容:

        在这里插入图片描述

      • 如果输入内容:

        在这里插入图片描述

6.8.1、具名插值用法

如果一个组件内有多个插槽,我们该给指定插槽传递内容那?

  • 有时我们需要多个插槽。例如对于一个带有如下模板组件:

    <div id="app">
        <test-slot>
        	<!-- 《MySql从入库到跑路》 -->
            <!-- 《社交网络》 -->
            <!-- 《开关机》 -->
        </test-slot>
    </div>
    
    <script type="text/javascript">
        Vue.component('test-slot',{
            template:`
            <div>
                程序员必读书籍:<slot>《Java从入门到入土》</slot>
                程序员必看电影:<slot>《黑客帝国》</slot>
                程序员必会技能:<slot>《Ctrl+C Ctrl+v》</slot>
            </div>
        `
        })
    
        new Vue({
            el:"#app"
        })
    </script>
    
  • 我们想要对插件选择然后赋值显示,这样就没办法了,但是slot标签提供了一个name属性,我们可以根据name属性进行插值

    <div id="app">
        <test-slot>
            <template v-slot:book>《MySql从入库到跑路》</template>
            <template v-slot:movie>《社交网络》</template>
            <template v-slot:skill>《开关机》</template>
        </test-slot>
    </div>
    
    <script type="text/javascript">
        Vue.component('test-slot',{
            template:`
            <div>
                程序员必读书籍:<slot name="book">《Java从入门到入土》</slot>
                程序员必看电影:<slot name="movie">《黑客帝国》</slot>
                程序员必会技能:<slot name="skill">《Ctrl+C Ctrl+v》</slot>
            </div>
        `
        })
    
        new Vue({
            el:"#app"
        })
    </script>
    
    • 结果:

      在这里插入图片描述

6.8.2、具名插槽的缩写

跟 v-on 和 v-bind 一样,v-slot 也有缩写,可以把 v-slot: 替换为字符 #

例如:v-slot:header 可以被重写为 #header

  • 优化以上代码,代码如下:
<div id="app">
    <test-slot>
        <template #book>《MySql从入库到跑路》</template>
        <template #movie>《社交网络》</template>
        <template #skill>《开关机》</template>
    </test-slot>
</div>

<script type="text/javascript">
    Vue.component('test-slot',{
        template:`
        <div>
            程序员必读书籍:<slot name="book">《Java从入门到入土》</slot>
            程序员必看电影:<slot name="movie">《黑客帝国》</slot>
            程序员必会技能:<slot name="skill">《Ctrl+C Ctrl+v》</slot>
        </div>
    `
    })

    new Vue({
        el:"#app"
    })
</script>

6.9、单文件组件

在前面的学习的传统的组件中存在以下问题:

  • 全局定义的组件必须保证组件的名称不重复。
  • 字符串模板缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的\。
  • 不支持 CSS 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏。
  • 没有构建步骤限制,只能使用 HTML 和 ES5JavaScript,而不能使用预处理器

针对传统组件的问题,Vue 提供了一个解决方案——使用 Vue 单文件组件。单文件组件的文件扩展名为.vue。 文件内部组成结构包含三个部分:

  1. template 组件的模板区域。
  2. script 业务逻辑区域。
  3. style 样式区域。
  • 下面是一个文件名为 Hello.vue 的简单实例:

    在这里插入图片描述

    通过单文件组件,我们可以升级组件代码,构建简洁和功能更丰富的组件,提高生产力。但是大家会很好奇, 为什么单文件组件在 HTML 页面使用那么很不方便,我们还要学习。因为在企业项目开发中,它运用场景更多是 在 Vue 脚手架项目结构中。

解释script内的代码:

  • 原代码

    <script>
    	Vue.extend({
    		name:"vue-test",
    		data(){
    			return {
    				msg: "Vue单文件组件真香"
    			}
    		}
    	})
    </script>
    
  • 导出时候的代码【Vue.extend()可以省略】:

    <script>
    	export default {
    		name:"vue-test",
    		data(){
    			return {
    				msg: "Vue单文件组件真香"
    			}
    		}
    	)
    </script>
    

七、Vue 组件拓展

7.1、Vue调试工具的安装和使用

Vue-devtools是生态中不可获取的一部分,是开发过程中必不可少的 Vue 的调试神器,主要用于调试观察 组件之间的数据参数动向,其高效、简洁、方便的特点可以极大程度的提高我们的开发效率,深受 vue 开发者的 喜爱。这里使用 Chrome 浏览器进行安装、配置和调试为主。

​ Vue-devtools 安装有两个主流方式,一种是官方方式,较为复杂繁琐,一种是使用市面上封装安装包,操 作简单明了。这里以后一种为主,前一种安装方式大家可以在扩展中自学。

  • Vue-devtools 安装与使用步骤具体操作如下:

    1. 谷歌浏览器搜拓展迷,或点击网址:https://www.extfans.com/all/搜索vue或devtools进行下载。

      在这里插入图片描述

    2. 安装好以后,为压缩包

      在这里插入图片描述

    3. 这时候我们点击chrome的右上角省略号,点击更多程序->扩展程序,就会进入如下页面(默认为空)。

      在这里插入图片描述

    4. 这时候我们把压缩包直接拉进里面就可以了,如果不行的话,就解压了再拉进去

      在这里插入图片描述

    5. 这时候我们就添加成功了。

      在这里插入图片描述

      如果出现chrome无法从该网站添加应用、扩展程序和用户脚本提示,那么我们打开一个新的标签页,打开这个路径:chrome://flags/#extensions-on-chrome-urls,把第一个的值Disabled改为Enabled重启浏览器就可以了

    6. 添加成功以后,我们可以编写一个简单的vue测试,然后用chrome浏览器打开

      <div id="app">
          {{n}}
          <button type="button" @click="add()">+</button>
      </div>
      
      <script type="text/javascript">
          new Vue({
              el:"#app",
              data:{
                  n:1
              },
              methods:{
                  add(){
                      this.n++;
                  }
              }
          })
      </script>
      
    7. 浏览器打开后,我们按F12打开“开发者模式”,找到vue。

      在这里插入图片描述

    8. 在这里面就可以对vue进行调试操作了。

      在这里插入图片描述

八、Vue-ClI

8.1、什么是 Vue-CLI?

​ Vue-CLI 是 Vue 官方提供的一种基于 Vue.js 进行快速开发的完整系统,可以快速搭建 Vue 的项目模板。 使用 Vue 脚手架开发项目有以下好处:

  • 统一的目录结构
  • 便捷的插件安装
  • 图形化界面操作

8.2、Vue-CLI 准备和安装

8.2.1、安装 Node.js
  • 为什么安装 Node?

    因为 Vue 脚手架的安装、启动、打包和后续项目中插件的安装都这里需要 node 的其中的 npm 包管理工 具。可以说 Node.js 是 Vue-CLI 开发的基石。

  • 在日常项目开发中,npm有以下的常见使用场景:

    • 允许用户从 NPM 服务器上下载并安装别人编写的命令行程序到本地使用。
    • 允许用户将自己编写的包或命令行程序上传到 NPM。
  1. 官网下载 Node.js 安装包: 下载 | Node.js 中文网 (nodejs.cn)

  2. 安装,基本无脑下一步

  3. 安装完毕,测试node

    • node -v测试 node 是否安装成功以及检查node版本。

    • npm -v 测试npm是否安装成功以及检查npm版本。

      在这里插入图片描述

  4. 配置 npm 下载插件的默认安装目录和缓存日志目录

    注意:因为有的同学电脑权限不足,可以提前先做安装目录中创建 node_global 和 node_cache 文件夹。

    打开 cmd 窗口,依次输入配置命令,盘符自己选。

    • npm config set prefix “D:\software\nodejs\node_global”

    • npm config set cache “D:\software\nodejs\node_cache”

      在这里插入图片描述

  5. 配置 node 所需环境变量

    • 进入环境变量对话框,在【系统变量】下新建【NODE_PATH】,值是node安装目录下的node_global中node_modules 的路径【D:\software\nodejs\node_global\node_modules】。

      • 注意:这里需要自己的在第二步选择的位置进行配置

        在这里插入图片描述

    • 将【用户变量】中的【Path】添加【D:\software\nodejs\node_global】

      • 注意:这里需要自己的在第二步选择的位置进行配置

        在这里插入图片描述

  6. 安装国内淘宝镜像

    • 我们通过 npm 命令下载 node 插件的时候因为访问的是国外网站,所以可能会出现下载的很缓慢或者干脆是直 接下载失败,在这种情况下,我们可以通过配置国内镜像来解决,一般配置的是淘宝 npm 镜像 cnpm。

    • 安装命令:

    npm install -g cnpm --registry=https://registry.npm.taobao.org
    
    • 测试命令:
    cnpm -v
    

    在这里插入图片描述

8.2.1、安装 Vue 脚手架

脚手架安装命令

  • npm install -g @vue/cli (国外)

  • cnpm install -g @vue/cli (国内,因为我们安装了淘宝的镜像,所以我们用这个)

    在这里插入图片描述

如果出现外部指令什么的,我们需要关闭原来命令提示符,然后再打开,因为我们配置环境变量后,需要重新启动这个才能有用

  • 测试命令:

    vue -V(这里是大写的 V

    在这里插入图片描述

  • 卸载命令

    npm uninstall -g @vue/cl
    

到这为止,Vue 脚手架已经安装完成了。但是有两个概念需要区分开:

  • 我们学习的 Vue 版本的 2.X,脚手架的版本是 5.0.4
  • Vue 可以理解为中式建筑风格,经过更新升级,现在是 2.X 版本
  • Vue 脚手架可以理解为盖房包工队,也在不断改造,现在是 5.0.4

九、CLI创建项目

Vue 脚手架创建项目有两种,一种是命令行创建方式,一种是图形化界面方式

  • 第一种命令行创建方式:

    1. 创建 Vue 项目所在文件夹

    2. 在文件夹中打开 cmd

      在这里插入图片描述

    3. 创建 vue 项目

      • 创建命令:vue create 项目名

      • 命令行操作:空格是选中或取消、方向键选择、A 是全选、回车是下一步

        在这里插入图片描述

      • 这里我们输入n,回车,然后Vue2(挺流行的),然后回车

        在这里插入图片描述

      • 等待创建完成

        在这里插入图片描述

    4. 创建完成

      在这里插入图片描述

    5. 安装结束,测试运行

      • 进入项目目录:cd 项目名

      • 启动开发服务:npm run serve

      • 停止(停止的时候用):ctrl+c

        在这里插入图片描述

    6. 打开浏览器,访问:localhost:8080,然后看到如下界面就代表成功了。

      在这里插入图片描述

  • 第二种图形化界面创建方式:

    1. 先连按两下ctrl+c,再按一下关闭上面启动的vue项目。

      在这里插入图片描述

    2. 创建 Vue 项目所在文件夹(上面创建过了)。

    3. 在文件夹中打开 cmd

    4. 输入命令打开图形化界面

      • 命令:vue ui

        在这里插入图片描述

    5. 这时候会自动打开一个页面,我们点击项目->在此创建新项目

      在这里插入图片描述

    6. 输入项目名,其他保持默认,然后点击下一步

      在这里插入图片描述

    7. 依旧选择vue2,创建项目

      在这里插入图片描述

    8. 第一次创建有点慢,耐心等待,完成以后如下图:

      在这里插入图片描述

    9. 测试运行:

      • 点击【任务】

      • 点击【serve】

      • 查看仪表盘状态

      • 绿色通过点击【启动 app】

      • 红色报错点击【输出】

        在这里插入图片描述

    10. 点击启动app后,就会跳到如下页面:

      在这里插入图片描述

9.1、项目结构解读

我们开始解析 Vue 脚手架项目的项目结构,熟悉结构才能更快速的开发。

在这里插入图片描述

在这里插入图片描述

9.1、项目修改测试

  • 先解读一下App.vue

    在这里插入图片描述

  • 我们将图片替换成自己导入的图片,并注释掉 HelloWorld 组件的使用

    在这里插入图片描述

  • 保存代码并刷新页面之后,页面显示的正是我们的更换的图片:

    在这里插入图片描述

通过上边的操作,大家会有一个感觉,Vue 脚手架项目并不是那么难。当然也需要我们不断的去熟悉项目结构。

十、Vuex

10.1、概述

10.1.1、组件之间共享数据的方式

在组件开发章节中,我们已经学习了组件的使用和组件之间进行数据的传递,这里我们做一下简单回顾:

  • 父向子传值:使用 v-bind 属性绑定,在子组件上绑定参数,传递数据
  • 子向父传值:使用 v-on 事件绑定,在子组件上绑定自定义事件,传递数据
  • 兄弟键传值:创建事件中心
    • 接受数据的组件:$on
    • 发送数据的组件:$emit

需要清楚的认识到,这三种方式不仅仅操作繁琐,而且比较适合于小范围内的数据共享,如果 Vue 应用程序 的数据共享数据需要大范围、使用比较频繁的话,这三种方式就有些捉襟见肘了。Vue 提供了一套较为成熟的解决方案------Vuex。

10.1.2、Vuex是什么

​ 在官方定义中,Vuex 是一个专门服务 Vue.js 应用程序的状态(数据)管理模式。它采用集中式存储管理应 用的所有组件的状态(数据),可以方便的实现组件之间的数据的共享。下图是 Vue 官方给出的 Vuex 理念示意图。

在这里插入图片描述

思考:什么是状态(数据)管理模式?如何把握住图中每个点表示的含义?

​ 所谓状态(数据)管理模式,简单的说就是,将多个组件(Vue Components)共享的变量全部存储在一个对 象里面。然后,将这个对象放在顶层的 Vue 实例中,让全局下的其他组件可以使用,多个组件就可以实现共享这 个对象中的所有变量属性。

​ 状态管理模式就是数据管理模式,Vuex 就是一个提供可以在多个组件间共享状态的插件,它可以理解为是 Vue 的数据库,可以存储全局的共享数据,总之就一句话,用它就对了。

  • 我们用下图来对比一下,Vuex 和之前学习的三种方式之间的区别。

    在这里插入图片描述

    • 左图是不使用 Vuex 进行状态管理的情况,如果要从最下一层的紫色组件向其他传递数据的话,不仅操作比较繁 琐,涉及面会很广,也不便于后期维护。
    • 在使用 Vuex 进行状态管理时,只需要共享一个 Store 对象,组件将需要共享的数据存入 Store 对象中,其他组件使用时直接从 Store 对象将数中读取即可,不需要再牵扯其他组件。
  • 思考:什么类型的数据适合存储到 Vuex 中?

    ​ 一般情况下,只有组件之间共享的数据,才有必要存储到 Vuex 中;对于组件中的私有数据,依旧存储在组件自 身的 data 中即可。当然,在实际开发中,我们需要具体情况具体分析。

10.2、Vuex的安装

这里我们采用的 Vue 脚手架项目开发,根据以下情况来完成 Vuex 的初始化。

  • 第一种:创建项目时选择安装 Vuex
  • 第二种:已存在项目安装配置 Vuex

我们这里只演示创建项目时安装。

  1. 我们使用vue ui命令创建项目,在项目所在文件夹cmd然后输入命令。

    在这里插入图片描述

  2. 然后就会自动打开可视化视图。

  3. 我们这里选择创建->在此创建项目。

    在这里插入图片描述

  4. 输入项目名称

    在这里插入图片描述

  5. 我们这里选择手动配置项目

    在这里插入图片描述

  6. 打开vuex配置,然后点击下一步

    在这里插入图片描述

  7. 进行配置,然后创建项目

    在这里插入图片描述

  8. 把配置保存为预设

    在这里插入图片描述

  9. 项目创建成功后,我们把项目运行

    在这里插入图片描述

  10. 启动App

    在这里插入图片描述

  11. 启动完后,我们把项目导入到Hbuilder中,可以发现,比普通的CLI项目多了几个文件

    在这里插入图片描述

  12. 到目前为止,就代表创建成功了。如果创建项目的时候出现Cannot read properties of undefined (reading 'indexOf')报错,那么我们就更换网络重新创建,大部分都是网络问题。

10.3、Vuex核心概念

Vuex的核心概念有以下几个:

  • State
  • Mutation
  • Action
  • Getters
1、State学习

​ state 是 Vuex 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 对象的 State 中进行存储,State 是一个“唯一数据源”。

//创建 store 数据源,提供唯一公共数据访问渠道
export default new Vuex.Store({
    //State 提供唯一的公共数据源,统一存放所有共享数据
    state: {
        //该数据将全局共享
        数据变量名 : 值
    }
})

注意,在 state 中:

  • state 是全小写
  • 数据的格式是:数据变量名:值
  • 多个之间使用逗号隔开

接下来,我们会通过一个全局的案例来串联四个常用属性。

案例示意图如下:

在这里插入图片描述

  1. 在 components 中创建 AddComponent 组件

    <template>
    	<div>
    	  <h1>全局变量num的值为:??</h1>
    	  <button type="button">num++</button>
    	</div>
    </template>
    
    <script>
    	export default {
    		name:"AddComponent"
    	}
    </script>
    
    <style>
    </style>
    
  2. 在 components 中创建 SubComponent 组件

    <template>
    	<div>
    	  <h1>全局变量num的值为:??</h1>
    	  <button type="button">num--</button>
    	</div>
    </template>
    
    <script>
    	export default {
    		name:"SubComponent"
    	}
    </script>
    
    <style>
    </style>
    
  3. 在 App.vue 页面使用 AddComponent 和 SubComponent 组件

    <template>
      <div id="app">
    	  <AddComponent></AddComponent>
    	  <hr />
    	  <SubComponent></SubComponent>
      </div>
    </template>
    
    <script>
    //导入
    import AddComponent from "./components/AddComponent.vue";
    import SubComponent from "./components/SubComponent.vue";
    
    export default {
      name: "App",
      components: {
    	//注册
        AddComponent,
    	SubComponent
      },
    };
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    

    注意:在使用其他组件时,不要忘了导入、注册后再使用

  4. 打开store文件夹下index.js文件,改造代码

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
    	  num:0
      },
      getters: {},
      mutations: {},
      actions: {},
      modules: {},
    });
    
  5. 把数据放到共享文件夹上了,其他文件就要读取数据了,读取的数据有两种:

    • 组件访问 state 中数据的第一种方式:

      this.$store.state.全局数据名称
      
    • 组件访问 state 中数据的第二种方式:使用 mapState 函数

    • 第一步:按需从Vuex中导入mapState函数

      import {mapState} from 'vuex'
      //注意 vuex 全小写 mapState 是小驼峰命名方式
      
    • 第二步:映射 mapState 为 computed 计算属性, 需要将刚才导入的 mapState 函数,映射为当前组件的 computed 中的计算属性,将需要的全局数据注入到当前组件中。

      computed:{
      	...mapState(['数据变量名 1','数据变量名 2'])
      }
      
    • 注意:这里的…是展开运算符,会将组件需要的全局数据映射为当前组件的计算属性。简单来说,通过这一步 操作,可以将 state 的数据复制到计算属性中,我们就可以在组件中使用了。

  6. 使用第一种方法改造AddComponent组件

    <template>
    	<div>
    	  <h1>全局变量num的值为:{{ $store.state.num }}</h1>
    	  <button type="button">num++</button>
      </div>
    </template>
    
    <script>
    	export default {
    		name:"AddComponent"
    	}
    </script>
    
    <style>
    </style>
    
  7. 这时候我们打开网页,查看效果,看看是否引入成功(如果报错,请查看[取消运行时代码检查]那一节)

  8. SubComponent组件使用第二种方式改造

    <template>
      <div>
        <h1>全局变量num的值为:{{ num }}</h1>
        <button type="button">num--</button>
      </div>
    </template>
    
    <script>
    //导入
    import { mapState } from "vuex";
    
    export default {
      name: "SubComponent",
      computed: {
        ...mapState(["num"]),
      },
    };
    </script>
    
    <style></style>
    
  9. SubComponent组件也进行修改后,结果如下:

    在这里插入图片描述

  10. 数值已经显示出来了,接下来该按钮的点击事件了。

2、Mutation 学习

Mutation 对象是 Vuex 对象中,唯一可以直接修改 state 的数据的对象

  • 只能通过 mutations 里的操作来变更 state 数据,不允许组件直接操作 state 中的数据。
  • 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。方便后期维护。

语法如下:

//创建 store 数据源,提供唯一公共数据访问渠道
export default new Vuex.Store({
    //State 提供唯一的公共数据源,统一存放所有共享数据
    state: {
        //该数据将全局共享
        数据变量名 :},
    mutations: {
        //用于全局的事件处理函数
        //函数的第一个形参是 state,用于方便调用 state 数据
        //其他参数 n 放置在 state 后面
        mu1(state){//函数体},
        mu2(state,n){ //函数体}
    }
})

注意:

  • 这里要注意 mutations 的写法,因为会有多个修改(默认自动生成)
  • 为了方便调用修改 state 中的数据,所以方法的第一个形参都是 state 对象。其他参数 n 放在 state 形参后面。
  • 其他参数 n 可以是普通变量(数字、字符串、布尔值等)、数组、对象等,而且参数名可以自定义。

按钮点击功能实现如下:

  1. 根据 mutations 基本用法,创建 num 变量加一和加 N 还有减一的操作。

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
        num: 0,
      },
      getters: {},
      mutations: {
    	  addNum(state){
    		  state.num++;
    	  },
    	  addNumN(state,n){
    		  state.num += n;
    	  },
    	  subNum(state){
    		  state.num--;
    	  }
      },
      actions: {},
      modules: {},
    });
    
  2. 方法定义好以后,我们需要在其他组件中调用mutations里写好的方法,调用的方法有两种,这里一个组件里用一种方法。

    • Vuex 提供了两种调用 mutations 中方法的方式。

      • 调用 mutations 中方法的第一种方式:在组件的方法中直接使用

        this.$store.commit('函数',参数);
        
      • 调用 mutations 方法的第二种方式:使用 mapMutations 函数

        • 第一步按需从 Vuex 中导入 mapMutations 函数

          import {mapMutations} from 'vuex'
          
        • 第二步、映射 mapMutations 函数映射为组件的 methods 中的方法 需要将刚才导入的 mapMutations 函数,映射为当前组件的 methods 中的方法,将需要使用的方法注入到当前组件中。

          methods:{
          	...mapMutations(['函数 1','函数 2']), 
              事件函数(){
                  //直接调用映射的函数
                  this.函数 1(参数);
              }
          }
          
    • 注意:

      • 这里的操作同 mapState 操作类似,都可以理解为将需要的内容从 Vuex 中复刻到当前组件中,组件 就可以根据需求通过 this 来调用数据或者方法。
      • 调用函数传递参数需要注意以下几点:
        • state 参数调用时不需要传,函数内部会自动传入。
        • 其他参数可以是基本值(数字、字符串、布尔值等)、数组、对象等,需要根据实际情况传入。
  3. 使用第一种方法在AddComponent组件中调用方法

    <template>
      <div>
        <h1>全局变量num的值为:{{ $store.state.num }}</h1>
        <button type="button" @click="add()">num++</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "AddComponent",
      methods: {
        //注意:组件里的方法不要和store中mutation中定义的方法名一样,否则会报错
        add() {
          this.$store.commit("addNum");
        },
      },
    };
    </script>
    
    <style></style>
    
    • 结果:

      在这里插入图片描述

  4. 在AddComponent组件中调用addNumN()方法

    <template>
      <div>
        <h1>全局变量num的值为:{{ $store.state.num }}</h1>
        <button type="button" @click="add()">num++</button>
    	<br />
    	<h1>全局变量num的值为:{{ $store.state.num }}</h1>
    	<button type="button" @click="addN()">num+N</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "AddComponent",
      methods: {
        add() {
          this.$store.commit("addNum");
        },
    	addN(){
            //后面的10为addNumN里的参数n
    		this.$store.commit("addNumN",10);
    	}
      },
    };
    </script>
    
    <style></style>
    
    • 结果:

      在这里插入图片描述

  5. 使用第二种方式实现num--按钮的点击事件

    <template>
      <div>
        <h1>全局变量num的值为:{{ num }}</h1>
        <button type="button" @click="sub()">num--</button>
      </div>
    </template>
    
    <script>
    //导入
    import { mapState, mapMutations } from "vuex";
    
    export default {
      name: "SubComponent",
      methods: {
        ...mapMutations(["subNum"]),
        sub() {
          this.$store.commit("subNum");
        },
      },
      computed: {
        ...mapState(["num"]),
      },
    };
    </script>
    
    <style></style>
    
    • 结果:

      在这里插入图片描述

3、Action学习

​ 在日常开发过程中,会从服务器端异步请求获取当然组件全局操作的数据。也意味着 state 中的数据并非一开始 设定好的,而是需要异步请求后再设置的。Vuex 提供专门用于异步操作的对象 Action

​ Action 是专门用于处理异步任务的,例如:异步数据请求、定时任务等。如果需要异步操作变更数据,必须通 过 Action,而不能直接使用 Mutation 或者 State。

而且重点是,在 Action 中对 State 的操作仍然需要通过触发 Mutation 方法的来变更数据。

注意:

  • Mutation 只能执行同步操作,可以操作 State 数据
  • Action 只能执行异步操作,必须通过触发 Mutation 来变更 State 数据

Action基本语法:

actions:{
    异步函数(context,param){
        //调用 mutations 中的方法
        context.commit('mutation 方法',参数)
    }
}

注意:

  • 在 actions 中,不能直接修改 state 中的数据,必须使用 mutations 对象中的方法。

  • context 参数是为了方便调用 mutations 中的方法,调用异步操作方法时不需要传。

  • param 是调用异步操作方法需要传入的参数,可以是基本值(数字、字符串、布尔值等)、数组、对象等, 参数名可以自定义。

  • 根据 actions 基本用法,使用定时器模拟异步任务,完成三秒之后 num 变量加 N 操作

    在这里插入图片描述

  1. 在actions中编写倒计时修改num的值

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
        num: 0,
      },
      getters: {},
      mutations: {
        addNum(state) {
          state.num++;
        },
        addNumN(state, n) {
          state.num += n;
        },
        subNum(state) {
          state.num--;
        },
      },
      actions: {
    	  asyncAdd(context,n){
    		  setTimeout(()=>{
    			  context.commit("addNumN",n);
    		  },3000)
    	  }
      },
      modules: {},
    });
    
    • Vuex 提供了两种调用 actions 中异步方法的方式。

      • 调用 actions 中方法的第一种方式:在组件的方法中直接使用

        this.$store.dispatch('异步函数名',参数);
        
      • 调用 actions 方法的第二种方式:使用 mapActions 函数

        • 第一步按需从 Vuex 中导入 mapActions 函数:

          import {mapActions} from 'vuex'
          
        • 第二步、映射 mapActions 函数映射为组件的 methods 中的方法: 需要将刚才导入的 mapActions 函数,映射为当前组件的 methods 中的方法,将需要使用的方法注入到当前组件中。

          methods:{
              ...mapActions(['函数 1','函数 2']), 
              事件函数(){
                  //直接调用映射的函数
                  this.函数 1(参数);
              }
          }
          
    • 注意:这里的操作同 mapMutations 操作类似,就不赘述了。

  2. AddComponent组件中调用定义的异步倒计时(使用第一种方式)

    <template>
      <div>
        <h1>全局变量num的值为:{{ $store.state.num }}</h1>
        <button type="button" @click="add()">num++</button>
        <br />
        <h1>全局变量num的值为:{{ $store.state.num }}</h1>
        <button type="button" @click="addN()">num+N</button>
        <br />
        <button type="button" @click="async()">3秒后num增加5</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "AddComponent",
      methods: {
        add() {
          this.$store.commit("addNum");
        },
        addN() {
          this.$store.commit("addNumN", 10);
        },
        async() {
          this.$store.dispatch("asyncAdd", 5);
        },
      },
    };
    </script>
    
    <style></style>
    
    • 结果:

      在这里插入图片描述

  3. 第二种方法就不再演示了,和上面一样。

4、Getter 学习

Getter 用于对 Store 中的数据进行加工处理形成新的数据;

  • Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性;
  • Store 中数据发生变化,Getter;
  • Getter 不会修改 state 里面的数据,只是包装作用。

定义Getter:

getters:{
    包装函数(state){
    	return '对 state 参数包装后的内容'
    }
}

注意:

  • 在 getters 中,只是对 state 中的数据的包装,并非直接修改 state 数据;
  • 形参中 state 参数类似 mutation 方法操作,为了方便调用 state 数据;
  • 方法中必须使用 return 关键字返回包装后内容。

根据 getters 基本用法,将页面中的“当前全局参数 num 的值是:”改造成 getter 写法。

  1. 在getters中添加方法,返回内容

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
        num: 0,
      },
      getters: {
    	  getNum(state){
    		  return "全局参数num变量的值为:" + state.num;
    	  }
      },
      mutations: {
        addNum(state) {
          state.num++;
        },
        addNumN(state, n) {
          state.num += n;
        },
        subNum(state) {
          state.num--;
        },
      },
      actions: {
        asyncAdd(context, n) {
          setTimeout(() => {
            context.commit("addNumN", n);
          }, 3000);
        },
      },
      modules: {},
    });
    
    • Vuex 提供了两种调用 getters

      • 调用 getters 中方法的第一种方式

        this.$store.getters.包装函数名;
        
      • 调用 getters 方法的第二种方式:使用 mapGetters 函数:

        • 第一步按需从 Vuex 中导入 mapGetters 函数

          import {mapGetters} from 'vuex'
          
        • 第二步、映射 mapGetters 函数映射为 computed 计算属性,需要将刚才导入的 mapGetters 函数,映射为当前组件的 computed 中的计算属性,然后就可以在组件中直接 使用了。

          computed:{
          	...mapGetters(['函数 1','函数 2'])
          }
          
    • 注意:这里的操作同 mapState 操作类似,就不赘述了。

  2. 分别在AppComponent和SubComponent中调用getters中方法(此处都用第一种方式调用)

    <template>
      <div>
        <h1>{{$store.getters.getNum}}</h1>
        <button type="button" @click="add()">num++</button>
        <br />
        <h1>{{$store.getters.getNum}}</h1>
        <button type="button" @click="addN()">num+N</button>
        <br />
        <button type="button" @click="async()">3秒后num增加5</button>
      </div>
    </template>
    
    <script>
    export default {
      name: "AddComponent",
      methods: {
        add() {
          this.$store.commit("addNum");
        },
        addN() {
          this.$store.commit("addNumN", 10);
        },
        async() {
          this.$store.dispatch("asyncAdd", 5);
        },
      },
    };
    </script>
    
    <style></style>
    
    • SubComponent中的修改就不展示了,结果如下:

      在这里插入图片描述

  3. 当我们修改getters方法中的返回值时,这三个组件显示的内容将都会一起改变。

5、总结

本章主要学习 Vuex 状态管理的基本操作。主要学习了 Vuex 的安装、配置和 Store 对象中的四大属性 State、 Mutation、Action、Getter 创建和调用。其中我们可以将四个属性分为两类:

1、 数据的基本操作

  • State:唯一的存储数据的公共数据源
  • Getter:类似计算属性,用于包装 State 数据,只包装,不修改

2、方法的基本操作

  • Mutation:同步数据操作方法,唯一可以直接操作 State 数据的
  • Action:异步数据操作方法,异步操作只能写在 Action 中,而且也不能直接修改 State 必须通过 Mutation。 对于四个属性的调用,都有两种,大家需要根据自身的需求使用。

6、取消运行时代码检查

当我们写完代码想要在页面查看效果的时候,会发现仪表盘报错,像如下这样:

在这里插入图片描述

这是因为创建项目的时候,默认开启了代码自动检查功能,有时候不是代码的错误,可能因为格式的错误报错不显示,很麻烦,有三种解决办法:

  • 方法一:

    1. 点击lint进行检查并修复文件,但有会造成代码修改

      在这里插入图片描述

    2. 这时候我们回到页面,(如果没有代码上的错误)就可以发现显示出来结果了。

  • 方法二:我们找到vue.config.js文件,添加设置

    在这里插入图片描述

修改完配置后,我们必须重新运行才能生效

  • 方法三:第三种方法就是我们上面创建项目的时候,取消勾选的line on serve选项了。

十一、Vue Router

11.1、Vue Router路由

11.1.1、概述

​ 在网络发达的现在,我们可以使用电脑、手机等多种电子设备,打开浏览器,浏览不同网站,访问网站的各 位链接信息。就在访问、操作网站的过程中,我们就在无形的使用路由这个技术。路由就是指根据不同的页面 URL 请求,分配到对应的处理程序的技术。

通常,路由分为前端路由和后端路由:

  • 后端路由:对于普通的网站,所有的超链接都是 url 地址,所有 url 都对应服务器上对应的资源。
  • 前端路由:主要通过 url 的 hash(#)和组件的关联,实现页面的切换,更加方便的实现了 SPA 单页面应用程序,说白了就是根据不同的路径显示不同的页面。

可以发现,不管是后台路由还是前端路由,都是一种映射关系

  • 后台路由是请求地址(方式)和处理函数之间的映射关系。
  • 前端路由是 hash 地址和组件之间的映射关系。
11.1.2、什么是 Vue Router

Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。

​ SPA 单页面应用指的是,整个网站只需要有一个页面,当 Vue 感知 URL 变化时,会动态的把当前的页面内容 清除掉,再把下一个页面的内容挂载到页面上。此时的路由就不是后端来做了,而是前端来做,判断页面到底显 示哪一个组件,再把以前的组件清除掉使用新的组件。就不会每一次跳转都请求 HTML 文件。

Vue 实现的单页面应用,页面组件片段切换快,用户体验好。

11.2、基本用法

基本使用步骤
  1. 依旧和上面一样,在vue项目文件夹路径输入cmd输入vue ui使用可视化创建项目的时候导入vue Router就可以使用了。

  2. 创建项目的时候选择Router选项。

    在这里插入图片描述

  3. 选择和上面步骤一样的配置,这里关闭代码检查。

    在这里插入图片描述

  4. 运行项目,启动app

    在这里插入图片描述

  5. 启动后,我们把项目导入到Hbuilder中,进行编辑,可以发现我们项目中多了些文件。

    在这里插入图片描述

  6. 到此为止我们项目创建就完成了。

  • 项目创建完成后,我们编写一个测试项目,页面顶端有两个a标签,点击每个链接下面会出现不一样的图片的效果

    • 效果图:

      在这里插入图片描述

  1. 我们首先随便找两张图片放到assets文件夹中

    在这里插入图片描述

  2. views文件夹里分别创建ChengShiComponent、XingKongComponent两个组件,内容如下:

    <template>
    	<div>
    		<img src="../assets/1.jpg"/>
    	</div>
    </template>
    
    <script>
    	export default {
            //一般和文件名一样就行
    		name:"ChengShiComponent"
    	}
    </script>
    
    <style>
    </style>
    
  3. 另一个组件也一样,只不过图片和name值不一样,这里就不展示了。

  4. 编写完两个组件后,我们在router->index.js文件里配置路由。

    import Vue from "vue";
    import VueRouter from "vue-router";
    //这个是自带的,没用,我们删掉就可以了。
    //import HomeView from "../views/HomeView.vue";
    
    //导入资源
    import ChengShiComponent from "../views/ChengShiComponent.vue";
    import XingKongComponent from "../views/XingKongComponent.vue";
    
    Vue.use(VueRouter);
    
    const routes = [
      // 配置资源路径,path:路径	component:导入的组件名 还有name属性,后面会讲
      {
        path: "/chengshi", // 第一路径前面必须带/
        component: ChengShiComponent,
      },
      {
        path: "/xingkong", // 第一路径前面必须带/
        component: XingKongComponent,
      }
      
    ];
    
    const router = new VueRouter({
      routes,
    });
    
    export default router;
    
  5. 配置完路径后,我们就需要修改App.vue文件进行调用路由

    <template>
      <div id="app">
        <nav>
    	  <!-- router-link相当于a标签,to相当于href属性 -->
          <router-link to="/chengshi">城市</router-link> |
          <router-link to="/xingkong">星空</router-link>
        </nav>
    	
    	<!-- 引用的路由显示的地方 -->
        <router-view />
      </div>
    </template>
    
    <style>
        #app {
          font-family: Avenir, Helvetica, Arial, sans-serif;
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
          text-align: center;
          color: #2c3e50;
        }
    
        nav {
          padding: 30px;
        }
    
        nav a {
          font-weight: bold;
          color: #2c3e50;
        }
    
        nav a.router-link-exact-active {
          color: #42b983;
        }
    </style>
    
  6. 编写完App.vue文件后,我们就可以回到页面查看结果了。

    在这里插入图片描述

  7. 点击城市或者星空的时候,下面就会显示对应的照片

    • 城市:

      在这里插入图片描述

    • 星空:

      在这里插入图片描述

  8. 这时候,我们发现图片太大,看起来很麻烦,这时候我们可以修改图片样式

  9. App.vue文件内下面的style内,添加图片样式就可以了。

    img {
    	width: 50%;
    	height: 50%;
    }
    
    • 结果:

      在这里插入图片描述

  10. 当我们点击不同的a标签的时候,下面的图片会改变,路径也会进行改变,路径就是我们在路由内设置的path

    在这里插入图片描述

11.3、路由重定向

​ 页面打开时,其实也是有一个链接地址\,现在我们希望默认地址可以指向其他链接,要实现这个功能, 我们需要用到路由重定向。路由重定向指的是,用户在访问地址 A 的时候,强制用户跳转到地址 B,从而展示特定的组件页面。

​ Vue 实现路由重定向是通过路由规则中的redirect 属性,为 path 指定一个新的路由地址,这样就完成了 路由重定向的设置。

routes: [
    {path : '/A 地址',redirect : '/B 地址'},
    {path : '/B 地址', component : 'B 组件'}
]
  • 任务1:实现打开网页默认显示城市图片,访问城市的路由,代码如下:

    import Vue from "vue";
    import VueRouter from "vue-router";
    //这个是自带的,没用,我们删掉就可以了。
    //import HomeView from "../views/HomeView.vue";
    
    //导入资源
    import ChengShiComponent from "../views/ChengShiComponent.vue";
    import XingKongComponent from "../views/XingKongComponent.vue";
    
    Vue.use(VueRouter);
    
    const routes = [
      // 配置资源路径,path:路径	component:导入的组件名 还有name属性,后面会讲
      {
    	// /为默认路径	redirect为重定向,意思为当访问localhost:8080的时候,会默认访问'/chengshi'路由,显示城市图片
        path: "/",
        redirect: "/chengshi",
      },
      {
        path: "/chengshi", // 第一路径前面必须带/
        component: ChengShiComponent,
      },
      {
        path: "/xingkong", // 第一路径前面必须带/
        component: XingKongComponent,
      }
      
    ];
    
    const router = new VueRouter({
      routes,
    });
    
    export default router;
    

11.4、嵌套路由

​ 在前面的章节中,我们学习了组件的开发和应用。在实际应用开发界面,通常由多层嵌套的组件组合而成。 比如,我们登录组件中,需要嵌套着立即注册和忘记密码操作。

在这里插入图片描述

​ 这里就需要使用到 Vue 路由中的嵌套路由操作,嵌套路由指的是通过路由规则的层级嵌套,可以在页面上展示出复杂的组件结构关系。

用法:

嵌套路由需求分析:

  • 点击父级路由链接显示模板内容
  • 模板内容中又有子级路由链接
  • 点击子级路由链接显示子级模板内容

实现思路:

  • 在父路由组件中添加子路由链接和路由占位符
  • 在父路由规则中,通过 children 属性,添加子路由规则

代码步骤:

  • 第一步:在父路由规则中,通过 children 属性,添加子路由规则

    routes: [
        {
            path : '/父 path 地址', component : 父组件,
            //children 数组表示子路由规则
            children : [
                {path : '/子 path 地址 1',component : 子组件 1},
                {path : '/子 path 地址 2',component : 子组件 2}
            ]
        }
    ]
    
  • 第二步:在父路由组件中添加子路由链接和路由占位符

    <template>
      <div>
        <img src="../assets/1.jpg" />
    	<br />
         <!-- 路径一定要包含父路由的路径,要完整 -->
        <router-link to="/父路由/子路由">a标签内容</router-link> |
        <router-link to="/父路由/子路由">a标签内容</router-link>
    
        <!-- 显示引用路由的位置 -->
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: "ChengShiComponent",
    };
    </script>
    
    <style></style>
    
  • 任务1:优化原代码,当点击城市的时候,在图片下方会出现一样的两个超链接,点击下方任意一个分类,下面会对应出现每个城市的分类。

    1. 创建HeNanComponent.vue组件,代表河南城市的分类

      <template>
      	<div>
      		<ul>
      			<li>郑州</li>
      			<li>洛阳</li>
      			<li>安阳</li>
      			<li>商丘</li>
      			<li>周口</li>
      			<li>信阳</li>
      		</ul>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:"HeNanComponent"
      	}
      </script>
      
      <style>
      </style>
      
    2. 创建HeBeiComponent.vue组件,代表河北城市分类

      <template>
      	<div>
      		<ul>
      			<li>石家庄</li>
      			<li>秦皇岛</li>
      			<li>邯郸</li>
      			<li>邢台</li>
      			<li>保定</li>
      			<li>张家口</li>
      		</ul>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:"HeBeiComponent"
      	}
      </script>
      
      <style>
      </style>
      
    3. 编写完组件后,我们就可以在router->index.js中进行这两个组件的路由配置

    import Vue from "vue";
    import VueRouter from "vue-router";
    //这个是自带的,没用,我们删掉就可以了。
    //import HomeView from "../views/HomeView.vue";
    
    //导入资源
    import ChengShiComponent from "../views/ChengShiComponent.vue";
    import XingKongComponent from "../views/XingKongComponent.vue";
    
    import HeNanComponent from "../views/HeNanComponent.vue";
    import HeBeiCompanent from "../views/HeBeiCompanent.vue";
    
    Vue.use(VueRouter);
    
    const routes = [
      // 配置资源路径,path:路径	component:导入的组件名 还有name属性,后面会讲
      {
    	// /为默认路径	redirect为重定向,意思为当访问localhost:8080的时候,会默认访问'/chengshi'路由,显示城市图片
        path: "/",
        redirect: "/chengshi",
      },
      {
        path: "/chengshi", // 第一路径前面必须带 /
        component: ChengShiComponent,
    	// 子组件路由配置
    	children:[
    		{
    			path: "henan", // 第二路径前面不要带 /
    			component: HeNanComponent
    		},
    		{
    			path: "hebei", // 第二路径前面不要带 /
    			component: HeBeiComponent
    		}
    	]
      },
      {
        path: "/xingkong", // 第一路径前面必须带 /
        component: XingKongComponent,
      }
      
    ];
    
    const router = new VueRouter({
      routes,
    });
    
    export default router;
    
    1. 配置完路由以后,我们就可以在ChengShiComponent.vue组件中添加router-link标签引用了

      <template>
        <div>
          <img src="../assets/1.jpg" />
      	<br />
           <!-- 路径一定要包含父路由的路径,要完整 -->
          <router-link to="/chengshi/henan">河南</router-link> |
          <router-link to="/chengshi/hebei">河北</router-link>
      
          <!-- 显示引用路由的位置 -->
          <router-view></router-view>
        </div>
      </template>
      
      <script>
      export default {
        name: "ChengShiComponent",
      };
      </script>
      
      <style></style>
      
    2. 这时候我们就可以在页面上测试预览了

      • 点击星空的结果:

        在这里插入图片描述

      • 点击城市的结果:

        在这里插入图片描述

        • 点击城市后,点击下面河南的结果:

          在这里插入图片描述

        • 点击城市后,点击下面河北的结果:

          在这里插入图片描述

    3. 至此,我们任务就完成了。

    注意:

    • 在父组件中需要整套的 router-link 和 router-view 配置
    • children 中配置和 routes 路由基本配置一致。
    • 除了上述两步操作,需要根据自身需求创建子组件。

11.5、命名路由和编程式路由导航

11.5.1、name命名路由

​ 有时候,通过一个名称来标识一个路由显得更方便一些,特别是在执行路由跳转、路由链接或者路由传参的 时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

语法: 在 routes 配置中使用 name 关键字进行命名

routes: [
    {
        path : '/path 地址',
        name : '路由的 name',
        component : 组件
    }
]

调用路由

  • 调用路由分为编程式导航调用路由的方法和声明式导航调用路由的方法,编程式导航下面会讲,声明式导航上面用到的<router-link to="/路径"></router-link>这种直接当做a标签引用的被称为声明式导航。

  • 声明式路由调用方法有三种:

    • 第一种

      //第一种
      <router-link to="/path 地址">提示文字</router-link>
      
    • 第二种

      //第二种:使用 path 关键字
      <router-link :to="{path : 'path 地址'}">提示文字</router-link>
      
    • 第三种

      //第三种:使用 name 关键字
      <router-link :to="{name : 'name 属性值'}">提示文字</router-link>
      
  • 注意:

    • 第一种方式调用 path 地址需要/
    • 第二种、第三种方式在调用时不需要/,而且需要使用:to,进行路径编译

编程式导航调用路由也有三种方法,和这个大体上差不多,下面会讲。

  • 任务1:优化原代码,给两个子路由添加name值,使调用子路由的时候,路径可以不写那么长

    1. 打开router->index.js文件,在两个子路由对象内添加name属性。

      import Vue from "vue";
      import VueRouter from "vue-router";
      //这个是自带的,没用,我们删掉就可以了。
      //import HomeView from "../views/HomeView.vue";
      
      //导入资源
      import ChengShiComponent from "../views/ChengShiComponent.vue";
      import XingKongComponent from "../views/XingKongComponent.vue";
      
      import HeNanComponent from "../views/HeNanComponent.vue";
      import HeBeiComponent from "../views/HeBeiComponent.vue";
      
      Vue.use(VueRouter);
      
      const routes = [
        // 配置资源路径,path:路径	component:导入的组件名 还有name属性,后面会讲
        {
          // /为默认路径	redirect为重定向,意思为当访问localhost:8080的时候,会默认访问'/chengshi'路由,显示城市图片
          path: "/",
          redirect: "/chengshi",
        },
        {
          path: "/chengshi", // 第一路径前面必须带 /
          component: ChengShiComponent,
          // 子组件路由配置
          children: [
            {
              path: "henan", // 第二路径前面不要带 /
              component: HeNanComponent,
      		//添加name属性,用处和path一样
      		name: "hn"
            },
            {
              path: "hebei", // 第二路径前面不要带 /
              component: HeBeiComponent,
      		//添加name属性,用处和path一样
      		name: "hb"
            },
          ],
        },
        {
          path: "/xingkong", // 第一路径前面必须带 /
          component: XingKongComponent,
        },
      ];
      
      const router = new VueRouter({
        routes,
      });
      
      export default router;
      
    2. 打开ChengShiComponent.vue组件,修改中to的值

      <template>
        <div>
          <img src="../assets/1.jpg" />
      	<br />
      	<!-- 方式一 -->
          <!-- <router-link to="/chengshi/henan">河南</router-link> | -->
      	<!-- 方式二 -->
          <!-- <router-link :to="{path:'/chengshi/henan'}">河南</router-link> | -->
      	<!-- 方式三 -->
          <router-link :to="{name:'hn'}">河南</router-link> |
      	
          <router-link :to="{name:'hb'}">河北</router-link>
      
          <!-- 显示引用路由的位置 -->
          <router-view></router-view>
        </div>
      </template>
      
      <script>
      export default {
        name: "ChengShiComponent",
      };
      </script>
      
      <style></style>
      
11.5.2、编程路式路由导航

Vue 路由中页面导航的两种方式:

  • 声明式导航:通过点击链接实现导航的方式,叫做声明式导航。例如:普通网页中的 a 链接或 vue 中的
  • 编程式导航:通过调用 API 实现导航的方式,叫做编程式导航;
  • 例如:JavaScript 中的 location 对象或 vue 中的 this.$router.push

Vue 实现编程式导航

常见 Vue 路由编程式导航 Api 如下:

  • this.$router.push(‘hash 地址’)
  • this.$router.go(n)

这里我们以第一种 push 方式为重点进行学习

语法如下:

//第一种
this.$router.push('路由配置的 path 地址');
//第二种:使用 path 关键字
this.$router.push({path : '路由配置的 path 地址'});
//第三种:使用 name 关键字
this.$router.push({name : '命名路由的 name 值'});

注意:

  • path 地址不需要加/
  • path 关键字后对应的是路由配置中的 path 地址
  • name 关键字后对应的是路由配置中的 name 属性值
  • this不能省略

可以发现,声明式是等价于 router.push(…)的。

  • 任务1:使用编程式导航改造上面的代码

    1. 当我们把引用河北的<router-link></router-link>改为<button></button>的时候,就发现没办法使用to引用路由了。

      <!-- <router-link :to="{name:'hb'}">河北</router-link> -->
      <!-- 使用按钮进行路由引用 to就没办法进行路由引用了 -->
      <button>河北</button>
      
    2. 这时候我们给按钮添加个点击方法,并在组件中添加methods方法,代码如下:

      <template>
        <div>
          <img src="../assets/1.jpg" />
      	<br />
      	<!-- 方式一 -->
          <!-- <router-link to="/chengshi/henan">河南</router-link> | -->
      	<!-- 方式二 -->
          <!-- <router-link :to="{path:'/chengshi/henan'}">河南</router-link> | -->
      	<!-- 方式三 -->
          <router-link :to="{name:'hn'}">河南</router-link> |
      	
          <!-- <router-link :to="{name:'hb'}">河北</router-link> -->
      	<!-- 使用按钮进行路由引用 to就没办法进行路由引用了 -->
      	<button @click="hebei()">河北</button>
      
          <!-- 显示引用路由的位置 -->
          <router-view></router-view>
        </div>
      </template>
      
      <script>
      export default {
        name: "ChengShiComponent",
        methods:{
      	  hebei(){
      		  //第一种方法
      		  //this.$router.push('hebei');
      		  //第二种方法
      		  //this.$router.push({path:'hebei'});
      		  //第三种方法
      		  this.$router.push({name:'hb'});
      	  }
        }
      };
      </script>
      
      <style></style>
      
    3. 到此为止,可以测试是否成功。(我的都成功了)

11.5.3、路由导航传参

​ 在 Vue 路由中,针对不同的路由导航方式有不同的传参方式。这里主要是根据 path 属性或 name 属性进行区 分,无论是上面讲的声明式还是编程式操作基本一致。

1、声明式导航传参

用于传参的键可以为两个名词

  • query
  • params

第一种:path+query属性

<router-link :to="{path : 'path 地址',query : {参数 1:值 1,参数 2:值 2}}">
    提示文字
</router-link>
  • 组件中获取方式

    {{this.$route.query.参数名}}
    

第二种:name+params属性

<router-link :to="{name : 'name 属性值',params : {参数 1:值 1,参数 2:值 2}}">
    提示文字
</router-link>
  • 组件中获取方式

    {{this.$route.params.参数名}}
    

注意

  • 这里的 this 可以省略

  • 这里是 r o u t e 不 是 route 不是 routerouter

  • 任务1:在河北和河南城市中新添加li内容为调用路由时的参数

    1. 使用第一种方式query添加HeNanComponent.vue中的li标签

      <template>
        <div>
          <ul>
            <li>郑州</li>
            <li>洛阳</li>
            <li>安阳</li>
            <li>商丘</li>
            <li>周口</li>
            <li>信阳</li>
      	  <!-- 获取调用路由路径中所传来的参数 -->
      	  <li>{{this.$route.query.henan}}</li>
          </ul>
        </div>
      </template>
      
      <script>
      export default {
        name: "HeNanComponent",
      };
      </script>
      
      <style></style>
      
    2. 使用第二种方式params添加HeBeiComponent.vue中的li标签

      <template>
      	<div>
      		<ul>
      			<li>石家庄</li>
      			<li>秦皇岛</li>
      			<li>邯郸</li>
      			<li>邢台</li>
      			<li>保定</li>
      			<li>张家口</li>
      			<!-- 获取调用路由路径中所传来的参数 -->
      			<li>{{this.$route.params.hebei}}</li>
      		</ul>
      	</div>
      </template>
      
      <script>
      	export default {
      		name:"HeBeiComponent"
      	}
      </script>
      
      <style>
      </style>
      
    3. 添加ChengShiConponent.vue组件中,调用路由的参数

      <template>
        <div>
          <img src="../assets/1.jpg" />
      	<br />
      	<!-- 方式一 -->
          <!-- <router-link to="/chengshi/henan">河南</router-link> | -->
      	<!-- 方式二 -->
          <!-- <router-link :to="{path:'/chengshi/henan'}">河南</router-link> | -->
      	<!-- 方式三 -->
            
      	<!-- 带参调用路由 参数query值为键值对的对象 -->
          <router-link :to="{name:'hn',query:{henan:'驻马店'}}">河南</router-link> |
      	
          <!-- 带参调用路由 参数params值为键值对的对象 -->
          <router-link :to="{name:'hb',params:{hebei:'衡水'}}">河北</router-link>
            
      	<!-- 使用按钮进行路由引用 to就没办法进行路由引用了 -->
      	<!-- <button @click="hebei()">河北</button> -->
          <!-- 显示引用路由的位置 -->
          <router-view></router-view>
        </div>
      </template>
      
      <script>
      export default {
        name: "ChengShiComponent",
        methods:{
      	  hebei(){
      		  //第一种方法
      		  //this.$router.push('hebei');
      		  //第二种方法
      		  //this.$router.push({path:'hebei'});
      		  //第三种方法
      		  this.$router.push({name:'hb'});
      	  }
        }
      };
      </script>
      
      <style></style>
      
      • 进行测试,可以发现传的值可以动态的添加到li标签中了。
2、编程式导航传参

编程式导航传参和声明式导航传参差不多,只不过不是用标签,而是在方法内传参。

代码步骤如下:

  1. 因为上面修改过子路由组件内获取参数了,所以我们只修改ChengShiComponent.vue组件就可以了。

    <template>
      <div>
        <img src="../assets/1.jpg" />
    	<br />
    	<!-- 方式一 -->
        <!-- <router-link to="/chengshi/henan">河南</router-link> | -->
    	<!-- 方式二 -->
        <!-- <router-link :to="{path:'/chengshi/henan'}">河南</router-link> | -->
    	<!-- 方式三 -->
    	<!-- 带参调用路由 参数query值为键值对的对象 -->
        <router-link :to="{name:'hn',query:{henan:'驻马店'}}">河南</router-link> |
    	
        <!-- <router-link :to="{name:'hb',params:{hebei:'衡水'}}">河北</router-link> -->
    	<!-- 使用按钮进行路由引用 to就没办法进行路由引用了 -->
    	<button @click="hebei()">河北</button>
    
        <!-- 显示引用路由的位置 -->
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: "ChengShiComponent",
      methods:{
    	  hebei(){
    		  //第一种方法
    		  //this.$router.push('hebei');
    		  //第二种方法
    		  //this.$router.push({path:'hebei'});
    		  
    		  //第三种方法
              /*
              * 因为这个子路由中获取写的是this.$route.params.hebei,
              * 所以这里参数名写 params,如果获取写的是query这里也需要修改
              */
    		  this.$router.push({name:'hb',params:{hebei:'衡水'}});
    	  }
      }
    };
    </script>
    
    <style></style>
    
  2. 修改完毕后可以测试是否成功,我这里是成功了,和声明式导航传参的结果一样。

本章主要学习 Vue 路由的基本使用。其中 Vue 路由主要分为两种:

  • 声明式路由导航
  • 编程式路由导航

对于路由的基本操作包含有:

  • 路由跳转导航
  • 路由导航传参

虽然两种不同的路由方式写法不同,但是基本操作大致一样。大家可以根据自身情况选择合适的操作。

Logo

前往低代码交流专区

更多推荐