写在最前

【尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通】 https://www.bilibili.com/video/BV1Zy4y1K7SH/?p=2&share_source=copy_web&vd_source=c8ae4150b2286ee39a13a79bbe12b843

视频出处如上 下面每天笔记将不写视频出处 为了兼容博客 下面也不放数字了 博客OTC插件自带了

Vue入门

学习日期: 11.12

所学内容概述

Vue概述

  • 动态构建用户界面的渐进式 JavaScript 框架
  • 借鉴 Angular 的模板数据绑定技术
  • 借鉴 React 的组件化虚拟 DOM 技术

Vue特点

  • 遵循MVVM模式
  • 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
  • 可以引入第三方库开发

搭建环境

比较简单 浏览器就跟之前导入插件一样,拖进去就行了 注意要打开允许访问文件网址 不然使用文件直接打开会报错 提醒你没装Vue

Vscode中只要在同级目录下创建一个js包 把课件的vue.js导入就行了

image-20221110182752809

小案例

跟着老师敲的小案例,和之前css html js差不多,填入了Vue的引用,也感觉到了方便,使用Vue感觉和后端代码就有点像了,也就是老师说的和命令式代码的区别。 注释是直接复制的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!--
初识Vue:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为【Vue模板】;
4.Vue实例和容器是一一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;

注意区分:js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'

2.js代码(语句)
(1). if(){}
(2). for(){}
-->
<!-- 准备好容器 -->
<div id="root">
<h1>你好, {{name.toUpperCase()}}</h1>
<h1>我的年龄是 {{age}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
//创建Vue实例
new Vue({
el:'#root', //el用于指定当前Vue实例为哪个容器服务 值通常为css选择器字符串
data:{ //data中用于存储数据 数据供el所指定的容器去使用
name:'u7Uu7',
age:'18'
}
})
</script>
</body>
</html>

BUG点

直接打开html文件,报这样的错误,看自己的地址和老师的不太一样,我是file:///E:/study/Vue/初识Vue/初识Vue.html,但是老师的是127.0.0.1:5500/xxx.xxx.xxx,然后看到弹幕说注意允许访问文件网址,然后自己觉得是插件设置问题,果然看到了,把选项打开问题解决了,到后面装了一个Live Server的插件,跑起来的链接和老师的差不多了,还很方便。

image-20221110183031172

扩展学习

日总结

今天的学习内容比较简单,简单的进入了 Vue的学习,比如写简单的字体打到网页,Vue是利用对象一样的方式实现的,可以控制属性,可变性,耦合性比较好。虽然web已经是很久之前学习的了,但是前端时间自己搞博客用的都是css和js代码,现在学Vue也稍微有点记忆,看的懂,可能主要还是今天的内容比较少吧,只是简单的页面。Vue给我的感觉就跟,SpringBoot和Spring一样,升级了js,但是之前js的东西都可以使用。我总结Vue的工作就是创建Vue实例,然后传入一个配置对象,就像编译性语言了。

Vue 数据代理

学习日期: 11.13

所学内容概述

MVVM模型

M:模型 data中的数据 V: 视图 模版代码 VM: 视图模型 Vue实例

  • 开启F12检查以后 在控制台所看到的都是VM中存在的 Vue实例,我们data中添加的,也只是添加到VM VM是自带一些本事自带的属性。在Vue模版都可以直接使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<h1>测试1:{{$options}}</h1>
<h1>测试2:{{$createElement}}</h1>
<h1>测试3:{{_c}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
//创建Vue实例
const vm = new Vue({
el:'#root',
data(){
return{
name: '金华职业技术学院',
address: '金华'
}
}
})
console.log(vm)
</script>
</body>
</html>

image-20221111143214821

数据代理

  • Object.defineProperty参数 第一个是对象 第二个是要代理的参数
  • 有get和set方法可以读取到代理参数的修改和获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false

//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
console.log('有人读取age属性了')
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
  • 解释什么是数据代理 通过obj2控制obj的x
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
let obj = {x:100}
let obj2 = {y:200}

Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})

小总结一下

  • 1.Vue中的数据代理:

    通过vm对象来代理data对象中属性的操作(读/写)

    2.Vue中数据代理的好处:

    更加方便的操作data中的数据

    3.基本原理:

    通过Object.defineProperty()把data对象中所有属性添加到vm上。

    为每一个添加到vm上的属性,都指定一个getter/setter。

    在getter/setter内部去操作(读/写)data中对应的属性。

BUG点

扩展学习

日总结

今天是算正式学习Vue的第二天,难度一般,有讲到复习js的部分。那部分自己没学过,但理解是什么意思了。今天的学习状态还不错的,因为没有指定计划的原因,学到晚上才发现,自己的进度其实是有点慢了的都,要抓紧时间了,前端自己是不太懂的,所以会跟着老师去敲代码,听一些细节,导致进度会慢很多,等到后面应该会好一点,也要抓紧了,按照自己的计划去学,同时还要复习java的,不能松下来。

Vue 事件 计算属性 监视

学习日期: 11.14

所学内容概述

事件处理

简单的点击时间用@click 修饰符和监听笔记都放在html代码中去了效果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: skyblue;
}
.box1{
padding: 5px;
background-color: skyblue;
}
.box2{
padding: 5px;
background-color: orange;
}
.list{
width: 200px;
height: 200px;
background-color: peru;
/* 滚动条 */
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<!--
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
-->
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 阻止默认事件(常用) -->
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>

<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
<!-- 修饰符可以连续写 -->
<!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
</div>

<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>

<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>

<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>

<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>

</div>
</body>

<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
alert('同学你好!')
// console.log(e.target)
},
showMsg(msg){
console.log(msg)
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
}
})
</script>
</html>

image-20221116091036100

计算属性

案例是输入姓和名,输出姓-名,自己就写出来了,但是没有用到计算属性computed

计算属性简写的话大概就是如此,firstName和lastName是默认值 然后通过双向绑定实现功能

1
2
3
4
5
computed:{
fullName(){
return this.firstName + '-' + this.lastName
}
}

监视

监视的方法名是watch,能监视某方法使用或者属性调用发生修改时候,进行执行。如果要开启深度监视的话,就要添加如下的参数,深度监视,就好比你要看一个数组,中的某个属性。

​ deep:true, //开启深度监视

如果使用监视和计算属性都使用,在方法中要使用延时执行setTimeout的话,只能在watch中使用,在computed会报错,但是如果不只是两个属性拼接,多个属性的话,使用监视就会很麻烦,使用时候是要看使用场景的。

BUG点

  • 金师附小在写调用接口的时候报了很多错误,都是一次一次改过来,慢慢解决的,一般问题有如下几点,后端请求少了或者在apifox不一样,还有测试的时候数据库的内容修改了,但是apifox上面写的测试没改,就要去问后端数据库的数据,不然写好接口没法测试。

扩展学习

金师附小写了几个ajax请求
image-20221114203302672

日总结

今天学习的内容还是蛮多的,学习状态也还不错,难度也一般,难点在对深层监视的时候,需要缕清楚逻辑,才知道自己所要监视的内容具体是哪条。Vue的话,感觉利用对象以及this代替Vue实例,很像函数,学习起来还算顺畅,就是自己的CSS和HTML不太熟悉,具体效果自己一下子搞不明白。

