文章目录
1. MVVM模式介绍(Vue的核心)
MVVM是 Model-View-ViewModel
的简写,即 模型-视图-视图模型。本质上就是MVC 的改进版。在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互。
- 模型
指的是后端传递的数据。 - 视图
指的是HTML页面。 - 视图模型(ViewModel对象)
【***】
是MVVM模式的核心,它是连接View和Model的桥梁,是一个同步View 和 Model的对象。
它有两个方向:一是将模型转化成视图,即将后端传递的数据转化成所看到的页面,实现的方式是:数据绑定。
二是将视图转化成模型,即将所看到的页面转化成后端的数据,实现的方式是:DOM事件监听。如果这两个方向都实现的,称之为数据的双向绑定。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉。
Model–View–ViewModel(MVVM) 是一个软件架构设计模式,是在MVC模式的基础上,经过MVP模式的过度最终演变而成,是现在非常流行的前端设计模式。
2. MVC和MVVM区别
MVC
在MVC模式中,当DOM元素触发事件的时候,由js进行DOM元素的获取以及数据变化的收集,然后更新数据,操作DOM更改数据。当页面元素较多时,频繁进行数据交互将对维护和性能造成很大影响。
MVVM
在MVVM模式下,DOM元素与某个对象进行挂载,DOM元素中的属性值与对象的属性值进行绑定对应,无论是DOM元素属性变化还是对象的属性变化都会影响另一方,因此使用这种模式开发显得项目清晰,高效。
3. SpringBoot的resources目录介绍
- public目录
优先级最低, 一般放一些公共资源,可以直接通过url访问。 - static目录
一般静态文件放在static下(比如css、js、image等),首页(index.html)有时也放在static里面 - templates目录
动态页面放在Templates下, 只能通过controller才能访问到该目录。(和以前的WEB-INF差不多), 里面放Thymeleaf的一些页面。 - resources目录(resources下的resources目录)
一般用于存放一些文件(上传下载的文件)
四个默认能够直接访问的优先级
classpath:/static
classpath:/public
classpath:/resources
classpath:/META-INF/resources
如果在上面4个默认的能访问的文件夹里如果有名字重复的静态资源那先访问哪个?
优先级:
/META-INF/resources/
> /resources/
> /static/
> /public/
**修改资源目录,自定义Spring boot加载前端静态资源路径** `spring.resources.static-locations=classpath:/templates/,classpath:/static/`
使用maven构建项目时,resources 目录就是默认的classpath路径
路径classpath开头加/
和不加的区别
# 开头不加 / ,maven项目就是默认的resources目录
classpath:mapper/*.xml
# 开头加上 / ,就是整个模块的根目录
classpath:/mapper/*.xml
4. Vue介绍
- Vue【读音
/vjuː/
】:是一套用于构建用户界面的渐进式
框架
涉及到数据交互的内容很多的情况,应用了vue之后将大大缩减工作量。 - Vue特点:
- 易用:在有HTML CSS JavaScript的基础上,能够快速上手。
- 灵活:简单小巧的核心,
渐进式
技术栈,足以应付任何规模的应用。 - 高效:体积小(20kb min+gzip 运行大小),2.0增加虚拟DOM,执行速度快。
vue两大特点:响应式编程、组件化。(渐进式)
vue的优势:体积小、简单、上手快、双向数据绑定、组件化、视图、虚拟DOM运行速度快等。
Vue是基于MVVM模式实现的一套框架,MVVM模式分离视图View和数据Model,通过自动化脚本实现自动化关联,ViewModel搭起了视图与数据的桥梁,同时在ViewModel里进行交互及逻辑处理。可以简单的理解,View就是HTML、DOM,数据Model就是要处理的Json数据。这种模式有以下优势:
- 低耦合
- 重用性
- HTML模板化
- 数据自动处理
- 双向绑定
-
使用Vue一般有三种用法
1)直接用<script>
引入
2)构建大型应用可以使用NPM安装
3)命令行工具 -
Vue的下载地址
https://cn.vuejs.org/v2/guide/installation.html
选择适合自己的版本下载就行。(学习一般使用开发版本)
5. vue入门案例
- 引入本地
vue.js
文件
<script src="js/vuejs-2.5.16.js"></script>
- 构建vue对象
<div id="app">
{{"message的值是:" + message}}
{{true? "OK":"Not OK"}} <!-- 表达式运算 -->
<!--{{ var a = 1 }}
{{ if (ok) { return message } }}-->
</div>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vue掌握指定的区域
data:{
message:'hello Vue'
} //对应的数据
}
) //创建了MVVM中的VM对象
</script>
6. Vue的生命周期
每个Vue实例在被创建时,都要经过一系列的初始化过程。例如,需要设置数据监听、编译模板、将实例挂载到DOM,并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了开发者在不同阶段添加自己的代码的机会。
钩子函数 | 说明 |
---|---|
beforeCreate | 在Vue实例创建之前执行的函数 |
created | 实例创建完成后调用。 |
beforeMount | 在Vue实例创建之后,数据未渲染负责接管DOM之前执行的函数 |
mounted | el挂载到实例上后调用,一般我们的第一个业务逻辑会在这里开始。 |
beforeDestory | 实例销毁之前调用。主要解绑一些使用addEventListener监听的事件等。 |
destroyed | Vue实例在执行vm.destroyed()命令之后,销毁之后执行的函数 |
beforeUpdata | 在Vue实例数据更新之前执行的函数 |
updated | 在Vue实例数据更新之后执行的函数 |
7. vue语法
7.1 模板语法{{ }}
,vue 的插值表达式
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值,Mustache 标签将会被替代为对应数据对象上属性的值。
特点:
- 绑定的数据对象上属性发生了改变,插值处的内容都会更新
- 每个绑定都只能包含单个表达式。
插值的语法有3中:文本、原始HTML、使用JavaScript表达式。
插值表达式:{{ 表达式 }}
对象 :
{{ {name:'jack'} }}
字符串 :{{ 'xxx' }}
判断后的布尔值:{{ true }}
三元表达式 :{{ true?'正确显示':'错误显示' }}
<div id="app">
{{message}}
{{true ? 'OK':'Not OK'}} <!-- 正确,显示OK -->
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }} <!-- 错误 -->
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }} <!-- 错误 -->
</div>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vue掌握指定的区域
data:{
message:'hello Vue'
} //对应的数据
}
) //创建了MVVM中的VM对象
</script>
{{mesage}}
在解析的时候会被JS中定义的Vue对象vm的data属性中的message的值替换,当message的值发生变化后,文本中的值也会自动变化。
【注意】模板语法不是写上就能有用的,需要满足两个条件
- Vue对象与dom元素挂载
- 语法中显示的表达式,要在Vue对象中找到对应的值。
如图:插值表达式可以显示文本,运算,字符串,boolean类型,三元运算符,对象等表达式
7.2 指令
指令(Directives)是带有 v-
前缀 的特殊特性。指令特性的值预期是单个JavaScript表达式(v-for是例外情况)。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM。
所谓的指令只是Vue数据绑定的一些固定写法,帮助我们实现一些基本的常用的数据绑定效果
7.2.1 v-on 指令(简写@)
<a v-on:click="doSomething">...</a>
可以缩写为@
<a @click="doSomething">...</a>
- v-on 指令:点击事件
<body>
<div id="app">
{{message}}
<button v-on:click="func1('hello')">vue的onclick</button>
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'hello Vue3'
}, //对应的数据
methods:{
func1:function (msg) {//msg='hello'
alert(msg)
}
}
}
) //创建了MVVM中的VM对象
</script>
2. v-on 指令:键盘事件
<body>
<div id="app">
<input type="text" v-on:keydown="fun($event)">
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'hello Vue3'
}, //对应的数据
methods:{
fun:function (event) {//事件对象包含 event=$event
alert(event.keyCode)
}
}
}
) //创建了MVVM中的VM对象
</script>
$event
表示Vue中的事件对象,类似js中的对象一样。
keyCode
是键盘的编码。
3. v-on :鼠标事件
<body>
<div id="app">
<!-- v-on:mouseover=“函数名()” 表示监听的是鼠标停留事件,@是v-on的简写 -->
<div @mouseover="fun($event)" style="height: 200px;width: 200px;background-color: green" >
</div>
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'hello Vue3'
}, //对应的数据
methods:{
fun:function (event) {//事件对象包含 event=$event
alert("进入范围")
}
}
}
) //创建了MVVM中的VM对象
</script>
v-on:mouseover=“函数名()”
:表示监听的是鼠标停留事件。(@
是v-on
的简写)
Vue指令(v-on)后缀修饰符
修饰符就是由点开头的指令后缀来表示的。相当于调用事件的方法。
v-on
指令在内嵌JS的的时候可以传递参数,同时可以传递$event
对象代表DOM对象发生的原生事件对象。
需要经常使用的$event
的属性和方法Vue给我们提供了一种简单写法:后缀修饰符,后缀修饰符可以连续使用。
后缀名称 | 说明 |
---|---|
.stop | 阻止单击事件继续传播 |
.prevent | 提交事件不再重载页面 |
.capture | 添加事件侦听器时使用 capture 模式 |
.self | 只当事件是从侦听器绑定的元素本身触发时才触发回调 |
.once | 只触发一次回调。 |
.left/right/middle | 只当点击鼠标左/右/中键时触发 |
native | 监听组件根元素的原生事件。 |
stop
阻止冒泡示例(内部标签传给外部标签的事件)
<body>
<div id="app">
<form @submit.prevent action="#" method="post">
<input type="text" name="username"><br/>
<input type="text" name="password"> <br/>
<input type="submit" value="login"> <br/>
</form>
<div>
<a @click.prevent href="http://www.baidu.com">进入百度页面</a>
</div>
<div @click="fun1" style="height: 200px;width: 200px;background-color: yellow">
<div @click.stop ="fun2" style="height: 100px;width: 100px;background-color: red">
</div>
</div>
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'hello Vue3'
} ,//对应的数据
methods:{
fun1:function () {
alert("我是黄色的")
},
fun2:function () {
alert("我是红色的")
}
}
}
) //创建了MVVM中的VM对象
</script>
vue中的按键修饰符
vue 允许为 v-on
在监听键盘事件时添加按键修饰符。相当于对keyCode
进行判断,如查是符合条件的按键才执行对应的函数。
<body>
<div id="app">
<input type="text" v-on:keyup.enter="fun1($event)">
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'hello Vue3'
} ,//对应的数据
methods:{
fun1:function (event) {
//if(event.keyCode==13){
alert("你点了回车")
//}
}
}
}
) //创建了MVVM中的VM对象
</script>
7.2.2 v-bind 指令(简写为冒号:
)
v-bind
可以用来在标签上绑定标签的属性(例如:img的src、title属性等等)和样式(可以用style的形式进行内联样式的绑定,也可以通过指定 class 的形式指定样式)。同时,对于绑定的内容,是做为一个JavaScript变量,因此,可以对该内容进行编写合法的JavaScript表达式。
v-bind用来动态的绑定一个或者多个特性。没有参数时,可以绑定到一个包含键值对的对象。常用于动态绑定class和style。以及href等。
简写为一个冒号:
<a v-bind:href="url">...</a>
可以缩写为:
<a :href="url">...</a>
- v-bind 绑定对象{}语法
//进行类切换的例子
<div id="app">
<!-- 当data里面定义的isActive等于true时,is-active这个类才会被添加起作用 -->
<!-- 当data里面定义的hasError等于true时,text-danger这个类才会被添加起作用 -->
<div v-bind:class="{'is-active':isActive, 'text-danger':hasError}"></div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
isActive: true,
hasError: false
}
})
</script>
最终渲染结果:
<!-- 因为hasError: false,所以text-danger不被渲染 -->
<div class = "is-active"></div>
- v-bind 绑定数组
<div id="app">
<!-- 数组语法:errorClass在data对应的类一定会添加 -->
<!-- is-active是对象语法,根据activeClass对应的取值决定是否添加 -->
<p v-bind:class="[{'is-active':activeClass},errorClass]">strive_day</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
activeClass: false,
errorClass: 'error,sorry'
}
})
</script>
最终渲染结果:
<!-- 因为activeClass: false,所以is-active不被渲染 -->
<p class = "text-danger"></p>
- v-bind 直接绑定一个对象
<div id="app">
<!-- 在vue实例的data中定义了classObject对象,这个对象里面是所有类名及其真值 -->
<!-- 当里面的类的值是true时会被渲染 -->
<div :class="classObject">12345</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
classObject:{
'is-active': false,
'text-danger':true
}
}
})
</script>
最终渲染结果:
<!-- 因为'is-active': false,所以is-active不被渲染 -->
<div class = "text-danger"></div>
7.2.3 v-text、v-html、v-bind 读data数据的三个指令
v-text
读data中的数据(代码),显示成字符串。v-html
读data中的数据(代码),如果是代码,则执行代码。v-bind
读data中的数据赋值给属性。
<body>
<div id="app">
<div v-text="message"></div>
<div >{{message}}</div>
<div v-html="message"></div>
<font v-bind:color="color1">我爱中国</font>
<font :color="color2">我爱中国</font>
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
message:'<font color="green"/>HelloWord</font>',
color1:'green',
color2:'red'
} ,//对应的数据
methods:{
}
}
) //创建了MVVM中的VM对象
</script>
7.2.4 v-if、v-show 两个控制显示的指令
v-if
、v-else-if
、v-else
:读data数据,判断是否显示。
根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。v-show
:读data数据,用于条件判断是否显示DOM。
要注意v-show的元素判断为否的时候并没有销毁DOM,只是简切换css的dispaly属性。该元素依旧被渲染存在。
v-if 和 v-show 的区别:
- v-if与v-show指令都是根据表达式的真假值判断元素的显示与隐藏。
- v-if是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
- v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
- 相比之下,v-show就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。
- 一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换(隐藏与显示),则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
7.2.5 v-for 循环指令[***]
vue中使用使用v-for
指令遍历元素。
在使用v-for指令时,可以对数组、对象、数字、字符串进行循环,来获取到源数据中的每一个值。使用v-for指令,必须使用特定语法alias in/of expression,其中items是源数据数组,而item则是被迭代的数组元素的别名,具体格式如下:
(1)v-for=”(元素) in 数组”
(2)v-for=”(value,key) in map集合”
(3)v-for=”(元素,index) in list集合”
<div v-for="item in items">
{{ item.text }}
</div>
案例:
<body>
<div id="app">
<!-- <option v-for="(元素) in 数组"></option>-->
<select >
<option v-for="(name) in arr">{{name}}</option>
</select>
<hr/>
<!-- <li v-for="(值,键)in map集合"></li>-->
<ul>
<li v-for="(value,key) in product">{{key}} , {{value}}</li>
</ul>
<hr/>
<!-- <tr v-for="(元素,索引 ) in 列表">-->
<table border="1px" width="100%">
<tr>
<td>序号</td>
<td>名字</td>
<td>年龄</td>
<td>地址</td>
</tr>
<tr v-for="(user,index ) in userList">
<td>{{index}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>{{user.address}}</td>
</tr>
</table>
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
arr:['人事部','Java','Python'],
product:{name:'苹果12',price:1000,address:'bj'},
userList:[{name:'jack',age:13,address:'bj'},{name:'rose',age:12,address:'bj'},{name:'tony',age:12,address:'bj'}]
} ,//对应的数据
methods:{
}
}
) //创建了MVVM中的VM对象
</script>
7.2.6 v-model 指令【***】
v-model指令用于在表单上创建双向数据绑定,限制在<input>、<select>、<textarea>、components
中使用。
v-model会忽略所有表单元素的value、checked、selected特性的初始值。因为它选择Vue实例数据做为具体的值。
<body>
<div id="app">
<!--更新-->
<form action="" method="post">
username:<input type="text" v-model="user.username" ><br/>
password:<input type="text" v-model="user.password" ><br/>
<input type="submit" value="login" ><br/>
</form>
<hr/>
{{user.username}}
{{user.password}}
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
user:{username:'jack',password:'123456'}
} ,//对应的数据
methods:{
}
}
) //创建了MVVM中的VM对象
</script>
v-model修饰符
v-model修饰符 | 用法描述 |
---|---|
.lazy | v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。<input v-model.lazy="message"> |
.number | 自动将用户的输入值转化为数值类型。<input v-model.number="message"> |
.trim | 自动过滤用户输入的首尾空格。<input v-model.trim="message"> |
7.2.7 v-once 指令
v-once关联的实例,只会渲染一次。之后重新渲染,实例极其所有的子节点将被视为静态内容跳过,这可以用于优化更新性能。
<span v-once>This will never change:{{message}}</span> <!-- 单个元素 -->
<div v-once><!-- 有子元素 -->
<h1>comment</h1>
<p>{{message}}</p>
</div>
<my-component v-once:comment="msg"></my-component> <!-- 组件 -->
<ul>
<li v-for="i in list">{{i}}</li>
</ul>
7.2.8 v-pre 指令
v-pre主要用来跳过这个元素和它的子元素编译过程。可以用来显示原始的Mustache标签。跳过大量没有指令的节点加快编译。
<div id="app">
<span v-pre>{{message}}</span> <!-- 这条语句不进行编译 -->
<span>{{message}}</span>
</div>
<!-- 最终仅显示第二个span的内容 -->
7.2.9 v-cloak指令
在使用Vue的过程中,当引入了vue.js这个文件之后,浏览器的内存中就存在了一个Vue对象,我们可以通过构造函数的方式创建出一个Vue的对象实例,后面就可以对这个实例进行操作。如果在这个过程中,网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。
v-cloak
:保持和元素实例的关联,直到结束编译后自动消失。
v-cloak
指令和CSS 规则一起用的时候,能够解决插值表达式闪烁的问题(即:可以隐藏未编译的标签直到实例准备完毕)
8. 计算属性、侦听器、过滤器
8.1 计算属性computed
在模板语法中可以写表达式,因此可以用来做计算甚至复杂的字符串处理。但是很明显这样去做并不好,尤其是当模板语法中的表达式比较复杂的时候,降低了可读性和可维护性。
可以使用Vue的计算属性computed.
计算属性就是当其依赖属性的值发生变化时,这个属性的值会自动更新,与之相关的DOM也会同步更新。这里的依赖属性值是data中定义的属性。
语法:
一般情况下只写一个默认是get方法,有的时候我们也需要使用set。
计算属性名:{
get:function(){},
set: function(){}
}
计算属性computed特点:
当计算属性依赖的数据发生变化的时候,计算属性就会重新计算
计算属性computed与方法method比较
计算属性是基于它依赖的属性进行计算的,也就是说我计算属性中用到的变量或者说依赖的属性没有发生变化就不会重新计算!!而方法调用一次则执行一次,无论依赖的属性和变量是否改变!!
因此计算属性computed在一定程度上,比如说算数较多,运算较为复杂,重新渲染的时候,性能要优于方法method!【**】
8.2 侦听器(watch)
侦听器,其实是对数据绑定的一种完善,很多时候我们不确定数据何时变化,DOM何时变化。但是又想要对即将发生的事件或者数据变化进行监控,这个时候可以使用侦听器。
侦听器主要是侦听数据。
语法格式:
watch
的作用可以监控一个值的变换,并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。
可以使用:vm.$watch( expOrFn, callback, [options] )
8.3 过滤器
过滤器是对数据进行筛选、过滤、格式化,例如时间格式化、英文大小写转换、状态转换等等。它与methods、computed和watch不同是,它不能改变原始值。
Vue.js允许自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值{{ }}
和v-bind表达式(从2.1.0+开始支持)。过滤器应该被添加在JavaScript表达式的尾部。
9. Axios的请求
Axios
是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。
github开源地址:https://github.com/axios/axios
Axios
特性
- 在浏览器中创建 XMLHttpRequests
- 在 node.js 则创建 http 请求
- 支持 Promise API
- 支持拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
axios使用案例
- axios.js引入项目
<script src="js/vuejs-2.5.16.js"></script>
<script src="js/axios-0.18.0.js"></script>
- get请求和post请求的使用
<body>
<div id="app">
<input @click="myget()" type="button" value="get请求1">
<br/>
<input @click="myget2()" type="button" value="get请求2">
<br/>
<input @click="mypost()" type="button" value="post请求">
</div>
</body>
<script>
//view model
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视频区域
data:{
} ,//对应的数据
methods:{
myget:function () { //通过地址传递数据
axios.get('/users/find.do?id=1')
.then(function (response) {//正常
console.log(response);
alert(response.data)
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
},
myget2:function () {
axios.get('/users/find.do', {//通过params传递数据
params: {
id: 1
}
})
.then(function (response) {
console.log(response);
})
.catch(function (err) {
console.log(err);
});
},
mypost:function () {
axios.post('/users/find2.do', {
id:1
}) //将{id:1} 这个 json提交给后台,后面需要一个对象来接收 ,参数要加@RequestBody
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
}
}
) //创建了MVVM中的VM对象
</script>
- 后台controller
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
@RequestMapping(path = "/find.do",method = {RequestMethod.GET})
public Object get(Integer id){
log.info("get id = "+id);
User user = new User();
log.info("get user = "+user);
user.setId(id);
user.setUsername("strive_day"+id);
return user;
}
//页面将{id:1} 这个json提交给后台,需要一个对象来接收 ,参数要加@RequestBody(json数据,RequestBody将json转换成User对象)
@RequestMapping(path = "/find2.do",method = {RequestMethod.POST})
public Object post(@RequestBody User u){//{id:1}
log.info("post id "+u.getId());
User user = new User();
user.setId(u.getId());
user.setUsername("strive_day"+u.getId());
return user;
}
}
10. springboot-vue实现CRUD案例
(1)后台使用springBoot
(2)前台使用Vue实现CRUD
- 数据库准备User表
#创建数库中的表
create table user(
id int primary key auto_increment,
age int,
username varchar(20),
`password` varchar(50),
email varchar(50),
sex varchar(20)
)
insert into `user` values ('1', '33', '张老师', '123', 'zzz@wzx.cn', '男 ');
insert into `user` values ('2', '31', '刘老师', '123', 'lll@wzx.cn', '女');
insert into `user` values ('3', '17', '赵工', '213', 'zg@wzx.cn', '女');
insert into `user` values ('4', '40', '高管', '213', 'gg@wzx.cn', 'female');
insert into `user` values ('5', '28', '李总', '312', 'lz@wzx.com', 'male');
insert into `user` values ('6', '34', '王董', '312', 'wd@wzx.com', 'male');
insert into `user` values ('7', '55', '孙老板', '4321', 'slb@wzx.com', '男');
insert into `user` values ('8', '19', '陈秘书', '4321', 'cms@wzx.com', '女');
- 配置application.properties
# app
spring.application.name=vuecrud
server.port=8080
# datasource hykari
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=861221293
spring.datasource.url=jdbc:mysql://localhost:3306/vue?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
# mybatis
mybatis.type-aliases-package=com.xgf.demo01curd.domain
# mybatis -dao interface 需要在Main里配置
# @MapperScan(basePackages = {"com.wzx.demo01curd.dao"})
# log
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# resources
spring.web.resources.static-locations=classpath:/static/,classpath:/templates/
启动类application添加@MapperScan
注解扫描dao
@SpringBootApplication
@MapperScan(basePackages = {"com.xgf.demo01curd.dao"})
public class Demo01curdApplication {
public static void main(String[] args) {
SpringApplication.run(Demo01curdApplication.class, args);
}
}
- 实体类User
@Data
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private int age;
private String email;
}
- 编写测试类
@SpringBootTest
@Slf4j
class Demo01curdApplicationTests {
@Autowired
UserService userService;
@Test
void test01() {
List<User> userList=userService.findAll();//#查询多条
User user =userService.findById(2);//查询一条
log.info("userList "+userList);
log.info("user "+user);
userService.deleteById(2);//删除一条
}
@Test
void test02() {
User user = new User();
user.setId(9);
user.setUsername("hello word");
//userService.saveUser(user);//创建一条
userService.updateUser(user);//更新一条
}
}
- 业务接口和实现类
UserService
public interface UserService {
List<User> findAll();
User findById(int id);
void deleteById(int id);
void saveUser(User user);
void updateUser(User user);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public List<User> findAll() {
return userDao.findAll();
}
@Override
public User findById(int id) {
return userDao.findById(id);
}
@Override
public void deleteById(int id) {
userDao.deleteById(id);
}
@Override
public void saveUser(User user) {
userDao.save(user);
}
@Override
public void updateUser(User user) {
userDao.update(user);
}
}
- dao层接口通过注解实现CRUD的sql语句
public interface UserDao {
@Select("select * from user order by id asc")
List<User> findAll();
@Select(" select * from user where id = #{id}")
User findById(int id);
@Delete("delete from user where id = #{id}")
void deleteById(int id);
@Insert("insert into user (username,password,email,age,sex)values(#{username},#{password},#{email},#{age},#{sex})")
void save(User user);
@Update("update user set username=#{username},password=#{password},email=#{email},age=#{age},sex=#{sex} where id =#{id}")
void update(User user);
}
- 创建异步信息处理类Result
@Data
public class Result {
private int code; //返回编码
private String msg; //提示信息
private Object data; //数据
//初始化静态方法(调用不用创建对象)
public static Result init(int code,String msg,Object data){
Result result = new Result();
result.code=code;
result.msg=msg;
result.data=data;
return result;
}
}
- restFul api 实现UserController调用
@RestController
@RequestMapping("/users")
public class UserController {
//由于Controller属于web层,web层调用Service
@Autowired
UserService userService;
//查询所有(get方式不传递参数)
@RequestMapping(path = "", method = {RequestMethod.GET})
public Object getList() {
List<User> data = userService.findAll();
return Result.init(200, "查询成功", data);
}
//查询指定id的user,(get方式,传递id)
@RequestMapping(path = "/{id}", method = {RequestMethod.GET})
public Object getOne(@PathVariable int id) {
User user = userService.findById(id);
return Result.init(200, "查询成功", user);
}
//根据id删除(delete方式,传递参数id)
@RequestMapping(path = "/{id}", method = {RequestMethod.DELETE})
public Object deleteOne(@PathVariable int id) {
try {
userService.deleteById(id);
return Result.init(200, "删除成功", null);
} catch (Exception e) {
e.printStackTrace();
return Result.init(200, "删除失败", null);
}
}
//增加user(post方式)
@RequestMapping(path = "", method = {RequestMethod.POST})
public Object createOne(@RequestBody User user) {
try {
userService.saveUser(user);
return Result.init(200, "新增成功", null);
} catch (Exception e) {
e.printStackTrace();
return Result.init(200, "新增失败", null);
}
}
//更新user(使用put方式,传递参数id)
//需要在User上要添加@RequestBody,因为Vue使用axios发送post请求时,是将json数据提交过来
//@RequestBody将json数据转化为对应的javabean对象
@RequestMapping(path = "/{id}", method = {RequestMethod.PUT})
public Object updateOne(@PathVariable int id,@RequestBody User user) {
//页面处理方式有两种(在前台页面,传递给后台id参数时)
//第一种就是地址带上id /users/8
//第二种就是页面上有个隐藏的input,值是id
try {
userService.updateUser(user);
return Result.init(200, "修改成功", null);
} catch (Exception e) {
e.printStackTrace();
return Result.init(200, "修改失败", null);
}
}
}
- 使用postman测试
测试get方式查询所有
http://localhost:8080/users/
测试get方式查询指定id
http://localhost:8080/users/6
测试delete方式删除
http://localhost:8080/users/7
测试update修改user(put方式)
http://localhost:8080/users/6
使用postman在Body中添加数据,设置为json格式
{
"id":6,
"username": "strive_day",
"password": "123456",
"sex": "male",
"age": 21,
"email": "strive_gf@wzx.com"
}
测试add增加user(post方式)
http://localhost:8080/users
- 编写js操作进行CRUD
module-user.js
//vue的核心对象 view model,用于对view和model进行双向绑定
//如果数据发生改变,页面同步显示,反之也成立
//ViewModel
var vm = new Vue(
{
el:'#app', //让vu掌握指定的视图区域div
data:{ //数据
user:{
id:-1,
username:'',
password:'',
email:'',
sex:'',
age:0
},
viewid:1, //控制视图的状态 ==1 显示列表,==2 添加, == 3 修改
users:[] //user集合
},
methods:{
//函数
findAll:function () { //查找所有user
var vm = this //设置ViewModel为当前对象操作
axios.get('/users') //get方式
.then(function (response) {//正常
console.log(response.data);
vm.users = response.data.data
vm.viewid = 1 //视图状态1:显示所有数据
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
},
findOne:function (id) { //通过id查找
var vm = this
axios.get('/users/'+id)
.then(function (response) {//正常
console.log(response.data);
vm.user = response.data.data
vm.viewid = 3
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
},
deleteById:function (id) { //通过id删除
axios.delete('/users/'+id)
.then(function (response) {//正常
console.log(response.data);
//vm.users = response.data.data
//vm.viewid = 1
vm.findAll() //删除之后调用查询所有
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
},
showSaveForm:function(){ //点击增加,将状态改为2,显示增加表单
var vm = this
vm.viewid = 2
},
back:function(){ //点击返回,将状态设为1,显示表单列表
var vm = this
vm.viewid = 1
},
saveUser:function () { //保存
var vm = this
axios.post('/users',vm.user)
.then(function (response) {//正常
console.log(response.data);
vm.findAll() //保存后再查询
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
},
updateUser:function (id) { //更新
var vm = this
axios.put('/users/'+id,vm.user)
.then(function (response) {//正常
console.log(response.data);
vm.findAll()
})
.catch(function (err) {//请求失败
console.log(err);
alert(response.err)
});
}
},
created:function () { //
//发送请求查询列表
// alert("vm创建成功")
//this表示当前vm对象
this.findAll()
}
}
) //创建了MVVM中的VM对象
vm对象的重点在于两个,一个是data,另一个是methods
data,可以接收服务端返回的数据,同步到页面显示
也可以将表单里面的数据同步保存,然后提交给后台。
- 编写前台页面
list-user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue_crud</title>
<script src="js/vuejs-2.5.16.js"></script>
<script src="js/axios-0.18.0.js"></script>
</head>
<body>
<div id="app">
<!-- 状态等于1显示列表 -->
<div v-if="viewid == 1">
<button @click="showSaveForm()" >新建</button>
<table border="1px" width="100%">
<tr>
<td>序号</td>
<td>id</td>
<td>用户名</td>
<td>密码</td>
<td>邮箱</td>
<td>年龄</td>
<td>性别</td>
<td>操作</td>
</tr>
<!-- v-for遍历 -->
<tr v-for="(user,index) in users">
<td>{{index+1}}</td>
<td>{{user.id}}</td>
<td>{{user.username}}</td>
<td>{{user.password}}</td>
<td>{{user.email}}</td>
<td>{{user.age}}</td>
<td>{{user.sex}}</td>
<td>
<button @click="deleteById(user.id)">删除</button>
<button @click="findOne(user.id)">编辑</button>
</td>
</tr>
</table>
</div>
<!-- 隐藏表单,v-if判断,如果为1,就显示所有的user列表,不显示该表单,如果非1显示该表单,根据viewid状态来判断应该显示的按钮 -->
<div v-if="viewid != 1">
<form>
<input type="hidden" v-model="user.id" >
用户名 :<input type="text" v-model="user.username" ><br/>
密码:<input type="text" v-model="user.password" ><br/>
邮箱:<input type="text" v-model="user.email" ><br/>
年龄:<input type="text" v-model="user.age" ><br/>
性别:<input type="text" v-model="user.sex" ><br/>
<button @click="back">返回</button>
<!-- 根据viewid来判断显示那个按钮 -->
<button v-if="viewid == 2" @click="saveUser()">保存</button>
<button v-if="viewid == 3" @click="updateUser(user.id)">修改</button><br/>
</form>
</div>
</div>
</body>
<!-- 引入js操作 -->
<script src="js/moduel-user.js"></script>
</html>
- 最终效果