day40-43-Vue2.js-路由VueRouter-Vuex状态管理-Axios异步

2023-12-14 21:34:41

今日内容

零、 复习昨日

image-20230629201822151

一、Vue简介


1.1 简介

Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式的js框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-routervue-resourcevuex)或既有项目整合。

  • 渐进式:明式渲染->组件系统->客户端路由->集中式状态管理->项目构建

  • 官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

  • 优点:

    • 易用: 熟悉HTML,CSS,JavaScript知识后,可快速上手Vue
    • 灵活: 渐进式框架
    • 高效: api20kb,加载快,内部虚拟DOM
  • vue2版本

  • 作者 尤雨溪

1.2 MVVM 模式的实现者——双向数据绑定模式

  • Model:模型层,在这里表示 JavaScript 对象
  • View:视图层,在这里表示 DOM(HTML 操作的元素)
  • ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 MVVM 架构中,是不允许 数据 和 视图 直接通信的,只能通过 ViewModel 来通信,而 ViewModel 就是定义了一个Observer观察者

  • ViewModel 能够观察到数据的变化,并对视图下对应的内容进行更新
  • ViewModel 能够监听到视图的变化,并能够通知数据发生改变

至此,我们就明白了,Vue.js 就是一个 MVVM 的实现者,他的核心就是实现了 DOM 监听数据绑定

1.3 其它 MVVM 实现者

  • AngularJS

    简单介绍一下,AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。

  • ReactJS

    React引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。

  • 微信小程序

    微信小程序的视图层和数据层就是通过MVVM进行绑定的。

1.4 为什么要使用 Vue.js

  • 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
  • 移动优先。更适合移动端,比如移动端的 Touch 事件
  • 易上手,学习曲线平稳,文档齐全
  • 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
  • 开源,社区活跃度高

1.5 Vue.js 的两大核心要素

1.5.1 数据驱动

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

1.5.2 组件化

  • 页面上每个独立的可交互的区域视为一个组件
  • 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
  • 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面

二、Vue的初体验


开发工具

  • VScode (今天)
  • HBuilder
  • WebStorm
  • IDEA

2.1在页面引入vue的js文件

注意:cdn是一种加速策略,能够快速的提供js文件

也可以自己下载到本地,自己引入vue.js

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>

2.2 准备dom

<!-- 创建一个div,id是app -->
<div id="app"></div>

2.3 创建vue对象,设计对象的内容

其中该vue对象,绑定了页面中id是app的那个div

<script>
		new Vue({
			el:"#app",
			data:{
				title:"hello vue!",
                nowdate:"hi!",
                age:18,
                flag:true
			}
		});
</script>
 #  el: element的简称,也就是Vue实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
 #  data: 用于提供数据的对象,里面存放键值对数据。

2.4 在页面的元素中使用插值表达式来使用vue对象中的内容

<div id="app">
	{{ title }}
</div>

三、 插值表达式

插值表达式是{{ }},即双花括号

3.0 运算

<div id="app">
        {{ 1 + 1}}<br>
        {{ 1 - 1}}<br>
        {{ 2 * 2}}<br>
        {{ 2 / 2}}<br>
        {{ 2 +"--"+ 2}}<br> <!-- 字符串拼接 -->
        {{ 2 > 1 ? "2大":"1大"}}<br>
 </div>  

3.1 取数据

插值表达式的可以是在View中获得Model中的内容

Model中的内容如下:

<body>
    <div id="app">
        {{ title }},
        {{arr}},
        {{user}},
        {{user.age}},
        {{user.username}}
    </div>
</body>
<scrip>
new Vue({
		el:"#app",
		data:{
            title:"hello world!",
            arr:[11,22,33],
            user:{
                age:18,
                username:"taotie"
            }
		},
		methods:{
			sayHello:function(){
				return "hello vue";
			}
		}
	});
</script>

3.2 获取数组中的内容

<div id="app">
	{{arr[2]}}
</div>

此时,页面上会显示“33”,也就是数组中的第三个元素被获取。

3.3 获取对象中的属性

<div id="app">
	{{ user.username }}
</div>

此时,页面上会显示“taotie”,也就是对象中username属性的值。

3.4 调用Vue中的方法

<div id="app">
		{{ fun1()}} <!-- 注意还要加参数列表() -->
</div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {  // 定义数据
            },
            methods: { // 定义函数
                // key:value
                fun1 : function(){
                    alert("调用fun1执行")
                    console.log("fun1输出");
                },
                // 上面函数定义,有简化写法
                fun2(){
                    console.log("fun2输出")
                    // 返回值在页面展现
                    return  "fun22222"
                }
            }
        })
    </script>

调用了vue对象中的fun2方法,控制台输出内容,页面并展示了方法的返回值

四、Vue对象总结


Vue.js通过加载js,实现对页面的快速渲染。vue封装的js该如何使用?

就必须了解MVVM双向数据绑定模式。Vue将视图层和数据层分离,通过MVVM建立视图层和数据层的连接。其中,插值表达式是一种连接方式,可以通过插值表达式以多种方式,快速的从数据层获取数据并展示在视图层上。数据层Vue对象,也是由很多部分组成,比如之前介绍的el,data,methods等,以及之后要介绍的mount,computed等。

五、Vue的分支 v-if


5.1 v-if

Vue中的分支语句v-if非常好理解,逻辑跟Java中的if-else相同。v-if语句块包含以下内容:

  • v-if
  • v-else
  • v-else-if

接下来以一个简单例子即可理解:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从这个例子可以看出,vue对象中的data提供了分支的条件。根据条件,如果是true,则v-if的内容就会显示,反之不显示

5.2 v-show

v-if和v-show之间有着看似相同的效果,但优化上却有区别。先看下面这个例子:

<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<p v-show="rich">
				有钱!
			</p>
			<p v-if="rich">
				有钱!
			</p>
			<button type="button" @click="rich=!rich">今晚彩票开奖</button>
		</div>	
	</body>
	<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
	<script>
		new Vue({	
			el:'#app',
			data:{
				rich:false
			},
			methods:{				
			}
		});
	</script>
</html>

通过点击“今晚彩票开奖”按钮,能切换rich的值,此时发现,v-if和v-show的显示状态都会来回切换。看起来是一样的,但通过查看控制台代码发现,v-show实际会将p标签的css样式的display属性设为none来达到隐藏的效果。

1585235916595

v-if是直接在页面上添加和删除p标签来达到效果,因此v-show在反复切换的应用场景下,效率比v-if更高。

六、Vue的循环 v-for【重点】


Vue中的循环关键字并没有Java的那么多,只有v-for,但用法上有多种。接下来我们来逐一介绍。

6.1 普通的for循环

我们需要定义数据源,然后通过v-for来遍历数据源,再使用插值表达式输出数据。

<body>
<div id="app">
    <ul>
        <!-- in是关键词,args是下方data中定义的数组,a是遍历出的结果变量 -->
        <li v-for="a in args">{{a}}</li>
    </ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            args:[1,2,3,4,5,6]
        }
    });
</script>

在这个例子中,数据源提供了一个数组。视图层通过v-for来循环输出多个li标签,非常简单。

6.2 带着索引的for

<body>
<div id="app">
    <ul>
        <li v-for=" (a,i) in args" :key='i'>{{i}}{{a}}</li>
    </ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            args:[1,2,3,4,5,6]
        }
    });
</script>

(a,i) 顺序建议不要变,第一个就是遍历的得到的值,第二个就是下标

此时的i就是每次循环的循环变量 ,从0开始一直到元素个数-1

6.3 遍历一个对象中的信息: v、k、i【了解】

<body>

<div id="app">

    <ul>
        <li v-for="(v,k,i) in student">{{i+1}}--{{k}}--{{v}}</li>
    </ul>

</div>
</body>