Vue 样式 渲染 列表

学习日期: 11.16

所学内容概述

绑定样式

  • Style类型我只在博客中使用过

  • 基本分为Class和Style两类 大致写法如下:

1
2
3
4
5
6
7
8
9
10
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br><br>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div><br><br>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br><br>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div><br><br>

条件渲染

  • v-if v-else-if v-else !template只能配合v-if使用 template不影响控制台时候看源代码的结构,节省资源

    该方法就和逻辑判断一样,如果成立显示,不成立直接消失,消失的时候F12都检查不到

  • v-show 这个就不成立的话,页面没对应的效果但是F12其实是在的,适合频繁的调用,不像if每次都要重新解析。

BUG点

  • 处理字符串的时候,刚开始不太清楚Vue中for循环中,取得的参数,刚开始以为和增强for循环一样,直接就是数组中的元素出来,但是打印完日志发现是下标,然后在自己代码上修改一下就好了。大致就是for循环出来本来是人名,但是是0 1 2 3 4这样的下标

img

扩展学习

金师附小渲染页面 数据处理

日总结

今天学习状态不错,内容较难,刚Vue学到渲染页面,就要把昨天调用的接口,中调数据,把原来页面的死数据,换成接口中调到的。刚开始还是有难度的,但是搞清楚了需求以及如何使用,一切都引刃而解了。其实调用接口就几种情况,调用完接口,去F12看参数返回的rows,提取出来给前端需要渲染的地方,一些数据是需要处理的,就自己写methods方法,把数据处理以后,传入需要调用对应数据的页面,数据处理的时候稍微有点难。总得来说正式深入Vue之前,用项目先锻炼自己,前面简单功能,以后学习的话应该会快很多。

Vue 列表渲染

学习日期: 11.17

所学内容概述

列表排序

也可以说是数组排序,数组中是对象,根据对象,有自带的方法排序 后面减前面是降序 前面减后面升序

1
2
3
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
})

对象数据更新

splice方法其实是删除以后在新增,相当于替换

1
2
this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'}) 
//删除数组下标为0的 添加1个数据 最后为新增的数据

添加数据

在需要添加的按钮先加@click事件,调用方法,然后在显示是h2中加v-if判断student.sex。底层逻辑是 给this.student 添加一个key为sex value为男的对象

this.$set(this.student,‘sex’,‘男’)

BUG点

扩展学习

  • 渲染了三个接口 一个添加重命名以及新增

下面是v-for对key的作用 相当于key的内部原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。

3. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
4. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。-->

v-for和直接for循环的区别

比如对一个数组遍历 使用v-for取出的值,直接就是元素,但是在Vue中直接使用for循环返回值是下标

日总结

今天学习内容较难,需要理解key和对象以及Vue实例之间的关系,又把几个接口能渲染的都渲染了,帮其他后端同学渲染了一些接口,学列表渲染的时候,清除明白的搞好了数组和对象如何调用属性和元素。还有之前不太清楚的for和v-for,foreach,if之类的方法复习了一遍,总的来说今天的学习状态,都是蛮不错的。

金师附小 Vue渲染

学习日期: 11.19

所学内容概述

  • 提交Execl

自己需要做一个提交execl文件,然后上传到数据库,起初以为是需要自己把execl变成json,然后传给后端接口的,因为apifox中也没有写需要传什么参数,而且在我自己写接口的时候,post请求一般都是接受一个json,后面自己写完execl转json,传的时候失败,然后告诉我后端接受的是file,我就把自己代码中未转换成json的file格式的,利用接口挂载穿进去还是不行。问了很多人,都认为是各式各样的问题,在晚上自己琢磨的时候,发现传送的时候,填的action路径,请求头,我就打印出来看了一下,发现少了解决跨域问题的点,加上去,顺利解决了,请求头也不会出现什么Accpetjson了

image-20221121134240835

BUG点

提交execl,500错误,文件传输失败,类型不匹配!解决步骤如上 代码如下

  • 就是利用element UI 实现的,之前不知道组件的实现原理,自己去搜索查询了资料,发现action后面只要填写你上传的post请求,就能直接传上file类型的,但是如果你后端是需要json的,就要加一个on-change绑定一个钩子函数对文件进行处理,然后再将处理的文件直接上传。on-change会自动收到你上传文件的参数
1
2
3
<el-upload class="upload-demo" action="/dev-api/business/teacherManage/import">
<el-button size="mini" type="primary">导入教师名单</el-button>
</el-upload>

扩展学习

遇到问题:每次运行金师附小的项目的时候,因为自己nodejs是19版本的,!16以上会对很多不兼容,所以每次我运行前都需要执行$env:NODE_OPTIONS=“–openssl-legacy-provider” 这样很麻烦,所以我就去解决

解决步骤:安装NVM,nodejs的版本管理工具,可以随意切换版本。出了点问题搞了两个多小时,因为很早我就安装过nodejs19.0.0的而且装了很多插件,hexo picgo啥的,我怕整坏了博客就没了。搞好以后发现,每次输入命令都说找不到,我就要一条一条的配置环境变量,这样太麻烦了,而且以后如果npm下东西,都要去配个变量岂不是得不偿失。就去用户和系统path用{}设置一个C盘到正式D盘的重定向,在切换版本的时候,文件夹目录也会改变,在每一个nodejs版本中都创建两个文件夹,一个node_global和node_cache。配置系统环境以及用户环境,问题解决,这样我用博客切换成19的,用Vue和其他的就用16的就好了。

image-20221118122844486

日总结

今天还是对金师附小项目的渲染,以及一些功能实现,前端的工作,学习状态其实不是很好,搞东西搞了太久,实现功能的代码其实又不多,有点付出和回报不成正比的感觉。做项目的过程中,和同学讨论也发现了我们学项目的一些问题,比如前端整合不用git,后端接口说明没在apifox描写明白,接口对不上,报错信息没有说清楚是什么类型的报错之类的问题。今天的难度还是有的,起初没沟通好,导致自己报500也不知道什么原因,晚上的时候,才搞清楚后端需要的需求,然后赶了出来,如果一开始就搞清楚,然后自己理解一些element ui的原理,应该很快就能解决了。

Vue 指令

学习日期: 11.20

所学内容概述

收集表单数据

image-20221123090944732

  • 比较重要的点我觉得在多选框和点提交时候,收集数据在控制台那边,其他比较普通css都学过

    1
    2
    3
    4
    5
    <!--userInfo.hobby是一个数组-->
    爱好:
    学习<input type="checkbox" v-model="userInfo.hobby" value="study">
    打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
    吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
  • 收集数据

    通过在form中添加<form @submit.prevent="demo">调用Vue实例中demo方法,点击提交执行。会将data中的数据收集console.log(JSON.stringify(this.userInfo)),主要是通过JSON.stringify所以如果我们需要用数据进行处理的话,就可以在方法中调用这个方法,是一个JSON返回。

过滤器

前端的过滤器其实是对数据的处理,和后端过滤请求是不一样的。案例就是对时间戳的时间格式的处理。使用方法是在需要处理的参数后面
{{time **|** timeFormater}},也可以多次使用。 全局过滤器需要在Vue实例外面用Vue.就能实现了

