让我们接着之前文章没讲完的部分来继续讲。

Cookie

在javaWeb开发中,cookie就像网站给浏览器贴的小纸条,用于记录一些用户信息或者状态,方便下一
次访问时识别用户身份或进行个性化服务。

cookie机制是相当容易理解的,它的工作原理如下:

  1. 当用户第一次访问网站时,服务器会在响应头中设置一个cookie,包含一些用户信息或者状态。
  2. 浏览器收到响应后,会将cookie保存到本地。
  3. 下一次用户访问网站时,浏览器会自动将cookie发送到服务器。
  4. 服务器收到cookie后,就可以根据cookie中的信息来识别用户身份或进行个性化服务。

结合生活解释就像你经常去一家店吃饭,久了店家都知道你喜欢吃什么,人一到连菜都不用点,直接上你喜欢的菜。

创建cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.iweb.servlet;
@WebServlet("/createCookieServlet")
public class CreateCookieServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
//处理响应中文代码问题
resp.setContentType("text/html;charset=utf-8");
//创建cookie对象
Cookie cookie1 = new Cookie("k1","v1");
Cookie cookie2 = new Cookie("k2","v2");
//通知客户端浏览器保存cookie
// cookie1.setMaxAge(0);设置cookie生命周期
//表示马上删除,不需要等待浏览器关闭
cookie1.setMaxAge(0);
resp.addCookie(cookie1);
resp.addCookie(cookie2);
resp.getWriter().write("cookie创建成功");
}
}

获取cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.iweb.servlet;
@WebServlet("/getCookieServlet")
public class GetCookieServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
//获取所有的cookie对象,返回的是数组
resp.setContentType("text/html;charset=utf-8");
Cookie[] cookies = req.getCookies();
for(Cookie cookie : cookies){
// cookie.getName()方法返回的是cookie的key的名字
// cookie.getValue()方法返回的是cookie的value值
resp.getWriter().write("cookie["+cookie.getName()+"="+cookie.getValue()+"]");
}
//查找指定的cookie
Cookie cookie = CookieUtils.getCookie("k1", cookies);
//如果不等于null,说明赋过值,找到了需要的cookie
if(cookie != null){
//找到需要修改的cookie对象
cookie.setValue("new_value1");
resp.addCookie(cookie);
}
}
}

Session

session是一种服务器端会话跟踪机制,用于在用户与服务器交互过程中保持用户的状态信息,与cookie不同,session的数据存储在服务器内存中,而cookie的数据存储在客户端浏览器。

session的生命周期

  • 创建:当用户第一次访问web系统时,服务器会创建一个session实例
  • 存在:session在用户访问系统期间一直存在,直到超时或销毁
  • 销毁:session可以通过调用invalidate()方法来销毁,或者是超时后由服务器自动销毁

获取session实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.iweb.servlet;
@WebServlet("/sessionServlet")
public class SessionServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
//获得session
HttpSession session = req.getSession();
//session.getId()得到session的会话id值,id是唯一的
resp.getWriter().write(session.getId());
//绑定数据
req.setAttribute("req1","val1");
session.setAttribute("ses1","val2");
req.getServletContext().setAttribute("con1","val3");
System.out.println("已经绑定了request,session,ServletContext保存了数据");
req.getRequestDispatcher("/mapServlet").forward(req,resp);
}
}

面试题:session和cookie的区别

  1. 数据存放的位置不同

    • cookie数据存放在客户端的浏览器上
    • session数据放在服务器上
  2. 安全程度不同

    • cookie不是很安全,考虑到安全应当使用session
  3. 性能使用程度不同

    • session会在一定时间内保存在服务器上,当访问增多,会占用服务器的性能,考虑到减轻服务器性能问题,应当使用cookie
  4. 数据存储大小不同

    • 单个cookie保存数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
    • session则存储在服务器

Filter

Filter过滤器,是JavaWeb三大组件之一(Servlet、Filter、Listener)。用于在请求到达servlet之间或者响应返回客户端之前对请求或者响应进行预处理或后处理。

Filter工作原理

当客户端浏览器向服务器发送请求时,请求会先经过一系列过滤器,每个过滤器都可以对请求进行修改、验证或者拦截。请求依次通过过滤器链,最终到达目标servlet。类似于小区的安检系统,进门要安检,出门也要过一次安检。