<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
    	el:'#app',
    	data:{
       	 	student:{
            	username:'小鱼',
				age:20,
				girl:'如花'
        	}
    	}
	});
</script>

v、k、i 这几个变量名字符可以自己定义,顺序不要变,分别表示每次循环内容的值、键、下标

  • v: 循环中每条数据的值 小鱼、20、如花
  • k: 循环中每天数据的键 username、age、girl
  • i: 循环的序号(下标),从0开始
页面效果如下:
1585236957814

6.4 遍历对象数组填充表格【重点】

<body>
    <div id="app">
        <h3>for循环-遍历对象数组</h3>
        <table border="2">
            <tr>
              <td>sid</td>  
              <td>sname</td>  
              <td>age</td>  
              <td>sex</td>  
            </tr>
            <tr v-for="stu in stuArr" >
                <td>{{stu.sid}}</td>
                <td>{{stu.sname}}</td>
                <td>{{stu.age}}</td>
                <td>{{stu.sex}}</td>
            </tr>
        </table>
    </div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script>
        var vue = new Vue({
            el:"#app",
            data:{
                stuArr:[
                {
                    sid:"2020101",
                    sname:"瑞虎",
                    age:23,
                    sex:"男"
                },
                {
                    sid:"2020102",
                    sname:"奇瑞",
                    age:24,
                    sex:"男"
                },
                {
                    sid:"2020103",
                    sname:"道奇",
                    age:24,
                    sex:"男"
                },
                {
                    sid:"2020103",
                    sname:"冠道",
                    age:24,
                    sex:"男"
                }
                ]
            }
        })
    </script>
</body>
页面效果
image-20230907172054875

七、Vue的属性绑定【重点】


Vue提供了多个关键字,能快速的将数据对象中的值绑定在视图层中,即MVVM

7.1 v-model

通过v-model将标签的value值与vue对象中的data属性值进行绑定。

<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
	</head>
	<body>
		<div id="app">
			<input type="text" v-model="title">
			{{title}}
		</div>
	</body>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	<script type="text/javascript">
		new Vue({
			el:'#app',
			data:{
				title:"hello vue"
			}
			
		})
	</script>
</html>

此时input标签中加入了“v-model=‘title’”,表示input的value值与vue对象data中的title属性绑定,当在input输入框中输入内容会实时修改title的值。于是{{title}}插值表达式能实时输出input输入框内的值。

页面效果如下:
1585238200588

7.2 v-bind

我们知道插值表达式是不能写在html的标签的属性内的,那如果一定要用vue中的属性作为html标签的属性的内容,就可以通过v-bind进行属性绑定。

<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
	</head>
	<body>
		<div id="app">
			<a v-bind:href="link"></a>
		</div>
	</body>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	<script type="text/javascript">
		new Vue({
			el:'#app',
			data:{
				link:'http://www.baidu.com'
			}
		})
	</script>
</html>

这样,a标签内的href属性就可以使用vue对象中的属性值。

注意: v-bind也可以简写,使用冒号“:”来代替

<a v-bind:href='link'></a>  等价于==>  <a :href='link'>

八、Vue的事件绑定【重点】

8.1 事件【重点】


语法:v-on:事件名称

可以简写为: @事件名称

注意: v-on都能绑定哪些事件? 其实vue并没提供什么事件,只是换了写法而已.所以之前原生js有哪些dom事件,现在vue就可以使用哪些事件

<button v-on:click="start()">开始</button>
<button @click="begin()">开始</button>

事件中触发的函数,在vue实例中的methods中进行定义,可以按照如下两种方式进行定义:

<script type="text/javascript">
  var vue = new Vue({
    el : "#app",
    data: {
    },
    methods:{
      // 方式1  
      // 函数名: function(参数)
      start : function(){
      },
      // 方式2
      // 函数名(参数)
      begin(){
      }			
    }
  })
</script>

练习: 设计一个div,其中有数字,再设计一个按钮,点击按钮使数字递增10


image-20230704151503386


练习

image-20230629223936108

输入A,B值后,点击计算按钮,在结果处展示相加后的结果

    <div id="app">
        <h3>简易加法计算器</h3>
        <div>
           数字A:<input type="text" v-model="valA"><br>
           数字B:<input type="text" v-model="valB"><br>
           <button v-on:click="add">计算</button>
           结果:<span>{{result}}</span>
        </div>
    </div>
    <script type="text/javascript" src="js/vue.min.js"></script>
    <script type="text/javascript">
        var app = new Vue({
            el:'#app',
            data:{
                valA:0,
                valB:0,
                result:0
            },
            methods:{
                add:function(){
                    this.result = parseInt(this.valA) + parseInt(this.valB);
                }
            }
        });
    </script>

扩展: 表单修饰符

练习1中涉及到表单数据转字符串问题,获取的表单数据是字符串,需要使用parseInt()转成数字

vue提供了表单修饰符表单输入绑定 — Vue.js (vuejs.org)

其中: .number,可以使表单的输入的数据直接就是数字,不需要再解析

<input v-model.number="age" type="number">

除了.number修饰符外,还有其他的修饰符, 比如.trim .lazy

8.2 事件修饰符[熟悉]

事件处理 — Vue.js (vuejs.org)

  • .stop 阻止事件冒泡
  • .prevent 阻止默认行为
  • .capture 实现捕获触发事件的机制
  • .self 实现只有点击当前元素时候,才会触发事件处理函数,另外只会阻止自己身上冒泡行为的触发
  • .once 只触发一次事件处理函数

演示: 阻止事件冒泡行为

image-20230629224612983

Vue阻止事件冒泡操作

image-20230629224722554

演示: 阻止事件默认行为

image-20230629225101917

8.3 按键修饰符[熟悉]

参考官方文档:按键事件处理 — Vue.js (vuejs.org)

演示: Vue按键修饰符,按下回车提交数据

image-20230629225235548

练习: 改造上题,输入完数字,按下回车即计算数据

8.4 练习

动态添加对象

效果图如下,输入数据,动态的添加到页面中

image-20230829143708364

<body>
    <div id="app">
        <!-- 输入框 -->
        <div>
            <label>ID
                <input type="text" v-model="id">
            </label>
            <label>Name
                <input type="text" v-model="name">
            </label>
            <button v-on:click="add">添加</button>
        </div>
        <!-- 列表 -->
        <div>
            <ul>
                <li v-for="item in list">{{item.id +'---'+item.name}}</li>
            </ul>
        </div>
    </div>
    <script type="text/javascript" src="js/vue.min.js"></script>
    <script type="text/javascript">
        var app = new Vue({
            el:'#app',
            data:{
                id: 0,
                name:'',
                list:[
                    {id:1,name:'zs'},
                    {id:2,name:'ls'},
                    {id:3,name:'ww'}
                ]
            },
            methods:{
                add:function(){
                    // 获得输入的值
                    var id_ = this.id;
                    var name_ = this.name;
                    // 向数组中添加元素
                    this.list.push({id:id_,name:name_});
                }
            }
        });
    </script>

九、计算属性:computed

9.1 什么是计算属性

计算属性的重点突出在 属性 两个字上(属性是名词),首先它是个 属性 其次这个属性有 计算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;

为什么要用,什么时候用计算属性?

  • 当{{}}表达式内写很多复杂代码时使用计算属性来抽取代码

9.2 演示

官网演示很详细…计算属性 — Vue.js (vuejs.org)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        {{ message.split('').reverse().join('-') }}<br>
        {{ message.split('').reverse().join('-') }}<br>

        <hr>
        {{reverseStr}}<br>
        {{reverseStr}}
    </div>

    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script>
        new Vue({
            el:'#app' ,
            data:{
                message:'abcdef'
            },
            methods:{
                sayHello(){
                    alert("hello")
                }
            },
            computed:{ // 计算属性,理解: 将{{}}中重复的代码抽取,复用
                reverseStr(){
                    return this.message.split('').reverse().join('*')
                }
            }
        })
    </script>