1
2
3
4
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})

指令

分为普通的内置指令和自定义指令

内置指令

v-text指令

1.作用:向其所在的节点中渲染文本内容。 2.与插值语法的区别:v-text会替换掉节点中的内容,则不会。

v-html指令

1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,则不会。
(2).v-html可以识别html结构。

v-cloak指令

1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
2.使用css配合v-cloak可以解决网速慢时页面展示出的问题。

v-once指令(鸡肋)

1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

v-pre指令

1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

自定义指令

在Vue实例中directives可以自定义指令分为函数式和对象式

big是函数式下面的fbind是对象式,一般有三种对象式的回调

(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
directives:{
big(element,binding){
console.log('big',this) //注意此处的this是window
// console.log('big')
element.innerText = binding.value * 10
},
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}

BUG点

日总结

学习了收集表单信息和过滤器以及指令。指令部分在Vue中部分还是很重要的,可以放一些参数,或者实现很多功能,其他部分的话觉得还是没什么难度。

Vue 生命周期和组件

学习日期: 11.23

所学内容概述

生命周期

  • 生命周期回调函数、生命周期函数、生命周期钩子。

作用

  • Vue在关键时刻帮我们调用的一些特殊名称的函数。

  • 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

  • 生命周期函数中的this指向是vm 或 组件实例对象。

生命周期其实是Vue实例的函数,可以理解成一个对象中封装了的方法。执行完会销毁,销毁后。之前的虚拟DOM转换为真实DOM的DOM会保留。

常用钩子

1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

非单项组件

  • Vue使用组件的三大步骤
    • 定义组件(创建组件)
    • 注册组件
    • 使用组件(写组件标签)

定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
//第一步:创建hello组件
const hello = Vue.extend({
template:`
<div>
<h2>你好啊!!{{name}}</h2>
</div>
`,
data(){
return {
name:'Tom'
}
}
})

注册组件

在new Vue中写components方法是局部组件

Vue.component(‘hello’,hello)是全局组件

写标签

也就是调用自定义组件

<school></school>

或者

<school/>

组件嵌套

在组件中注册局部组件component,在组件中注册的组件是该组件的子类,也是嵌套。

扩展学习

383.赎金信

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false
magazine 中的每个字符只能在 ransomNote 中使用一次。

自己做出来了感觉题目不是很难,但是自己用了这么多行代码才实现,耗时也有点长了,逻辑性还是很清晰的,就似乎有点太麻烦了,所以我自己的方法不是最优解。找到一个优解在下面。利用26个空数组,和char的数字形式,当作数组的下标,每存在一次+1,调用一次减1,最后判断是否<0了,<0就为false,全部都不小于0就为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public static boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()){ //长度小于直接返回
return false;
}
Map<String, Integer> map = new HashMap<>(); //统计magazine 每个字母和出现次数
for (String i : magazine.split("")){
int count = 1;
if (map.get(i) == null){
map.put(i,count);
continue;
}
map.computeIfPresent(i,(key, value) -> value + 1);
}
Map<String, Integer> map1 = new HashMap<>(); //ransomNote 每个字母和出现次数
for (String x : ransomNote.split("")){
if (map.get(x) == null){ //有字母不在magazine中直接返回
return false;
}
int count1 = 1;
if (map1.get(x) == null){
map1.put(x,count1);
continue;
}
map1.computeIfPresent(x,(key, value) -> value + 1); //x对应的value + 1
}
for (String y : map1.keySet()){ //遍历key 次数大于返回false 全部通过才返回true
if ( map1.get(y) > map.get(y)){
return false;
}
}
return true;
}
}

优解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()) {
return false;
}
int[] cnt = new int[26];
for(char c : magazine.toCharArray()) {
cnt[c - 'a'] ++;
}
for(char c : ransomNote.toCharArray()) {
cnt[c - 'a'] --;
if(cnt[c - 'a'] < 0) {
return false;
}
}
return true;
}

日总结

今天的学习状态一般的,可能是因为今天的学习难度提上去了,是对于组件的各种理解。开始是对Vue的声明周期的理解,接受起来还是很容易的,和大部分的编程语言的生命周期都差不多。到了非单组件部分就有点难了,引出了一个新的概念VC,看弹幕和博客,我理解的话,有点类似于子类父类,Vue(Vm)是VC的父类,所以VC可以用Vm的各种东西,如果VC中没有设置的属性,它会往上面找,会找到Vm,最上级的是Object。今天全是理解上面的情况,听起来也非常困,看博客又不是很明白。

Vue单组件分析

学习日期: 11.26

所学内容概述

单组件

最常用的默认 开发就是这个样子 需要下载Vue脚手架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
export default {
name:'School',
data(){
return {
name:'尚硅谷',
address:'北京昌平'
}
},
methods: {
showName(){
alert(this.name)
}
},
}
</script>

脚手架结构

个人理解:

  • 创建完vue目录如下
  • image-20221202093053971

执行步骤

  1. 项目入口文件在main.js中,通过引入vue和App(最高级的vue文件)
  2. 在APP中已经引入其他的组件
  3. 其他组件中按照vue语法写页面以及功能
  4. 终端输入npm run sever 跑起来

对main.js理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
项目入口文件
*/
//引入VUe
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭vue提示
Vue.config.productionTip = false
/*
引用的vue是vue.runtime.xxx.js
和完整版的vue的区别
vue.js是完整版的,包含核心功能和模板解析器
vue.runtime.xxx.js是运行时候的vue 只有核心功能没有模版解析器
需要通过render函数,收到createElement函数指定内容,我下面写的就是创建元素为App的而App在上面我已经引入他会自动找到App,然后通过App引入的组件,创建页面
*/
new Vue({
el:'#app',
render: h => h(App),
})//el:'#app'

BUG点

刚开始不知道啥原因,之前报的错误更多,然后把文件名和属性名都改成了大驼峰命令规则,少了一个错误,这里又报错,CSDN搜索说是代码不规范,用vscode自带的shift+alt+f格式化代码以后,然后又报错,再搜发现是有个地方缩进有问题。命名必须双驼峰不知道为什么…

image-20221201170606432

扩展学习

力扣算法题

  • 链表,java中似乎是没有需要自己创建一个链表类

  • 题目不难,但是使用链表的话,逻辑就要不一样了,需要一个慢指针和一个快指针,快指针走的速度是慢指针的两倍(一个next和两个next)。当快指针到null或者快指针下一个是null,当前的慢指针所指的位置就是中间结点。

给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}

日总结

在学vue讲原理性的时候也听到挺明白的,分析了脚手架的原理,从哪里开始找到哪里的组件,调用,自己都顺了一遍,做了一题力扣算法题,不是很难,但是因为没系统学过数据结构,不太明白链表是什么,解决方法是模仿C中的p指针的。

Vue关键字

学习日期: 11.29

所学内容概述

ref的使用

  • ref之前在项目中经常遇到都不知道是干嘛的,今天知道了,他可以调用真实DOM,在组件中写入ref属性

image-20221205083116622

