概述
Vue.js - 渐进式 JavaScript 框架 | Vue.js
vue: 是一个javascript的渐进式框架,实质就是js文件
渐进式: 易用、灵活、高效
什么是响应式: 根据不同条件做出相应调整
mvvm
MVVM理论上与Vue并没有什么联系,Vue的本质是Vue.js,官网上写的很清楚。MVVM是一种思想,并不只是Vue用,用任何语言都可以用这种思想,他提倡的是一种前后端分离的思想。
MVVM分为三个部分:分别是M(Model,模型层 ),V(View,视图层),VM(ViewModel,V与M连接的桥梁,也可以看作为控制器)
M:模型层,主要负责业务数据相关;
V:视图层,顾名思义,负责视图相关,细分下来就是html+css层;
VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点;
MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦;
VM这个概念非常重要。可以理解下MVC中的C(控制层)被换成了VM(ViewModel:)层。它本质上就是MVC 的改进版。MVVM层实现了前后端更好的分离
组件
组件基础
创建一个组件
使用组件
html ---> 组件 name.vue
脚手架 vue-cli node 服务 一路next
使用npm
1. 安装node
安装node(步骤省略,一路next即可)
安装成功以后执行命令:node -v
出现如下界面即为安装成功
加快安装:淘宝镜像
g = global
npm node服务器中自带的命令
npm install -g cnpm --registry=https://registry.npm.taobao.org
2. 安装 vue-cli
npm install -g vue-cli
3. 安装 webpack
npm install webpack -g
4.安装打包的客户端
npm install webpack-cli -g
创建项目
npm uninstall vue-cli
npm install @vue-cli
vue create myvue2
(若无法执行脚本,报出如下错误)
(需要运行如下代码)
get-ExecutionPolicy
set-ExecutionPolicy RemoteSigned
get-ExecutionPolicy -List
(这样就可以执行脚本命令了)
先切换到项目存放的路径下
项目创建成功!
项目测试:
cd myvue
npm run serve
浏览器访问路径:http://localhost:8080/
Tomcat 与 Node 的区别
1. Tomcat java代码
服务器 端口号 8080 端口被占用时,报错
2. Node 服务器
前端服务器 前端代码
默认端口号 8080 被占用时 自动切换端口:8081 8082
路由
定义路由的时候 path 的路径一定是唯一的
如果写 name 对应的值 也得是唯一
App.vue 是所有组件的父类
<router-view/> 容器 作用:存放组件 一般存放到父组件里面
在vue 里面 @ 代表的是 src
<template> 标签里面只能有一个根标签,多个根标签会出现如下错误
1. 写组件
<template>
<div>
<h1>我的第一个vue组件</h1>
</div>
</template>
2.在router/index.js 中配置路径
3. 访问
启动命令
npm run xxx
在 package.json 文件中
组件介绍
<template> HTML 内容 </template> <script> 脚本 new Vue({ }) </script> <style> CSS 样式 </style> |
自定义端口号:
安装 axios
$ npm install axios
安装 ui
npm i element-ui
在paxkage.json中可查看是否安装,和安装的版本
使用axios 和 element
后端部分
新建项目
配置 pom.xml 文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.17</version>
</dependency>
<!--连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!--mp-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!--自动生成代码-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
</dependencies>
配置 application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///gcf?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
# 时间 格式化 java.util
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.jackson.serialization.write-date-keys-as-timestamps=false
#logging.level.com.baomidou.ant.test.dao=debug
#mybatis-plus my_name myName
mybatis-plus.configuration.map-underscore-to-camel-case=true
# 配置sql 日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus.configuration.log-impl=
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
# 逻辑删除 未删除的默认为 0 已删除 1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
mybatis-plus.global-config.db-config.logic-delete-value=1
自动生成代码
public class MyTest {
public static void main(String[] args) {
//
FastAutoGenerator.create("jdbc:mysql:///gcf", "root", "root")
// 全局配置
.globalConfig((scanner, builder) -> builder
.author("郜超凡")
.outputDir("D:\\CodeFile\\mpSpringBook\\mp_sb01\\src\\main\\java")
)
// 包配置
.packageConfig(
(scanner, builder) ->
builder
.parent("com.aaa")
.pathInfo(Collections.singletonMap(OutputFile.xml, "D:\\CodeFile\\mpSpringBook\\mp_sb01\\src\\main\\resources\\mapper")))
// 策略配置
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.controllerBuilder().enableRestStyle().enableHyphenStyle()
.entityBuilder().enableLombok().addTableFills(
new Column("create_time", FieldFill.INSERT)
).build())
/*
模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker
.templateEngine(new BeetlTemplateEngine())
.templateEngine(new FreemarkerTemplateEngine())
*/
.execute();
}
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}
运行文件MyTest.java 输入想要生成的表名
生成结果:
后端配置跨越:
1. 注解(只有当前类或当前方法可以跨域)
2. 配置文件(全局跨域)
@Configuration
public class CrossConfig {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
// corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*"); // 允许所有的头
corsConfiguration.addAllowedOrigin("*"); //
corsConfiguration.addAllowedMethod("*"); // * get put delete head post
source.registerCorsConfiguration("/**", corsConfiguration); // 所有的路由都能够进行跨域
return new CorsFilter(source);
}
}
测试
@SpringBootApplication
@MapperScan("com.aaa.mapper")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
后端代码完成
前端部分
到element官网复制一个表格样式
稍加修改
<template>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column
prop="deptno"
label="部门编号">
</el-table-column>
<el-table-column
prop="dname"
label="部门名称">
</el-table-column>
<el-table-column
prop="loc"
label="地址">
</el-table-column>
</el-table>
</template>
发送请求
created() {
// vue node 服务器 8899
// axios tomcat 8080
// axios 发出请求
// dept http://localhost:8080/dept
this.$axios.get("dept").then(res=>{
console.log(res.data);
this.tableData=res.data;
})
}
效果展示
布局菜单
1. 配置菜单和路由
2. 在main 里面写一个容器
3. 配置嵌套路由
效果如下:
登录页
1. 写一个登录页面 login.vue
2. 在 index.js 中设置,让其默认访问登录页
效果:
3. 提交后让其跳转到菜单页
404页面
当路由不存在的时候跳转到 404 页面
在路由文件里面写路由的规则
router/index.js
项目优化
登录验证
数据库:
后端:
先自动生成
再写一个工具类 在com/aaa/util路径下 ResponseVo.java
@Data
@AllArgsConstructor // 全参构造器
@NoArgsConstructor // 无参构造器
public class ResponseVo<T> {
/**
* code
*/
private Integer code;
/**
* msg
*/
private String msg;
/**
* data
*/
private T data;
public ResponseVo(T data) {
this.data = data;
}
/**
* 成功
* @param data
* @return
* @param <T>
*/
public static <T> ResponseVo SUCCESS(T data){
return new ResponseVo(200,"操作成功!",data);
}
/**
* 失败
* @param data
* @return
* @param <T>
*/
public static <T> ResponseVo FAIL(T data){
return new ResponseVo(500,"操作失败!",data);
}
}
控制层 UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
@PostMapping
public ResponseVo login(@RequestBody User user){
System.out.println(user);
Boolean login = userService.login(user);
return new ResponseVo(login);
}
}
服务层接口 IUserService
public interface IUserService extends IService<User> {
Boolean login(User user);
}
服务层实现接口 UserServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private UserMapper userMapper;
@Override
public Boolean login(User user) {
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("user", user.getUser());
wrapper.eq("pwd", user.getPwd());
User one = this.getOne(wrapper);
return one != null;
}
}
前端:
登录组件 login.vue 表单提交部分添加 post 请求
submitForm(formName) {
this.$axios.post("user", {user: this.ruleForm.uname, pwd: this.ruleForm.pass}).then(res => {
console.log(res);
if (res) {
// 标识
// 存放用户名字
localStorage.setItem("user",this.ruleForm.uname);
// ruleForm
// localStorage.setItem("userInfo",JSON.stringify(this.ruleForm));
// location.href="/main";
// 成功提交时,更改路由
this.$router.push({name: 'main'});
} else {
console.log('error submit!!');
return false;
}
});
},
路由守卫
在 main.js 文件中配置
// 路由守卫
router.beforeEach((to, from, next) => {
// to 代表的是要到达的地方
// from 即将离开的地方
// next 路径跳转
//
if (to.path === '/' || localStorage.getItem("user")) {
next() // to 允许用户继续导航到目标页面
} else {
next("/") // 将用户重定向到根路径 '/'
}
// 确保用户在未登录状态下无法访问除根路径外的其他页面,如果用户已经登录或者访问的是根路径,则允许用户继续导航
})
拦截器
拦截器可以在发送请求或接收响应时对数据进行全局处理,比如添加公共请求头、错误处理、数据转换等。
在 main.js 文件中配置
/**
* 请求拦截器
*/
instance.interceptors.request.use(config => {
// 头里面加一个参数 myname
config.headers.myname = localStorage.getItem("user");
//
// config["headers"]['myname']
return config;
})
/**
* 响应拦截器
*/
instance.interceptors.response.use((response) => {
return response.data.data; // 提取响应中的数据,并将其作为处理后的数据返回
});
分页(单表)
前端:到 element 官网 找一个分页模板,稍加修改
<div class="block">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.currPage"
:page-sizes="[5, 10, 15, 20]"
:page-size="page.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="page.total">
</el-pagination>
</div>
后端:配置分页插件
@Configuration
public class PagePlugins {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
调用分页插件
效果
动态查询(单表)
前端:
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="用户名">
<el-input v-model="formInline.user" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" circle @click="onSubmit"></el-button>
</el-form-item>
</el-form>
后端:
@GetMapping
public ResponseVo findAll(PageInfo pageInfo, User user) {
Page page1 = new Page(pageInfo.getCurrPage(), pageInfo.getPageSize());
QueryWrapper queryWrapper = new QueryWrapper<>();
// 添加动态模糊查询条件
if (user.getUser() != null && user.getUser() != "") {
queryWrapper.like("user", user.getUser());
}
Page page = userService.page(page1,queryWrapper);
return ResponseVo.SUCCESS(page);
}
效果:
多表分页和动态查询
数据库 员工表emp 和 部门表dept
后端:
实体类Emp
@Getter
@Setter
@ToString
public class Emp implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "empno", type = IdType.AUTO)
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private LocalDate hiredate;
private BigDecimal sal;
private BigDecimal comm;
private Integer deptno;
@TableField(exist = false) // 代表不是数据库中的一个列
private Dept dept;
}
mapper接口
public interface EmpMapper extends BaseMapper<Emp> {
Page findAll(Page page1, @Param("emp") Emp emp);
}
mapper映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aaa.mapper.EmpMapper">
<resultMap id="getAllEmp" type="com.aaa.entity.Emp" autoMapping="true">
<id column="empno" property="empno"></id>
<association property="dept" javaType="com.aaa.entity.Dept" autoMapping="true">
<id column="deptno" property="deptno"></id>
</association>
</resultMap>
<select id="findAll" resultMap="getAllEmp">
SELECT * FROM emp e
JOIN dept d ON e.deptno=d.deptno
<where>
<if test="emp.ename!=null and emp.ename!=''">
and ename like concat('%',#{emp.ename},'%')
</if>
<if test="emp.deptno!=null and emp.deptno!=''">
and e.deptno=#{emp.deptno}
</if>
</where>
</select>
</mapper>
service层 接口 IEmpService
public interface IEmpService extends IService<Emp> {
Page findAll(Page page1, Emp emp);
}
service层 接口实现 IEmpServiceImpl
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {
@Resource
private EmpMapper empMapper;
@Override
public Page findAll(Page page1, Emp emp) {
return empMapper.findAll(page1,emp);
}
}
controller层 EmpController
@RestController
@RequestMapping("/emp")
public class EmpController {
@Resource
private IEmpService empService;
/**
* 查询所有的员工的信息 以及员工的部门的信息
* 并进行分页
* @return
*/
@GetMapping
public ResponseVo getAll(PageInfo pageInfo, Emp emp){
Page<Emp> page1 = new Page<>(pageInfo.getCurrPage(), pageInfo.getPageSize());
Page empList = empService.findAll(page1,emp);
return ResponseVo.SUCCESS(empList);
}
}
前端:
emp.vue
<template>
<div>
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="员工姓名">
<el-input v-model="formInline.ename" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="部门名称">
<el-select v-model="formInline.deptno" placeholder="请选择">
<el-option v-for="item in options" :key="item.deptno" :label="item.dname" :value="item.deptno">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" circle @click="onSubmit"></el-button>
</el-form-item>
</el-form>
<br>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column
prop="empno"
label="员工编号">
</el-table-column>
<el-table-column
prop="ename"
label="姓名">
</el-table-column>
<el-table-column
prop="job"
label="工作">
</el-table-column>
<el-table-column
prop="mgr"
label="上司编号">
</el-table-column>
<el-table-column
prop="hiredate"
label="入职日期">
</el-table-column>
<el-table-column
prop="sal"
label="工资">
</el-table-column>
<el-table-column
prop="comm"
label="提成">
</el-table-column>
<el-table-column
prop="dept.dname"
label="部门名称">
</el-table-column>
</el-table>
<br>
<div class="block">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.currPage"
:page-sizes="[5, 10, 15, 20]"
:page-size="page.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="page.total">
</el-pagination>
</div>
</div>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
export default {
methods: {
tableRowClassName({row, rowIndex}) {
if (rowIndex % 4 === 1) {
return 'warning-row';
} else if (rowIndex % 4 === 3) {
return 'success-row';
}
return '';
},
handleSizeChange(val) {
this.page.pageSize=val;
this.queryData();
},
handleCurrentChange(val) {
this.page.currPage=val;
this.queryData();
},
queryData(){
let newParam={...this.page,...this.formInline}; // 把两个对象合并成一个对象
this.$axios.get("emp",{params:newParam}).then(res=>{
console.log(res);
// 给total进行赋值
this.page.total=res.total;
this.tableData=res.records;
})
},
queryDeptData() {
this.$axios.get("dept").then(res => {
console.log(res)
// 给total进行赋值
this.options = res;
})
},
// 动态查询提交
onSubmit() {
this.queryData()
}
},
data() {
return {
tableData: [],
// 分页
page:{
currPage:1,
pageSize:5,
total:0
},
// 动态查询
formInline: {
ename: '',
deptno:'',
},
options:[],
}
},
created() {
this.queryData();
// 部门的信息
this.queryDeptData();
}
}
</script>
效果:
组件的运用
局部组件
先写一个简单的局部组件 jubu.vue
<template>
<div>
这是一个局部组件
</div>
</template>
写一个测试组件 test01.vue 来调用局部组件
效果
全局组件
quanju.vue
在 main.js 文件中配置
这样 在 test01.vue 中就可以直接调用了
效果
组件传值
父传子
1. 父组件的 data 中定义有一个变量
2. 在父组件中绑定一个属性传递给子组件
3. 子组件接收这个值
使用 props 来接收父组件中的值
使用父组件传递过来的值
效果
子传父
1. 子组件在进行传值的时候一般通过按钮传递
2. 点击按钮的时候需要自定义一个事件(通过这个事件进行传值)
3. 父组件使用一下子组件中定义的事件接收值
4. 通过函数接收值
5. 使用由子组件传递过来的值
分页组件
paging.vue
<template>
<div class="block">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="mydata.currPage"
:page-sizes="[5, 10, 15, 20]"
:page-size="mydata.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="mydata.total">
</el-pagination>
</div>
</template>
<script>
export default({
props:['mydata'],
methods:{
handleSizeChange(val) {
this.page.pageSize = val;
this.page.currPage=this.mydata.currPage;
this.$emit('mychangeData',this.page);
},
handleCurrentChange(val) {
this.page.currPage = val;
this.page.pageSize=this.mydata.pageSize;
this.$emit("mychangeData",this.page);
},
},
data(){
return{
page: {
currPage: 1,
pageSize: 5,
total: 0,
},
}
}
})
</script>
在 main.js 文件中将 paging.vue 定义为全局组件
// 1. 引入组件
import paging from '@/views/component/paging.vue'
// 2. 将这个组件注册成一个全局的组件
Vue.component("my-paging",paging);
在需要的地方使用分页
<!--引入分页的组件-->
<my-paging :mydata="page" @mychangeData="changeData"></my-paging>
子组件中的值传给父组件的时候 需要绑定一个事件 如果不绑定事件没法传值
changeData(obj){
this.page.pageSize=obj.pageSize;
this.page.currPage=obj.currPage;
this.queryData();
},