</body>
</html>

9.3 计算属性与方法的区别

看下面这个例子

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>布局篇 计算属性</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
</head>
<body>

<div id="vue">
    <p>调用当前时间的方法:{{currentTime1()}}</p>
    <p>当前时间的计算属性:{{currentTime2}}</p>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: '#vue',
        data: {
            message: 'Hello Vue'
        },
        methods: {
            currentTime1: function () {
                return Date.now();
            }
        },
        computed: {
            currentTime2: function () {
                this.message;
                return Date.now();
            }
        }
    });
</script>
</body>
</html>

说明

  • methods:定义方法,调用方法使用 currentTime1(),需要带括号,每次都会重新执行
  • computed:定义计算属性,调用属性使用 currentTime2,不需要带括号;this.message 是为了能够让 currentTime2 观察到数据变化而变化

注意:methods 和 computed 里不能重名

测试效果

仔细看图中说明,观察其中的差异

9.4 结论

调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销

计算属性的执行时机是在created钩子函数之前执行!且只执行一次!

  • 函数每次调用都会执行
  • 计算属性是页面加载时执行一次,后续每次调用都是之前的值不再计算

自学: 侦听器侦听器 — Vue.js (vuejs.org)

十、Vue的组件化【理解】

Vue的组件化设计思想借鉴了Java的面向对象思想。Java认为万物皆对象,在Vue中,万物皆组件。

也就是说,在实际的vue项目中,以及使用了Vue框架的项目中,Vue的对象都会以组件的形式出现,能被反复使用

要想实现组件化,需要在页面中注册组件:关于注册的方式有两种,分别是全局注册本地注册

组件其实就是页面内的部分代码,完成某个功能

10.1 组件的全局注册

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue组件的全局注册</title>
</head>
<body>
    <div id="app">
        <model1></model1>
        <model1></model1>
        <model1></model1>
    </div>
        <hr/>
    <div id="app1">
        <model1></model1>
        <model1></model1>
        <model1></model1>
    </div>
</body>

<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
	//通过Vue.component实现组件的全局注册,全局注册后的组件可以被重复使用。
    // 名字小写,不能驼峰
    Vue.component("model1",{
        template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
        data:function(){
            return {
                title:"hello vue"
            }
        },
        methods:{
            btnfn:function(){
                alert("hello !!!");
            }
        }
    });

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

</html>

10.2 组件的本地(局部)注册

vue的全局注册,也就意味着在页面的任意一个被vue绑定过的div中,都可以使用全局注册了的vue组件。

但是,如果是对vue组件进行本地注册,那么在其他被vue绑定的div中,不能使用该组件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue组件的本地(局部)注册</title>
</head>
<body>

    <div id="app">
        <model11></model11>
    </div>
	<hr/>
    <!--在这里使用组件model11会报错-->
    <div id="app1">
        <model11></model11>
    </div>
</body>

<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        components:{
            "model11":{
                template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
                data:function(){
                    return {
                        title:"hello vue"
                    }
                },
                methods:{
                    btnfn:function(){
                        alert("hello !!!");
                    }
                }
            }
        }
    })
    new Vue({
        el:'#app1'
    })
</script>

10.3 注意特性【重点】

  • 组件是一段完整的页面代码,包括html标签,css样式,js数据和函数

  • 组件要设置名字,使用时是将名字作为标签使用,特别的要使用小写,多个单词要不能驼峰命名!可以连字符,a-b,不能写aB

  • componet中注册的组件中的data,必须是以函数的形式,且**return返回对象**

如下:

data:function(){
     return {
           title:"hello vue"
     }
 }
  • template标签内,必须有且只能有一个根标签,且一般都是div标签

  • template支持模板字符串

    image-20230629233337909

    image-20230629233345633

十一、 组件的生命周期【面试】

Vue中的组件也是有生命周期的。一个Vue组件会经历:创建、初始化、绑定、更新、销毁等阶段,不同的阶段,都会有相应的生命周期钩子函数被调用。

<html>

<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
</head>

<body>

    <div id="app">
        <p>{{name}}</p>
        <button @click="update">update</button>
        <button @click="destroy">destroy</button>
    </div>
    <script type="text/javascript">
        new Vue({
            el: "#app",
            data: {
                a: 1,
                name: "haha"
            },
            methods: {
                update() {
                    this.name = Math.random() + "hehe";
                },
                destroy() {
                    this.$destroy();
                }
            },
            // 声明钩子函数放在methods方法外
            beforeCreate() {
                console.log("创建vue实例之前");
                console.log(this.a) // undefined
            },
            created() {
                //
                console.log("vue实例创建成功");
                console.log(this.a);
            },
            beforeMount() {
                console.log("挂载之前");
            },
            mounted() {
                // 页面加载完之后执行
                console.log("挂载完毕")
            },
            beforeUpdate() {
                // 可执行多次
                console.log("data更新之前执行");
            },
            updated() {
                // 可执行多次
                console.log("data更新之后执行");
            },
            beforeDestroy() {
                // 只执行一次
                console.log("vue实例销毁之前执行");
            },
            destroyed() {
                // 只执行一次
                console.log("vue实例销毁之后执行");
            }

        });
    </script>
</body>
</html>
组件的生命周期钩子
1585275468102
钩子函数解释
beforeCreate在实例初始化之后,数据观测和事件配置之前被调用 此时data 和 methods 以及页面的DOM结构都没有初始化 什么都做不了
created 在实例创建完成后被立即调用此时data 和 methods已经可以使用 但是页面还没有渲染出来,可以在这里进行数据的初始化、异步请求数据、监听事件等操作
beforeMount在挂载开始之前被调用 此时页面上还看不到真实数据 只是一个模板页面而已
mounted mounted 钩子函数是组件挂载到 DOM 后被调用的。在这个阶段可以访问到组件所依赖的 DOM 元素,进行 DOM 操作、绑定事件等
beforeUpdate数据更新时调用,发生在虚拟DOM打补丁之前。 页面上数据还是旧的
updated由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。 页面上数据已经替换成最新的
beforeDestroy实例销毁之前调用
destroyed实例销毁后调用

十二、Vue-Cli搭建Vue项目【重点】


12.1 什么是vue-cli

cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js

12.2 node.js的介绍及安装

node.js的介绍

node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。

node.js的安装

从官网下载安装即可:
http://nodejs.cn/download/

image-20230630171746807 image-20230630171801300 image-20230630171838210 image-20230630171902451 image-20230630171935797

? 不要勾选这个,否则会下载很多东西

image-20230630171950352
测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。(环境变量已经自动配置好)
image-20230630172915127

node -v 是 查看node的版本

npm -v 是 查看npm版本

npm是安装工具的工具包,类似于Linux中yum,Linux中安装软件 yum install ,node中安装软件使用npm install

设置npm源 , 配置镜像后,下载资源就会很快.

image-20230630173211116

# 【推荐这个】
npm config set registry https://registry.npmmirror.com

# 安装完vue-cli后再来执行这句话,开启镜像,使镜像生效
vue config --set useTaobaoRegistry true

可以到C:\Users\Administrator中一个名为.vuerc文件查看useTaobaoRegistry是否为true

12.3 使用node.js 安装 vue-cli

(管理员权限)使用如下命令安装vue-cli

npm install @vue/cli -g

# 如果出错,可以卸载,重新执行上一步安装
npm uninstall @vue/cli -g
  • npm: 使用node.js的命令
  • install: 安装
  • @vue/cli: 要安装的vue-cli
  • -g: 全局安装
当出现以下界面,表示正在安装:
image-20230630174255457image-20230630174544138

安装vue-cli检测

image-20230630174643935

12.4 使用vue-cli命令创建项目

进入工作空间,执行命令创建

  • vue create 项目名

