一,Vue
1.vue基本使用
Vue.js 是什么
1. Vue 是一套用于构建用户界面的渐进式 Javascript 框架; 2. 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用; 3. Vue 采用组件化模式,提高代码复用率、更好的代码维护; 4. 声明式编码,无需直接操作 DOM,提高开发效率; 5. 总而言之,vue就是一个MVVM的实现者,核心就是实现DOM监听和数据绑定
1,第一个Vue程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue引入</title>
<script src="../js/vue.js" type="text/javascript"></script>
<!-- 设置为 false 以阻止 vue 在启动时生成生产提示。 -->
<script>Vue.config.productionTip = false</script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script type="text/javascript">
// 创建Vue的实例
const vm = new Vue({
// 绑定实例容器
el:"#app",
// 定义数据内容
data:{
message:"Hello Vue!"
}
})
</script>
</body>
</html>
其中
1. div 标签定义的内容就是 Vue 实例生效的容器;
2. el 用于绑定实例容器;
3. data 就是定义 Vue 实例数据的地方;
4. vm 绑定,当 data 数据发生改变时,页面内容也立刻发生改变;
5. 通过胡须表达式(插值语法) {{xxx}} 可以取出 Vue 实例中 data 属性中定义的 xxx 的值;
6. 双大括号会将数据解释为普通文本,而非 HTML 代码。
2,Vue指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达 式 ( v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
①v-bind指令
v-bind 是属性绑定指令,会把绑定的内容当做 js 表达式进行解析;
一般情况下,解析标签属性、解析标签体内容、绑定事件等,使用 v-bind 指令;
格式为: v-bind 缩写 :属性名="..."
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app" v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</div>
</body>
<script>
var app = new Vue({
el:'#app',
data:{
message:'页面加载于 ' + new Date().toLocaleString(), // 时间
}
})
</script>
</html>
②v-model指令
v-model 就是 Vue 中的数据绑定指令;
只能用于表单类元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值;
v-model 指令其实就是 v-bind:value="属性值" + v-on:input="函数名"。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单双向数据绑定</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{message}}</p>
<input type="text" v-model="message">
</div>
<script>
var first = new Vue({
el:"#app",
data:{
message:"表单双向数据绑定"
}
})
</script>
</body>
</html>
③v-html指令
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>加载HTML代码</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app" v-html="message">
</div>
<script>
var first = new Vue({
el:"#app",
data:{
message:"<h1>Hello Vue!</h1>"
}
})
</script>
</body>
</html>
④v-show指令
v-show指令用来控制html元素是否显示
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>控制html元素显示</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p v-show="isTrue">{{message}}</p>
<input type="text" v-model="message">
<button v-on:click="change">点击我改变</button>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
message : "控制html元素显示",
isTrue : true,
},
methods:{
change:function () {
this.isTrue = !this.isTrue;
}
}
})
</script>
</body>
</html>
3.事件处理和流程控制
①v-on指令
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue模板语法</title>
<script src="../js/vue.js" type="text/javascript"></script>
<!-- 设置为 false 以阻止 vue 在启动时生成生产提示。 -->
<script>Vue.config.productionTip = false</script>
</head>
<body>
<div id="app">
<p>{{number}}</p>
<button v-on:click="add">增加</button>
<button v-on:click="add2($event,1)">增加2</button>
</div>
<script>
var first = new Vue({
el: "#app",
data: {
number: 1,
},
methods: {
add: function () {
console.log(event)
this.number += 1;
},
add2: function (event, num) {
console.log(event)
this.number += 1;
}
}
})
</script>
</body>
</html>
其中
方法定义在 Vue 实例的 methods 属性中;
方法调用时,如果没有参数传递,则直接写方法名即可,此时会默认传递当前事件 event 过去; 如果有参数传递,则需要写方法名和圆括号,此时 event 事件就会丢失,可以在参数传递的时候使 用 $event 进行占位;
在方法中,如果想要使用 Vue 实例中的 data 属性数据,使用 this.xxx 的方式,这个 this 就代表了 当前 Vue 的实例对象;
②文档事件
如果函数需要在文档加载完后执行,需要定义 mouted 属性或者 created 属性
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>文档事件</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{number}}</p>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
number : "文档事件",
},
methods:{
documentEven:function (data) {
alert(data);
}
},
mounted:function () {
this.documentEven("文档加载后执行");
}
})
</script>
</body>
</html>
③条件语句
Vue中的条件判断使用 v-if v-else-if v-else指令;
and 表示与,or 表示或。
<meta charset="UTF-8">
<title>文档事件</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{number}}</p>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
number : "文档事件",
},
methods:{
documentEven:function (data) {
alert(data);
}
},
mounted:function () {
this.documentEven("文档加载后执行");
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件指令</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p v-if="weekday == '星期一'">
今天是星期一
</p>
<p v-else-if="weekday == '星期二'">
今天是星期二
</p>
<p v-else-if="weekday == '星期三'">
今天是星期三
</p>
<p v-else-if="weekday == '星期四'">
今天是星期四
</p>
<p v-else-if="weekday == '星期五'">
今天是星期五
</p>
<p v-else>
今天是周末
</p>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
weekday:"星期五"
}
})
</script>
</body>
</html>
④循环语句
1. Vue中循环语句使用v-for指令,v-for 指令是以 x in sites 形式的特殊语法, sites 是源数据数组,x 是数组元素迭代的别名;
2. 如果需要循环的下标,可以写成(x,index) in sites,index从0开始;
3. 一共可以有三个参数,value、name、index,分别对应值、键、索引。
<p v-else-if="weekday == '星期四'">
今天是星期四
</p>
<p v-else-if="weekday == '星期五'">
今天是星期五
</p>
<p v-else>
今天是周末
</p>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
weekday:"星期五"
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>循环指令</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<table border="1" cellspacing="0">
<thead>
<tr>
<td>id</td>
<td>username</td>
<td>password</td>
</tr>
</thead>
<tbody>
<tr v-for="(user,index) in userList">
<td>{{index + 1}}</td>
<td>{{user.username}}</td>
<td>{{user.password}}</td>
</tr>
</tbody>
</table>
</div>
<script>
var first = new Vue({
el:"#app",
data:{
userList:[
{username:"张三",password:"123"},
{username:"李四",password:"111"},
{username:"王五",password:"222"},
{username:"赵六",password:"333"},
]
}
})
</script>
</body>
</html>
4.Vue表单
①输入框
直接使用input框和textarea框
②复选框
复选框也是使用 v-model 实现双向数据绑定,需要注意的是定义的值是一个js数组
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>复选框</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>复选框:</p>
<label for="movie">电影</label>
<input type="checkbox" id="movie" value="电影" v-model="checkedNames">
<label for="game">游戏</label>
<input type="checkbox" id="game" value="游戏" v-model="checkedNames">
<label for="book">看书</label>
<input type="checkbox" id="book" value="看书" v-model="checkedNames">
<br>
<span>选择的值为:{{checkedNames}}</span>
</div>
<script>
var first = new Vue({
el: '#app',
data: {
checkedNames: [],
}
})
</script>
</body>
</html>
③单选框
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>单选框</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>单选框:</p>
<label for="man">男</label>
<input type="radio" id="man" value="男" v-model="sex">
<br>
<label for="woman" >女</label>
<input type="radio" id="woman" value="女" v-model="sex">
<br>
<span>选中值为:{{sex}}</span>
</div>
<script>
var first = new Vue({
el: '#app',
data: {
sex: '',
}
})
</script>
</body>
</html>
④下拉框
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>下拉框</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<p>下拉框:</p>
<select v-model="url" >
<option value="">选择一个网站</option>
<option value="www.baidu.com">百度</option>
<option value="www.google.com">谷歌</option>
</select>
<br/>
<span>选中值为:{{url}}</span>
</div>
<script>
var first = new Vue({
el: '#app',
data: {
url: '',
}
})
</script>
</body>
</html>
二,Thymeleaf
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为“.html”,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示。
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下两个个极吸引人的特点:
-
它支持 html 原型
-
Jsp中EL表达式和JSTL标签能完成工作,它都几乎都能完成
1. 环境搭建
在pom.xml导入依赖
<!--Thymeleaf 启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在application.yml文件中添加配置
spring:
thymeleaf:
#文件存放路径前缀
prefix: classpath:/templates/
encoding: UTF-8
#文件类型
mode: html
cache: false
在resources下创建templates 目录,用于存放.html文件
2.Thymeleaf 变量表达式
Thymeleaf 作为一种模板引擎,它拥有自己的语法规则,常用的变量表达式
属性 | 描述 | 实例 |
---|---|---|
th:text | 文本替换,转义特殊字符 | <span th:text="${info}"></span> |
th:value | 替换 value 属性 | <input th:value = "${user.name}" /> |
th:each | 遍历,支持 Iterable、Map、数组等 | <table> <tr th:each="m:${session.map}"> <td th:text="${m.getKey()}"></td> <td th:text="${m.getValue()}"></td> </tr> </table> |
th:if | 根据条件判断是否需要展示此标签 | <a th:if ="${userId == collect.userId}"> |
th:selected | select 选择框选中 | <select> <option>---</option> <option th:selected="${name=='a'}"> 成都 </option> <option th:selected="${name=='b'}"> 北京</option>** </select> |
3.案例
使用Thymeleaf手动实现表格分页
前后端不分离
controller层
package com.hqyj.j220701.springboot.springbootdemo01.user.controller;
import com.hqyj.j220701.springboot.springbootdemo01.user.dto.UserInfo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Controller
@RequestMapping("/api/user")
public class UserController {
@RequestMapping("/list")
public String getUserListView(Integer pageNumber, Model model){
if (Objects.isNull(pageNumber)){
pageNumber = 0;
}
/* if (null == pageNumber){
pageNumber = 0;
}*/
List<UserInfo> userInfoList = new ArrayList<>();
for (int i = 0; i < 10 ; i++) {
UserInfo userInfo1 = new UserInfo();
userInfo1.setUserName("zhangsan" + i);
userInfo1.setPassword("123456");
userInfo1.setSex("m");
userInfo1.setAge(20);
userInfoList.add(userInfo1);
}
//分页的设计
int pageSize = 2;//页面大小
int total = userInfoList.size();//总记录数
int totalPage = total / pageSize;//一共多少页
//如果取模有余数,表示还有数据未被分页,total加一
if (total % pageSize != 0){
totalPage++;
}
//当前页
int fromIndex = (pageNumber * pageSize);//开始下标
int toIndex = fromIndex + pageSize;//当前页结束下标
//防止下标越界
if (toIndex >= total){
toIndex = total;
}
//区间:[fromIndex,toIndex)
List<UserInfo> subList = userInfoList.subList(fromIndex, toIndex);
model.addAttribute("list",subList);
model.addAttribute("pageNumber",pageNumber);
model.addAttribute("totalPage",totalPage);
return "user_list";
}
}
在dto层创建用户信息类UserInfo
package com.hqyj.j220701.springboot.springbootdemo01.user.dto;
import lombok.Data;
@Data
public class UserInfo {
private String userName;
private String password;
private String sex;
private Integer age;
}
html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div>
<table class="table table-bordered">
<thead>
<tr>
<th>姓名</th>
<th>密码</th>
<th>性别</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr th:each="user:${list}">
<td th:text="${user.userName}"></td>
<td th:text="${user.password}"></td>
<td th:text="${user.sex}"></td>
<td th:text="${user.age}"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="modal-footer">
<ul class="pagination pull-right">
<li><a th:href="@{/api/user/list(pageNumber=0)}">首页</a></li>
<li th:if="${pageNumber} > 0">
<a th:href="@{/api/user/list(pageNumber= ${pageNumber} - 1)}">前一页</a>
</li>
<li th:if="${pageNumber} < ${totalPage} - 1">
<a th:href="@{/api/user/list(pageNumber= ${pageNumber} + 1)}">后一页</a>
</li>
<li><a th:href="@{/api/user/list(pageNumber=${totalPage} - 1)}">尾页</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>
前后端分离
后端代码相同,前端代码在Hbuilder编写
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery-3.6.0.min.js"></script>
<link href="css/bootstrap.min.css" rel="stylesheet" />
<script src="js/vue.js"></script>
</head>
<body>
<div class="container" id="app">
<div class="row">
<div>
<table class="table table-bordered">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>密码</th>
<th>性别</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr v-for="(user,index) in userList">
<td>{{index + 1}}</td>
<td>{{user.userName}}</td>
<td>{{user.password}}</td>
<td>{{user.sex}}</td>
<td>{{user.age}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="modal-footer">
<ul class="pagination pull-right">
<li>
<a href="javascript:void(0);" v-on:click="firstPage()">首页</a>
</li>
<li v-if="pageNumber>0">
<a href="javascript:void(0);" @click="prePage()">上一页</a>
</li>
<li v-if="pageNumber<totalPage-1">
<a href="javascript:void(0);" @click="nextPage()">下一页</a>
</li>
<li>
<a href="javascript:void(0);" v-on:click="lastPage()">尾页</a>
</li>
</ul>
</div>
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
userList: [],
pageNumber: 0,
totalPage: 0,
total: 0
},
methods: {
getUserList: function() {
var _this = this;
var params = {
pageNumber: this.pageNumber
};
$.ajax({
url: "http://localhost:8082/api/user/list",
type: "GET",
dataType: "json",
data: params,
success: function(data) {
if (data.code == 200) {
_this.userList = data.userList;
_this.pageNumber = data.pageNumber;
_this.totalPage = data.totalPage;
_this.total = data.total;
} else {
alert("接口调用失败");
}
},
error: function() {
alert("请求失败");
}
});
},
firstPage: function() {
this.pageNumber = 0;
this.getUserList();
},
prePage: function() {
this.pageNumber--;
this.getUserList();
},
nextPage: function() {
this.pageNumber++;
this.getUserList();
},
lastPage: function() {
this.pageNumber = this.totalPage - 1;
this.getUserList();
}
},
mounted: function() {
this.getUserList();
}
})
</script>
</body>
</html>
前后端分离时会遇到一个问题:跨域问题
前后端代码完成后进行调试,当前端页面访问后台接口时会出现跨域问题。
跨域问题的根本原因:浏览器有同源策略限制,当前域名的js只能读取同域下的窗口属性,这是一个基础安全功能。那么什么是同源策略呢?即两资源的URL中 协议,域名,端口,都相同则称为同源,若两资源为不同源,则不允许共享资源。例如:后台接口域名为百度一下,你就知道,前端发送请求情况如下:
请求地址 | 结果 | 原因 |
---|---|---|
http://www.baidu.com/dir1/b.html | 成功 | 同一域名端口,相同文件夹 |
http://www.baidu.com/dir2/a.html | 成功 | 同一域名端口,不同文件夹 |
https://www.baidu.com/dir1/b.html | 失败 | 不同协议,http与https |
http://www.baidu.com:8000/dir1/b.html | 失败 | 不同端口,默认为80 |
新浪网 | 失败 | 不同域名 |
复杂的跨域请求,浏览器会先发送一个OPTIONS探路,收到响应允许跨域后会再发送真实请求。此时服务端写代码时,还需响应(OPTIONS)允许跨域。
后台解决跨域问题:
创建CorsWebFilter
@Configuration
public class CorsWebConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOriginPattern("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return new CorsFilter(source);
}
}