过滤器的应用场景

  • 权限验证:检查用户是否有访问特定资源的权限。例如某些网站未登录不能查看商品,评论,不能将商品添加到购物车,得把拦截下来,让你先登录,也就是在访问前,要先经过filter
  • 字符编码处理:统一设置请求和响应的字符编码,防止中文乱码问题

filter入门案例

  1. 定义类,实现Filter接口,并重写其所有方法
  2. 配置filter拦截器路径资源
  3. 在doFilter方法中进行放行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.iweb.util;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

//这里配置的是过滤器拦截的路径,/*表示拦截所有路径
@WebFilter("/*")
public class TestFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException{
}

//doFilter专门用于拦截请求的方法
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}

Filter拦截路径配置

拦截路径表示filter会对请求的那些种资源进行拦截,使用@WebFilter注解进行配置,比如@WebFilter(“拦截路径”)

拦截路径有如下几种配置方式:

  • 拦截具体的资源:/index.html 只有访问index.html时才会被拦截
  • 目录拦截:/user/* 访问/user下所有的资源都会被拦截
  • 后缀名拦截:*.html 访问后缀名为html资源,都会被拦截
  • 拦截所有:/* 访问所有资源,都会被拦截

过滤器处理中文乱码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.iweb.util;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")
public class CharacterEncodingFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException{
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
//设置请求编码为UTF-8
request.setCharacterEncoding("utf-8");
//设置响应内容为UTF-8
response.setContentType("text/html;charset=utf-8");
//放行
filterChain.doFilter(request,response);
}
}

过滤器链

在一个web应用中可以配置多个过滤器,这些过滤器会按照配置的顺序形成一个过滤器链。请求会依次通过过滤器链中的每个过滤器,响应则会按照相反的顺序返回。有两个过滤器FilterA和FilterB,配置顺序为FilterA在前,FilterB在后,那么请求的处理顺序为:FilterA—>FilterB—>目标servlet,响应处理顺序为:servlet—>FilterB—>FilterA

Listener

listener表示监听器,监听就是在ServletContext、Session、Request三个对象创建和销毁。

MVC分层

MVC概念

MVC全称:Model模型、View视图、Controller控制器。首先明确的一点是它是一种开发设计思想,将业务逻辑、数据处理、页面显示分别抽取出来统一放到一个地方。MVC的理念就是将软件代码拆分为组件,单独开发,组合使用(降低耦合度)。

  • view视图:负责提供页面为用户展示数据,不接受任何与显示数据无关的代码。(html、jsp)
  • Controller控制层:servlet作为控制器,用来接受用户的请求,然后获取请求中的数据,然后派发页面,是一个调度者角色,转发某个页面,或者重定向到某个页面(servlet)
  • Model模型:将业务逻辑相关的数据封装成具体的JavaBean类,其中不掺杂任何与数据相关处理的代码(bean/pojo/entity)

MVC的作用就是为了降低耦合度,让代码合理分层,方便后期的升级和维护。

Vue介绍

如果使用原生JS来处理界面交互行为,开发效率比较低,而在现在的企业级项目开发中,一般使用Vue这样的js框架来简化操作,提供开发效率。

概述

vue是一款用于构建用户界面的渐进式的javaScript框架。

构建用户界面

构建用户界面是指在vue中,可以基于数据渲染出用户看到的界面,将来服务器返回给前端的原始数据,如下:

1
2
3
4
5
userList:[
{"name":"jack","age":21},
{"name":"tom","age":22},
{"name":"lucy","age":19}
]

上面的这些原始数据,用户是看不懂的,而我们开发人员,可以使用vue中提供的操作,将原始数据遍历,解析出来,从而渲染呈现出用户所能看懂的界面:

姓名 年龄 操作
jack 21 删除增加修改
tom 22 删除增加修改
lucy 19 删除增加修改

渐进式

渐进式就是循序渐进,Vue生态中的语法非常多,比如声明式渲染,组件系统,路由,状态管理,构建工具等等。

我们使用vue框架,不需要把所有的组件全部学习完毕才可以使用vue,而是我们学习一点就可以使用一点:

  • 声明式渲染:就可以用vue来构建用户界面
  • 组件:就可以使用vue中的组件,来复用
  • 路由:就可以在vue中来使用路由功能了

框架

框架用于快速构建项目,简化开发提供开发效率的。

入门案例

分为三步骤:

  1. 新建HTML页面,引入vue.js文件
1
2
<!--引用vue.js脚本-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  1. 准备元素(div),交给Vue控制

这是个容器,vue所管理的范围,这里将来会编写一些用于渲染的逻辑代码。

1
<div id="app"></div>
  1. 创建vue的应用实例对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
</head>
<body>
<div id="app">
{{message}}
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
let obj = {
data(){
return {
message: "hello world"
}
}
};
Vue.createApp(obj).mount("#app");
</script>
</body>
</html>

注意

  • vue中定义数据,必须通过data方法来定义,data返回的是一个对象,这里对象中定义数据
  • 插值表达式中编写的变量,一定是vue中data定义的数据,如果插值表达式编中写了一个变量,但是在vue中未定义,会报错
  • vue应用实例接管的区域是’#app’,超出这个范围,就不受vue控制了,所以vue里面的插值表达式,一定要写在
    的里面

vue指令

指令:HTML标签上带有v-前缀的特殊属性,不同指令就具有不同意义。例如:v-if、v-for

指令 作用
v-bind 为HTML标签绑定属性值,设置href,css样式等
v-model 在表单元素上创建双向数据绑定
v-on 为HTML标签绑定事件
v-if/v-else/v-else-if 条件渲染某个元素,判断为true则渲染否则不渲染
v-show 根据条件展示某个元素,区别在于切换的是display属性的值
v-for 遍历容器的元素或者对象的属性

v-bind

作用:动态为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
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<h2>指令一:强制数据绑定</h2>
<a href="url">访问百度1(无效)</a><br/>
<a v-bind:href="url">访问百度2</a><br/>
<!-- v-bind可以简写为: -->
<a :href="url">访问百度3</a>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
let obj = {
data(){
return {
url: "http://www.baidu.com"
}
}
};
Vue.createApp(obj).mount("#app");
</script>
</body>
</html>

注意

v-bind所绑定的数据,必须是在data中定义数据。

v-on

作用:为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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<h2>绑定事件监听</h2>
<button v-on:click="btnClick1">绑定事件1</button>
<button @click="btnClick2">绑定事件2</button>
<button @click="btnClick3('hello')">绑定事件3</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
let obj = {
data(){
return {}
},
methods: {
btnClick1(){
alert("点击事件1");
},
btnClick2(){
alert("点击事件2");
},
btnClick3(msg){
alert(msg);
}
}
};
Vue.createApp(obj).mount("#app");
</script>
</body>
</html>

注意

  • v-on:后面的事件名称是之前原生事件属性名去掉on
  • 单击事件:事件属性名是onclick,在vue中使用是 v-on:click,简写@click

条件判断

v-if=”表达式”,表达式的值为true,显示,false,隐藏

原理:基于条件判断,来控制创建或删除元素节点(条件渲染)

场景:要么显示,要么不显示,不频繁切换的场景

v-show

v-show=”表达式”,表达式值为true,显示,false,隐藏

原理:基于css样式display来控制显示与隐藏

场景:频繁切换显示隐藏

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<h2>v-show</h2>
<h3 v-show="isShow">{{message}}</h3>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
let obj = {
data(){
return {
message: "hello world",
isShow: false,
score: 56
}
}
};
Vue.createApp(obj).mount("#app");
</script>
</body>
</html>

面试题:v-if和v-show的区别?

  • v-if根据条件销毁或重建dom元素,初始条件为假时元素不会被渲染
  • v-show始终保留dom元素,仅通过display:none控制显示或隐藏状态

v-for

作用:列表渲染,遍历元素或者对象

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<h2>v-for</h2>
<select>
<option value="">--请选择--</option>
<option v-for="(city,index) in cityList" :value="city.id">
{{index}}----{{city.name}}
</option>
</select>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
let obj = {
data(){
return {
cityList: [
{id:1, name:"北京"},
{id:2, name:"上海"},
{id:3, name:"广州"}
]
}
}
};
Vue.createApp(obj).mount("#app");
</script>
</body>
</html>

参数:

  • cityList要遍历的数组
  • city遍历出来的元素
  • index为索引下标,从0开始,可以省略 v-for=”city in cityList”
  • key作用:给元素添加唯一表示,推荐使用id作为key(唯一),不推荐使用index作为key

v-model

作用:在表单元素上使用,双向数据绑定,可以方便的获取或设置表单项数据

语法:v-model=”变量名”

双向数据绑定:是指vue中的数据变化,会影响视图中的数据展示,视图中的输入的数据变化,也会影响vue的数据的模型

Vue工程化

上述开发模式存在一些问题:

  • 不规范:每次开发都是从头开始,比较麻烦
  • 复用:多个页面的组件不能复用

企业开发方式主要有:

  • 模块化:将js、css封装成一个个可以复用的模块
  • 组件化:将组件,CSS,js封装称为一个组件,便于管理
  • 规范化:提供一套标准的规范的目录接口或编码规范
  • 自动化:项目的构建、测试、部署全部都是自动完成

学习vue的官方提供的脚手架帮我们完成前端的工程化。

Vue项目创建

vite是一个轻量级的,速度极快的下一代前端构建工具,用于快速生成一个工程化的Vue项目,非常适合前端开发。

Vue的主要特点:

  • 极速启动:利用浏览器原生ES模块支持,无需打包,启动速度极快
  • 快速热更新:代码保存后立即显示效果
  • 按需编译:只编译修改的部分

要想使用vite创建vue项目,必须依赖环境:node.js。vite本身也是依赖node.js的,下载node.js

1. 跳过node.js安装步骤

node.js安装完毕后,会自动配置好环境变量,我们验证一下是否安装成功:

1
node -v

2. 配置淘宝镜像,提升加速下载

1
npm config set registry=http://registry.npm.taobao.org

npm介绍

NPM(Node Package Manager) Node.js的默认包管理器。

在开发前端项目的过程中,我们需要相关的依赖,可以直接通过npm命令,直接从远程仓库中将依赖直接下载到本地。

项目创建完毕以后,进入vite-project项目目录,执行命令安装当前项目的依赖:

1
npm i

创建项目以安装依赖的过程,都是需要联网的。如果网络不好,可能会造成依赖下载不完成报错,继续再次执行命令安装

启动项目

我们使用hbuilderx直接打开vue项目。

1
npm run dev #启动vue项目

在浏览器中输入:http://localhost:5173/即可打开页面

项目目录结构

目录/文件 说明
node_modules npm加载的项目依赖模块存放第三方依赖包
src 这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:assets:目录用来存放项目中所有的静态资源文件(css、fonts等)。components:目录用来存放项目中所有的自定义组件。App.vue:是项目的根组件。main.js:是整个项目的打包入口文件。index.css:是项目的全局样式表文件
assets 静态资源目录,如图片、字体等
public 公共资源目录
.gitignore .gitignore是Git的忽略文件
index.html 是SPA单页面应用程序中唯一的HTML页面
package.json 项目的包管理配置文件
vite.config.js vite项目配置文件
README.md 项目的说明文档,markdown格式
dist 使用npm run build命令打包后会生成该目录

项目运行流程:
在工程化的项目中,vue要做的事情很简单,通过main.js把app.vue渲染到index.html的指定区域中。
1)项目首页(项目入口)index.html

1
2
3
4
5
6
7
<body>
<div id="app"></div>

<script type="module" src="/src/main.js"></script>

</body>


在body体中有一个div标签,其id属性值为app,还引入了一个js,script标签会连接到src/main.js内容。
main.js入口文件
1
2
3
4
5
6
7
8
// 1.导入createApp函数
import { createApp } from 'vue'
// 2.导入待渲染的css文件
import './style.css'
// 3.导入App.vue组件
import App from './App.vue'
// 4.调用createApp函数,创建vue应用实例,调用mount()把vue应用实例挂在到(id选择器)app区域中
createApp(App).mount('#app')

在main.js中,新建了一个vue实例,并使用#app挂在到index.html中的app区域,但是#app区域没有任何内容,需要注意的是我们在创建vue应用的实例的时候,传入了一个App组件,App组件称为根组件,也就是main.js关联到App.vue组件。
3).根组件的App.vue
清空App.vue的默认内容
1
2
3
4
5
6
7
8
9
10
11
12
<script>
js
</script>

<template>
html
</template>

<style>
css
</style>


其中.vue是vue项目中的组件文件,称为单文件组件。vue的单文件组件会将一个组件的逻辑(js),模板(html),样式(css)封装在同一个文件里面(.vue)
API风格
选项式API:可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <template>
<input type="button" @click="inputClick" v-model="count"/>
</template>
<script>
// export default 就是默认导出
export default{
data(){
return {
count:0
}
},
methods:{
inputClick(){
this.count++;
}
}
}
</script>

组合式API
组合式API:是vue3中新增的API风格,它是基于响应式API的,使用组合式API可以更灵活的组织组件的逻辑。
组合式API的主要优势:

  • 更灵活:可以根据需要组织代码,而不是被选项式API的固定结构限制
  • 更强大:可以使用vue3提供的新功能,如组合式API、响应式API等
  • 更高效:可以避免选项式API的性能问题,如重复渲染、内存泄漏等

vue-router路由

vue-router是一个专门用于vue.js的路由管理库,他通过将url映射到组件,实现页面的跳转和管理。与传统的标签不同,vue-router提供的组件能够实现无刷新的页面跳转,如:打开网站首页时默认显示登录页面,登录成功后进入到主页面,此时就需要使用路由。
VueRouter主要由三个部分组成:

路由实现步骤

安装路由组件:

1
npm install vue-router

确认路由组件是否安装成功,可打开项目下package.json文件,找到dependencies配置项
1
2
3
4
 "dependencies": {
"vue": "^3.3.8",
"vue-router": "^4.5.1" // 路由组件的版本号,如果安装失败或者未安装,则此配置不会出现
},

自定义路由组件:
将路由跳转的目标页面(组件)放到src的其他目录下(pages):
在pages下创建login.vue和home.vue
1
2
3
4
5
6
7
8
<-- home.vue -->
<template>
<div>
<h1>应用程序主页面</h1>

</div>

</template>

1
2
3
4
5
6
<-- login.vue -->
<template>
用户名:<input type="text"/>
密码:<input type="password"/><br/>
<input type="button" value="登录"/>
</template>

配置路由
在项目src/router目录下创建router.js路由文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // 1.从vue-router中导入按需两个方法
import { createRouter,createWebHashHistory } from "vue-router"
// 2.导入路由页面组件
import Home from '../pages/home.vue'
import Login from '../pages/login.vue'

// 3.创建路由实例对象
const router=createRouter({
// 指定路由的工作模式
history:createWebHashHistory(),
// 注册路由组件
routes:[
// path是请求路径,也就是地址栏显示的路径,component是放路由组件的
{path:"/",redirect:"/login"}, // 页面默认加载的路由,redirect表示重定向到path路径到/login
{path:"/home",component:Home,name:"/home"},
{path:"/login",component:Login,name:"/login"}]
})

// 4.导出路由对象
export default router

在main.js中导入挂载路由模块
1
2
3
4
5
6
7
8
9
10
    import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 导入路由配置文件
import router from './router/router.js'

// 挂载vue实例上
const app=createApp(App);
app.use(router)
app.mount('#app')

实现路由跳转:
在app.vue中使用router-view组件,动态视图组件,用来渲染展示与路由路径对应的组件。
1
2
3
4
5
    app.vue
<template>
<router-view></router-view>

</template>

接下来点击登录按钮需要跳转到home.vue时,如何实现?需要用到编程式路由导航。

编程式路由导航

编程式路由导航通过js来实现路由跳转,vue-Router提了两个主要的AIP来进行编程式路由导航 router.push和routerreplace。
router.push方法用于向浏览器的历史记录中添加一条新的记录,用户可以通过浏览器的后退按钮返回到前一个页面。
router.replace方法与router.push类似,但是他不会在历史记录中添加的新的记录,而是替换掉当前的历史记录。

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
    <template>
用户名:<input type="text" v-model="user.username"/><br/>
密码:<input type="text" v-model="user.password"/>{{errorMsg}}
<br/>
<input type="button" value="登录" @click="userLogin"/>
</template>

<script>
export default{
data(){
return {
user:{
username:"admin",
password:"123456"
},
errorMsg:""
}
},
methods:{
userLogin(){
// 此处用if语句模拟后台验证用户名和密码
if(this.user.username=='admin' && this.user.password=='123456'){
// 登录成功
this.errorMsg="";
// 跳转到home主页面
this.$router.replace("/home");
}else{
this.errorMsg="用户名或密码错误";
}
}
}
}
</script>

<style>
</style>

将login.vue中的代码替换:
1
2
3
4
5
6
<template>
用户名:<input type="text" v-model="user.username"/><br/>
密码:<input type="text" v-model="user.password"/>{{errorMsg}}
<br/>
<input type="button" value="登录" @click="userLogin"/>
</template>

声明式路由导航

vue-router提供了全局组件router-linke,类似a标签的作用。

1
2
3
4
5
6
7
<router-link to="/">跳转到首页</router-link><br/><p></p>

<router-link to="/login">跳转到登录页</router-link><p></p>

<router-link to="/home">跳转到主页</router-link><p></p>

<router-link to="/home" replace>跳转到主页</router-link>

router-link跳转的时候默认为push,如果设置为replace,router-link组件添加replace属性即可。

嵌套路由

案例:由login登录成功后,进入home主页面,在主页面中点击左侧菜单(router-link)进行页面切换,点击首页,商品管理,用户管理,系统配置等等链接时,蓝色区域做局刷新。
经过刚才的操作,我们可以看到,在页面点击左侧菜单,右侧主展示区域,就会显示出对应的页面。App.vue 中的router-view是最底层的出口,渲染最高级路由匹配到组件,同样的,一个被渲染的组件同样可以包含自己的嵌套router-view

vue生命周期

vue的生命周期是指vue对象从创建到销毁过程,vue生命周期包含8个阶段,每个阶段触发一个生命周期事件,会自动执行一个生命周期方法,这些生命周期方法也成为钩子函数。

表格描述:
| 生命周期阶段 | 生命周期事件 | 生命周期方法 | 描述 |
| —- | —- | —- | —- |
| 初始化 | beforeCreate | | 实例初始化完成,数据观测和事件配置之前调用 |
| 初始化 | created | | 实例初始化完成,数据观测和事件配置完成,但是DOM还没有挂载 |
| 挂载 | beforeMount | | 实例挂载之前调用,相关的render函数首次被调用 |
| 挂载 | mounted | | 实例挂载完成,DOM已经挂载完成 |
| 更新 | beforeUpdate | | 数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前 |
| 更新 | updated | | 数据更新时调用,发生在虚拟DOM重新渲染和打补丁之后 |
| 销毁 | beforeDestroy | | 实例销毁之前调用,实例仍然完全可用 |
| 销毁 | destroyed | | 实例销毁后调用,实例的所有事件监听器会被移除,所有子实例也会被销毁 |
| 激活 | activated | | 组件被激活时调用,组件从缓存中被激活 |
| 不激活 | deactivated | | 组件被停用时调用,组件被缓存 |

vue官网提供了从vue到销毁vue对象的这个过程及各个阶段对应的钩子函数
其中我们只需要关注一个mounted
mounted:挂载完成,vue初始化成功,HTML页面渲染成功后,在页面初始化后自动的发送ajax请求到后台获取数据。
mounted钩子函数在组件实例创建完成后调用,此时组件的el属性已经指向了挂载的DOM元素,我们可以在mounted钩子函数中访问和操作DOM元素。

1
2
3
4
5
6
mounted(){
// 发送ajax请求
axios.get("http://localhost:8080/user").then(res=>{
console.log(res.data);
})
}

Ajax

我们前端页面中的数据,来自于后台,那么我们的后端和前端是互不影响的2个程序,那么我们前端应该如何从后台获取数据?因为是2个程序,所以必须涉及到2个程序的交互,需要用到我们接下来要学习的ajax。
Ajax:Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),作用如下:

  • 与服务器进行数据交换,通过ajax可以给服务器发送请求,并获取服务器响应的数据。
  • 异步交互:不重新加载整个页面的情况下,与服务器交互数据并更新局部网页的技术。
    与服务器进行数据交互
    前端可以通过ajax技术,向后台服务器发送请求,后台服务器接受到了前端的请求,从数据库中获取前端需要的数据,然后响应给前端,前端在通过我们学习的vue技术,可以数据展示到页面上,这样用户就能够看到完整的页面了。
    异步交互
    可以在不重新加载整个页面的情况下,与服务器交互数据并更新局部网页的技术。

同步异步

ajax的局部刷新功能是因为ajax请求是异步的,对应的又同步请求。
同步请求:浏览器页面在发送请求给服务器,在服务器端处理请求的过程中,浏览器页面不能做其他的操作,只能等服务器响应结果后才能,浏览器页面才能继续做其他的操作。
异步请求:浏览器页面发送请求给服务器端,在服务器处理请求的过程中,浏览器页面还可以做其他的操作。

Aixos

使用原生的ajax请求的代码编写比较繁琐,Aixos是对原生的ajax进行封装,简化书写。
安装Aixos

1
npm install axios -s

检查package.json文件中是否安装axios
1
2
3
4
5
"dependencies": {
"axios": "^1.11.0",
"vue": "^3.3.8",
"vue-router": "^4.5.1"
},

配置axios
为了简化axios的使用,可以创建一个axios实例并且进行全局配置,这样可以避免在每个组件中重复配置axios,文件夹建立在src/axios,命名为axios.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    //1.导入axios
import axios from "axios"
//2.使用axios.create创建axios实例
const axiosObj=axios.create({
/**
* 后端服务器地址,每次使用axios异步请求
* 都要填写域名比较麻烦,可以设置一个baseURL
* 这样可以少一些代码
*/
baseURL:"http://localhost:8080",
timeout:2000 // 配置请求超时单位 单位ms
})