我用三条控制台打印看结果 如果是自定义组件的ref 调用的时候就是实例对象VC

1
2
3
console.log(this.$refs.title); //真实DOM
console.log(this.$refs.btn); //真实DOM
console.log(this.$refs.sch); //School组件的实例对象 VC

image-20221205083212800

props配置

  • 接受数据并声明

    image-20221205084125196

  • 接收的方法大致有三种,比较常用的还是最简单的那种,因为类型之类的一般都固定好了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //简单声明接收
    props: ['name','sex','age'],
    //接收的同时对数据进行类型限制
    props:{
    name:String,
    sex:String,
    age:Number
    },
    props:{
    name:{
    type:String,
    required:true //true 为必填
    },
    age:{
    type:Number,
    default:99
    },
    sex:{
    type:String,
    default:"男", //默认值
    required:true
    }
    }

mixin混合

  • 利用mixin混合就是复用配置 可以把多个组件共用的配置提取成一个混入对象
  • 我觉得在组件需要大量复用配置的时候,比较好用
  • 混合的时候,如果配置之间有冲突,以自定义组件的为主
  1. 定义混合mixin 创建js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    export const hunhe = {
    methods: {
    showName(){
    alert(this.name)
    }
    },
    mounted() {
    console.log('你好啊!')
    },
    }
    export const hunhe2 = {
    data() {
    return {
    x:100,
    y:200
    }
    },
    }
  2. 使用混入:需要导入混合的js
    全局混入:Vue.mixin(xxx) 在main.js中使用

  3. 局部混入:mixins:['hunhe','hunhe2']

插件

  • 这个功能很nb,也超级使用,导入插件并使用的话,一些功能可以随心所欲的使用
  1. 功能:增强Vue 相当于合理的外挂

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件 其实就是写一个js文件 然后写一个install方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    export default {
    install(Vue,x,y,z){
    console.log(x,y,z)
    //全局过滤器
    Vue.filter('mySlice',function(value){
    return value.slice(0,4)
    })
    //定义全局指令
    Vue.directive('fbind',{
    //指令与元素成功绑定时(一上来)
    bind(element,binding){
    element.value = binding.value
    },
    //指令所在元素被插入页面时
    inserted(element,binding){
    element.focus()
    },
    //指令所在的模板被重新解析时
    update(element,binding){
    element.value = binding.value
    }
    })
    //定义混入
    Vue.mixin({
    data() {
    return {
    x:100,
    y:200
    }
    },
    })
    //给Vue原型上添加一个方法(vm和vc就都能用了)
    Vue.prototype.hello = ()=>{alert('你好啊')}
    }
    }
  4. 使用在main.js中 先引入插件包 然后Vue.use(引入的名字,需要传递的数据)

scoped样式

  • 解决style问题的
  • style如果自己定义好名字了,在App.vue中导入,如果vue文件同时调用这个方法,就会出现谁后导入用谁的风格,比如student.vue和school.vue中style都有.demo方法,在App.vue导入两个vue,谁后导入应用谁的demo方法。
  • 但是如果有scoped面试局部应用,在谁页面就用谁的style样式 <style scoped>

扩展学习

今天开始每天三-四道sql题目 力扣练手

image-20221205150658629

日总结

发现自己刷完sql或者算法题,再去看视频,状态会好上不少。最近做的题目,主要是让自己先不要忘记,练手,过段时间就要去给自己提升一下难度了,抽空去学习一下数据结构。稍微不太理解的点在ref中,只知道会调用DOM。还不清楚拿到真实DOM后,能进行什么操作。

Vue TodoList案例

学习日期: 11.30

所学内容概述

TodoList案例

  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。 我在App.vue中设置的todos数组就是一个状态提升

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    props我觉得是该案例比较重要的点 不同的vue可以相互传数据 在之前帮前端做项目的时候,遇到过这样的问题,但是当时我们都不会,用了其他方式实现,这个很方便

    ​ (1).父组件 ==> 子组件 通信 子组件直接用父组件传props的东西

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)父组件要用子组件的数据需要写一个函数接收

  3. 使用v-model时:v-model绑定的值不能是props传过来的值

    • 如果同时要get和修改Props数据,要绑定一个computed计算方法,写get和set

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      isAll:{
      get(){
      return this.doneTotal === this.countTodo && this.countTodo > 0
      },
      set(value){
      this.todos.forEach(todo => {
      todo.done = value
      });
      }
      },

扩展学习

今天做了三题sql题目,不是单纯的查找了,有修改和一个删除的,题目很简单的,但是之前学数据清洗的时候没有做update和delete的操作,所以不太熟悉

1
2
3
4
5
6
7
8
9
# 删除person表中email相同的 id大的那个
DELETE p1.*
from
Person p1,
Person p2
where
p1.email = p2.email
and
p1.Id > p2.Id

日总结

今天做了一个经典的TodoList案例,在之前javaweb’的时候也写过,挺简单的,但是在这Vue中所学到的知识,基本都在案例中有所体现,之前不知道如何游刃有余使用的computed,props等通过这个案例也都融会贯通了,根据需求自己也都能实现该项目了。难度一般吧,希望前面小基础学完后面的能学习顺利下去。

webStorage

学习日期: 12.05

所学内容概述

学习本地存储,自己应用到之前做的TodoList案例当中去。

本地存储

存储在浏览器,分两种。为localStorage,sessionStorage。只是方法名不一样,存储位置也不一样。对象名不一样方法名是一样的。都在存在浏览器中,唯一不同的点是 localStorage重启以后数据还在。但是sessionStorage重启后,数据就清空了。两个存储点都只能存放5M的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script type="text/javascript" >
let p = {name:'张三',age:18}
function saveData(){
localStorage.setItem('msg','hello!!!')
localStorage.setItem('msg2',666)
localStorage.setItem('person',JSON.stringify(p))
}
function readData(){
console.log(localStorage.getItem('msg'))
console.log(localStorage.getItem('msg2'))
const result = localStorage.getItem('person')
console.log(JSON.parse(result))
// console.log(localStorage.getItem('msg3'))
}
function deleteData(){
localStorage.removeItem('msg2')
}
function deleteAllData(){
localStorage.clear()
}
</script>

应用到TodoList就是这样的状态,值得注意的是,需要加一个watch监控,并且深度监控,才能监控到数组中对象的变化。还有一个地方是JSON.stringify,可以把对象(JSON语法)还原成原本的样子,这样才能添加到web存储空间。

1
2
3
4
5
6
7
8
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
}

image-20221208193001540

BUG点

在做向右轮转的时候,编译器报错了,刚开始没想到只要对每个下标 进行 取余就行 自己想到的是一直减去数组长度,直到下标值是最接近0的正数,因为空间复杂度为2了导致运行时间会很长,超时了,代码修改完就没问题了。

image-20221209152507954

扩展学习

今天做了两道算法题,都还是有难度的,解题思路很多,自己两个都是先用暴力做出来,然后再去想着如何优化。第一题自己优化出来了,后面那题稍微难一点,题解两种优化方式自己只会第二种,第一种有点太巧妙了。自己题解写在代码中