注意bug: 创建项目时如果出现报错,提示no permitted 即没有权限在此处创建项目,这个是因为磁盘权限不够,需要修改硬盘权限

image-20230904163107101

image-20230630180103755

选择具体features

image-20230630180251951

选择vue版本

image-20230630180445128

一般选择第一项,为的是将文件分离打包

image-20230630180546418

是否将此操作存储为模板,本例选择n,选择为y就会在下次创建时使用

image-20230630180626238

创建完毕

image-20230630180841197

运行项目

image-20230630181044188

image-20230630181126743

12.5 使用图形界面方式创建

打开命令行,输入vue ui

会自动打开浏览器,弹出页面

image-20230701130829566

创建项目

image-20230701134703173

image-20230701134835493

image-20230701134927540

选择插件

image-20230701134954698

选择版本

image-20230701135119621

开始创建

image-20230701135215555

创建成功后,跳转控制面板页面

image-20230701135337501

启动项目& 关闭

image-20230701135438284

12.6 项目结构

image-20230701141932969

文件名解释
buildwebpack的一些配置
config不同环境的配置,比如开发环境,生产环境等
node_modulesnpm install下载的依赖包
public用于存放公共js文件夹、json文件和图片等
dist存放项目打包后的文件
src项目的源码
src/assets存放静态资源文件,如图片等
src/components存放公共组件
src/router静态路由表
src/storevuex
src/views开发的页面资源,路由页面相关的
src/main.js对应App.vue创建vue实例,也是入口文件
src/App.vue入口页面
gitignoregit管理时,忽略的文件或文件夹
package.json项目的核心配置记录,可以查看当前安装依赖的版本号
index.html首页的入口
vue.config.js可选的配置文件,存放在根目录中,@vue/cli-service 启动的时候会自动加载

可以在package.json中修改端口

image-20230701143121782

image-20230701143202078

12.7 启动项目

进入项目,使用命令行输入npm run serve启动

image-20230904152831220

十三、idea开发vue

idea中安装Vue插件

image-20230602163052107

然后使用idea打开之前创建的vue项目即可

ps: 也可使用idea直接创建vue项目,但是创建的vue版本默认为Vue3,无法改成vue2,暂时先不使用

BUG总结

cmd中可以启动vue项目,但是idea中的终端无法执行npm run serve启动项目,提示npm不是内部或外部命令

image-20231204161750405

解决方案: 以管理员身份启动idea,打开项目运行

十四、单文件组件【重点】

14.1 介绍

以前

在很多 Vue 项目中,我们使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#app'}) 在每个页面内指定一个容器元素。

这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:

  • 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
  • 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

现在

文件扩展名为 .vuesingle-file components (单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。

单文件组件的组成结构:

  • template 组件的模块区域

  • script 业务逻辑区域

  • style 样式区域

14.2 使用

在工程的src/components下创建一个TestSingeVuePage.vue单文件

ps: 命名规则 驼峰版TestSingeVuePage 或者 test-single-vue

image-20230701193211146

填入内容

image-20230701200506779

<template>
  <div>
     <h1 class="red">这是测试单vue文件组件</h1>
    <h3>{{info}}</h3>
    <button @click='show()'>show</button>
  </div>
</template>

<script>
export default {
  name: "TestSingleVuePage",
  data:function (){
    return {
      info:"data-信息"
    }
  },
  methods:{
    show(){
      alert("单页面内弹出...")
    }
  }
}
</script>

<style scoped>
  .red {
    color: red;
  }
</style>

注册组件

  • 哪里注册? 哪个组件需要使用另一个组件,就在哪注册!

  • 例如: A.vue需要使用B.vue的组件内容

    • 就在V.vue中1)导入B组件,2)注册组件,3)使用组件
  • 在App.vue文件内注册组件

image-20230701201344985

使用组件

  • 在App.vue页面的template内使用组件

image-20230701200731630

启动测试

image-20230701200812767

14.3 练习

练习: 再创建一个单页面TestSingleVuePage2.vue页面,定义一些内容, 然后在TestSingleVuePage.vue页面内使用该页面

注意:

import Demo1 from "@/components/Demo1.vue";
import Demo2 from "./components/Demo2.vue";

其中./ 是相对路径,@是从根路径

十五、Vue组件间的参数传递[熟悉]


15.1 父传子

通过子组件的props部分,来指明可以接收的参数,父组件通过在标签中写明参数的键值对来传递参数。

? props是表示一个组件的参数部分,那么props的写法有两种:

? 1)props:[参数列表] , 比如: props:[‘MyProp1’,‘MyProp2’,…]

? 2)props:{参数名1:{type:String,required:true,default:‘XX’},参数名2:{…}}

演示

创建子组件,设置参数 等待父组件给其传值

<template>
<div>
  <h1>这是 子组件</h1>
  <span>{{name}}</span> --
  <span>{{age}}</span> --
  <span>{{sex}}</span>
</div>
</template>

<script>
export default {
  name: "MySon",
  props:{
    name: {
      // 类型
      type: String,
      // 默认值
      default: '无名'
    },
    age: Number,
    sex: Boolean
  }
}
</script>

<style scoped>
</style>

创建父组件,给子组件传值

<template>
  <div>
    <h1>这是 父组件 </h1>
    <!-- 父组件使用子组件 -->
    <!--
         可以通过 v-bind 动态赋值,比如 v-bind:name="",
         也可以简写:age=""
         也可以静态赋值,比如 messages="" , 注意没有冒号
    -->
    <MySon v-bind:name="username" :age="age" sex="true"></MySon>
  </div>
</template>

<script>
import MySon from "@/components/MySon";

export default {
  name: "MyFather",
  components: {
    MySon
  },
  data: function () {
    return {
      username: "小儿砸",
      age: 18
    }
  }
}
</script>

<style scoped>

</style>

在App.vue注册父组件并使用父组件演示效果即可

image-20230701212133867

注意:props支持的类型:String、Number、Boolean、Array、Object、Date、Function、Symbol

image-20230905100941693

15.2 子传父

使用组件中的props属性是无法完成子组件向父组件传值的,在Vue的设计中,props是单向数据流,只能是父组件通过props向子组件传递数据.

image-20230701212537907

那么,如果需要子组件向父组件传递值该如何做?

  • Vue提供了方案: 在子组件中使用自定义事件向父组件传递数据.

具体步骤:

  • 子组件定义一个函数,通过事件触发函数,函数内通过$emit 函数将数据发给父组件

    this.$emit('父组件中的函数名', 数据)
    
  • 父组件在使用子组件的时候,通过@emit中的函数名绑定数据,然后再将接收到数据传递给父组件自己的函数即可

子组件

<template>
  <div>
    <h1>这是 子组件2</h1>
    <button @click="sendNum">子组件将值num=10传递给父组件</button>
  </div>
</template>

<script>
export default {
  name: "MySon2",
  props: {},
  data: function () {
    return {
      num: 10
    }
  },
  methods: {
    sendNum() {
      // 触发父组件的事件,同时给事件传参
      this.$emit('addFun', this.num)
    }
  }
}
</script>

<style scoped>
</style>

父组件

<template>
  <div>
    <h1>这是 父组件2 </h1>
    <span>sum = {{sum}}</span>
    <MySon2 @addFun="addSum(num)"></MySon2>
  </div>
</template>

<script>
import MySon2 from "@/components/MySon2";

export default {
  name: "MyFather2",
  components: {
    MySon2
  },
  data: function () {
    return {
      sum: 0
    }
  },
  methods:{
    addSum(v){
      this.sum += v;
    }
  }
}
</script>

<style scoped>

</style>

在App.vue注册父组件并使用父组件演示效果即可

image-20230701214725152

image-20230701214751783

十六、Vue-router 路由【重点】

16.1 介绍

路由的本质就是一种对应关系,比如说我们在url地址中输入我们要访问的url地址之后,浏览器要去请求这个url地址对应的资源。