//3.导出axios实例
export default axiosObj

为了方便在 vue组件中使用axios实例,将其挂载到vue的全局对象上,这样每个组件都可以访问它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    import { createApp } from 'vue'
import App from './App.vue'
// 导入路由配置文件
import router from './router/router.js'
// 导入axios实例
import axiosInstance from './axios/axios.js'

// 挂载vue实例上
const app=createApp(App);
app.use(router)
// 全局注册axios
app.config.globalProperties.$axios=axiosInstance

app.mount('#app')

使用axios
在需要进行axios做异步请求的地方使用this.$aixos.get()this.$aixos.post()等异步请求方法。
以用户登录为例:
前端vue项目地址:http://localhost:5173
后端项目地址:http://localhost:8081
后端用户登录API:http://localhost:8081/工程名/aixosServlet?username=xxx&password=xxx

login.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
    <template>
用户名:<input type="text" v-model="user.username"/><br/>
密码:<input type="text" v-model="user.password"/>{{errorMsg}}
<br/>
<input type="button" value="登录" @click="userLogin"/>
</template>

<script>
export default{
data(){
return {
user:{
username:"admin",
password:"123456"
},
errorMsg:""
}
},
methods:{
userLogin(){
let _this=this;
this.$axios.get("/axiosServlet",{
params: {
username: this.user.username,
password: this.user.password
}
}).then(function (response){
//then() 请求成功后,获得响应数据
if(response.data.code==200){
_this.$router.replace("/home");
}else{
_this.errorMsg="账号或密码错误";
}

}).catch(function (error){
// 请求失败时,打印错误信息
console.log(error)
})

}
}
}
</script>