//输入一个升序的数组,然后返回一个数据各位置平方,也是升序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public int[] sortedSquares(int[] nums) {
//第一种 自己就是直接每位平方再重新排序 执行时间5ms
//for (int i = 0; i < nums.length; i++) {
// nums[i] = nums[i]*nums[i];
//}
//Arrays.parallelSort(nums);
//return nums;
//下面是双指针
//思路 因为是升序,所以数的平方的最大值一定是两边,把这个大的数 放新数组右边 然后两界限往内夹
int[] result = new int[nums.length];
int k = result.length-1; //给result倒着插入的下标
int right = result.length-1; //右界限
int left = 0; //左界限
while(left <= right){ //当左边等于右边 停止
if (nums[left]*nums[left] < nums[right]*nums[right]){ //两边判断大小 右边大
result[k--] = nums[right]*nums[right]; //大的插入最新数组右边 右边下标-1
right--; //右界限往内移1
}else{ //左边大
result[k--] = nums[left]*nums[left]; //大的插入最新数组右边 左边下标-1
left++; //左界限往内移1
}
}
return result;
}

日总结

今天的任务不是很重,学习难度也比较简单,如果之前自己会的话,之前做前端项目的时候,就可以把图片的地址存进sessionStorage中去,这样不用每次切换页面的时候都需要发送请求给后端,从数据库中拿路径了,list的时候就不用一直挂载了,只要挂载once就好了。其他的时候直接从本地中读取。做算法的时候总是会需要超时问题,因为自己思路都是分析要求,实现要求,从脑子常理判断,然后再去实现,这样不算是实现算法的逻辑,还是需要多做题,培养逻辑,虽然自己能把问题解决,但是运行时长和内存都不是优解。尤其是要考虑好空间复杂度的问题,用自带的方法,也会有坑(可能底层逻辑也是循环)。

Vue 组件自定义事件

学习日期: 12.06

所学内容概述

自定义组件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

    2. 第二种方式,在父组件中:

      1
      2
      3
      4
      5
      <Demo ref="demo"/>
      ......
      mounted(){
      this.$refs.xxx.$on('atguigu',this.test)
      }
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('atguigu',数据)

  5. 解绑自定义事件this.$off('atguigu')

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  7. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中要么用箭头函数,否则this指向会出问题!

应用TodoList

在Header和Footer中把子给父传数据中,应用自定义组件的方法。差不多就两个 自己有一个删除的 自己直接写到Footer里面了。

image-20221212194306954

值得注意的是在触发自定义事件的时候需要传入数据.把todoObj传进去

this.$emit('addTodo1',todoObj)

扩展学习

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

1
2
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void rotate(int[] nums, int k) {
//int count = nums.length;
//int newNums[] = new int[count];
//for (int i = 0; i < count; i++) {
// newNums[(i+k) % count] = nums[i];
//}
//System.arraycopy(newNums, 0, nums, 0, count); //打印
//先全部反转 再将下标为k-1分割线 两边反转
reversal(nums,0,nums.length-1);
reversal(nums,0,k-1);
reversal(nums,k,nums.length-1);
}
//方法二
//反转 下标为start 和 end 间的反转
public int[] reversal(int[] nums,int start,int end){
while (start<end){
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
return nums;
}

日总结

今天的学习任务不是很重,难度一般。学习了自定义组件,在子传父数据的时候使用,创建解绑和触发,其实就是加完@,然后再去组件中触发事件,需要传数据的时候,传入数据,然后就是自己对是对TodoList案例的改造。今天做的力扣算法题难度一般,总的来说今天的状态还不错了。

Vue TodoList案例完善

学习日期: 12.27

所学内容概述

全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    1
    2
    3
    4
    5
    6
    7
    new Vue({
    ......
    beforeCreate() {
    Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    },
    ......
    })
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      1
      2
      3
      4
      5
      6
      7
      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.$bus.$on('xxxx',this.demo)
      }
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

消息订阅与发布(pubsub)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      1
      2
      3
      4
      5
      6
      7
      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)取消订阅。

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

扩展学习

力扣算法练习 两个一般难度 暴力做的出来 算法优解理解了很久 两题做了一下午

日总结

今日是寒假学习一来第一天开始记笔记,之前学习的东西以及算法题目啥的都没有记,觉得这样自己学习的东西会很快忘记掉,所以打算寒假学习有时间还是要把自己所学的东西记下来,这样效率也会高不少,今天的学习内容不是很难,还是对案例的优化。重点在全局事件总线,它和消息订阅所实现的功能差不多的,但是还是全局事件比较方便而且不依靠第三方插件。然后是对文本框点击编辑按钮后,对文本框的聚焦,会发现是实现功能上先后逻辑顺序的问题导致无法实现的,我想到的是用一个setTimeout函数延时执行,也能实现,但是功能实用性不强就是了,nextTick就能很好的解决这个问题,在下一次DOM更新结束后执行,学习状态还不错。

Vue 动画

学习日期: 12.28

所学内容概述

css的动画基础

离场和进场的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.hello-enter-active{
animation: atguigu 0.5s linear;
}
.hello-leave-active{
animation: atguigu 0.5s linear reverse;
}
@keyframes atguigu {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
  • 需要在vue中调用的时候要用transition标签给包裹起来,可以自己设置名字名字为.hello-leave-active的第一位自己随意设置
  • 添加apper属性开始刷新就会有动画

Vue的过度效果

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 设置刷新就实现动画效果*/
h1{
background-color: orange;
transition: 0.5s linear;
}
/* 进入的起点 ,离开的终点*/
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
/* 离开的起点 ,开始的终点*/
.hello-enter-to,.hello-leave{
transform: translateX(0);
}

用第三方动画animate

1
npm install animate.css #安装第三方模块

在App.vue中导入

使用如下 离开和进入的动画是可选的 在animate.style官方可见使用和用法

image-20221228201357552

日总结

今天学了动画的效果,感觉还是不错的,第三方的动画库里面有丰富的动画,进入或者退出的时候使用。自己做的效果也还可以,没什么大的问题,学习效率一般,只学了早上和晚上的时间。等过两天身体好了,加快一下学习速度,在寒假结束之前把Vue和数据结构和算法学光。

Vue配置代理 --解决跨域问题

方法一

在vue.config.js中添加如下配置:

1
2
3
devServer:{
proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

VUE github案例

案例目标 输入github自带的api地址 请求该地址获取返回值 并进行处理展示在列表中

网页分两部分 上面是Search组件 发请求以及收集请求数据 下面为列表展示部分 将收集到的请求存入List中 用到了全局事件总线$bus

image-20230208185854818

具体实现代码如下

因为要实现全局事件总线所以要在main.js中加入配置

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false

new Vue({
el:'#app',
render:h => h(App),
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
})

Search.Vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyword"/>&nbsp;
<button @click="searchUsers">Search</button>
</div>
</section>
</template>

<script>
import axios from 'axios'
export default {
name: "Search",
data () {
return {
keyword:''
}
},
methods: {
searchUsers(){
// 传入第一次请求数据
this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errorMsg:'',usersList:[]})
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功',response.data.items)
// 如果请求成功 将返回的值传给List
this.$bus.$emit('updateListData',{isLoading:false,errorMsg:'',usersList:response.data.items})
},
error => {
console.log('请求失败',error.message)
// 请求失败 返回值为报错信息
this.$bus.$emit('updateListData',{errorMsg:error.message,usersList:[]})
}
)
}
}
}
</script>