那么url地址和真实的资源之间就有一种对应的关系,就是路由。

路由分为前端路由后端路由

1)后端路由是由服务器端进行实现,并完成资源的分发(我们之前的项目)

image-20230701221228697

2)前端路由是依靠hash值(锚链接)的变化进行实现 (vue官网就是使用这种)

image-20230701221239070

前端路由的基本概念:
根据不同的事件来显示不同的页面内容,即事件与事件处理函数之间的对应关系
前端路由主要做的事情就是监听事件并分发执行事件处理函数

16.2 vue的路由

Vue默认属于单页面的程序,主要通过URL中的hash来实现不同页面之间的切换。这种切换方式称作前端路由。

Vue Router的核心作用

  • 跳转/切换页面

    • 声明式(写标签)

      <router-link to="要跳转的组件名称">
      
    • 编程式(写代码)

      this.$router.push("要跳转的组件名称")
      
  • 跳转携带数据

    • 编程式导航实现传递参数
      • 传递 this.$router.push({path:‘路径’,params:{参数名:值,…}})
      • 接收 this.$router.params.参数名
  • 路由模式

    • hash模式:监听浏览器地址hash值变化,执行相应的js切换网页;
    • history模式:利用history API实现url地址改变,网页内容改变;
    • 它们最明显的区别就是hash会在浏览器地址后面增加#号

16.3 安装路由模块

介绍 | Vue Router (vuejs.org)

npm install vue-router
# 如果报错,可能是npm版本过高,存在兼容性问题,那么使用如下命令
npm install --legacy-peer-deps vue-router@3.5.2
# 或
npm install --legacy-peer-deps vue-router@3.1.3

ps: 今天练习过一次以后,后续在创建项目时可以直接选择路由部分内容,创建出的项目直接就配置好了

16.5 声明式路由

需求:在主页与个人中心之间切换显示

16.5.1 创建MyInfo.vue

路由页面建议创建在src/views/下

<template>
  <div>
    <h1>个人中心</h1>
  </div>
</template>

<script>
export default {
  name: "MyInfo"
}
</script>

<style scoped>

</style>

16.5.2 创建静态路由表

在/src下创建router文件夹,

在/src/router/创建index.js文件

填充以下内容

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '../components/HelloWorld'
import MyInfo from "../views/MyInfo";

Vue.use(Router)
// export是导出路由对象,只有在这里导出了,main.js文件头上面才能 import 导入
export default new Router({
    routes: [
        {
            path: '/', // 匹配<router-link to="">中的路径
            name: 'HelloWorld', // 这个name属性可写可不写
            component: HelloWorld // 要跳转的组件对象
        },
        {
            path: '/MyInfo',
            name: 'MyInfo',
            component: MyInfo
        }
    ],
    // mode: 'history'
    mode: 'hash'
})

16.5.3 main.js引入路由模块并使用

在main.js中引入路由模块并使用

import Vue from 'vue'
import App from './App'
import router from './router' //引入上一步导出的路由模块,并设置名字为router
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  // router:router  // 使用路由,可以简写为router
    router
}).$mount('#app')

16.5.4 App.vue使用路由

<template>
  <div id="app">
    <ul>
      <li>
        <!-- 
           <router-link>用于导航,其实就是a标签
           to表示要跳转的资源的路径
           tag 将router-link渲染成想要的原生标签,默认是a标签,可以改成button
        -->
        <router-link to="/" tag="button">首页</router-link>
      </li>
      <li>
        <router-link to="/MyInfo">个人信息</router-link>
      </li>
    </ul>
    <!-- router-view 路由填充位,要跳转的组件在此展示,位置任意 -->
    <router-view/>
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {}
}
</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>

启动测试

练习完后可以重写创建vue项目,选择Router依赖,这样创建出的项目自带路由配置

16.6 编程式路由

刚才演示的是声明式路由,也可以使用编程式路由

演示: 主页与公共页面切换

创建公告页Note.vue

<template>
<div>
  <h1>公告页面</h1>
  <ol>
    <li>老杨不瞌睡了</li>
    <li>萌萌也+1</li>
    <li>相宇明天好好做操</li>
  </ol>
</div>
</template>

<script>
export default {
  name: "Note"
}
</script>
<style scoped>
</style>

src/router/index.js 设置路由规则

export default new Router({
    routes: [
        {
            path: '/',
            name: 'HelloWorld',
            component: HelloWorld
        },
        {
            path: '/MyInfo',
            name: 'MyInfo',
            component: MyInfo
        },
        {
            path: '/Note',
            name: 'Note',
            component: Note
        }
    ],
    // mode: 'history'
    mode: 'hash'
})

App.vue设置标签,绑定事件,事件触发函数,函数内编写代码实现路由

this.$router.push("/路径") --> <router-link to="/路径">

image-20230701235337484

this.$router.push("/Note")
// 注意!!!!别写错,有个叫做this.$route

bug: 第一次点击跳转没事,再点一次报错

解决:

  • 方式1: 设置异常捕获 (推荐)
// 在router中的index.js中加上以下代码,注意加在use之前写
const routerPush = Router.prototype.push;
Router.prototype.push = function (location) {
    return routerPush.call(this, location).catch((err) => {});
};
  • 方式2:
const currentRoute = this.$router.currentRoute;
if (currentRoute.path !== '/Note') {
    this.$router.push({
        path:"/Note",
        query:{username:"社恐的少聪"}
    })
}

16.7 参数的传递

16.7.1 声明式路由传参

演示App.vue中将数据传递给MyInfo.vue

在路由表中设参

export default new Router({
  routes: [
	...
    {
      path:'/MyInfo/:id', //设参
      component:MyInfo
    }
  ]
})

在App.vue中传参

<template>
  <div id="app">
    <ul>
      <li>
        <router-link to="/" tag="button">首页</router-link>
      </li>
      <li>
        <!-- 跳转至MyInfo,并携带id 1001 -->  
        <router-link to="/MyInfo/1001">个人信息</router-link>
      </li>
    </ul>
    <!-- router-view 用于显示匹配的组件,类似显示路由到的组件的区域,位置任意 -->
    <router-view/>
  </div>
</template>

<script>
...
</script>

<style>
...
</style>

在MyInfo.vue中接参

<template>
  <div>
    <h1>个人信息</h1>
    <span>id-->{{id}}</span>
  </div>
</template>

<script>
export default {
  name: "EmployeeList",
  data:function(){
    return {
      id:this.$route.params.id // 注意是$route不是$router
    }
  }
}
</script>
<style scoped>
</style>

16.7.2 编程式路由传参

演示App.vue中将数据传递给Note.vue

App.vue的编程跳转js中设置要传递的数据

  methods:{
    toNote(){
      //this.$router.push("/Note")
      this.$router.push({path:"/Note",query:{username:'老王'}})
    }
  }

Note.vue中接收参数

<template>
<div>
  <h1>网站公告</h1>
  <span>id --> {{id}}</span>
</div>
</template>

<script>
export default {
  name: "Note",
  data:function(){
    return {
      id:this.$route.query.username  
        // 注意是query,因为是按照路径方式发送,注意查看浏览器url
    }
  }
}
</script>

ps: 后续也可以通过VueX来完成传递数据

16.8 嵌套路由

嵌套路由 | Vue Router (vuejs.org)

多层多级组件嵌套时的路由

例如:

image-20230905155542421

需求: 在个人中心组件中,再设置三个链接,可以点击切换[头像|家庭信息|学校信息]

代码:

创建三个组件页面Touxiang.vue,Family.vue,School.vue

在MyInfo组件中设置路由链接发出请求路 径,并且设置路由填充位

<template>
<div>
  <h1>个人中心</h1>
  <h3>id = {{id}}</h3>
