Vue
是什么 : 一套用于构建用户界面的渐进式框架
术语 : Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
为什么 : 自前后端分离以后 , 当用户进行访问时 , 先返一个页面回去 , 然后发送异步请求填充数据 , 很方便
怎么做 : 我要怎么准备他想要的数据是要点
01 入手
①建一个Static Web项目 , 导入vue和jQuery的东西 , 然后操作
②配置好TomCat
我的印象里 : TomCat有三 , 由Maven的 , Web的 , Spring内嵌的 , 这里用Web的
怎么弄 : 点上面栏的Add Configuration左上角加一个Local的猫
1 端口8080 并选Chrome
2 右边点Configure确定猫设置好了路径对
3 Deployment要选整个vue-demo ,上下文路径改成一个/
HelloWorld
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello-vue</title>
<script src="../../js/vue/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
</script>
</body>
</html>
入门分析
#1 双括号{{ }}代表取值
#2 上方引用vue路径过程 : 由当前html所在包往上两级到views,再往上两级到vue-demo,在由此往下找到js包下vue包下vue.js
#3 我的理解 : 一般一个html里对这个标签只会new一个Vue , 其中el表示指定哪个标签,data表示数据 , 下面还会有mounted,methods等
02 指令
01 {{ 大括号取值 }}
#1 取值 {{ message }}
这个只能放在标签之间的内容里 , 不能放在标签里面
关于
<script>
var app = new Vue({
el: '#app',
#要点1 : 指定vue解析模板的范围,这个只能指定body内部的标签
#要点2 : data内则是定制解析模板需要的数据,也叫属性
data: {
message: 'Hello Vue!'
}
})
</script>
02 v-bind: (:)取值
第二种取值
<div id="app" >
<span v-bind:title="title">
{{msg}}
</span>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
msg : 'msg标签内',
title : '标签内title'
}
})
</script>
问题
# 如果我想给标签内的属性取值, 比如span内的title属性取值 , 步骤如下
---------------------------------------------------
#1 找到span要赋值的属性叫title
#2 在vue的data内添加title属性并赋值
#3 关于v-bind:可以直接写成一个冒号 :
敲一下锤子看看效果
03 v-model全局
v-bind: 算是局部+单项取值(绑定数据)
意思就是v-bind算是从data内写好值然后赋给标签内的属性 ,
v-model就不一样了 , 比如v-model : title = “titlea”
那所有赋值为titlea的值都和data内赋值同步变化
**要点 **
作用 : 页面填数据 , 比如好几个地方要填昵称 , 那用户只要填一个,其他要填的地方就会补充好了
#1 v-model是全局+双向绑定数据
#2 v-model也相当于value了, 如果是v-model:value的话其实可以简写为v-model="值"
举个例子
<div id="app" >
<input name="nickName" v-bind:value="content">
<input name="nickkName" v-model:value="content">
{{ content }}
</div>
<script>
var app = new Vue({
el : '#app',
data : {
msg : 'msg标签内',
content : 'dafei'
#1 这个时候,如果在v-bind的框内写东西,只有它自己编
#2 在v-model的框写东西,所有赋值为content的内容跟着一起变
就是上面仨content都会一起变
}
})
</script>
</body>
</html>
04 v-html
当我们在内容里面写html标签想让他生效而不是硬生生显示这段东西的话用这个标签就可以了
<div id="app" >
<div v-html="seemenow"></div>
<br/>
{{seemenow}}
</div>
<script>
var app = new Vue({
el : '#app',
data : {
seemenow : '<span style="color: red;">你看</span>'
}
})
效果
①贴了v-html标签的显示的是红色字有效果的
②直接取seemenow值的看到的就是一串字没有效果
<span style="color: red;">你看</span>
05 v-if
为什么 : 比如在填性别 , 如果是只有男女的话就可以直接
{{ emp.sex === 0? '男':'女'}}
但是如果又加了一个外星人怎么办?
<div id="app" >
<span v-if="age < 18">小于18岁</span>
<span v-else-if="age > 18">大于18岁</span>
<span v-else="age = 18">等于18岁</span>
<div v-html="msg"></div>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
age : 30,
msg : '<span style="color: cornflowerblue">Holalalala</span>'
}
})
</script>
06 v-for
跟之前的for一个道理 , 写法不一样 , 这是以前的thymeleaf语法
<tr th:each="emp : ${employees}">
<td th:text="${emp.name}">名字</td>
<td th:text="${emp.age}">年龄</td>
<td th:text="${emp.email}">邮箱</td>
</tr>
这是vue的
<div id="app">
<ul>
<li v-for="e in arr">{{e}}</li>
</ul>
<ul>
<li v-for="(e,index) in arr">{{index}}-{{e}}</li>
</ul>
<ul>
<li v-for="emp in emps">{{emp}}</li>
</ul>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
arr:["a","b","c","d"],
emps:[
{id:1,name:"one",age:1},
{id:2,name:"two",age:2},
{id:3,name:"three",age:3},
{id:4,name:"four",age:4},
]
}
})
</script>
分析
#1 第一种 以数组为例
<li v-for="e in arr">{{e}}</li>
#2 第二种 加了索引的,一定要(元素,索引)
<li v-for="(e,index) in arr">{{index}}-{{e}}</li>
#3 第三种,还可以是集合
<li v-for="emp in emps">{{emp}}</li>
07 v-on
之前加入方法有两种方式,
一种是直接在标签里插入[事件+方法]然后在JS里补
<button name="btn" onclick="hello()">
然后在script里写
<script>
function(){
// xxxxx
}
</script>
另一种直接在JS里找到这个按钮然后再点事件
<button name="btn" id="btnID">
<script>
$(function () {
// 函数体;
$('#btnID').click(){
//xxxxxxxxx
}
});
</script>
回顾 : 获取元素的三种方式
其实还有很多种这里回忆常用的三种
<script>
#1 如果某类标签只有一个,找这个可以这样
$('input')
#2 通过id找
$('#spanID')
#3 通过class属性找
$('.myClass')
</script>
v-on就是绑定事件用的,要点如下
#1 表达方式有两种:
v-on:click="clickMe"
@click="clickMe"
#2 使用方法
->先确定好在哪里绑定事件,写好上面的东西
->在vue内开一个methods:
->具体看这个例子
例子
<div id="app" >
<span v-bind:title="title">
{{msg}}
</span>
<br/>
<button name="btn" v-on:click="clickMe">按钮1</button>
<br/>
<button name="btn" @click="clickMe">按钮2</button>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
msg : '测试方法用的, 贴两种',
},
methods :{
clickMe:function () {
alert('HolaTina!');
}
}
})
</script>
03 $event
01 组成
关于事件 , 其组成可以分为四个部分 :
事件源, 事件函数 , 事件对象 , 事件函数传参
可以这样举个例子就懂了,这四样分别是:
①input标签②点击事件的函数clickMe(function)
③事件对象 : 事件触发到结束整个过程信息记录对象
④参数
这个事件对象有点不好理解 , 但是可以在F12里看到 , 比如这样↓
02 事件对象
👉为了看到这个事件对象我们要做点准备工作
<div id="app" >
<button name="btn" v-on:click="clickMe($event)">Button</button>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
msg : '关于事件'
},
methods:{
clickMe:function (event) {
console.log(event);
}
}
})
</script>
👉事件具体,包括各种东西
PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
isTrusted: true
altKey: false
altitudeAngle: 1.5707963267948966
azimuthAngle: 0
bubbles: true
button: 0
buttons: 0
cancelBubble: false
cancelable: true
clientX: 39
clientY: 20
composed: true
ctrlKey: false
currentTarget: null
defaultPrevented: false
detail: 1
eventPhase: 0
fromElement: null
height: 1
isPrimary: false
layerX: 39
layerY: 20
metaKey: false
movementX: 0
movementY: 0
offsetX: 29
offsetY: 9
pageX: 39
pageY: 20
path: (6) [button, div#app, body, html, document, Window]
pointerId: 1
pointerType: "mouse"
pressure: 0
relatedTarget: null
returnValue: true
screenX: 415
screenY: 295
shiftKey: false
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}
srcElement: button
tangentialPressure: 0
target: button
tiltX: 0
tiltY: 0
timeStamp: 1016.6000000014901
toElement: null
twist: 0
type: "click"
view: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
which: 1
width: 1
x: 39
y: 20
[[Prototype]]: PointerEvent
03 要点
①这个$event :
当事件触发时 , vue会创建事件对象, 然后会赋值给$event , 就上面这么大一坨
这个$event就是这么写的 , 不能乱改成别的
②获得事件源
用event.target就可以了,具体看这个
console.log(event.target)打印出来就长这样
<button name="btn">Button</button>
③拿到东西
比如我要拿到里面的emp
<div id="app">
<ul>
<li v-for="emp in emps" v-on:click="clickMe($event,emp)">
{{emp.id}}--{{emp.name}}--{{emp.age}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
{id:1,name:"one",age:1},
{id:2,name:"two",age:2},
{id:3,name:"three",age:3},
{id:4,name:"four",age:4},
]},
methods:{
clickMe:function (event,emp) {
console.log(event);
console.log(emp);
}
}
})
</script>
04 vue的属性
#1 必会
el , data , methods , filters , mounted
#2 拓->精
created 是一个函数, 在vue实例创建完成后被立即调用(html加载完成之前,执行) ,
computed 用来计算 ,
template 用来设置模板,会替换页面元素,包括占位符,
render 创建真正的Virtual Dom,
watch 监听data中数据的变化
接下來把兩個沒説完的屬性filters和mounted説完
01 filters
!!!注意 : 前面我不是写了一个v-if吗 , 我把那个跟这个搞混了,
过滤器 ,用在比如 : 男/女之外如果还有个无 ,那以前的就用不上了
emp.sex === 0 ? 'male':'female'
所以可以用这个
<div id="app">
<ul>
<li v-for="emp in emps" v-on:click="clickMe($event,emp)">
{{emp.id}}--{{emp.name}}--{{emp.age}}--{{emp.sex | sexFilter}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
{id:1,name:"one",age:1,sex:0},
{id:2,name:"two",age:2,sex:1},
{id:3,name:"three",age:3,sex:2},
{id:4,name:"four",age:4,sex:1},
]},
methods:{
clickMe:function (event,emp) {
console.log(event);
console.log(emp);
}
},
filters:{
sexFilter:function (value) {
if (value === 0){
return '男';
}else if (value === 1){
return '女';
}else{
return '无';
}
}
}
})
</script>
分析
#1 {emp.sex | sexFilter}
# 这个竖杠| 是管道 , 在下面的函数不是传了value吗,就是emp.sex的值通过管道经过过滤器.然后进了这个过滤器进行逻辑判断来显示结果,具体看方法体
Filters
①是什么 : 上面的这个其实是局部的Filter , 还有全局的(了解)
即在创建 Vue 实例之前全局定义过滤器:
<script>
# 写在script里的
Vue.filter('sexFilter',function(value)){
if (value === 0){
return '男';
}else if (value === 1){
return '女';
}else{
return '无';
}
}
</script>
区别&要点
①在Vue创建之前提前设置了全局过滤器
②注意,一个页面一般就一个Vue实例
③当局部和全局过滤器都在的时候 , 局部优先级>全局
02 mounted
它没有大括号,它是一个function
<script>
// 这个在vue里,跟el他们同级,这里略写前面部分
mounted:function(){
console.log("----------");
}
是什么 : 关于Vue的过程其实是这样的
过程
#1 先new Vue({..}) (创建Vue实例)
#2 初始化vue实例各种事件,生命周期相关等等的东西
#3 加载vue指定模板
#4 解析vue指定模板
#5 使用默认的data数据填充样板
#6 执行mounted方法
#7 返回初始化好了的vue实例对象
如何可以得知 ?在两个地方打印可以发现
①在mounted方法体中打印vue发现undefined ,
但是打印this(当前构建的vue实例对象)就有
②在方法外打印vue就有
这说明mounted方法在vue实例初始化完成之前执行的
作用
①进行vue实例data数据的加载 , vue会在mounted中发送异步请求 , 从接口服务器中拿模板要的数据
解释 : 先前提及了,Browser发送请求时,前端接收请求返回页面模板 , 但是毕竟要填充数据啊 , 比如我sex值都是012但是页面显示男女无,所以要经过if判断之类的 , 那没数据怎么判断,而且最终我都是要返回一个页面回去的 , 所以在vue实例初始化完成前,在mounted方法执行时会发送异步请求拿后端数据过来塞进去 , 然后才有if判断 , 换句话说也算是mounted存在的意义吧, 给vue中去后端拿数据填充争取一段时间- -我是这么想的
示例
<div id="app" xmlns:v-on="http://www.w3.org/1999/xhtml">
<ul>
<li v-for="emp in emps" v-on:click="clickMe($event,emp)">
{{emp.id}}--{{emp.name}}
{{emp.age}}--{{emp.sex | sexFilter}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
]},
methods:{
clickMe:function (event,emp) {
console.log(event);
console.log(emp);
}
},
filters:{
sexFilter:function (value) {
if (value === 0){
return '男';
}else if (value === 1){
return '女';
}else{
return '无';
}
}
},
mounted:function () {
#1 在这里争取时间从后端拿数据过来填上
var _this = this;
console.log("--------------");
$.get("/data/emps.json",{},function (data) {
console.log(data); // 后端拿数据回来
_this.emps = data; // 注意这里可以省略data不用写直接写emps
})
}
})
</script>
05 生命周期
在创建实例到初始化完成过程中重要的简说成一句话就是 :
mounted方法发起异步请求触发数据变动->页面刷新
就我改代码的时候不是改完就F5页面马上出效果了吗 , 其实一有改动它就会发现然后刷新 , 内部涉及虚拟html,dom啥的- -
其实有个图大概是这样我就不贴过来了:
new Vue() →初始化事件对象和生命周期 →初始化注入和校验 → 是否指定"el"选项(元素)?
→一般来说搞前端的都是选否 , 因为有自己template模板编译到render(渲染)函数中
→因为我只是弄入门简单学一下 , 所以没有弄模板就是自己弄个el写一下 , 于是会将el外部的HTML作为template编译
→挂载完毕
然后,当data被修改时 , 就是内部发生变动时,会马上通过虚拟DOM重新渲染并应用更新
→往后就是到销毁路上的接触绑定销毁子组件以及事件监听器等,搞完对象就销毁了
06 综合案例
以之前的mp-demo来说
步骤1 :
先把项目改成SpringMVC项目 . 这意味着要在pom里添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
跨域访问问题
当A服务器访问B服务器的时候,若
①使用协议
②IP
③端口
有其中之一不一样 , 那么就叫跨域访问
问题主要是Browser折腾出来的 , 就第一个请求过去是有带头的,但是回来又出去以后回来要求带头不带头就芭比q了, 所以得解决一下这个问题 , 弄这个也不是不好 , 可以防止别人刷网页 , 加这个就好了
// 在启动类App里加这个[跨域访问配置]
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//放行哪些原始域
.allowedOriginPatterns("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET", "POST", "PUT", "DELETE","OPTIONS")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
}
};
}
步骤2 :
控制器写一下 , 然后接口记得改一下 , vue-demo的端口是8080 , 所以mp这边改一个,
在配置文件里改成
server.port = 8888
list
list.html写了一个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>list</title>
<script src="../../js/jquery/jquery.js"></script>
<script src="../../js/vue/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="emp in emps">
{{emp.id}}--{{emp.name}}--
{{emp.age}}--{{emp.admin | adminFilter}}
<a v-bind:href="'edit.html?id=' + emp.id">编辑</a>
</li>
</ul>
</div>
</body>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
]
},
filters:{
adminFilter:function (value) {
if (value === 0){
return '管理员';
}else {
return '普通用户';
}
}
},
mounted:function () {
var _this = this;
$.get("http://localhost:8888/employees/list",{},function (data) {
_this.emps = data;
console.log(data);
})
}
})
</script>
</html>
add
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>list</title>
<script src="../../js/jquery/jquery.js"></script>
<script src="../../js/vue/vue.js"></script>
</head>
<body>
<div id="app">
<form id="formId">
<input type="hidden" name="id">
名称:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
爱好:
<input type="checkbox" name="hobby" value="c">c
<input type="checkbox" name="hobby" value="go">go
<input type="checkbox" name="hobby" value="pyhon">python
<input type="checkbox" name="hobby" value="java">java
<br>
<input type="button" id="btn" @click="submitForm()" value="提交"></input>
</form>
</div>
</body>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
]
},
filters:{
adminFilter:function (value) {
if (value === 0){
return '管理员';
}else {
return '普通用户';
}
}
},
methods:{
submitForm: function() {
$.post("http://localhost:8888/employees/save",$('#formId').serialize(),function (data) {
// 返回JsonResult
console.log(data);
if (data.code === '200'){
window.location.href = "list.html";
}else{
alert("跳不过去list页面");
}
})
}}
})
</script>
</html>
add要点
前端 :
①主要是表单提交需要方法 , 要去methods里面补一下提交方法
②逻辑判断code===200?注意这个200是字符串要加引号啊我设的是String不是Integer
edit
注意 这里要扒拉id需要借助代码 , 贴在script下面了
原理是根据问号?和点.进行分割然后弄成key-value这样扒拉第一个就是id就拿到值了
<script>
# 提前在这里备注一下逻辑
/**
* 1.在list页面点[编辑]的时候要跳过来---注意vue和拼接
* 2.数据回显--在mounted里做,查到那条数据返回来
* 3.数据更改后要提交,要在methods里补充表单提交方法
* 4.逻辑判断:提交后要返回list页面
*/
var app = new Vue({
el : '#app',
data : {
emp:{}
},
methods:{
submitForm:function(){
$.ajax({
url : "http://localhost:8888/employees/update",
type : "PUT",
data : $('#formId').serialize(),
success : function (data) {
if (data.code === '200'){
window.location.href = "list.html";
}else {
console.log(data.msg);
}
}
})
}},
mounted:function () {
// 数据回显
var id = getParams().id;
$.get("http://localhost:8888/employees/detail",{id:id},function (data) {
console.log(data);
})
}
});
//获取url上的请求参数
function getParams() {
//获取问号及问号后面的内容
var url = window.location.search;
var params = new Object();
if (url.indexOf("?") !== -1) {
//截取问号后面的内容,再使用&分割多个属性
var arr = url.substr(1).split("&");
for (var i = 0; i < arr.length; i++) {
//使用=分割为keyvalue
var keyValue = arr[i].split("=");
params[keyValue[0]] = keyValue[1];
}
}
return params;
}
</script>
edit要点
①ajax的时候注意前台编辑传过来拉去后台的数据–表单提交的数据 , 用
data : $('#formId').serialize()
②关于后面INSERT的结果点编辑数据没有回显是为什么
精度问题 :
问题 : 数据库中id是1465396696468033540 结果传到前端变成1465396696468033500,后面几位精度缺失了
原因 : Number精度16位(雪花ID是19位的),so:JS的Number数据类型导致的精度丢失
public class Employee {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
// 加这个注解就行了
还有
如果数据没有回显 , 可能是 :
①表单数据提交有问题 : 看看是不是定位表单的时候jQuery找元素写错了 , 看看用的是id定位还是class定位还是标签类型定位啥的
②数据塞进去回显的时候 , 是不是变量名写错了 ,比如_this.emp跟data里的emp名字没对上
delete
自己写了得 , 我先在这里写思路然后再去实操
#1 在list.html页面加一个删除按钮 , 弹框是否确认删除
#2 触发方法,在methods里加上,code===200跳转到list.html
#3 在↑基础上逻辑代码里加上后台删除操作
#4 不需要异步
问题
我原来href里用的跟edit一样页面?id=这样 , 结果它连着按好几次删除才能删掉 , 我发现第一次list请求被cancel第二次是delete请求被cancel但是东西删了 , 还没找到原因 , 然后问了毛伟杰 , 参考了他的 , 主要修改在 :
①href = “#”
②把id放在函数里
# 这里我才弄明白了v-bind和v-on的存在意义
因为需要id , 然后id来源于vue内的data赋值emp,我要拿到的话肯定跟vue关联 , 那我在函数里需要参数id , 为emp.id , 这个emp.id需要vue内 , 所以在点击事件的同时还要关联vue,就是v-on:click="del(emp.id)"
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>list</title>
<script src="../../js/jquery/jquery.js"></script>
<script src="../../js/vue/vue.js"></script>
</head>
<body>
<div id="app">
<a href="ad.html">添加</a>
<ul>
<li v-for="emp in emps">
{{emp.id}}--{{emp.name}}--{{emp.age}}--{{emp.admin | adminFilter}}
<a v-bind:href="'edit.html?id=' + emp.id">编辑</a>
<a href="#" v-on:click="del(emp.id)">删除</a>
</li>
</ul>
</div>
</body>
<script>
var app = new Vue({
el : '#app',
data : {
emps:[
]
},
methods:{
del:function (id) {
let flag = window.confirm("是否确认删除?");
if (flag){
$.ajax({
url : "http://localhost:8888/employees/delete",
type: "DELETE",
data: {id:id},
success:function (data) {
if (data.code === '200'){
alert(data.msg);
window.location.href = "list.html"
}else {
alert(data.msg);
}
}
})
}
}
},
filters:{
adminFilter:function (value) {
if (value === 0){
return '管理员';
}else {
return '普通用户';
}
}
},
mounted:function () {
var _this = this;
$.get("http://localhost:8888/employees/list",{},function (data) {
_this.emps = data;
console.log(data);
})
}
})
//获取url上的请求参数
function getParams() {
//获取问号及问号后面的内容
var url = window.location.search;
var params = new Object();
if (url.indexOf("?") !== -1) {
//截取问号后面的内容,再使用&分割多个属性
var arr = url.substr(1).split("&");
for (var i = 0; i < arr.length; i++) {
//使用=分割为keyvalue
var keyValue = arr[i].split("=");
params[keyValue[0]] = keyValue[1];
}
}
return params;
}
</script>
</html>