List.vue

收集数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<div class="row">
<!-- 还没点击过搜索不显示 用v-for 结合动态属性 -->
<div v-show="info.usersList.length" class="card" v-for="(user) in info.usersList" :key="user.id">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width:100px'/>
</a>
<p class="card-text">{{user.login}}</p>
</div>
<!-- 第一次登陆 -->
<h1 v-show="info.isFirst">欢迎使用</h1>
<!-- 加载中 -->
<h1 v-show="info.isLoading">加载中........</h1>
<!-- 报错 -->
<h1 v-show=info.errorMsg>报错啦: {{info.errorMsg}}</h1>
</div>
</template>

<script>
export default {
name: "List",
data () {
return {
//返回值封装成集合
info:{
isFirst:true,
isLoading:false,
errorMsg:'',
usersList:[]
}
}
},
mounted(){
this.$bus.$on('updateListData',(dataObject) => {
console.log('我是List组件我收到了数据',dataObject)
// this.info = dataObject
//es6语法 如果前面的...this.info 有的 后面dataObject也有 就替换 dataObject没有的 就用前面的为数据
//因为后面我传入dataObject的时候丢掉了isFirst 但是第一次还没传参的时候是有的 为防止丢失 使用了这样的方法
this.info = {...this.info,...dataObject}
})
}

};
</script>

<style>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}

.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}

.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}

.card-text {
font-size: 85%;
}

</style>

Vue插槽

学习日期: 2.14

所学内容概述

Vue插槽的操作

分为三块 分别是默认插槽 具名插槽 作用域插槽

默认插槽

1
2
3
4
5
6
7
8
9
10
11
12
父组件
<Category>
<div>
html结构
</div>
</Category>
子组件
<template>
<div>
<slot>插槽默认内容</slot>
</div>
</template>

具名插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
父组件中:
<Category>
<template slot="center">
<div>html结构1</div>
</template>

<template v-slot:footer>
<div>html结构2</div>
</template>
</Category>
子组件中:
<template>
<div class="category">
<h3>{{this.title}}分类</h3>
<slot name="center">默认值1</slot>
<slot name="footer">默认值2</slot>
</div>
</template>

作用域插槽

1. 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

2. 具体编码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
父组件中:
<Category>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</Category>

<Category>
<template slot-scope="scopeData">
<!-- 生成的是h4标题 -->
<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
</template>
</Category>
子组件中:
<template>
<div>
<slot :games="games"></slot>
</div>
</template>

<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
}
},
}
</script>

扩展学习

蓝桥杯2022真题 统计单词每个字母出现的次数取最多的 打印字母和出现的次数 使用了Ascii码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
String word = scan.next();
int[] count = new int[26];
for(int i=0;i<word.length();++i){
char num = word.charAt(i);
count[num-97] += 1;
}
int maxNum = count[0];
char maxWord = 'a';
for(int x=0;x<26;x++){
if(maxNum < count[x]){
maxNum = count[x];
maxWord = (char) (x+97);
}
}
System.out.println(maxWord);
System.out.println(maxNum);



scan.close();
}
}

日总结

今天的学习内容不是很多,刚开学还没调整好状态,先对寒假的学习内容看了一点,然后制订了一下大数据以及后面Java进阶的计划。并把近一年的Java学习,打算用xmind做一个思维导图的总结,也当做回顾一下Java SE的基础知识,看哪些难点自己忘记了,再去巩固一下。后面的Java学习计划打算等大数据搞完,再重新调整制定一下。总得来说,今天学习的新知识不是很多,也不是很难,主要是复习。

Vuex(重点)

学习日期: 3.21

所学内容概述

1.概念

​ 在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

2.使用情况

​ 多个组件需要共享数据时

3.搭建vuex环境

  1. 创建文件:src/store/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex插件
    Vue.use(Vuex)

    //准备actions对象——响应组件中用户的动作
    const actions = {}
    //准备mutations对象——修改state中的数据
    const mutations = {}
    //准备state对象——保存具体的数据
    const state = {}

    //创建并暴露store
    export default new Vuex.Store({
    actions,
    mutations,
    state
    })
  2. main.js中创建vm时传入store配置项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ......
    //引入store
    import store from './store'
    ......

    //创建vm
    new Vue({
    el:'#app',
    render: h => h(App),
    store
    })

4.基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //引用Vuex
    Vue.use(Vuex)

    const actions = {
    //响应组件中加的动作
    jia(context,value){
    // console.log('actions中的jia被调用了',miniStore,value)
    context.commit('JIA',value)
    },
    }

    const mutations = {
    //执行加
    JIA(state,value){
    // console.log('mutations中的JIA被调用了',state,value)
    state.sum += value
    }
    }

    //初始化数据
    const state = {
    sum:0
    }

    //创建并暴露store
    export default new Vuex.Store({
    actions,
    mutations,
    state,
    })
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

5.getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

  2. store.js中追加getters配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ......

    const getters = {
    bigSum(state){
    return state.sum * 10
    }
    }

    //创建并暴露store
    export default new Vuex.Store({
    ......
    getters
    })
  3. 组件中读取数据:$store.getters.bigSum

6.四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    1
    2
    3
    4
    5
    6
    7
    computed: {
    //借助mapState生成计算属性:sum、school、subject(对象写法)
    ...mapState({sum:'sum',school:'school',subject:'subject'}),

    //借助mapState生成计算属性:sum、school、subject(数组写法)
    ...mapState(['sum','school','subject']),
    },
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    1
    2
    3
    4
    5
    6
    7
    computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    ...mapGetters(['bigSum'])
    },
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    1
    2
    3
    4
    5
    6
    7
    methods:{
    //靠mapActions生成:incrementOdd、incrementWait(对象形式)
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

    //靠mapActions生成:incrementOdd、incrementWait(数组形式)
    ...mapActions(['jiaOdd','jiaWait'])
    }
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    1
    2
    3
    4
    5
    6
    7
    methods:{
    //靠mapActions生成:increment、decrement(对象形式)
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),

    //靠mapMutations生成:JIA、JIAN(对象形式)
    ...mapMutations(['JIA','JIAN']),
    }

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

扩展学习

模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    const countAbout = {
    namespaced:true,//开启命名空间
    state:{x:1},
    mutations: { ... },
    actions: { ... },
    getters: {
    bigSum(state){
    return state.sum * 10
    }
    }
    }

    const personAbout = {
    namespaced:true,//开启命名空间
    state:{ ... },
    mutations: { ... },
    actions: { ... }
    }

    const store = new Vuex.Store({
    modules: {
    countAbout,
    personAbout
    }
    })
  3. 开启命名空间后,组件中读取state数据:

    1
    2
    3
    4
    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
  4. 开启命名空间后,组件中读取getters数据:

    1
    2
    3
    4
    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
  5. 开启命名空间后,组件中调用dispatch

    1
    2
    3
    4
    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
  6. 开启命名空间后,组件中调用commit

    1
    2
    3
    4
    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

日总结