<!--  /my/tx-->
<!--  /my/family-->
<!--  /my/school-->
  <router-link to="/my/tx">头像</router-link>|
  <router-link to="/my/family">家庭信息</router-link>|
  <router-link to="/my/school">学校信息</router-link>|
  <router-view></router-view>
</div>
</template>

在router/index.js中设置myinfo组件的子组件

        {
            path:'/my',
            name:'MyInfo',
            component:MyInfo,
            children:[
                // 子路径要与父类拼接成完整路径,此处不需要加/
                { path:'tx',component:Touxiang},
                { path:'family',component:Family},
                { path:'school',component:School},
            ]
        },

如果有路径中有数据的话,子组件一样可以取值

<!--  MyInfo.Vue-->
<template>
<div>
  <h1>个人中心</h1>
  <h3>id = {{id}}</h3>
<!--  /my/tx-->
<!--  /my/family-->
<!--  /my/school-->
  <router-link to="/my/1001/tx">头像</router-link>|
  <router-link to="/my/1002/family">家庭信息</router-link>|
  <router-link to="/my/1003/school">学校信息</router-link>|
  <router-view></router-view>
</div>
</template>
        {
            path:'/my/:id',// :id是用来匹配 路由路径中的变量的
            name:'MyInfo',
            component:MyInfo,
            children:[
                // 子路径要与父类拼接成完整路径
                { path:'tx',component:Touxiang},
                { path:'family',component:Family},
                { path:'school',component:School},
            ]
        },

子组件Touxiang.vue,Family.vue,School.vue中可以使用this.$route.params.id取出路径中的数据1001,1002,1003

16.9 其他

路由重定向,导航守卫等等信息,查看官网自查

重定向| Vue Router (vuejs.org)

十七、 Vuex【重点】


17.1 什么是Vuex

Vuex 是什么? | Vuex (vuejs.org)

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

说人话: 实现多个组件的数据共享,即一个数据需要在多个组件中使用,那么就需要把这个数据存储在VueX中

Vuex五大核心要素

  • state:存放状态,比如需要全局共享的数据
  • getters:可以对state中的数据进行额外处理,类似计算属性的作用
  • mutations:通过提交mutations来改变state的状态
  • actions:异步的mutations,可以通过dispatch调用mutaitons,从而改变state
  • modules:模块化状态管理,每个模块拥有自己的 state、mutation、action、getter

vuex

Vue组件通过dispatch调用Vuex中actions的方法,进行异步操作。

actions中的方法中,通过commit,调用mutations中方法,以保证state中数据的同步。

如果不需要异步操作,可以直接在组件通过commit调用mutations中的方法对state中共享的数据进行操作。

17.2 安装

在项目根目录执行如下命令来安装 Vuex

若失败,可使用cnpm

# 本例使用的vue2,所以安装vuex装的是vuex3!!! vue3对应的Vuex4
npm install vuex@3

image-20230702172304145

17.3 配置 vuex

在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  }
})

修改 main.js 增加刚才配置的 store/index.js,关键代码如下:

import Vue from 'vue'
import App from './App.vue'
// 引入路由模块
import router from './router'
// 引入Vuex模块
import store from './store'

Vue.config.productionTip = false
// vue2 创建方式
new Vue({
  render: h => h(App),
  router,
  store  // 使用Vuex
}).$mount('#app')

17.2 17.3小节中的[安装|配置]也可以不做,因为后续再创建项目时直接选以下依赖,创建好项目直接就配置好了

image-20230706100127943

  • 一个是路由Router,一个是状态管理Vuex

17.4 演示

17.4.1 存&取

  • 在Vuex中设置数据,在任何组件取出
// store/index.js文件的state中定义数据
export default new Vuex.Store({
    state: {
        username:"无名",
        count:443,
        tga:"tga"
    },
    mutations: {
    }
})

在任何组件中使用 以下命令取值

this.$store.state.username
// username是state中定义的key

17.4.2 计算属性使用state[熟悉]

<template>
<div>
  计算属性取值:{{num}}
</div>
</template>

<script>
export default {
  name: "TestVueX",
  computed:{  // 【注意: 计算属性的写法】
    num() { 
      return this.$store.state.count
    }
  }
}
</script>

重要用法: state中的数据要放在组件的computed中而不是data中!

为什么?

这是因为data 中的内容只会在 created 钩子触发前初始化一次,具体来说就是data中设置count: this. s t o r e . s t a t e . c o u n t 则 c o u n t 的值是 c r e a t e d 钩子执行前 t h i s . store.state.count则count的值是created钩子执行前this. store.state.countcount的值是created钩子执行前this.store.state.count的值,赋值之后属性的值就是纯粹的字面量,之后this.$store.state.count 如何变化均影响不到count的取值。而 computed 则是通过依赖追踪实现的,计算属性在它的相关依赖发生改变时会重新求值。

简而言之就是说,Vuex存储的数据如果有变化,computed中的数据就会变化,但是data中的不会变化

17.4.3 mapState[熟悉]

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性

<template>
<div>
  计算属性取值:{{count}}|
  计算属性取值:{{tga}}|
  计算属性取值:{{username}}|
</div>
</template>

<script>
// 【注意】 是 {} 引入
// 使用 import mapState from 'vuex' 的方式会将整个 vuex 模块导入,并将其中的 mapState 函数赋值给 mapState 变量。
// 而使用 import { mapState } from 'vuex' 的方式则是只导入 vuex 模块中的 mapState 函数
import {mapState} from 'vuex'
export default {
  name: "TestVueX",

  // 如果使用和状态名一样的计算属性名,还可以这样写
  // 映射 this.count 为 store.state.count
  // computed: mapState(['count','tga','username']),

  // 如果有其他的计算属性,并且需要将vuex的state与其混合使用
  // 可以使用对象展开运算符'...'
  computed: {
    // 使用对象展开运算符将此对象混入到外部对象中
    // 映射为当前组件的计算属性
    ...mapState(['count','tga','username']),
  },

}
</script>

<style scoped>
</style>

17.4.4 修改数据

  • 在任何组件中修改Vuex中的数据
// store/index.js文件的mutations中定义方法修改数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    state: {
        username:"无名"
    },
    mutations: {
        // 参数1 是Vuex-state对象
        // 参数2 是其他组件使用时传过来的值,这个参数叫做payload
        updateName(state,v){
            state.username = v  // 修改Vuex中的值
        }
    }
})

在其他组件中通过事件触发函数,在其中使用Vuex的方法修改数据

<template>
  <div id="app">
    <!--  演示Vuex取值  -->
    <h1>Vuex --> {{ username }}</h1>
    <input v-model="name">
    <button @click="editName">修改vuex中的值</button>

  </div>
</template>

<script>

export default {
  name: 'App',
  methods: {
    editName(){
      // 修改Vuex数据
      // updateName是 src/store/index.js中mutations中定义的函数名
      this.$store.commit('updateName', this.name)
    }
  },
  data: function () {
    return {
      // 从Vuex中取出数据  
      username: this.$store.state.username,
      name:""
    }
  }
}
</script>

17.5.解决浏览器刷新后 Vuex 数据消失问题

  • 问题描述

Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。

  • 解决方案

采用持久化,VueX整合插件实现持久化:vuex-persistedstate

  • 安装插件

    npm install vuex-persistedstate
    

    image-20230702181913147

  • 配置

    在/src/store/index.js中实现配置

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 引入持久化插件
    import vuexPersistedstate from "vuex-persistedstate";
    
    Vue.use(Vuex)
    export default new Vuex.Store({
        state: {
            username:"无名66"
        },
        mutations: {
            updateName(state,v){
                state.username = v
            }
        },
        plugins:[vuexPersistedstate()] // 加入插件
    })
    
  • 测试

    浏览器刷新测试效果

17.6 其他Vuex知识

参考官网学习后续 Vuex (vuejs.org)

