0.VUE-cli
0.1安装node-js
-
安装包安装:从官网直接下载最新的nodejs,安装的方式就是双击下一步安装完成即可。安装完成后打开cmd命令行
-
压缩包安装:参考网上博客(重点是配置环境变量)
node -v #展示node 的版本号
npm -v #展示Npm 的版本号
#如果都能出来版本则 node安装成功!
#设置npm 的镜像,默认的镜像是外网,速度较慢 设置为国内的taobao的镜像的
npm config set registry https://registry.npm.taobao.org/
#设置完成后,通过npm config ls 查看是否设置成功 只要能看到配置中registry="https://registry.npm.taobao.org/" 说明设置成功。
#安装vue的脚手架
npm install -g vue-cli
#安装完成后,通过命令,查看vue的版本
vue -V
#出现版本号 2.9.6 说明vue安装成功
**注意 **:如果vue的版本号未展示,则需要手动添加 环境变量的配置,找到nodejs的安装目录,
0.2安装完成后,可以使用vue-cli创建前端项目
在cmd 窗口下进行执行
1. vue init webpack vue-first
#使用vue命令创建前端的项目 vue init 进行初始化。webpack是基于什么模板进行创建。最后跟着的是项目的名称
? Project name vue-first
#项目的名称 默认即可
? Project description this is first vue-cli
#项目的描述 默认即可
? Author mhb <541109497@qq.com>
#项目的作者,获取到git的用户名 。如果没有不影响。默认即可
? Vue build standalone
#默认即可回车
? Install vue-router? Yes
#是否安装vue的路由,必须输入y 敲击回车
? Use ESLint to lint your code? No
#是否使用SsLint规范代码,选择N 敲击回车
? Set up unit tests No
#是否安装unit测试 选择N 敲击回车
? Setup e2e tests with Nightwatch? No
#是否安装e2e用户测试 ,选择n 回车
? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys)
> Yes, use NPM
Yes, use Yarn
No, I will handle that myself
#敲击回车,进行安装
1. 跨域冲突
methods:{
login:function () {
//去请求后台服务
/**
* 同源策略:请求的协议,请求的ip地址,请求的端口号任意一个不一致,就会产生跨域问题。
* 3种解决方案:
* 1.由前台进行代理
* 2.由后台解决跨域 只需要在controller层加入注解@CrossOrogin
* 3.使用nginx进行代理
*/
axios.post("/api/user/login",this.user).then(res=>{
if (res.data.code==1){
this.$router.push("/studentlist");
}else{
alert(res.data.message);
}
})
}
}
1.1 前台代理方式(config/index.js)
//代理
proxyTable: {
//对所有请求开头为api的进行代理
"/api":{
target:"http://localhost:8080/",
changeOrigin: true, // 跨域
//对返回的路径进行重写
pathRewrite: {
'^/api': '/'
}
}
},
1.2 后台加入注解
- 再controller层中加入注解
@CrossOrogin
1.3 nginx进行代理
web开发领域中,经常采用前后端分离模式。这种模式下,前端和后端分别是独立的web应用程序,比如后端是Java 程序,前端是React或Vue应用。各自独立的web app在互相访问时,势必存在跨域问题。解决跨域问题一般有两种思路:
- CORS:在后端服务器设置HTTP响应头,把你需要运行访问的域名加入加入Access-Control-Allow-Origin中。
- jsonp:把后端根据请求,构造json数据,并返回,前端用jsonp跨域。
Nginx针对CORS提供了一种跨域解决方案,比如前端后端存在跨域时,前端和后端如果使用http进行交互,请求会被拒绝。这时候可以通过设置后台服务响应头方式解决。
server {
listen 80;
server_name localhost;
location / {
add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'POST, GET, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#跨域OPTIONS请求,set response header后直接204返回
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://test;
}
}
2.main.js文件
一些公共的配置内容可以放置在main.js中
2.1 全局注册组件(头部、尾部)
//封装头部以及尾部的页面
import header from './components/header'
import footer from './components/footer'
//自定义标签 1.资源标签属性,2.具体的资源内容
Vue.component("my-header",header);
Vue.component("my-footer",footer);
<!-- 在需要使用的位置,引入自定义标签组件-->
<my-header></my-header>
2.2 路由守卫
- to: 即将要进入的目标
- from: 当前导航正要离开的路由
- next: 这是一个常见的错误来源,可以通过 RFC 来消除错误
//配置路由守卫 判断如果用户未登录则不能访问其他页面
router.beforeEach((to, from, next) => {
debugger;
axios.get("/api/user/isAuthcation").then(res=>{
var isAuthenticated=false;
if (res.data.code==1){
isAuthenticated=true;
}
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
})
3.router
3.1 引入组件(router/index.js)
//从资源目录下引入组件页面并且给与别名
import index from '@/components/index'
3.2 配置路由
//可以在routes中配置多个路由
routes: [
{
path: '/', //访问该页面资源的路径,如果为/ 则代表为首页
name: 'index', //当前页面自定义的名称
component: index //页面的资源
}
3.3 父子路由
- router-view: 子页面加载的位置
- 页面配置
<template>
<div>
<!-- 展示父页面-->
<h1>这是student主页面</h1>
<router-link to="/studentlist/findAll">查询所有</router-link>
<router-link to="/studentlist/insert">新增</router-link>
<button @click="upd">修改</button>
<div style="width: 500px;height: 100px;background-color: red">
<!-- 子页面加载的位置-->
<router-view/>
</div>
</div>
</template>
- 父子路由配置
{
path:'/studentlist',
name:'studentlist',
component:studentlist,
//配置子页面
children:[
{path:'findAll', name:'findAll', component:findAll,},
{path:'insert', name:'insert', component:insert,},
{path:'update', name:'update', component:update,},
]
},
3.4 访问资源不存在页面跳转
import error from '@/components/404'
{
//若访问的路径资源存在,则不会拦截
path:"*",
component:error
}
3.5 编程式导航
- 链接跳转,不携带参数
<!-- to ="页面的路径"-->
<router-link to="/regist">注册</router-link>
- 链接跳转,携带参数(获取)
<!--
v-bind: 可以简写 :
当使用:to="{name:'去往页面的名称',params:{key:value}}"
-->
<router-link :to="{name:'regist',params:{id:1}}">注册2</router-link>
<!--
{{$route.params.id}} 获取到传递的参数
-->
<h2>{{$route.params.id}}</h2>
- 按钮跳转,携带普通参数
<button @click="toregist(2)">注册3</button>
methods:{
toregist:function (id) {
//编程式导航(传参为json对象)
this.$router.push({name:'regist',params:{id:id}})
}
}
<!--
{{$route.params.id}} 获取到传递的参数
-->
<h2>{{$route.params.id}}</h2>
- 按钮跳转,对象
<button @click="upd">修改</button>
methods:{
upd:function () {
var obj={name:"李四",addr:"丽人科技园"}
this.$router.push({name:"update",params:{obj:obj}})
}
mounted(){
this.student= this.$route.params.obj
}
3.6 全局配置axios
- 在main.js引入axios
import axios from 'axios';
- 设置全局引用
//引入axios 全局进行使用
Vue.prototype.$axios=axios;
- 使用(this.$axios.post)
//去后台请求
this.$axios.post("/user/login",this.user).then(res=>{
if (res.data.code==1){
this.$router.push("/index")
}
3.7 axios配置配置默认api
- 配合跨域请求使用
//将axiios共同的url进行配置
axios.defaults.baseURL="/api"
- 跨域请求路径不需要再写
/api
this.$axios.post("/user/login",this.user).then(res=>{
if (res.data.code==1){
this.$router.push("/index")
}
4. element-ui
4.1 安装和引入
- npm 安装
npm i element-ui -S
- 引入:在 main.js 中写入以下内容
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
4.2 Form表单(组件、验证)
(1)表单属性
- :model 绑定的vue数据对象(data中定义)
- :rules 属性传入约定的验证规则(data中定义)
- ref 定义别名,在点击按钮事件时,将别名传入函数进行处理(methods中定义函数,有固定验证逻辑)
<el-form :model="user" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="userName">
<el-input v-model="user.userName"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="user.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login('ruleForm')">登录</el-button>
<el-button @click="register">注册</el-button>
</el-form-item>
</el-form>
(2)vue 数据及方法
data(){
return {
user:{},
rules: {
userName: [
//required 必填项 message 提示信息 trigger 什么时候触发(blur失去焦点触发)
{required: true, message: '请输入用户名', trigger: 'blur'},
//min设置最小的长度 max 最大长度
{min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码名称', trigger: 'blur'},
{min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur'}
]
}
}
},
methods:{
// 登录
login:function (ruleForm) {
this.$refs[ruleForm].validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
(3)重置按钮
<el-button @click="resetForm('ruleForm')">重置</el-button>
resetForm(ruleForm) {
this.$refs[ruleForm].resetFields();
},
4.3 表格注意事项
- :data 数据源(data中定义)
- prop 数据元对象属性
- template 当时用到element时,从table表格中获取到某条数据,要包含在 template中
slot-scope=“scope” 给表格域取别名,通过别名.row获取表格中某条数据
<!-- 按钮-->
<el-table-column
fixed="right"
label="操作"
width="100">
<!--
1.template 当时用到element时,从table表格中获取到某条数据,要包含在 template中
2.slot-scope="scope" 给表格域取别名,通过别名.row获取表格中某条数据
-->
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">编辑</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
4.4 对话框
- :visible.sync=“dialogVisible” 模态窗口是否展示通过 dialogVisible变量 进行控制 如果为true则展示为false则隐藏
- dialogVisible(一个变量,用于控制模态窗口的展示,通常定义在data中,初始值给false,在需要操作下此此模态框时,通过此变量名称控制)
- :before-close 关闭前调用的函数
4.5 左侧导航栏
- @select=“handleSelect” 点击此menu下的某个item时,此方法会默认传递两个值
- 其中key:表示此item配置的index值
- 此方法配合父子路由可以实现容器:父容器中子页面的更替
- 页面中含有其他组件时,子容器应该包含在一对div中
<div>
<el-container style="height: 500px; border: 1px solid #eee">
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<!-- @select="handleSelect" 点击此menu下的某个item时,此方法会默认传递两个值
(其中key:表示此item配置的index值)-->
<el-menu @select="handleSelect">
<el-submenu index="1">
<template slot="title"><i class="el-icon-picture-outline"></i>轮播图管理</template>
<el-menu-item-group>
<template slot="title">轮播图</template>
<el-menu-item index="/rotation">列表管理</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-main>
<!-- 子页面书写位置-->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
- 实现方法(父子路由跳转)
- key表示index的值,也是页面的路径
handleSelect:function (key,keyPath) {
this.$router.push(key);
console.log(key)
console.log(keyPath)
}
4.6 弹出框
- 用于弹出提示信息
showResult:function(msg){
var h = this.$createElement;
this.$notify({
title: '提示信息',
message: h('i', { style: 'color: teal'}, msg)
});
},
5 分页
-
element已经将前端的分页操作进行了封装,只需要后端传回
数据的总条数
和当前页的数据
即可 -
前端向后端发起请求时,需要传递参数
当前页码
和每页多少条数据
(1)前端分页插件
- 以下的方法均定义在methods中
@size-change 每页条数发生变化时触发的方法,
其指定方法:不需要传参,在指定的方法中会有形参val表示当前每页条数
@current-change 页码发生变化时触发的方法
其指定方法:不需要传参,在指定的方法中会有形参val表示当前页数 - 以下的属性均定义在data中(可以封装一个对象,用于专门管理分页)
:current-page 表示当前页(页码定位到第几页)
:page-sizes 每页条数可选集合
:page-size 表示当前页展现的条数
:total 数据总条数(后端数据库使用分页技术,查询出总条数传递给前端)
<!-- 分页-->
<!--
- 以下的方法均定义在methods中
@size-change 每页条数发生变化时触发的方法,
其指定方法:不需要传参,在指定的方法中会有形参val表示当前每页条数
@current-change 页码发生变化时触发的方法
其指定方法:不需要传参,在指定的方法中会有形参val表示当前页数
- 以下的属性均定义在data中(可以封装一个对象,用于专门管理分页)
:current-page 表示当前页(页码定位到第几页)
:page-sizes 每页条数可选集合
:page-size 表示当前页展现的条数
:total 数据总条数(后端数据库使用分页技术,查询出总条数传递给前端)
-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="params.currentPage"
:page-sizes="params.pageSizes"
:page-size="params.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="params.total">
</el-pagination>
(2)vue中数据及方法
//分页相关(初始值)(以下值在data中封装)
params:{
currentPage:1,
pageSizes:[2,4,6,8],
pageSize:2,
total:10
},
//以下方法在methods中
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.params.pageSize=val;
this.findAll();
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.params.currentPage=val;
this.findAll();
},
(3)前端发起请求
findAll:function () {
var _this=this;
//携带参数:1.当前页码 2.每页的条数 axios.get("/api/student/findAll/"+this.params.currentPage+"/"+this.params.pageSize).then(function (res) {
if (res.data=="fail"){
alert("没有权限");
_this.$router.push("/");
}else {
console.log(res);
//赋值总数据条数
_this.params.total=res.data.total;
//赋值后端分页查询,当前页的数据
_this.studentList=res.data.data;
}
});
(4)后端处理分页
- 业务层处理分页(JPA)
@Override
public BaseResp findAll(int page, int size) {
// 使用jpa分页,page-1(page默认从0开始)
PageRequest of = PageRequest.of(page-1, size);
// 拿到分页后的page对象
Page<TbStudent> listStu = studentRepository.findAll(of);
/*
1.listStu.getContent() 分页查询到的当页数据(数据集合)
2.listStu.getTotalElements() 数据总条数
*/
return new BaseResp(1,"分页成功",listStu.getContent(),listStu.getTotalElements());
}
- 数据响应格式
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseResp {
// 请求响应状态码 1.表示成功 0.表示失败
private int code;
// 响应回去的操作信息
private String message;
// 响应回去的数据
private Object data;
// 响应回去查询到的总数据条数
private Long total;
// 标识响应失败
public BaseResp ERROR(){
return new BaseResp(0,"失败!",null,null);
}
// 标识响应生工
public BaseResp SUCCESS(){
return new BaseResp(1,"成功!",null,null);
}
}
6.文件上传
6.1 图片上传(服务器端上传七牛云)
- **七牛云java开发文档:**https://developer.qiniu.com/kodo/1239/java
(1)引入七牛云依赖
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.10.99]</version>
</dependency>
<!-- 可能需要另外导入gson包-->
6dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<scope>compile</scope>
</dependency>
(2)创建上传文件工具类
-
使用数据流上传,SpringMVC提供了MultipartFile对象用于文件上传(可以获取到数据流)
-
构造一个带指定 Region 对象的配置类 (用于指定上传服务器的地区)
-
//…生成上传凭证,然后准备上传
- 在密钥管理中获取(AK,SK)
- 指定上传到的仓库名称
-
解析上传成功的结果(自定义生成图片网络路径(图片名可以通过DefaultPutRet对象生成随机hash码值))
public class UploadUtil {
public static String upload(MultipartFile multipartFile){
//构造一个带指定 Region 对象的配置类 (用于指定上传服务器的地区)
Configuration cfg = new Configuration(Region.huabei());
cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
// 1.在密钥管理中获取
String accessKey = "3eUFp3juAOQ3fozMNB8aiAxho-VgJbd5CP6ncHRF";
String secretKey = "vXfUJtFm_20PIHsjeT-COUhPYgoh8EpnNIPlUhBs";
// 2.指定上传的账户
String bucket = "java-third";
//默认不指定key的情况下,以文件内容的hash值作为文件名
String key = null;
try {
//获取到数据流
InputStream inputStream = multipartFile.getInputStream();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(inputStream,key,upToken,null, null);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
// 返回图片外链地址
return "http://rii26poru.hb-bkt.clouddn.com/"+putRet.hash;
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
} catch (UnsupportedEncodingException ex) {
//ignore
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
(3)yml配置文件(配置七牛云)
#一级(在配置文件中顶格编写)
qiniuyun:
#七牛云许可凭证(配置文件可获取)
accessKey: 3eUFp3juAOQ3fozMNB8aiAxho-VgJbd5CP6ncHRF
#七牛云密钥(配置文件可获取)
secretKey: vXfUJtFm_20PIHsjeT-COUhPYgoh8EpnNIPlUhBs
#所上传的空间名
bucket: java-third
(4)编写接口测试
- 可以使用postman测试
@RequestMapping("upload")
public String upload(@RequestParam("file")MultipartFile multipartFile){
String upload = UploadUtil.upload(multipartFile);
return upload;
}
6.2 图片上传前端
- 使用elementui组件实现
(1)分析
- 第一步: 选中图片上传时,需要先将图片上传到七牛云,并将图片网络地址返回赋值给组件,实现图片回显
- 第二步: 点击提交按钮,将图片网络地址网络地址与其他信息一起保存到数据库,并回显到页面
(2)第一步
-
action 上传图片请求的路径(直接请求后端,将选中的图片上传到七牛云)
-
:on-success 图片上传成功的回调函数(方法中默认含有两参数,其中1参res表示上传图片的网络地址)
-
:before-upload 图片上传前的验证信息函数
-
imageUrl 为vue中定义的变量,用于图片地址的灵活交换与回显
-
:src 图片绑定的url,其变量可以在vue的data设置**,在成功回调函数中为其赋值(实现图片回显)**
<!-- 图片上传--> <!-- action 上传图片请求的路径 :on-success 图片上传成功的回调函数(方法中默认含有两参数,其中1参res表示上传图片的网络地址) :before-upload 图片上传前的验证信息 :src 图片绑定的url,其变量可以在vue的data设置,在成功回调函数中为其赋值(实现图片回显) --> <el-form-item label="图片" prop="imgUrl"> <el-upload class="avatar-uploader" action="/api/student/upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload"> <el-image v-if="imageUrl" :src="imageUrl" class="avatar" fit="cover"></el-image> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </el-form-item>
-
函数(methods)
// 图片上传成功的回调函数 handleAvatarSuccess(res, file) { //方式1:实现图片回显(本地变量) // this.imageUrl = URL.createObjectURL(file.raw); //方式2:实现图片回显(网络图片地址) this.imageUrl = res; console.log(this.imageUrl); console.log(res); //将图片网络地址赋值给对象 this.student.imgUrl=res; }, //图片上传前的验证函数 beforeAvatarUpload(file) { const isJPG = file.type === 'image/png'; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error('上传头像图片只能是 JPG 格式!'); } if (!isLt2M) { this.$message.error('上传头像图片大小不能超过 2MB!'); } return isJPG && isLt2M; },
(3)第二步
- 当element组件用到表格中数据时,即从table表格中获取到某条数据为elementui组件属性赋值时,要将组件包含在 template中
- slot-scope=“scope” 指明当前表格的作用域
- scope.row.imgUrl 指明本行数据对象的某个属性
<!-- 图片显示-->
<el-table-column prop="imgUrl" label="图片">
<template slot-scope="scope">
<el-image :src="scope.row.imgUrl" fit="cover" style="width: 50px; height: 60px"></el-image>
</template>
</el-table-column>
7.VantUI
7.1 安装和引入
- 安装
# Vue 2 项目,安装 Vant 2
npm i vant@latest-v2
- 全局引入
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
- 安装其他
npm install axios
7.2 vue-cookie 的使用
- 安装
npm install vue-cookie
- 引入
import vuecookie from 'vue-cookie'
Vue.prototype.$vuecookie=vuecookie;
- 使用
//将token设置到 cookie中
this.$vuecookie.set("token",res.data.data);
- 结合后端验证
//登录成功后,会将后台返回的token 设置到cookie中,
// 我们就需要从cookie中获取到token设置到axios请求头部中(后端验证从头部获取token的值并进行jwt验证),我们需要使用 路由守卫(在每次路由跳转前设置默认路由)
router.beforeEach((to, from, next) => {
var isAuthenticated=false;
//从cookie中获取token
var token= vuecookie.get("token");
//判断token不为Null
if (token!=null){
//用户是登录状态
//同时还要将token设置到axios的请求头部中
axios.defaults.headers={
token:token
}
}
//去后台验证当前用户的登录状态
axios.post("/user/isAuthenticated").then(res=>{
if (res.data.code==1) {
isAuthenticated=true;
}
//登录状态才可以跳转
if (to.name !== 'login' && !isAuthenticated) next({ name: 'login' })
else next()
})
})