<style>
</style>

后端控制器代码
前后端的ajax通信几乎用的都是json格式的数据,所以在开发过程中,我们经常会涉及到JSON数据的转换,JSON本来是JS里的内容,有时后端要传递复杂对象和数据给前端,所以需要用到JSON转换。
1).引入依赖
fastJson是一个java库,可以将java对象转换为JSON格式,也可以将JSON格式的字符串转换为java对象,首先,在项目中引入fastJson库,maven配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <dependency>
<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<version>3.0.1</version>

</dependency>

<!-- fastjson依赖 -->
<dependency>
<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.78</version>

</dependency>

aixosServletd
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
    package com.iweb.servlet;

import com.alibaba.fastjson.JSON;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

@WebServlet("/aixosServlet")
public class AixosServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 接受前端发送过来的账号和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
HashMap<String,Object> map=new HashMap<String,Object>();
System.out.println(username);
System.out.println(password);
// 连接数据库
if(username.equals("admin")&&password.equals("123456")){
// 登录成功
map.put("code",200);
}else{
// 账号或密码错误
map.put("code",500);
}
// 向前端页面发送字符串
// JSON.toJSONString()将map对象转换为String字符串
resp.getWriter().write(JSON.toJSONString(map));
}
}

CORS 头缺少 ‘Access-Control-Allow-Origin’这个是跨域了,跨域是指违背了同源策略,同源策略规定了三个东西一致:协议名,主机名,端口号。
Vite解决跨域
在vite开发中,可以通过配置代理来解决跨域问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    //1.导入axios
import axios from "axios"
//2.使用axios.create创建axios实例
const axiosObj=axios.create({
/**
* 后端服务器地址,每次使用axios异步请求
* 都要填写域名比较麻烦,可以设置一个baseURL
* 这样可以少一些代码
*/
baseURL:"/api",
timeout:2000 // 配置请求超时单位 单位ms
})

//3.导出axios实例
export default axiosObj

原本跨域的请求就会通过代理服务器转发,从而解决跨域问题。