Vue总结

  • 基本语法
    • 插值表达式,用来取出data或者computed中的数据,也可以在其中做运算,甚至调用函数
    • v-if,v-show 用来决定标签是否展现(场景:分页按钮,权限菜单按钮)
    • v-for 用来遍历生成表格数据
    • v-model和v-bind,用来绑定变量,实现双向绑定(数据变化)
    • v-on或者@ ,用来事件绑定
  • 特性
    • 计算属性
      • 什么时候需要用? 当需要对data中数据运算且重复使用该运算结果时就可以将运算定义在计算属性
    • 侦听器(自学)
    • 组件化
      • 一套完整的代码,包含html,css,js
      • 将代码抽取封装,独立,方便使用和维护,可以实现spa开发
      • 需要特殊语法
        • template里面只能一个根标签,一般都是div
        • js中需要使用export将代码内容导出
        • js中data中的数据需要定义在函数的return中
  • vue工程化
    • 会使用vue-cli脚手架创建项目
    • 熟悉项目结构
      • src
        • asstes 静态资源
        • components 公共的组件
        • router 路由表
        • store vuex管理数据状态
        • views 路由的页面
        • App.vue 主页面
        • main.js 主配置文件
      • package.json 定义运行脚本,修改端口
      • node_modules 定义了一些项目运行需要的依赖,一般别人的项目都删除了,我们要运行需要先执行npm install把这些依赖下载
    • 需要掌握路由技术router
      • 页面切换就需要路由
    • 数据共享技术 vuex
      • 当 存储数据时定义vuex中的state存储即可

十八、Promise

主要解决异步深层嵌套的问题 ,promise 提供了简洁的API 使得异步操作更加容易,为后面学习axios提供基础

18.1 以前ajax中的问题

// ajax async JavaScript and xml --> 异步请求技术
$.ajax({
    url:'http://localhost:8080/login',
    type:'get',
    //data:'username=zs&password=123456',
    data:{username:'zs',password:'123456'},
    success:function(ret){
        
    },
    error:function(){
        
    }
})
console.log()
  • 异步请求外的代码,不能使用异步请求中的返回的数据
  • 多次异步调用结果顺序不确定
  • 异步调用的结果如果确实存在依赖关系,就需要嵌套处理

演示:

问题一: 异步请求外的代码,不能使用异步请求中的返回的数据

image-20230704232817410

像上面这样,下面的那个打印日志的结果是:

image-20230704232949139

并没有将ajax中请求的结果拼接,因为ajax是异步请求,即执行ajax时,会不等待回调执行就会去执行后续代码,也就是说,执行那个输出日志时,ajax的回调可能还没执行呢

问题二: 多次异步调用 结果顺序不确定

image-20230704233053371

分别多测试几次

多次刷新,多次请求,会发现结果顺序跟代码的上下顺序并不一样.

问题三: 异步调用的结果如果确实存在依赖关系,就需要嵌套处理

假如,三个请求的结果确实需要等输出了hello1再输出hello2,最后输出hello3的话,就需要嵌套处理.

image-20230704233218191

image-20230704233228739

18.2 Promise

Promise 是一种用于处理异步操作的前端开发技术。它是 ECMAScript 6(ES6)规范中引入的一种对象,旨在解决 JavaScript 中的回调地狱问题,使异步操作更加优雅和可维护。

在传统的 JavaScript 开发中,异步操作通常使用回调函数来处理。然而,当嵌套多个异步操作时,回调函数的嵌套会导致代码结构复杂、难以理解和维护。Promise 提供了一种更好的解决方案。

image-20230704233613080

基本语法

- 创建Promise对象§,构造函数中传递函数,该函数中用于处理异步任务

- 该函数中resolve和reject两个参数用于处理成功和失败的两种情况,并通过实例对象p调用then方法获取处理的结果

image-20230704233655407

示例

<script type="text/javascript">
    var p = new Promise(function(resolve,reject){
        /**
          * resolve和reject都是方法
          * 成功是调用resolve([参数])函数
          * 失败时调用reject([参数])函数 
          */
        // 异步请求
        $.ajax({
            url:'http://localhost:8080/vue/hello',
            success:function(data) {
                resolve(data);
            },
            error:function(){
                reject("出错啦");
            }
        })
    });
    p.then(function(ret){
        console.log(ret);
    },function(ret){
        console.log(ret);
    })
</script>

将以上代码封装成一个函数,就可以将jQuery中ajax嵌套调用的那种情况变得优雅一点

<script type="text/javascript">
    function queryData(path){
        var p = new Promise(function(resolve,reject){
            /**
             * resolve和reject都是方法
             * 成功是调用resolve([参数])函数
             * 失败时调用reject([参数])函数 
             */
            // 异步请求
            $.ajax({
                url:path,
                success:function(data) {
                    resolve(data);
                },
                error:function(){
                    reject("出错啦");
                }
            })
        });
        return p;
    }
    queryData('http://localhost:8080/vue/hello1')
    .then(function(ret){
        console.log(ret);
    })
</script>

十九、Axios【重点】


19.1 什么是 Axios

Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,它的主要作用就是实现 AJAX 异步通信,其功能特点如下:

由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架

19.2 Axios的使用

19.2.1 安装vue axios

npm install --save axios vue-axios

image-20230702185555289

19.2.2 在需要使用的页面中引入

比如UserInfo.vue页面需要发请求,那就在页面的script中引入即可

import axios from 'axios'

19.2.3 发送axios请求

需求: 点击按钮,发送请求,从后端服务器获得部门列表,并且在前端使用表格table展示所有部门信息

<template>
<div>
  <button @click="getInfo">查询数据</button>
  <hr>
    {{deptList}}
      <hr>
  <table>
    <th>
        部门编号
    </th>
    <th>
      部门名称
    </th>
    <th>
      部门地址
    </th>
    <tr v-for="dept in deptList">
        <td>{{dept.deptno}}</td>
        <td>{{dept.dname}}</td>
        <td>{{dept.loc}}</td>
    </tr>
  </table>
</div>
</template>

<script>
// 引入axios    
import axios from 'axios'
export default {
  name: "TestAxios",
  data(){
    return {
      deptList:null
    }
  },
  methods:{
    getInfo(){
      // axios.get('/api/dept').then(function(ret){
      //   console.log(ret)
      //   this.deptList = ret.data // 【有bug,因为this问题】
      // })
      axios.get('/api/dept').then((ret)=>{
        console.log(ret) // 【注意:这个返回值不一般】  
          // 详情请见19.4章节【19.4 Axios的响应】  
          // 可以查看官网https://www.axios-http.cn/docs/res_schema
          this.deptList = ret.data.data   
      })
    }
  }
}
</script>

<style scoped>
</style>

19.2.4 服务端解决跨域问题

SpringBoot的controller层类上添加@CrossOrigin注解即可

19.2.5 BUG

在axios的回调中,无法使用this.数据 获得vuedata中的数据

造成axios取不到值得原因主要是this回调问题。当执行完函数function时,this指向放生变化。导致this指向函数体本身。这时this.data里的数据取不到。

简单直接的解决就是把function写成箭头函数形式,箭头函数不是构造函数,不会指定this,而是抓取上下文this作为自己的this

19.3 其他api演示

可以查看官网请求配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

也可以看示例

<!-- get请求(1) 路径拼接参数 -->
axios.get('http://localhost:8080/vue/user?id=456').then(function(ret){
    console.log(ret.data); 
})

<!-- get请求(2) Restful风格参数 -->
axios.get('http://localhost:8080/vue/user/123').then(function(ret){
    console.log(ret.data.); 
})

