目录
简单介绍
MVVM
下载安装Node.js
安装Vue.js插件
新建Vue.js项目
下载vue依赖库
Vue工程目录结构
修改代码模板
vue组件中,添加模型数据
Vue双向绑定
动态绑定
vue组件中,显示图片
单选框绑定
复选框绑定
Vue的script表达式
Vue实例声明周期
Vue事件
判断语句
列表渲染
综合练习
Vue路由
路由跳转
嵌套路由
跨域问题
axios解决跨域问题
Vue中配置代理
跨域增删改查操作
简单介绍
Vue.js(读音 /vjuː/, 类似于 view)是一套用于构建用户界面的渐进式框架。
Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,易于上手,便于与第三方库或既有项目整合。
Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)https://cn.vuejs.org/
MVVM
Vue是基于MVVM架构模式的JavaScript框架,MVVM是Model-View-ViewModel的缩写,它是一种前端架构模式,与MVC(Model-View-Controller)和MVP(Model-View-Presenter)类似,但它更加关注于数据绑定和视图模板。
在MVVM模式下,视图层(View)与数据层(Model)之间没有直接的联系,而是通过中间层(ViewModel)进行交互、通信,(ViewModel负责将模型层的数据转换成视图层的数据,同时监听视图层的变化并将变化的数据传递给模型层)Model和ViewModle之间的交互是双向的。因此,View数据的变化会同步到Model中,而Model中数据的变化也会影响。
- Model:代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑
- View:代表UI组件,他负责将数据模型转化成UI展示出来
- ViewModle:监听数据模型的改变和控制视图行为、处理用户交互;简单理解就是一个同步View和Model的对象,连接Model和View
下载安装Node.js
Node.js是一个让JavaScript运行在服务端的开发平台,(类似Maven)它可用于方便地搭建响应速度块,易于拓展的网络应用。Download | Node.jsNode.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.https://nodejs.org/en/download/ 安装完以后,运行cmd,在dos界面中,输入“node -v”查看安装版本,验证Node.js是否已成功安装。
node -v
Node.js可以作为前端开发库的存在,需要设置拉取镜像地址,国内的服务器速度较快,一般设置为淘宝镜像。(直接在dos界面中粘贴这个地址)
npm config set registry=https://registry.npm.taobao.org
查看镜像设置是否成功
npm config list
安装Vue.js插件
IDE工具栏中:
文件——设置——插件——Marketplace下搜索Vue.js即可
新建Vue.js项目
- 注意:项目名称不要以大写命名,全小写
终端输入 npm run serve
npm run serve
最后,打开浏览器
连续按下Ctrl+c,可以关闭前端服务器
下载vue依赖库
npm install --save axios vue-router echarts element-plus @element-plus/icons-vue
安装以下包并将它们作为依赖项保存在项目`package.json`文件中:
- axios:一种流行的基于承诺的 HTTP 客户端,用于从 JavaScript 向 API 发出请求。
- vue-router:一个 Vue.js 插件,用于在您的应用程序中实现客户端路由。
- echarts:一个强大的图表和可视化库,用于在 Web 应用程序中显示数据。
- element-plus:一个 Vue.js UI 库,提供一组可定制和响应式 UI 组件。
- @element-plus/icons-vue:一组用于 Element Plus 库的图标。
该--save选项会将这些包添加到dependencies在 中的部分package.json,这意味着它们是你的应用程序运行所必需的。
Vue工程目录结构
node_modles :插件目录
public:静态资源
src:源码目录
main.js:程序入口文件
vue.config.js:可选的配置文件,如果项目的根目录中存在此文件,它会被@vue/cli-service自动加载
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,//忽略未使用变量或组件报错问题
lintOnSave:false,
devServer:{
port:8086//改变端口
}
})
构建
npm run build
修改代码模板
设置——编辑器——文件和代码模板
<template>
<div>
#[[$END$]]#
</div>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
<style scoped>
</style>
vue组件中,添加模型数据
添加模型数据
<script>
export default {
data() {
return {
msg:"哈喽"
}
}
}
</script>
展示模型数据
<template>
<div>
{{msg}}}
</div>
</template>
启动服务
npm run serve
注意 :vue数据中的html代码,双大括号会将数据解释为普通文本,而非HTML代码,为了输出真正的HTML,需要使用v-html指令
添加模型数据:
info:"<span style='color: red'>hello</span>"
展示模型数据:
<div v-html="info"> </div>
Vue双向绑定
模型和视图组件的双向绑定,界面数据发生变化时,模型数据变化,反之也一样(不用再频繁的document.id了)
添加模型数据:
name:" "
展示模型数据:
姓名:<input type="text" v-model="name"><br>
{{name}}
当文本框内容发生变化时,name属性值随之变化。同样,当name属性值发生变化时,文本框内容随之变化
动态绑定
当元素的属性来自Vue对象数据时,使用v-bind表示
divcss:"color:blue"
<div v-bind:style="divcss">div内容</div>
可缩写为:
<div :style="divcss">div内容</div>
vue组件中,显示图片
添加模型数据:
pic:require("@/img/wallhaven-9dxlw8.jpg")
注意: 图片如果是本地文件 需要加require,网络图片的URL路径就不需要加require了
展示模型数据:
url路径的三种方式:
-
“@”符号在src目录下查找资源
-
“../” 在当前目录的父目录下查找
-
“./” 当前目录下查找
<img :src="pic" width="500" height="300">
单选框绑定
添加模型数据:
sex:""
展示模型数据:
<input type="radio" v-model="sex" value="男">男
<input type="radio" v-model="sex" value="女">女
{{sex}}
当sex的值发生变化时,和该sex的值对应的单选框被选中,当用户点击不同单选框时,也会将点中单选框的value值,赋给sex
复选框绑定
添加模型数据:
likeArray:[]
展示模型数据:
<input type="checkbox" v-model="likeArray" value="唱">唱
<input type="checkbox" v-model="likeArray" value="跳">跳
<input type="checkbox" v-model="likeArray" value="rap">rap
<input type="checkbox" v-model="likeArray" value="篮球">篮球
{{likeArray}}
Vue的script表达式
在使用 { { } } 显示数据时,可以支持一些简单的运算,加减乘除、字符串运算、三目运算等..
添加模型数据:
money:3000
展示模型数据:
年薪:{{money*13}}
Vue实例声明周期
每个vue实例在被创建时,都要经过一系列的初始化过程--例如,需要设置数据监听、编译模板、将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运算一些叫做生命周期钩子的函数,这些生命周期钩子函数可以用来在特定时间点执行代码,这给了用户在不同阶段添加自己的代码的机会
每个Vue实例声明周期分为四大类:
- 创建-----created
- 挂载,将数据渲染到页面上-----mounted
- 更新,在数据更改时调用-----updated
- 销毁-----unmounted
- 每个阶段都有一个beforeXXX方法,在执行某个阶段之前执行
Vue事件
添加模型数据:
<script>
export default {
data() {
return {
userName:"",
pwd:""
}
},
//methods方法中,访问模板数据需要加this
methods:{
land(){
if(this.userName=="爪哇" && this.pwd =="123"){
alert("登陆成功")
}else {
alert("登陆失败")
}
}
}
}
</script>
展示模型数据:
<template>
<div>
用户名:<input type="text" v-model="userName"><br>
密码:<input type="password" v-model="pwd"><br>
<input type="button" value="登录" @click="land()">
</div>
</template>
判断语句
当v-if 中的条件为真时,渲染该组件,如果为假则不做渲染
添加模型数据:
<script>
export default {
data() {
return {
grade:""
}
}
}
</script>
展示模型数据:
<template>
<div>
请输入成绩:<input type="text" v-model="grade"><br>
评价:<span v-if="grade>=60">及格</span>
<span v-else>不及格</span>
</div>
</template>
列表渲染
Vue组件 遍历
添加模型数据:
<script>
export default {
data() {
return {
userList:[{id:2,name:"张三丰",money:2500},
{id:3,name:"张无忌",money:3500},
{id:4,name:"章若楠",money:100000}]
}
}
}
</script>
展示模型数据:
<table border="1" width="80%" cellpadding="0">
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>工资</th>
</tr>
</thead>
<tbody>
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.money}}</td>
</tr>
</tbody>
</table>
遍历整数
添加模型数据:
pages:5
展示模型数据:
<a v-for="n in pages" :href="'findByItem?pageNO='+n" style="margin-left: 10px">{{n}} </a>
综合练习
添加模型数据:
<script>
//导出模块默认值
export default {
//data函数,返回一个包含组件使用的数据的对象。可以通过组件的方法和模板访问和修改此数据
data() {
return {
userList:[{id:1,name:'迪丽热巴',money:10000,status:"会员"},
{id:2,name:'古力娜扎',money:5500,status:"会员"},
{id:3,name:'马尔扎哈',money:200,status:"非会员"},
{id:4,name:'白鹿',money:8000,status:"会员"}],
showAdd:false,//添加界面的状态
userObj:{},//空对象,接收用户输入
showUpdate:false,
updateObj:{}
}
},
methods:{
add(){
//在数组中追加元素
this.userList.push(this.userObj);
//将userobj引用新对象,清空界面数据
this.userObj={};
//隐藏添加界面
this.showAdd=false;
},
changeStatus(id){
//根据指定的条件在对象数组中查找对象
let user = this.userList.find(n=>n.id==id);
user.status = '会员'
},
del(id){
//查询指定id,对应下标
let index = this.userList.findIndex(n=>n.id==id);
//按下标 截取元素
this.userList.splice(index,1);
},
findById(id){
this.updateObj = this.userList.find(n=>n.id==id);
this.showUpdate=true;
},
}
}
</script>
展示模型数据:
<!--模板-->
<template>
<div>
<input type="button" value="添加" @click="showAdd = true">
<!--构建表格-->
<table border="1" width="80%" cellpadding="0">
<!--表头-->
<thead>
<tr>
<th>id</th>
<th>名字</th>
<th>工资</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<!--表格正文-->
<tbody>
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.money}}</td>
<td>{{user.status}}</td>
<td>
<input type="button" v-if="user.status=='非会员'" value="升级为会员" @click="changeStatus(user.id)">
<input type="button" value="删除" @click="del(user.id)">
<input type="button" value="修改" @click="findById(user.id)">
</td>
</tr>
</tbody>
</table>
<!--showAdd为真时,渲染该组件-->
<div class="dialog" v-if="showAdd">
<div class="content">
编号:<input type="text" v-model="userObj.id"><br>
姓名:<input type="text" v-model="userObj.name"><br>
工资:<input type="text" v-model="userObj.money"><br>
状态:<input type="radio" value="会员" v-model="userObj.status">会员
<input type="radio" value="非会员" v-model="userObj.status">非会员<br>
<input type="button" value="添加" @click="add">
</div>
</div>
<div class="dialog" v-if="showUpdate">
<div class="content">
编号:{{updateObj.id}}<br>
姓名:{{updateObj.name}}<br>
工资:<input type="text" v-model="updateObj.money"><br>
状态:{{updateObj.status}}<br>
<input type="button" value="修改" @click="showUpdate=false">
</div>
</div>
</div>
</template>
样式:
<style scoped>
.dialog{
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: rgba(66, 185, 173, 0.53);
display: flex;
justify-content: center;
align-items: center;
}
.content{
background-color: azure;
border: 1px solid black;
width: 600px;
height: 400px;
padding: 20px;
}
</style>
Vue路由
在web开发中,路由是根据url分配到对应的处理程序,对于大多数单页面应用,都推荐使用官方支持的vue-router。vue-router通过管理url,实现url和组件的对应,以及通过url进行组件之间的切换。
一、在src目录下,新建router文件夹 新建index.js、routes.js
二、在routes.js文件中,注册URL和VUE组件的映射关系
const routes = [
{name:"Login", //路由名称
path:"/login", //访问组件的URL路径
component: () => import('@/login.vue')},//导入vue组件
{name:"App",
path:"/app",
component: () => import('@/App')},
];
export default routes ;
三、完成index.js
//创建了路由器实例
import {createRouter,createWebHistory} from 'vue-router'
//将路由的配置信息传递给了 routes 变量
import routes from "./routes"
const router = createRouter({
history:createWebHistory(),
routes
});
export default router ;
四、在main.js中导入路由
import router from "@/router/index"
五、在APP.vue中添加路由视图
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
六、在main.js中,导入路由视图
import { createApp } from 'vue'
import App from './App.vue'
import router from "@/router/index"
createApp(App).use(router).mount('#app')
打开浏览器 - Local: http://localhost:8086/后面跟具体的路径
路由跳转
1、通过标签,完成跳转
<router-link to="/press">跳转..</router-link>
<a href="/press">跳转..</a>
2、通过js代码完成跳转
land(){
if(this.userName=="爪哇" && this.pwd =="123"){
//两种方式都可以
this.$router.push("/userPress");
location.href="/userPress"
}else {
alert("登陆失败")
}
嵌套路由
在一个Vue组件中,再嵌入路由视图,称为嵌套路由
如果在父级路由对应的组件中定义了子级路由的标签,那么子级路由渲染的内容会插入到父级路由组件中的标签中,这样子级路由就可以继承父级路由的布局。
一、在routes.js中,完成路由注册
const routes = [
{name:"Index",
path:"/index",
component: () => import('@/index'),
children:[
{name:"Order",
path:"/index/order",//嵌套路由的path路由,先写父路由路径,在写子路由路径
component: () => import('@/order/order')},
{name:"Room",
path:"/index/room",
component: () => import('@/order/room')}
]
}
];
export default routes ;
二、在Vue组件中,添加路由视图
<template>
<div>
<div><h1>标题</h1></div>
<div style="display: flex">
<div style="width: 200px;height: 400px">
<router-link to="/index/order">订单</router-link><br>
<a href="/index/room">房间管理</a>
</div>
<div style="width: 800px;height: 400px">
<router-view></router-view>
</div>
</div>
</div>
</template>
跨域问题
跨域问题来自浏览器安全机制,浏览器安全的基石是“同源政策”。其目的,是为了保障用户信息的安全,防止恶意的网站窃取数据。
同源政策即:防止Cookie在不同的服务器之间共享,以保证cookie信息的安全性。如果非同源,共有三种行为受到限制;Cookie无法读取、DOM无法获得、Ajax请求无法发送。
在同源政策中,A网页设置的Cookie,B网页不能打开,除非这两个网页“同源”,即:协议、域名、端口都相同。
axios解决跨域问题
axios中,可以通过配置代理,解决跨域问题。
客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念。
所以,可以配置一个代理的服务器请求另一个服务器中的数据,然后把请求出来的数据返回到代理服务器中。代理服务器再返回数据给客户端,从而实现跨域访问数据。
Vue中配置代理
在vue.config.js中添加proxy配置
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false,
devServer:{
port:8086,
proxy: {
"/project": {
target: "http://localhost:8088",
pathRewrite: {
"^/project": "/"
}
}
}
}
})
客户端以/project开始的请求,由代理服务器访问http://localhost:8088服务器。
跨域增删改查
即前后端分离开发
后台服务器控制组件
@RestController
@RequestMapping("student")
public class StudentController {
@Autowired
private IStudentService service;
@RequestMapping("add")
public String add(StudentBean studentBean, MultipartFile pic) throws IOException {
String fileName = pic.getOriginalFilename();
fileName = System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
pic.transferTo(new File("E:/mavenstudy/JavaWeb/studentProject/face/"+fileName));
studentBean.setFace(fileName);
service.add(studentBean);
return "ok" ;
}
@RequestMapping("del")
public String del(Integer id){
service.del(id);
return "ok";
}
@RequestMapping("upd")
public String upd(Integer id,String phone){
service.upd(id,phone);
return "ok";
}
@RequestMapping("findByItem")
public IPage<StudentBean> findByItem(Integer pageNO, String name, LocalDate startTime,LocalDate endTime){
return service.findByItem(pageNO,name,startTime,endTime);
}
@RequestMapping("findById")
public StudentBean findById(Integer id){
return service.findById(id);
}
}
1、在Vue组件中引入axios库。
import axios from 'axios';
- 它是一个基于Promise的HTTP客户端,可以用于浏览器和node.js中的AJAX请求。
2、定义data数据,包括分页对象cutObj、查询对象queryObj、添加对象addObj、修改对象updateObj,以及控制添加和修改(弹出层显示)的showAdd和showUpdate布尔值。这些js对象,用于存储前端与后端之间的数据交互信息。它们可以用来存储响应数据中的各种数据项,如分页信息、查询条件、添加信息等等,以便前端对这些数据进行展示和处理。
data() {
return {
cutObj:{},
queryObj:{},
addObj:{},
showAdd:false,
updateObj:{},
showUpdate:false
}
}
3、在模板中使用v-for指令渲染学生信息表格,并使用v-if指令控制添加和修改的弹出框。
<template>
<!--构建表格-->
<div>
<table border="1" width="80%" >
<thead>
<th>姓名</th>
<th>生日</th>
<th>电话</th>
<th>操作</th>
</thead>
<tbody>
<!--遍历records数组-->
<tr v-for="student in cutObj.records">
<td>{{student.name}}</td>
<td>{{student.birthday}}</td>
<td>{{student.phone}}</td>
<!--绑定点击事件,调用函数的同时,将当前student.id作为参数传递-->
<td><input type="button" value="修改" @click="findById(student.id)"></td>
<td><input type="button" value="删除" @click="del(student.id)"></td>
</tr>
</tbody>
</table>
<!--遍历分页对象中的pages数组,绑定属性值,每个按钮的值等于正在循环的当前页码(即n)-->
<div>
<input type="button" v-for="n in cutObj.pages" :value="n" @click="findByItem(n)" style="margin-left: 10px">
</div>
<!--双向绑定-->
姓名:<input type="text" v-model="queryObj.name"><br>
起始日期:<input type="text" v-model="queryObj.startTime"><br>
结束日期:<input type="text" v-model="queryObj.endTime"><br>
<input type="button" value="查询" @click="findByItem(1)">
<hr>
<input type="button" value="添加" @click="showAdd=true">
</div>
<!--showAdd为真时,渲染该组件-->
<div class="dialog" v-if="showAdd">
<div class="content">
姓名:<input type="text" v-model="addObj.name"><br>
生日:<input type="text" v-model="addObj.birthday"><br>
电话:<input type="text" v-model="addObj.phone"><br>
头像:<input type="file" id="faceFile"><br>
<input type="button" value="添加" @click="addStudent()"><br>
</div>
</div>
<div class="dialog" v-if="showUpdate">
<div class="content">
<!--显示图片,src绑定表达式(路径+文件名)-->
<img :src="'/project/face/'+updateObj.face" width="80" height="80"><br>
姓名:{{updateObj.name}}<br>
生日:{{updateObj.birthday}}<br>
电话:<input type="text" v-model="updateObj.phone"><br>
<input type="button" value="修改" @click="updateStudent()"><br>
</div>
</div>
</template>
弹出层样式
<style scoped>
.dialog{
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: rgba(66, 185, 173, 0.53);
display: flex;
justify-content: center;
align-items: center;
}
.content{
background-color: azure;
border: 1px solid black;
width: 600px;
height: 400px;
padding: 20px;
}
</style>
4、在methods中定义一系列方法,findByItem方法用于查询学生信息、addStudent方法用于添加学生信息、del方法用于删除学生信息、findById方法用于根据ID查询学生信息、updateStudent方法用于修改学生信息。
methods:{
findByItem(pageNO){
this.queryObj.pageNO=pageNO;
axios.get("/project/student/findByItem",{
params:this.queryObj
}).then(resp=>{
this.cutObj = resp.data;
})
},
addStudent(){
/*在使用axios进行表单数据类型的请求时,需要将表单数据转为FormData对象,
然后将该对象作为参数传递给axios的POST方法。*/
let formObj = new FormData();
//FormData对象将表单数据转换为键值对形式的数据,添加
formObj.append("name",this.addObj.name);
formObj.append("birthday",this.addObj.birthday);
formObj.append("phone",this.addObj.phone);
//通过DOM操作,获取(文件上传控件)元素,且只获取该文件上传控件中选择的第一个文件
//.files 是一个 FileList 对象,它包含了文件上传控件中所有选择的文件
formObj.append("pic",document.getElementById("faceFile").files[0]);
/*设置HTTP请求头的Content-Type字段为表单数据类型的请求,
以便服务器端能够正确解析表单数据。*/
let config={
headers:{'Content-Type':'multipart/form-data'}
}
axios.post("/project/student/add",formObj,config).then(resp=>{
if(resp.data=="ok"){
this.findByItem(1);
this.showAdd=false;
}
})
},
del(id){
axios.get("/project/student/del",{params:{id}}).then(resp=>{
if(resp=="ok"){
this.findByItem(1);
}
}
)
},
findById(id){
axios.get("/project/student/findById",{params:{id}}).then(resp=>{
this.updateObj=resp.data;
this.showUpdate=true;
})
},
updateStudent(){
axios.get("/project/student/upd",{params:this.updateObj}).then(resp=>{
if(resp.data=="ok"){
this.findByItem(1);
this.showUpdate=false;
}
})
}
}
5、在created钩子函数(它在组件创建完成后立即执行)中调用findByItem方法查询学生信息,并将查询结果赋值给cutObj。
methods:{
},
created() {
//this代表当前的Vue实例
this.findByItem(1);
}