今天的学习内容是vuex,感觉它的功能就是像是一个Java中的static,设置全局变量或者全局方法,比之前的组件间通讯都好用,流程梳理明白掌握起来也不难,有复杂计算逻辑的需要放Mutations中,普通的就放actions中就行,所有可以使用计算属性的,都可以用mapstate,模块化是算扩展了,也不算很难,以后为了规范性以及代码可读性,估计还是使用vuex模块化的比较多。

Vue路由(重点)

学习日期:3.26-3.27

所学内容概述

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //引入VueRouter
    import VueRouter from 'vue-router'
    //引入Luyou 组件
    import About from '../components/About'
    import Home from '../components/Home'

    //创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
    routes:[
    {
    path:'/about',
    component:About
    },
    {
    path:'/home',
    component:Home
    }
    ]
    })

    //暴露router
    export default router
  4. 实现切换(active-class可配置高亮样式)

    1
    <router-link active-class="active" to="/about">About</router-link>
  5. 指定展示位置

    1
    <router-view></router-view>

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(多级路由)

  1. 配置路由规则,使用children配置项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    routes:[
    {
    path:'/about',
    component:About,
    },
    {
    path:'/home',
    component:Home,
    children:[ //通过children配置子级路由
    {
    path:'news', //此处一定不要写:/news
    component:News
    },
    {
    path:'message',//此处一定不要写:/message
    component:Message
    }
    ]
    }
    ]
  2. 跳转(要写完整路径):

    1
    <router-link to="/home/news">News</router-link>

4.路由的query参数

  1. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>

    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link
    :to="{
    path:'/home/message/detail',
    query:{
    id:666,
    title:'你好'
    }
    }"
    >跳转</router-link>
  2. 接收参数:

    1
    2
    $route.query.id
    $route.query.title

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      {
      path:'/demo',
      component:Demo,
      children:[
      {
      path:'test',
      component:Test,
      children:[
      {
      name:'hello' //给路由命名
      path:'welcome',
      component:Hello,
      }
      ]
      }
      ]
      }
    2. 简化跳转:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!--简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>

      <!--简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>

      <!--简化写法配合传递参数 -->
      <router-link
      :to="{
      name:'hello',
      query:{
      id:666,
      title:'你好'
      }
      }"
      >跳转</router-link>

6.路由的params参数

  1. 配置路由,声明接收params参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    {
    path:'/home',
    component:Home,
    children:[
    {
    path:'news',
    component:News
    },
    {
    component:Message,
    children:[
    {
    name:'xiangqing',
    path:'detail/:id/:title', //使用占位符声明接收params参数
    component:Detail
    }
    ]
    }
    ]
    }
  2. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>

    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link
    :to="{
    name:'xiangqing',
    params:{
    id:666,
    title:'你好'
    }
    }"
    >跳转</router-link>

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    1
    2
    $route.params.id
    $route.params.title

7.路由的props配置

​ 作用:让路由组件更方便的收到参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
name:'xiangqing',
path:'detail/:id',
component:Detail,

//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}

//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true

//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //$router的两个API
    this.$router.push({
    name:'xiangqing',
    params:{
    id:xxx,
    title:xxx
    }
    })

    this.$router.replace({
    name:'xiangqing',
    params:{
    id:xxx,
    title:xxx
    }
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退

10.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    1
    2
    3
    <keep-alive include="News"> 
    <router-view></router-view>
    </keep-alive>

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

12.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //全局前置守卫:初始化时执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
    console.log('beforeEach',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
    next() //放行
    }else{
    alert('暂无权限查看')
    // next({name:'guanyu'})
    }
    }else{
    next() //放行
    }
    })

    //全局后置守卫:初始化时执行、每次路由切换后执行
    router.afterEach((to,from)=>{
    console.log('afterEach',to,from)
    if(to.meta.title){
    document.title = to.meta.title //修改网页的title
    }else{
    document.title = 'vue_test'
    }
    })
  4. 独享守卫:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    beforeEnter(to,from,next){
    console.log('beforeEnter',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    if(localStorage.getItem('school') === 'atguigu'){
    next()
    }else{
    alert('暂无权限查看')
    // next({name:'guanyu'})
    }
    }else{
    next()
    }
    }
  5. 组件内守卫:

    1
    2
    3
    4
    5
    6
    //进入守卫:通过路由规则,进入该组件时被调用
    beforeRouteEnter (to, from, next) {
    },
    //离开守卫:通过路由规则,离开该组件时被调用
    beforeRouteLeave (to, from, next) {
    }

13.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

BUG

路由跳转to中参数跳路径的时候,不知道怎么拼接字符串,里面好像不太方便,然后前端的告诉我使用模版字符串,"``"双引号中两个 dian便能使用,在用${参数}

image-20230329203707672

总结

这两天把Vue的重中之重的路由学完了,基本都是掌握了,内容还是挺多的,但是我觉得不是很难,之前给金师附小渲染的时候,主要接触的就是路由,所以掌握就没什么难度,我的理解就是通过路由实现跳转不同的组件,不用刷新页面,然后就是缓存以及守卫,能控制路由跳转时候实现一些功能,也是比较简单,就几个方法几个参数,

Vue3(重点)

学习日期:3.28-3.30

所学内容概述

Vue3快速上手

1.Vue3简介

2.Vue3带来了什么

1.性能的提升
  • 打包大小减少41%

  • 初次渲染快55%, 更新渲染快133%

  • 内存减少54%

2.源码的升级
  • 使用Proxy代替defineProperty实现响应式

  • 重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript
  • Vue3可以更好的支持TypeScript
4.新的特性
  1. Composition API(组合API)

    • setup配置
    • ref与reactive
    • watch与watchEffect
    • provide与inject
  2. 新的内置组件

    • Fragment
    • Teleport
    • Suspense
  3. 其他改变

    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符

一、创建Vue3.0工程

1.使用 vue-cli 创建

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

1
2
3
4
5
6
7
8
9
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.使用 vite 创建

官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite

vite官网:https://vitejs.cn

  • 什么是vite?—— 新一代前端构建工具。
  • 优势如下:
    • 开发环境中,无需打包操作,可快速的冷启动。
    • 轻量快速的热重载(HMR)。
    • 真正的按需编译,不再等待整个应用编译完成。
  • 传统构建 与 vite构建对比图

image-20230328155027874

image-20230328155054495

1
2
3
4
5
6
7
8
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

二、常用 Composition API

官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html

1.拉开序幕的setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。
  2. setup是所有Composition API(组合API)“ 表演的舞台 ”
  3. 组件中所用到的:数据、方法等等,均要配置在setup中。
  4. setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  5. 注意点:
    1. 尽量不要与Vue2.x配置混用
      • Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
      • 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
      • 如果有重名, setup优先。
    2. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

2.ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

3.reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

4.Vue3.0中的响应式原理

vue2.x的响应式
  • 实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

      1
      2
      3
      4
      Object.defineProperty(data, 'count', {
      get () {},
      set () {}
      })
  • 存在问题:

    • 新增属性、删除属性, 界面不会更新。
    • 直接通过下标修改数组, 界面不会自动更新。

Vue3.0的响应式

5.reactive对比ref

  • 从定义数据角度对比:
    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