<!-- get请求(3) 使用params传递参数 -->
axios.get('http://localhost:8080/vue/user',{
    params:{
        id:20001
    }
}).then(function(ret){
    console.log(ret.data); 
})
<!-- post请求,发送的是json,后台需要使用@RequestBody -->
axios.post('http://localhost:8080/vue/user/json',{
    username:'张三',
    password:'987654'
}).then(function(ret){
    console.log(ret.data); 
})
<!-- put请求 -->
axios.put('http://localhost:8080/vue/user/form',{
    username:'张三丰',
    password:'11111'
}).then(function(ret){
    console.log(ret.data); 
}).catch(function (err){
   console.log(err)
})

19.4 Axios的响应

官网: https://www.axios-http.cn/docs/res_schema

响应结果的主要属性:

  • data: 服务器响应回的数据
  • headers: 响应头信息
  • status: 响应状态码
  • statusText: 响应状态信息

需要特别注意,我们后台返回的数据在data中,即data中是后台返回的R,我们ret.data获得到的是R,如果还有继续获得其中的数据,还需要再ret.data.data

19.5 axios的全局配置

官网: 默认配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

我们讲一个特别实用的,我们在项目中调用数十次乃至百次接口是很常见的,后期一旦根域名发生改变,所有接口都需要修改,非常繁琐且容易出错。

axios提供了设置根域名的方法
在main.js全局入口文件中,设置:

import axios from "axios";
axios.defaults.baseURL = 'http://localhost:8888'

在其他vue页面中使用axios发请求的时候

axios.get('/api/dept').then((ret)=>{
console.log(ret)
this.deptList = ret.data.data
})

19.6 响应拦截

还有请求拦截,暂时用不上先不讲

响应拦截可以拦截到axios发请求后的那个回调response对象,然后对其进行处理

  • 实战,将response数据简化后返回

    // 添加Axios响应拦截器
    axios.interceptors.response.use(function (response) {
      //console.log('响应拦截',response)
      return response.data;
    }, function (error) {
      console.log(error)
    });
    

    这样,在使用axios的页面,从回调中获得数据时,只需要ret.data,而不需要再ret.data.data

二十、综合练习

前端vue+后端SpringBoot实现crud

登录

image-20230907172328825

跳转主页,查询全部

image-20230907172403400

主页按钮,点击跳转添加页面(注意是子路由)

image-20230907172439935

表格每行后有更新按钮,点击跳转更新页面,要携带数据,因为要查询数据回显(组件间传值)

image-20230907172527139

删除

模糊搜索

image-20230907172604411

附录

对象展开运算符

对象展开运算符 ...(也称为扩展运算符)是一种用于展开对象或数组的语法,它允许将一个对象或数组拆分成单独的元素,并在其他对象或数组中使用。

在 JavaScript 中,... 运算符可以用在多种场景中,下面分别介绍它们的用法。

  1. 展开对象:
    可以使用对象展开运算符 ... 来创建一个新对象,并将另一个对象中的属性和值复制到新对象中。这可以用于对象浅拷贝或对象合并。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { ...obj1, city: 'New York' };
    
    console.log(obj2);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  2. 合并对象:
    将多个对象合并成一个新对象的简便方法。如果有相同的属性,后面的对象的属性值会覆盖前面的对象的属性值。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { city: 'New York' };
    const obj3 = { ...obj1, ...obj2 };
    
    console.log(obj3);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  3. 展开数组:
    在数组中使用 ... 可以将一个数组展开为独立的元素,可以用于创建一个新数组或者在函数调用时传递参数。

    示例:

    const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4, 5];
    
    console.log(arr2);
    // 输出:[1, 2, 3, 4, 5]
    
    const arr = [1, 2, 3];
    someFunction(...arr); // 将数组中的元素作为参数传递给函数
    

password:‘987654’
}).then(function(ret){
console.log(ret.data);
})

axios.put(‘http://localhost:8080/vue/user/form’,{
username:‘张三丰’,
password:‘11111’
}).then(function(ret){
console.log(ret.data);
}).catch(function (err){
console.log(err)
})


## 19.4 Axios的响应

> 官网: https://www.axios-http.cn/docs/res_schema
>
> 响应结果的主要属性:
>
> - data:   服务器响应回的数据
> - headers:  响应头信息
> - status:   响应状态码
> - statusText: 响应状态信息
>
> ---
>
> **需要特别注意**,我们后台返回的数据在data中,即data中是后台返回的R,我们ret.data获得到的是R,如果还有继续获得其中的数据,还需要再ret.data.data

## 19.5 axios的全局配置

> 官网: [默认配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)](https://www.axios-http.cn/docs/config_defaults)

> 我们讲一个特别实用的,我们在项目中调用数十次乃至百次接口是很常见的,后期一旦根域名发生改变,所有接口都需要修改,非常繁琐且容易出错。
>
> **axios提供了设置根域名的方法**。
> 在main.js全局入口文件中,设置:
>
> ```js
> import axios from "axios";
> axios.defaults.baseURL = 'http://localhost:8888'
> ```
>
> 在其他vue页面中使用axios发请求的时候
>
> ```js
> axios.get('/api/dept').then((ret)=>{
> console.log(ret)
> this.deptList = ret.data.data
> })
> ```

## 19.6 响应拦截

> 还有请求拦截,暂时用不上先不讲

> 响应拦截可以拦截到axios发请求后的那个回调response对象,然后对其进行处理
>
> - 实战,将response数据简化后返回
>
>   ```js
>   // 添加Axios响应拦截器
>   axios.interceptors.response.use(function (response) {
>     //console.log('响应拦截',response)
>     return response.data;
>   }, function (error) {
>     console.log(error)
>   });
>   ```
>
>   这样,在使用axios的页面,从回调中获得数据时,只需要ret.data,而不需要再ret.data.data



# 二十、综合练习

前端vue+后端SpringBoot实现crud

> 登录

[外链图片转存中...(img-3mzvrhim-1702287645913)]

> 跳转主页,查询全部

[外链图片转存中...(img-0xdwLdq2-1702287645914)]

> 主页按钮,点击跳转添加页面(注意是子路由)

[外链图片转存中...(img-SwTM49Pk-1702287645914)]

> 表格每行后有更新按钮,点击跳转更新页面,要携带数据,因为要查询数据回显(组件间传值)

[外链图片转存中...(img-wexw5r9W-1702287645914)]

> 删除

> 模糊搜索

[外链图片转存中...(img-QQG4SE1D-1702287645914)]

# 附录

## 对象展开运算符

对象展开运算符 `...`(也称为扩展运算符)是一种用于展开对象或数组的语法,它允许将一个对象或数组拆分成单独的元素,并在其他对象或数组中使用。

在 JavaScript 中,`...` 运算符可以用在多种场景中,下面分别介绍它们的用法。

1. 展开对象:
   可以使用对象展开运算符 `...` 来创建一个新对象,并将另一个对象中的属性和值复制到新对象中。这可以用于对象浅拷贝或对象合并。

   示例:

   ```js
   const obj1 = { name: 'John', age: 30 };
   const obj2 = { ...obj1, city: 'New York' };
   
   console.log(obj2);
   // 输出:{ name: 'John', age: 30, city: 'New York' }
  1. 合并对象:
    将多个对象合并成一个新对象的简便方法。如果有相同的属性,后面的对象的属性值会覆盖前面的对象的属性值。

    示例:

    const obj1 = { name: 'John', age: 30 };
    const obj2 = { city: 'New York' };
    const obj3 = { ...obj1, ...obj2 };
    
    console.log(obj3);
    // 输出:{ name: 'John', age: 30, city: 'New York' }
    
  2. 展开数组:
    在数组中使用 ... 可以将一个数组展开为独立的元素,可以用于创建一个新数组或者在函数调用时传递参数。

    示例:

    const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4, 5];
    
    console.log(arr2);
    // 输出:[1, 2, 3, 4, 5]
    
    const arr = [1, 2, 3];
    someFunction(...arr); // 将数组中的元素作为参数传递给函数
    

文章来源:https://blog.csdn.net/weixin_39641494/article/details/134932692
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。