6.setup的两个注意点

  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit

7.计算属性与监视

1.computed函数
  • 与Vue2.x中computed配置功能一致

  • 写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import {computed} from 'vue'

    setup(){
    ...
    //计算属性——简写
    let fullName = computed(()=>{
    return person.firstName + '-' + person.lastName
    })
    //计算属性——完整
    let fullName = computed({
    get(){
    return person.firstName + '-' + person.lastName
    },
    set(value){
    const nameArr = value.split('-')
    person.firstName = nameArr[0]
    person.lastName = nameArr[1]
    }
    })
    }
2.watch函数
  • 与Vue2.x中watch配置功能一致

  • 两个小“坑”:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //情况一:监视ref定义的响应式数据
    watch(sum,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
    },{immediate:true})

    //情况二:监视多个ref定义的响应式数据
    watch([sum,msg],(newValue,oldValue)=>{
    console.log('sum或msg变化了',newValue,oldValue)
    })

    /* 情况三:监视reactive定义的响应式数据
    若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
    若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
    */
    watch(person,(newValue,oldValue)=>{
    console.log('person变化了',newValue,oldValue)
    },{immediate:true,deep:false}) //此处的deep配置不再奏效

    //情况四:监视reactive定义的响应式数据中的某个属性
    watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
    },{immediate:true,deep:true})

    //情况五:监视reactive定义的响应式数据中的某些属性
    watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
    },{immediate:true,deep:true})

    //特殊情况
    watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
    },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
3.watchEffect函数
  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
    1
    2
    3
    4
    5
    6
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
    watchEffect(()=>{
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
    })

8.生命周期

vue2.x的生命周期lifecycle_2
vue3.0的生命周期lifecycle_2
  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
    • beforeCreate===>setup()
    • created=======>setup()
    • beforeMount ===>onBeforeMount
    • mounted=======>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated =======>onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted =====>onUnmounted

9.自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。

  • 类似于vue2.x中的mixin。

  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

10.toRef

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,'name')

  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

三、其它 Composition API

1.shallowReactive 与 shallowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。

  • 什么时候使用?

    • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

2.readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据被修改时。

3.toRaw 与 markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象。
    • 应用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

4.customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

  • 实现防抖效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <template>
    <input type="text" v-model="keyword">
    <h3>{{keyword}}</h3>
    </template>

    <script>
    import {ref,customRef} from 'vue'
    export default {
    name:'Demo',
    setup(){
    // let keyword = ref('hello') //使用Vue准备好的内置ref
    //自定义一个myRef
    function myRef(value,delay){
    let timer
    //通过customRef去实现自定义
    return customRef((track,trigger)=>{
    return{
    get(){
    track() //告诉Vue这个value值是需要被“追踪”的
    return value
    },
    set(newValue){
    clearTimeout(timer)
    timer = setTimeout(()=>{
    value = newValue
    trigger() //告诉Vue去更新界面
    },delay)
    }
    }
    })
    }
    let keyword = myRef('hello',500) //使用程序员自定义的ref
    return {
    keyword
    }
    }
    }
    </script>

5.provide 与 inject

  • 作用:实现祖与后代组件间通信

  • 套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

  • 具体写法:

    1. 祖组件中:

      1
      2
      3
      4
      5
      6
      setup(){
      ......
      let car = reactive({name:'奔驰',price:'40万'})
      provide('car',car)
      ......
      }
    2. 后代组件中:

      1
      2
      3
      4
      5
      6
      setup(props,context){
      ......
      const car = inject('car')
      return {car}
      ......
      }

6.响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

四、Composition API 的优势

1.Options API 存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

2.Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

五、新的组件

1.Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

2.Teleport

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

    1
    2
    3
    4
    5
    6
    7
    8
    <teleport to="移动位置">
    <div v-if="isShow" class="mask">
    <div class="dialog">
    <h3>我是一个弹窗</h3>
    <button @click="isShow = false">关闭弹窗</button>
    </div>
    </div>
    </teleport>

3.Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验

  • 使用步骤:

    • 异步引入组件

      1
      2
      import {defineAsyncComponent} from 'vue'
      const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
    • 使用Suspense包裹组件,并配置好defaultfallback

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <template>
      <div class="app">
      <h3>我是App组件</h3>
      <Suspense>
      <template v-slot:default>
      <Child/>
      </template>
      <template v-slot:fallback>
      <h3>加载中.....</h3>
      </template>
      </Suspense>
      </div>
      </template>

六、其他

1.全局API的转移

  • Vue 2.x 有许多全局 API 和配置。

    • 例如:注册全局组件、注册全局指令等。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      //注册全局组件
      Vue.component('MyButton', {
      data: () => ({
      count: 0
      }),
      template: '<button @click="count++">Clicked {{ count }} times.</button>'
      })

      //注册全局指令
      Vue.directive('focus', {
      inserted: el => el.focus()
      }
  • Vue3.0中对这些API做出了调整:

    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

      2.x 全局 API(Vue 3.x 实例 API (app)
      Vue.config.xxxx app.config.xxxx
      Vue.config.productionTip 移除
      Vue.component app.component
      Vue.directive app.directive
      Vue.mixin app.mixin
      Vue.use app.use
      Vue.prototype app.config.globalProperties

2.其他改变

  • data选项应始终被声明为一个函数。

  • 过度类名的更改:

    • Vue2.x写法

      1
      2
      3
      4
      5
      6
      7
      8
      .v-enter,
      .v-leave-to {
      opacity: 0;
      }
      .v-leave,
      .v-enter-to {
      opacity: 1;
      }
    • Vue3.x写法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      .v-enter-from,
      .v-leave-to {
      opacity: 0;
      }

      .v-leave-from,
      .v-enter-to {
      opacity: 1;
      }
  • 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes

  • 移除v-on.native修饰符

    • 父组件中绑定事件

      1
      2
      3
      4
      <my-component
      v-on:close="handleComponentEvent"
      v-on:click="handleNativeClickEvent"
      />
    • 子组件中声明自定义事件

      1
      2
      3
      4
      5
      <script>
      export default {
      emits: ['close']
      }
      </script>
  • 移除过滤器(filter)

    过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。

总结

花了两三天的时间把Vue3基础给学习了,Vue3给我最直观的改变就是使用了composition API把setup作为舞台,将原来的vue2那种数据以及方法在不同生命周期地方,使用不同的方法,都放在了setup中,最后只要return就行了,和java和python中所定义的方法一样,里面有属性以及一些方法。而且代码看起来舒服很多,可读性对我而言强了,上手还是蛮快的,也没什么难点,生命周期部分也非常的知名见意,我觉得vue3的setup这样的写法还是很适合我的。而且原本Vue2中的语法在vue3中都是可以使用的,虽然官方不建议这么做,但是也能写能用,如果和Vue3有冲突的地方,会以Vue3为主,因为自己是使用vite创建的vue3工程,npm run dev跑起来真的比serve快了很多,打包也快了不少。我觉得Vue3的核心应该是响应式proxy,属性值的读写、属性的添加、属性的删除都可以通过proxy完成,所以我认为该点为核心部分,而且基本API都离不开proxy。