概述
个人博客编写
后记
2022.12.2.4 : 30、此项目告一段落、
编撰此博客本意里除去对找工作的帮助、更多地是想帮助未走过的人去探探路、总结经验、少走弯路、知识的宝贵不在于无价、而是无私、天下熙熙皆为利往、人们之所以对某事产生敬仰、究其原由不过是你做了、而我做不到或者不愿做。
此篇博客算不上精品、很多处都是点到为止、能用即可、不求甚解、但在我看来、又有那么点好、谦虚点、这篇博客至少能帮你们在编写前后端分离的个人博客项目中、解决70%的问题、而且略有延伸、我这个人一点爱钻牛角尖、又恰逢强迫症、所以大多问题的解决方法都不会是似是而非、定然是知其然、知其所以然。
闲话少叙、这篇博客主要写在使用springboot+vue编写前后端分离的个人博客项目中会遇到的困惑与解决方法、至于项目架构方面的思考可以选择性看、毕竟初期设想不可能与最终一致、所以多关注问题及其解决方案才是能收获最大的。
项目地址:http://8.134.81.146/
运行方法
1.只考虑开发环境下的运行,而不是上传至服务器的运行(两者有区别,末尾有解释)
-
本项目将前端代码与后端代码分开编写与运行、后端负责处理数据、前端负责视图跳转、这也是当下对前后端分离的解释(刚开始我误解了前后端分离的意思,所以此处我说明一下)
-
前端运行方式:因为使用vue脚手架vue-cli所以我们需要node.js、运行:
npm run dev # 运行项目
详细启动方式在往期博客中寻找:(4条消息) Vue笔记基础_邹飞鸣的博客-CSDN博客
-
后端启动、需要jdk8、然后直接在idea中启动、就和普通的springboot项目启动一致
-
sql代码:直接是根目录下一sql结尾的文件,你可以复制文件内容然后在Mysql运行即可
GitHub地址
此项目已开源github:https://github.com/feimingabandon/blog
注:此博客集百家所长、有的解决方法下会标注参考文档、有的可能会有遗漏、望海涵、或联系我修改。
技术:SpringBoot、Mysql、Vue
要求:
1、前后端分离、有后台系统
2、
-
数据库设计:
-
用户表(user):user_id(自增、非空、序号与博客表对应(一对多))、
user_name(用户名)、
user_password(密码)、
user_permissions(权限:1(管理员)、2(普通用户)、3(游客))、
nickname(昵称)、
mail(用户邮箱)、
head(用户头像)、
register_time(注册时间)、
birthday(用户生日)、
age(用户年龄)
注:普通用户可添加文章、但需要经过管理员审核、游客只能查看后台无法进行操作。游客可评论(需要审核)
-
博客表(blog):
- 主要存储博客内容与关联评论
- 字段设计
- blog_id(自增)、
- title(标题)、
- blog_Summary(摘要)
- blog_content(博客内容)、
- user_id(表示是哪个用户发的博客)、
- audit(审核状态:0(通过)、1(未通过)、2(未审核))、
- like(点赞数,可多次点赞)、
- release_time(发布时间)、
- mtime(修改时间)、
- browse(浏览量)、
- top(置顶:0:置顶,1:不置顶)
-
评论表(comment)
- 存储评论,包括二级评论,且暂且只设计二级评论回去再改良
- 字段设计
- comment_id(自增)、
- blog_id(表示属于哪个博客)、
- level(1:表示父级为博客,2:表示是评论下的评论(即回复))、
- level2(默认为-1,即不是二级评论则为可,如果是二级评论则记录父级评论id)
- user_name(发表用户)、
- release_time(发布时间)、
- content(评论内容)、
- audit(审核状态(0(通过)、1(未通过)、2(未审核))、
- like(点赞数,可多次点赞))
-
分类(标签)表(tags)
- 用来对文章进行分类、一个文章可有多个标签。
- 我们建立两张表,一张只写各种类型的标签,另一张写文章与标签的映射关系,如一篇文章有两个标签,那么在第二张表中就记录两行,记录格式:id、blog_id、tag_id。这样写的两行中id自增、blog_id不变、tag_id一行写一种即可。
- 字段设计
- id(自增)、tag(表示标签,所以一行代表一个标签,然后后期还可以添加标签)
-
文章标签映射表(tags_blog)(不使用映射,直接在博客表中存储tag,多个tag以逗号分隔)- 让标签与文章形成多对一的形式
- 字段
- id(自增)、blog_id(文章id)、tag_id(标签id)
标签功能可以不设计如上两表,而是在博客表中添加个字段,存储所有标签,标签与标签间逗号分隔
-
3、功能
- 博客大全、博客查询、博客详情、用户登陆、后台管理、
- 整个页面分为页首、页脚、页左、页右、只有中间内容显示会改变,是博客详情或者博客查询结果或大全
- 页首放侧边栏、页脚放联系方式、页左放玩偶、页右放回到最上面
- 前端使用 vuesax ui、饿了么、等等ui框架
图片存储在后端
前端
1、页首
- 菜单
白 / 黑 切换- 看板娘
音乐播放器
2、页脚
3、首页
4、详情页
5、登陆页
6.Vuesax的使用
1、安装
# install
npm install vuesax # OR yarn add vuesax
# 上面npm出错可换成cnpm
2、使用
Vuesax 是一个 Vuejs 库。要使用它,请添加以下代码:
import Vue from 'vue'
import Vuesax from 'vuesax'
import 'vuesax/dist/vuesax.css' //Vuesax styles
Vue.use(Vuesax, {
// options here
})
问题
1.背景图片不能全屏的问题
<template >
<div id="building">
</div>
</template>
<style scoped>
#building {
background: url("../assets/test01.jpg") ; /* "https://api.ixiaowai.cn/api/api.php" */
height: 100%;
width: 100%;
position: fixed;
background-size: 100% 100%;
top: 0; /* 这里是设置与顶部的距离*/
left: 0; /* 这里是设置与左边的距离*/
}
</style>
2、内容长度超出div让滑动显示
在相应div下设置此参数
<div style="overflow-y:scroll;overflow-x:hidden;height:100%">
3、添加一言
创建 vue 文件,
<template>
<!-- 请注意,以下的示例包含超链接,您可能需要手动配置样式使其不变色。如果您嫌麻烦,可以移除。 -->
<p id="hitokoto"><a id="hitokoto_text">,获取中...</a></p>
</template>
<script >
fetch('https://v1.hitokoto.cn?c=i')
.then(response => response.json())
.then(data => {
const hitokoto = document.getElementById('hitokoto_text')
hitokoto.innerText = data.hitokoto
})
.catch(console.error)
</script>
4、打字机效果
因为与一言结合使用,所以在一言基础上修改了代码
参考地址:vue实现打字机动画_远方_ry的博客-CSDN博客
全部代码:
<template>
<!-- 请注意,以下的示例包含超链接,您可能需要手动配置样式使其不变色。如果您嫌麻烦,可以移除。 -->
<p ><a class='typewriter'>{{typewriter}}</a></p>
</template>
<script >
export default {
name: "Poetry",
data () {
return {
typewriter: '',
i: 0,
timer: 0,
str: 'Hi, i´m a web Designer' // 本质上是设置这里的值为自己需要的即可,使用this.str
}
},
mounted () {
this.typeing()
// 这块内容是一言的,相当于获取一个文本并赋值给this.str变量
fetch('https://v1.hitokoto.cn?c=i')
.then(response => response.json())
.then(data => {
this.str = data.hitokoto // data.hitokoto这个是需要一言网页返回后才有值
})
.catch(console.error)
},
// 这里应该是动态增减的实现
methods: {
typeing () {
if (this.i <= this.str.length) {
this.typewriter = this.str.slice(0, this.i++) + ''
this.timer = setTimeout(() => {
this.typeing()
}, 150)
} else {
clearTimeout(this.timer)
}
}
}
}
</script>
5、效果:一张背景图占全屏,背景图有诗句、下滑才是正文
1、如果处理不好会出现正文被背景图覆盖了一部分,本质上是块级元素大小未处理好。
我们先定义一个总块级元素< div>、此块级元素范围占全部网页(包括下拉)、然后在里面设置第一个 < div>,此元素存放背景图和诗句,大小只需要设置height: 100%;
即可,即高度占一整个屏幕。然后在此div
下在创建的div
里面的内容就会挨揍背景图往下输出而不是被背景图覆盖
结构:
<div class="building">
<div style="height: 100%" >
<Poetry id="poetry_text"></Poetry>
</div>
<div>
<br>
<h1 >我是大帅比</h1>
<h1>我是大帅比</h1>
</div>
</div>
</script>
<style scoped>
.building {
background: url("../assets/test01.jpg") ; /* "https://api.ixiaowai.cn/api/api.php" */
height: 100%;
width: 100%;
position: absolute; /*fixed:绝对定位,absolute:固定在那,滑动时位置改变*/
background-size: 100% 100%;
top: 0; /* 这里是设置与顶部的距离*/
left: 0; /* 这里是设置与左边的距离*/
}
#poetry_text{
color: aliceblue; // 白色
}
</style>
6、文字垂直居中
我们知道左右居中很方便text-align: center;
即可,但是如果想以百分比形式去垂直居中,可以使用表格属性
// 父级需要被设置的属性,宽高display、很重要的三属性
.poetry_text{
height: 100%;
width: 100%;
color: aliceblue;
text-align: center;
display: table;/*父元素设置表格属性*/
}
// 子级需要被设置的属性、一定要父级设置了display子级设置才起作用
.portry_text_span{
display: table-cell;/*img设置成表格元素属性*/
vertical-align: middle;/*两个display设置后这个属性就起作用*/
}
=====
<div class="poetry_text">
<Poetry class="portry_text_span"></Poetry>
</div>
注:此操作是在父级范围内垂直居中,如果父级范围出现问题可能导致偏差
6.2、文字最下面居中
在上面的基础上我们设置position: absolute;bottom: 7px;width:100%;
属性即可,width属性一定要100%
<div class="poetry_text">
<div class="portry_text_span">
<p>
<Poetry ></Poetry>
</p>
<p style="position: absolute;bottom: 7px;width:100%;font: 30px '华文行楷'">︾︾︾o(>﹏<)o不要向下滑动︾︾︾</p>
</div>
7、添加动态樱花
直接在index.html文件中添加
<script src="./sakura.js"></script>
注意js文件要保存下来,然后确定好路径就可以了
js文件:
//樱花 sakura
var stop, staticx;
var img = new Image();
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUgAAAEwCAYAAADVZeifAAAACXBIWXMAAACYAAAAmAGiyIKYAAAHG2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBSaWdodHM9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9yaWdodHMvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NDFDMjQxQjYyNjIwNjgxMTgwODNEMjE2MDAzOTU1NDQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDozNDVjOWViOC04NDc4LTFkNDctOGRjMi0yZDkyOGNhYTYxZWQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6YjAzN2ZiMGItNTU5Mi0xYjRkLWJjZGQtOWU4NGExMDJiMGM2IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTA1LTA5VDE0OjQ5OjM3KzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0wNS0wOVQxNDo1MToyNSswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0wNS0wOVQxNDo1MToyNSswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMjVlZWE3LTEyY2QtMTY0NC04ZDAzLWFjOTE2ZTAxZDQ1YyIgc3RSZWY6ZG9jdW1lbnRJRD0idXVpZDoxRDIwNUFGNjZCRDlFNTExOUM5REMwMzg2RjlEQjFGNyIvPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDphYmMzNjIzMy1hOWNkLWNiNDQtODViYi0zZTgyMjEwYmIxMjYiIHN0RXZ0OndoZW49IjIwMTgtMDUtMDlUMTQ6NTE6MjUrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjAzN2ZiMGItNTU5Mi0xYjRkLWJjZGQtOWU4NGExMDJiMGM2IiBzdEV2dDp3aGVuPSIyMDE4LTA1LTA5VDE0OjUxOjI1KzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2Vx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";
// 樱花数量 (添加)
var sakuraNum = 21;
// 樱花越界限制次数, -1不做限制,无限循环 (添加)
var limitTimes = -1;
// 定义限制数组 (添加)
var limitArray = new Array(sakuraNum);
for(var index = 0;index < sakuraNum;index++){
limitArray[index] = limitTimes;
}
// 定义樱花, idx 是修改添加的
function Sakura(x, y, s, r, fn, idx) {
this.x = x;
this.y = y;
this.s = s;
this.r = r;
this.fn = fn;
this.idx = idx;
}
// 绘制樱花
Sakura.prototype.draw = function(cxt) {
cxt.save();
var xc = 40 * this.s / 4;
cxt.translate(this.x, this.y);
cxt.rotate(this.r);
cxt.drawImage(img, 0, 0, 40 * this.s, 40 * this.s)
cxt.restore();
}
// 修改樱花位置,模拟飘落.
Sakura.prototype.update = function() {
this.x = this.fn.x(this.x, this.y);
this.y = this.fn.y(this.y, this.y);
this.r = this.fn.r(this.r);
// 如果樱花越界, 重新调整位置
if(this.x > window.innerWidth || this.x < 0 ||
this.y > window.innerHeight || this.y < 0) {
// 如果樱花不做限制
if (limitArray[this.idx] == -1) {
this.r = getRandom('fnr');
if(Math.random() > 0.4) {
this.x = getRandom('x');
this.y = 0;
this.s = getRandom('s');
this.r = getRandom('r');
} else {
this.x = window.innerWidth;
this.y = getRandom('y');
this.s = getRandom('s');
this.r = getRandom('r');
}
}
// 否则樱花有限制
else {
if (limitArray[this.idx] > 0) {
this.r = getRandom('fnr');
if(Math.random() > 0.4) {
this.x = getRandom('x');
this.y = 0;
this.s = getRandom('s');
this.r = getRandom('r');
} else {
this.x = window.innerWidth;
this.y = getRandom('y');
this.s = getRandom('s');
this.r = getRandom('r');
}
// 该越界的樱花限制数减一
limitArray[this.idx]--;
}
}
}
}
SakuraList = function() {
this.list = [];
}
SakuraList.prototype.push = function(sakura) {
this.list.push(sakura);
}
// list update 方法
SakuraList.prototype.update = function() {
for(var i = 0, len = this.list.length; i < len; i++) {
this.list[i].update();
}
}
// list draw 方法
SakuraList.prototype.draw = function(cxt) {
for(var i = 0, len = this.list.length; i < len; i++) {
this.list[i].draw(cxt);
}
}
SakuraList.prototype.get = function(i) {
return this.list[i];
}
SakuraList.prototype.size = function() {
return this.list.length;
}
// 位置随机策略
function getRandom(option) {
var ret, random;
switch(option) {
case 'x':
ret = Math.random() * window.innerWidth;
break;
case 'y':
ret = Math.random() * window.innerHeight;
break;
case 's':
ret = Math.random();
break;
case 'r':
ret = Math.random() * 6;
break;
case 'fnx':
random = -0.5 + Math.random() * 1;
ret = function(x, y) {
return x + 0.5 * random - 1.7;
};
break;
case 'fny':
random = 1.5 + Math.random() * 0.7
ret = function(x, y) {
return y + random;
};
break;
case 'fnr':
random = Math.random() * 0.03;
ret = function(r) {
return r + random;
};
break;
}
return ret;
}
// 樱花入口
function startSakura() {
requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
var canvas = document.createElement('canvas'),
cxt;
staticx = true;
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;');
canvas.setAttribute('id', 'canvas_sakura');
document.getElementsByTagName('body')[0].appendChild(canvas);
cxt = canvas.getContext('2d');
var sakuraList = new SakuraList();
// sakuraNum 樱花个数 (原版为50个)
for(var i = 0; i < sakuraNum; i++) {
var sakura, randomX, randomY, randomS, randomR, randomFnx, randomFny;
randomX = getRandom('x');
randomY = getRandom('y');
randomR = getRandom('r');
randomS = getRandom('s');
randomFnx = getRandom('fnx');
randomFny = getRandom('fny');
randomFnR = getRandom('fnr');
sakura = new Sakura(randomX, randomY, randomS, randomR, {
x: randomFnx,
y: randomFny,
r: randomFnR
}, i);
sakura.draw(cxt);
sakuraList.push(sakura);
}
stop = requestAnimationFrame(function() {
cxt.clearRect(0, 0, canvas.width, canvas.height);
// 修改樱花位置逻辑
sakuraList.update();
// 画出修改后的樱花
sakuraList.draw(cxt);
// 递归 修改位置, 画出修改后的樱花
stop = requestAnimationFrame(arguments.callee);
})
}
window.onresize = function() {
var canvasSnow = document.getElementById('canvas_snow');
// canvasSnow 在改变浏览器大小的时候会为null (修改空指针异常), 不过在改变大小时体验稍差
if (canvasSnow) {
canvasSnow.width = window.innerWidth;
canvasSnow.height = window.innerHeight;
}
}
img.onload = function() {
startSakura();
}
// 没看懂哪里调用了, 应该是关闭樱花特效的方法. 还请大佬们解释自己是怎么使用的.
function stopp() {
if(staticx) {
var child = document.getElementById("canvas_sakura");
child.parentNode.removeChild(child);
window.cancelAnimationFrame(stop);
staticx = false;
} else {
startSakura();
}
}
8、使用Element框架
npm 安装
推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。
npm i element-ui -S
引入 Element(针对npm安装)
完整引入
1、在引入 Element 时,可以传入一个全局配置对象。该对象目前支持 size
与 zIndex
字段。size
用于改变组件的默认尺寸,zIndex
设置弹框的初始 z-index(默认值:2000)。
在 main.js 中写入以下内容:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
//Vue.use(Element, { size: 'small', zIndex: 3000 });
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
以上代码便完成了 Element 的引入。需要注意的是,样式文件需要单独引入。
9、Uncaught SyntaxError: Unexpected token '<'
今天在运行 Vue 项目时,发现报了一个 Uncaught SyntaxError: Unexpected token '<'
错误,如图
解决办法:把放在 Vue项目 src/assets
里边的资源文件放到 public
文件夹下来引用
2.1 assets文件夹与static文件夹的区别
区别一:assets文件是src下的,所以最后运行时需要进行打包,而static文件不需要打包就直接放在最终的文件中了
区别二:assets中的文件在vue中的template/style下用…/这种相对路径的形式进行引用,在script下必须用@import的方式引入,而static下的文件在.vue中的任何地方只需使用…/这种相对路径的方式引入,
参考文档:解决 Uncaught SyntaxError: Unexpected token ‘<‘ 错误解决方法_优雅哥cc的博客-CSDN博客
10、诗词显示两行
问题:之前是只有一行所以使用行内标签< span>、但如果有两个行内标签它们会并排显示,即会尽量显示在一行内,所以我使用style="display:block"
将行内标签强制变为块元素,块元素就是一个占一行
11、博客常见左中右布局
只需要在父级< div>标签下设置一个div
标签,然后根据盒子模型,将margin:auto
值设置成这样
padding-right:20px
:在div
里面的内容到边框距离
<div style="top: 0;left: 0;width: 100%;height: 100%;">
<!--中间-->
<div style="width: 70%;margin:auto;">
</div>
</div>
12、背景色只有一个屏幕大小被渲染了,下拉后就无了
大概率是设置了高度为100%,把高度删除然后设置为: overflow:hidden
意思:HTML溢出;
所以我们设置足够多的空间让它不溢出
height:给元素指定高度,一旦元素的内容超出这个高度就会溢出
min-height:给元素设置最小高度,当内容少的时候,元素是这个高度,当内容超出时,元素高度会自动适应内容,不会出现内容溢出情况。
overflow 属性规定如何处理如何处理不符合元素框的内容。
属性:
overflow:hidden
:内容会被修剪,但浏览器不会显示供查看内容的滚动条
overflow:visible
:内容不修剪,会呈现在元素框之外
overflow:scroll
:会修剪,但是浏览器会显示滚动条以查看其余内容
overflow:auto
:由浏览器决定如何显示,如果需要,则显示滚动条
13、看板娘
注:此插件那用但是偶尔会出现人物加载失败的情况,具体原因就是人物模型是在线加载的,有时候网站会打不开,可以将人物模型下载至本地
1.去 GitHub 下载插件 :看板娘插件
下载完成后将 assets 整个文件复制在项目的静态目录下(static文件夹下)
2、导入jQuery、这里为了方便使用链接,可自行下载 jQuery 文件
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
3、在index.html中引入文件并初始化、结合上面的jquery后的整体index文件为:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>blog_vue</title>
<link rel="stylesheet" type="text/css" href="static/assets/waifu.css"/>
</head>
<body>
<!-- 看板娘 -->
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script src="static/assets/waifu-tips.js"></script>
<script src="./static/assets/autoload.js"></script>
<script src="./static/assets/live2d.js"></script>
<!--初始化数据,也可以在waifu-tips.js文件中初始化,最后调用方法initModel-->
<script type="text/javascript">
live2d_settings['modelId'] = 1;
live2d_settings['modelTexturesId'] = 87;
initModel("/static/assets/waifu-tips.json")
</script>
</body>
</html>
- 定制属于你的看板娘
- 修改
waifu-tips.js
顶部的设置参数 (或初始化前设置 - 修改
waifu-tips.json
,定制看板娘提示语,打造专属看板娘
- 修改
设置参数
Tips: waifu-tips.js
已自带默认参数,如无特殊要求可跳过
- 后端接口
live2d_settings['modelAPI']
看板娘 API 地址,默认值'//live2d.fghrsh.net/api/'
live2d_settings['tipsMessage']
提示语读取路径,默认值'waifu-tips.json'
(一般在initModel
时指定)live2d_settings['hitokotoAPI']
一言 API 接口,可选'lwl12.com'
,'hitokoto.cn'
,'jinrishici.com'
(古诗词)
- 默认模型
live2d_settings['modelId']
默认模型(分组) ID,可在 Demo 页[F12]
呼出控制台(Console)
找到live2d_settings['modelTexturesId']
默认材质(模型) ID,可在 Demo 页[F12]
呼出控制台(Console)
找到
- 工具栏设置
live2d_settings['showToolMenu']
, 显示工具栏,true
|false
live2d_settings['canCloseLive2d']
, 关闭看板娘 按钮,true
|false
live2d_settings['canSwitchModel']
, 切换模型 按钮,true
|false
live2d_settings['canSwitchTextures']
, 切换材质 按钮,true
|false
live2d_settings['canSwitchHitokoto']
, 切换一言 按钮,true
|false
live2d_settings['canTakeScreenshot']
, 看板娘截图 按钮,true
|false
live2d_settings['canTurnToHomePage']
, 返回首页 按钮,true
|false
live2d_settings['canTurnToAboutPage']
,跳转关于页 按钮,true
|false
- 模型切换模式
live2d_settings['modelStorage']
,记录 ID (刷新后恢复),true
|false
live2d_settings['modelRandMode']
,模型切换,可选'rand'
(随机) |'switch'
(顺序)live2d_settings['modelTexturesRandMode']
,材质切换,可选'rand'
|'switch'
- 提示消息选项
live2d_settings['showHitokoto']
,空闲时一言,true
|false
live2d_settings['showF12Status']
,控制台显示加载状态,true
|false
live2d_settings['showF12Message']
,提示消息输出到控制台,true
|false
live2d_settings['showF12OpenMsg']
,控制台被打开触发提醒,true
|false
live2d_settings['showCopyMessage']
,内容被复制触发提醒,true
|false
live2d_settings['showWelcomeMessage']
,进入面页时显示欢迎语,true
|false
- 看板娘样式设置
live2d_settings['waifuSize']
,看板娘大小,例如'280x250'
,'600x535'
live2d_settings['waifuTipsSize']
,提示框大小,例如'250x70'
,'570x150'
live2d_settings['waifuFontSize']
,提示框字体,例如'12px'
,'30px'
live2d_settings['waifuToolFont']
,工具栏字体,例如'14px'
,'36px'
live2d_settings['waifuToolLine']
,工具栏行高,例如'20px'
,'36px'
live2d_settings['waifuToolTop']
,工具栏顶部边距,例如'0px'
,'-60px'
live2d_settings['waifuMinWidth']
面页小于 指定宽度 隐藏看板娘,例如'disable'
(停用),'768px'
live2d_settings['waifuEdgeSide']
看板娘贴边方向,例如'left:0'
(靠左 0px),'right:30'
(靠右 30px)live2d_settings['waifuDraggable']
拖拽样式,可选'disable'
(禁用),'axis-x'
(只能水平拖拽),'unlimited'
(自由拖拽)live2d_settings['waifuDraggableRevert']
,松开鼠标还原拖拽位置,true
|false
- 其他杂项设置
live2d_settings['l2dVersion']
,当前版本 (无需修改)live2d_settings['l2dVerDate']
,更新日期 (无需修改)live2d_settings['homePageUrl']
,首页地址,可选'auto'
(自动),'{URL 网址}'
live2d_settings['aboutPageUrl']
,关于页地址,'{URL 网址}'
live2d_settings['screenshotCaptureName']
,看板娘截图文件名,例如'live2d.png'
定制提示语
Tips: waifu-tips.json
已自带默认提示语,如无特殊要求可跳过
-
"waifu"
系统提示
"console_open_msg"
控制台被打开提醒(支持多句随机)"copy_message"
内容被复制触发提醒(支持多句随机)"screenshot_message"
看板娘截图提示语(支持多句随机)"hidden_message"
看板娘隐藏提示语(支持多句随机)"load_rand_textures"
随机材质提示语(暂不支持多句)"hour_tips"
时间段欢迎语(支持多句随机)"referrer_message"
请求来源欢迎语(不支持多句)"referrer_hostname"
请求来源自定义名称(根据 host,支持多句随机)"model_message"
模型切换欢迎语(根据模型 ID,支持多句随机)"hitokoto_api_message"
,一言 API 输出模板(不支持多句随机)
-
"mouseover"
鼠标触发提示(根据 CSS 选择器,支持多句随机) -
"click"
鼠标点击触发提示(根据 CSS 选择器,支持多句随机) -
"seasons"
节日提示(日期段,支持多句随机)
14、定制鼠标样式的坑
1、定制鼠标样式:
body{
/*鼠标样式改变*/
cursor: url("static/pence.ico"),auto;
}
2、鼠标点击效果
<!--鼠标点击效果-->
<script src="https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@3.0.8/js/maodian.js"></script>
坑:
1.图片不能大于32*32
2.尽量是ico格式
3.逗号后面加auto
附:
在线修改图片大小网址:在线图片大小修改器,图片尺寸修改,格式转换【免费】
png等格式转ico:PNG转ICO - 在线转换图标文件
15、悬挂的喵:点击回到顶部
1.把下面html的内容与你的index.html内容进行比较,差不多就是如下格式:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
然后是大量的css内容
<style>
...
</style>
然后导入jquery
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
最后导入js代码
<script>
$(function () {
...
})
</script>
上面要导入的详细信息在下面的代码中。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>返回顶部(小猫特效)</title>
<style>
@media (max-width:1000px) {
.back-to-top {
display: none !important;
}
}
@-webkit-keyframes wrench {
0% {
-webkit-transform: rotate(-12deg);
transform: rotate(-12deg)
}
8% {
-webkit-transform: rotate(12deg);
transform: rotate(12deg)
}
10% {
-webkit-transform: rotate(24deg);
transform: rotate(24deg)
}
18%,
20% {
-webkit-transform: rotate(-24deg);
transform: rotate(-24deg)
}
28%,
30% {
-webkit-transform: rotate(24deg);
transform: rotate(24deg)
}
38%,
40% {
-webkit-transform: rotate(-24deg);
transform: rotate(-24deg)
}
48%,
50% {
-webkit-transform: rotate(24deg);
transform: rotate(24deg)
}
58%,
60% {
-webkit-transform: rotate(-24deg);
transform: rotate(-24deg)
}
68% {
-webkit-transform: rotate(24deg);
transform: rotate(24deg)
}
100%,
75% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
}
@keyframes wrench {
0% {
-webkit-transform: rotate(-12deg);
-ms-transform: rotate(-12deg);
transform: rotate(-12deg)
}
8% {
-webkit-transform: rotate(12deg);
-ms-transform: rotate(12deg);
transform: rotate(12deg)
}
10% {
-webkit-transform: rotate(24deg);
-ms-transform: rotate(24deg);
transform: rotate(24deg)
}
18%,
20% {
-webkit-transform: rotate(-24deg);
-ms-transform: rotate(-24deg);
transform: rotate(-24deg)
}
28%,
30% {
-webkit-transform: rotate(24deg);
-ms-transform: rotate(24deg);
transform: rotate(24deg)
}
38%,
40% {
-webkit-transform: rotate(-24deg);
-ms-transform: rotate(-24deg);
transform: rotate(-24deg)
}
48%,
50% {
-webkit-transform: rotate(24deg);
-ms-transform: rotate(24deg);
transform: rotate(24deg)
}
58%,
60% {
-webkit-transform: rotate(-24deg);
-ms-transform: rotate(-24deg);
transform: rotate(-24deg)
}
68% {
-webkit-transform: rotate(24deg);
-ms-transform: rotate(24deg);
transform: rotate(24deg)
}
100%,
75% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg)
}
}
.faa-parent.animated-hover:hover>.faa-wrench,
.faa-wrench.animated,
.faa-wrench.animated-hover:hover {
-webkit-animation: wrench 2.5s ease infinite;
animation: wrench 2.5s ease infinite;
transform-origin-x: 90%;
transform-origin-y: 35%;
transform-origin-z: initial
}
.faa-parent.animated-hover:hover>.faa-wrench.faa-fast,
.faa-wrench.animated-hover.faa-fast:hover,
.faa-wrench.animated.faa-fast {
-webkit-animation: wrench 1.2s ease infinite;
animation: wrench 1.2s ease infinite
}
.faa-parent.animated-hover:hover>.faa-wrench.faa-slow,
.faa-wrench.animated-hover.faa-slow:hover,
.faa-wrench.animated.faa-slow {
-webkit-animation: wrench 3.7s ease infinite;
animation: wrench 3.7s ease infinite
}
@-webkit-keyframes ring {
0% {
-webkit-transform: rotate(-15deg);
transform: rotate(-15deg)
}
2% {
-webkit-transform: rotate(15deg);
transform: rotate(15deg)
}
4% {
-webkit-transform: rotate(-18deg);
transform: rotate(-18deg)
}
6% {
-webkit-transform: rotate(18deg);
transform: rotate(18deg)
}
8% {
-webkit-transform: rotate(-22deg);
transform: rotate(-22deg)
}
10% {
-webkit-transform: rotate(22deg);
transform: rotate(22deg)
}
12% {
-webkit-transform: rotate(-18deg);
transform: rotate(-18deg)
}
14% {
-webkit-transform: rotate(18deg);
transform: rotate(18deg)
}
16% {
-webkit-transform: rotate(-12deg);
transform: rotate(-12deg)
}
18% {
-webkit-transform: rotate(12deg);
transform: rotate(12deg)
}
100%,
20% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
}
@keyframes ring {
0% {
-webkit-transform: rotate(-15deg);
-ms-transform: rotate(-15deg);
transform: rotate(-15deg)
}
2% {
-webkit-transform: rotate(15deg);
-ms-transform: rotate(15deg);
transform: rotate(15deg)
}
4% {
-webkit-transform: rotate(-18deg);
-ms-transform: rotate(-18deg);
transform: rotate(-18deg)
}
6% {
-webkit-transform: rotate(18deg);
-ms-transform: rotate(18deg);
transform: rotate(18deg)
}
8% {
-webkit-transform: rotate(-22deg);
-ms-transform: rotate(-22deg);
transform: rotate(-22deg)
}
10% {
-webkit-transform: rotate(22deg);
-ms-transform: rotate(22deg);
transform: rotate(22deg)
}
12% {
-webkit-transform: rotate(-18deg);
-ms-transform: rotate(-18deg);
transform: rotate(-18deg)
}
14% {
-webkit-transform: rotate(18deg);
-ms-transform: rotate(18deg);
transform: rotate(18deg)
}
16% {
-webkit-transform: rotate(-12deg);
-ms-transform: rotate(-12deg);
transform: rotate(-12deg)
}
18% {
-webkit-transform: rotate(12deg);
-ms-transform: rotate(12deg);
transform: rotate(12deg)
}
100%,
20% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg)
}
}
.faa-parent.animated-hover:hover>.faa-ring,
.faa-ring.animated,
.faa-ring.animated-hover:hover {
-webkit-animation: ring 2s ease infinite;
animation: ring 2s ease infinite;
transform-origin-x: 50%;
transform-origin-y: 0;
transform-origin-z: initial
}
.faa-parent.animated-hover:hover>.faa-ring.faa-fast,
.faa-ring.animated-hover.faa-fast:hover,
.faa-ring.animated.faa-fast {
-webkit-animation: ring 1s ease infinite;
animation: ring 1s ease infinite
}
.faa-parent.animated-hover:hover>.faa-ring.faa-slow,
.faa-ring.animated-hover.faa-slow:hover,
.faa-ring.animated.faa-slow {
-webkit-animation: ring 3s ease infinite;
animation: ring 3s ease infinite
}
@-webkit-keyframes vertical {
0% {
-webkit-transform: translate(0, -3px);
transform: translate(0, -3px)
}
4% {
-webkit-transform: translate(0, 3px);
transform: translate(0, 3px)
}
8% {
-webkit-transform: translate(0, -3px);
transform: translate(0, -3px)
}
12% {
-webkit-transform: translate(0, 3px);
transform: translate(0, 3px)
}
16% {
-webkit-transform: translate(0, -3px);
transform: translate(0, -3px)
}
20% {
-webkit-transform: translate(0, 3px);
transform: translate(0, 3px)
}
100%,
22% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0)
}
}
@keyframes vertical {
0% {
-webkit-transform: translate(0, -3px);
-ms-transform: translate(0, -3px);
transform: translate(0, -3px)
}
4% {
-webkit-transform: translate(0, 3px);
-ms-transform: translate(0, 3px);
transform: translate(0, 3px)
}
8% {
-webkit-transform: translate(0, -3px);
-ms-transform: translate(0, -3px);
transform: translate(0, -3px)
}
12% {
-webkit-transform: translate(0, 3px);
-ms-transform: translate(0, 3px);
transform: translate(0, 3px)
}
16% {
-webkit-transform: translate(0, -3px);
-ms-transform: translate(0, -3px);
transform: translate(0, -3px)
}
20% {
-webkit-transform: translate(0, 3px);
-ms-transform: translate(0, 3px);
transform: translate(0, 3px)
}
100%,
22% {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0)
}
}
.faa-parent.animated-hover:hover>.faa-vertical,
.faa-vertical.animated,
.faa-vertical.animated-hover:hover {
-webkit-animation: vertical 2s ease infinite;
animation: vertical 2s ease infinite
}
.faa-parent.animated-hover:hover>.faa-vertical.faa-fast,
.faa-vertical.animated-hover.faa-fast:hover,
.faa-vertical.animated.faa-fast {
-webkit-animation: vertical 1s ease infinite;
animation: vertical 1s ease infinite
}
.faa-parent.animated-hover:hover>.faa-vertical.faa-slow,
.faa-vertical.animated-hover.faa-slow:hover,
.faa-vertical.animated.faa-slow {
-webkit-animation: vertical 4s ease infinite;
animation: vertical 4s ease infinite
}
@-webkit-keyframes horizontal {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0)
}
6% {
-webkit-transform: translate(5px, 0);
transform: translate(5px, 0)
}
12% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0)
}
18% {
-webkit-transform: translate(5px, 0);
transform: translate(5px, 0)
}
24% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0)
}
30% {
-webkit-transform: translate(5px, 0);
transform: translate(5px, 0)
}
100%,
36% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0)
}
}
@keyframes horizontal {
0% {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0)
}
6% {
-webkit-transform: translate(5px, 0);
-ms-transform: translate(5px, 0);
transform: translate(5px, 0)
}
12% {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0)
}
18% {
-webkit-transform: translate(5px, 0);
-ms-transform: translate(5px, 0);
transform: translate(5px, 0)
}
24% {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0)
}
30% {
-webkit-transform: translate(5px, 0);
-ms-transform: translate(5px, 0);
transform: translate(5px, 0)
}
100%,
36% {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0)
}
}
.faa-horizontal.animated,
.faa-horizontal.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-horizontal {
-webkit-animation: horizontal 2s ease infinite;
animation: horizontal 2s ease infinite
}
.faa-horizontal.animated-hover.faa-fast:hover,
.faa-horizontal.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-horizontal.faa-fast {
-webkit-animation: horizontal 1s ease infinite;
animation: horizontal 1s ease infinite
}
.faa-horizontal.animated-hover.faa-slow:hover,
.faa-horizontal.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-horizontal.faa-slow {
-webkit-animation: horizontal 3s ease infinite;
animation: horizontal 3s ease infinite
}
@-webkit-keyframes flash {
0%,
100%,
50% {
opacity: 1
}
25%,
75% {
opacity: 0
}
}
@keyframes flash {
0%,
100%,
50% {
opacity: 1
}
25%,
75% {
opacity: 0
}
}
.faa-flash.animated,
.faa-flash.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-flash {
-webkit-animation: flash 2s ease infinite;
animation: flash 2s ease infinite
}
.faa-flash.animated-hover.faa-fast:hover,
.faa-flash.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-flash.faa-fast {
-webkit-animation: flash 1s ease infinite;
animation: flash 1s ease infinite
}
.faa-flash.animated-hover.faa-slow:hover,
.faa-flash.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-flash.faa-slow {
-webkit-animation: flash 3s ease infinite;
animation: flash 3s ease infinite
}
@-webkit-keyframes bounce {
0%,
10%,
100%,
20%,
50%,
80% {
-webkit-transform: translateY(0);
transform: translateY(0)
}
40%,
60% {
-webkit-transform: translateY(-15px);
transform: translateY(-15px)
}
}
@keyframes bounce {
0%,
10%,
100%,
20%,
50%,
80% {
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0)
}
40%,
60% {
-webkit-transform: translateY(-15px);
-ms-transform: translateY(-15px);
transform: translateY(-15px)
}
}
.faa-bounce.animated,
.faa-bounce.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-bounce {
-webkit-animation: bounce 2s ease infinite;
animation: bounce 2s ease infinite
}
.faa-bounce.animated-hover.faa-fast:hover,
.faa-bounce.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-bounce.faa-fast {
-webkit-animation: bounce 1s ease infinite;
animation: bounce 1s ease infinite
}
.faa-bounce.animated-hover.faa-slow:hover,
.faa-bounce.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-bounce.faa-slow {
-webkit-animation: bounce 3s ease infinite;
animation: bounce 3s ease infinite
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg)
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg)
}
100% {
-webkit-transform: rotate(359deg);
-ms-transform: rotate(359deg);
transform: rotate(359deg)
}
}
.faa-parent.animated-hover:hover>.faa-spin,
.faa-spin.animated,
.faa-spin.animated-hover:hover {
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite
}
.faa-parent.animated-hover:hover>.faa-spin.faa-fast,
.faa-spin.animated-hover.faa-fast:hover,
.faa-spin.animated.faa-fast {
-webkit-animation: spin .7s linear infinite;
animation: spin .7s linear infinite
}
.faa-parent.animated-hover:hover>.faa-spin.faa-slow,
.faa-spin.animated-hover.faa-slow:hover,
.faa-spin.animated.faa-slow {
-webkit-animation: spin 2.2s linear infinite;
animation: spin 2.2s linear infinite
}
@-webkit-keyframes float {
0% {
-webkit-transform: translateY(0);
transform: translateY(0)
}
50% {
-webkit-transform: translateY(-6px);
transform: translateY(-6px)
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0)
}
}
@keyframes float {
0% {
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0)
}
50% {
-webkit-transform: translateY(-6px);
-ms-transform: translateY(-6px);
transform: translateY(-6px)
}
100% {
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0)
}
}
.faa-float.animated,
.faa-float.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-float {
-webkit-animation: float 2s linear infinite;
animation: float 2s linear infinite
}
.faa-float.animated-hover.faa-fast:hover,
.faa-float.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-float.faa-fast {
-webkit-animation: float 1s linear infinite;
animation: float 1s linear infinite
}
.faa-float.animated-hover.faa-slow:hover,
.faa-float.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-float.faa-slow {
-webkit-animation: float 3s linear infinite;
animation: float 3s linear infinite
}
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1.1);
transform: scale(1.1)
}
50% {
-webkit-transform: scale(0.8);
transform: scale(0.8)
}
100% {
-webkit-transform: scale(1.1);
transform: scale(1.1)
}
}
@keyframes pulse {
0% {
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1)
}
50% {
-webkit-transform: scale(0.8);
-ms-transform: scale(0.8);
transform: scale(0.8)
}
100% {
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1)
}
}
.faa-parent.animated-hover:hover>.faa-pulse,
.faa-pulse.animated,
.faa-pulse.animated-hover:hover {
-webkit-animation: pulse 2s linear infinite;
animation: pulse 2s linear infinite
}
.faa-parent.animated-hover:hover>.faa-pulse.faa-fast,
.faa-pulse.animated-hover.faa-fast:hover,
.faa-pulse.animated.faa-fast {
-webkit-animation: pulse 1s linear infinite;
animation: pulse 1s linear infinite
}
.faa-parent.animated-hover:hover>.faa-pulse.faa-slow,
.faa-pulse.animated-hover.faa-slow:hover,
.faa-pulse.animated.faa-slow {
-webkit-animation: pulse 3s linear infinite;
animation: pulse 3s linear infinite
}
.faa-parent.animated-hover:hover>.faa-shake,
.faa-shake.animated,
.faa-shake.animated-hover:hover {
-webkit-animation: wrench 2.5s ease infinite;
animation: wrench 2.5s ease infinite
}
.faa-parent.animated-hover:hover>.faa-shake.faa-fast,
.faa-shake.animated-hover.faa-fast:hover,
.faa-shake.animated.faa-fast {
-webkit-animation: wrench 1.2s ease infinite;
animation: wrench 1.2s ease infinite
}
.faa-parent.animated-hover:hover>.faa-shake.faa-slow,
.faa-shake.animated-hover.faa-slow:hover,
.faa-shake.animated.faa-slow {
-webkit-animation: wrench 3.7s ease infinite;
animation: wrench 3.7s ease infinite
}
@-webkit-keyframes tada {
0% {
-webkit-transform: scale(1);
transform: scale(1)
}
10%,
20% {
-webkit-transform: scale(.9) rotate(-8deg);
transform: scale(.9) rotate(-8deg)
}
30%,
50%,
70% {
-webkit-transform: scale(1.3) rotate(8deg);
transform: scale(1.3) rotate(8deg)
}
40%,
60% {
-webkit-transform: scale(1.3) rotate(-8deg);
transform: scale(1.3) rotate(-8deg)
}
100%,
80% {
-webkit-transform: scale(1) rotate(0);
transform: scale(1) rotate(0)
}
}
@keyframes tada {
0% {
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1)
}
10%,
20% {
-webkit-transform: scale(.9) rotate(-8deg);
-ms-transform: scale(.9) rotate(-8deg);
transform: scale(.9) rotate(-8deg)
}
30%,
50%,
70% {
-webkit-transform: scale(1.3) rotate(8deg);
-ms-transform: scale(1.3) rotate(8deg);
transform: scale(1.3) rotate(8deg)
}
40%,
60% {
-webkit-transform: scale(1.3) rotate(-8deg);
-ms-transform: scale(1.3) rotate(-8deg);
transform: scale(1.3) rotate(-8deg)
}
100%,
80% {
-webkit-transform: scale(1) rotate(0);
-ms-transform: scale(1) rotate(0);
transform: scale(1) rotate(0)
}
}
.faa-parent.animated-hover:hover>.faa-tada,
.faa-tada.animated,
.faa-tada.animated-hover:hover {
-webkit-animation: tada 2s linear infinite;
animation: tada 2s linear infinite
}
.faa-parent.animated-hover:hover>.faa-tada.faa-fast,
.faa-tada.animated-hover.faa-fast:hover,
.faa-tada.animated.faa-fast {
-webkit-animation: tada 1s linear infinite;
animation: tada 1s linear infinite
}
.faa-parent.animated-hover:hover>.faa-tada.faa-slow,
.faa-tada.animated-hover.faa-slow:hover,
.faa-tada.animated.faa-slow {
-webkit-animation: tada 3s linear infinite;
animation: tada 3s linear infinite
}
@-webkit-keyframes passing {
0% {
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
opacity: 0
}
50% {
-webkit-transform: translateX(0%);
transform: translateX(0%);
opacity: 1
}
100% {
-webkit-transform: translateX(50%);
transform: translateX(50%);
opacity: 0
}
}
@keyframes passing {
0% {
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
opacity: 0
}
50% {
-webkit-transform: translateX(0%);
-ms-transform: translateX(0%);
transform: translateX(0%);
opacity: 1
}
100% {
-webkit-transform: translateX(50%);
-ms-transform: translateX(50%);
transform: translateX(50%);
opacity: 0
}
}
.faa-parent.animated-hover:hover>.faa-passing,
.faa-passing.animated,
.faa-passing.animated-hover:hover {
-webkit-animation: passing 2s linear infinite;
animation: passing 2s linear infinite
}
.faa-parent.animated-hover:hover>.faa-passing.faa-fast,
.faa-passing.animated-hover.faa-fast:hover,
.faa-passing.animated.faa-fast {
-webkit-animation: passing 1s linear infinite;
animation: passing 1s linear infinite
}
.faa-parent.animated-hover:hover>.faa-passing.faa-slow,
.faa-passing.animated-hover.faa-slow:hover,
.faa-passing.animated.faa-slow {
-webkit-animation: passing 3s linear infinite;
animation: passing 3s linear infinite
}
@-webkit-keyframes passing-reverse {
0% {
-webkit-transform: translateX(50%);
transform: translateX(50%);
opacity: 0
}
50% {
-webkit-transform: translateX(0%);
transform: translateX(0%);
opacity: 1
}
100% {
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
opacity: 0
}
}
@keyframes passing-reverse {
0% {
-webkit-transform: translateX(50%);
-ms-transform: translateX(50%);
transform: translateX(50%);
opacity: 0
}
50% {
-webkit-transform: translateX(0%);
-ms-transform: translateX(0%);
transform: translateX(0%);
opacity: 1
}
100% {
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
opacity: 0
}
}
.faa-parent.animated-hover:hover>.faa-passing-reverse,
.faa-passing-reverse.animated,
.faa-passing-reverse.animated-hover:hover {
-webkit-animation: passing-reverse 2s linear infinite;
animation: passing-reverse 2s linear infinite
}
.faa-parent.animated-hover:hover>.faa-passing-reverse.faa-fast,
.faa-passing-reverse.animated-hover.faa-fast:hover,
.faa-passing-reverse.animated.faa-fast {
-webkit-animation: passing-reverse 1s linear infinite;
animation: passing-reverse 1s linear infinite
}
.faa-parent.animated-hover:hover>.faa-passing-reverse.faa-slow,
.faa-passing-reverse.animated-hover.faa-slow:hover,
.faa-passing-reverse.animated.faa-slow {
-webkit-animation: passing-reverse 3s linear infinite;
animation: passing-reverse 3s linear infinite
}
@-webkit-keyframes burst {
0% {
opacity: .6
}
50% {
-webkit-transform: scale(1.8);
transform: scale(1.8);
opacity: 0
}
100% {
opacity: 0
}
}
@keyframes burst {
0% {
opacity: .6
}
50% {
-webkit-transform: scale(1.8);
-ms-transform: scale(1.8);
transform: scale(1.8);
opacity: 0
}
100% {
opacity: 0
}
}
.faa-burst.animated,
.faa-burst.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-burst {
-webkit-animation: burst 2s infinite linear;
animation: burst 2s infinite linear
}
.faa-burst.animated-hover.faa-fast:hover,
.faa-burst.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-burst.faa-fast {
-webkit-animation: burst 1s infinite linear;
animation: burst 1s infinite linear
}
.faa-burst.animated-hover.faa-slow:hover,
.faa-burst.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-burst.faa-slow {
-webkit-animation: burst 3s infinite linear;
animation: burst 3s infinite linear
}
@-webkit-keyframes falling {
0% {
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
opacity: 0
}
50% {
-webkit-transform: translateY(0%);
transform: translateY(0%);
opacity: 1
}
100% {
-webkit-transform: translateY(50%);
transform: translateY(50%);
opacity: 0
}
}
@keyframes falling {
0% {
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
opacity: 0
}
50% {
-webkit-transform: translateY(0%);
-ms-transform: translateY(0%);
transform: translateY(0%);
opacity: 1
}
100% {
-webkit-transform: translateY(50%);
-ms-transform: translateY(50%);
transform: translateY(50%);
opacity: 0
}
}
.faa-falling.animated,
.faa-falling.animated-hover:hover,
.faa-parent.animated-hover:hover>.faa-falling {
-webkit-animation: falling 2s linear infinite;
animation: falling 2s linear infinite
}
.faa-falling.animated-hover.faa-fast:hover,
.faa-falling.animated.faa-fast,
.faa-parent.animated-hover:hover>.faa-falling.faa-fast {
-webkit-animation: falling 1s linear infinite;
animation: falling 1s linear infinite
}
.faa-falling.animated-hover.faa-slow:hover,
.faa-falling.animated.faa-slow,
.faa-parent.animated-hover:hover>.faa-falling.faa-slow {
-webkit-animation: falling 3s linear infinite;
animation: falling 3s linear infinite
}
.topButton {
display: none !important
}
#goToTop {
display: none !important
}
.back-to-top {
cursor: pointer;
position: fixed;
right: 80px;
top: -900px;
z-index: 2;
width: 70px;
height: 900px;
background: url("http://zhouql.vip/cdn/images/scroll.png");
transition: all .5s ease-in-out;
opacity: 1;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body style="height: 2000px;">
<div class="back-to-top cd-top faa-float animated cd-is-visible" style="top: -900px;"></div>
<script>
$(function () {
$(window).scroll(function () {
var scroHei = $(window).scrollTop();
if (scroHei > 500) {
$('.back-to-top').css('top', '-200px');
// $('.back-to-top').fadeIn();
} else {
$('.back-to-top').css('top', '-999px');
// $('.back-to-top').fadeOut();
}
})
$('.back-to-top').click(function () {
$('body,html').animate({
scrollTop: 0
}, 600);
})
})
</script>
</body>
</html>
15、css中的单位:效果之一,文字随着页面大小变化而变化
单位 | 解析 |
---|---|
px | 绝对单位。像素 |
em | 相对单位。相对于其父元素的font-size,如父元素font-size: 12px;那么子元素1em = 12px,1em = 24px,… |
rem | 相对单位。相对于其根元素()的font-size,如html{font-size: 12px};那么页面中1em = 12px,1em = 24px,… |
vw | 相对单位。相对页面视口的宽度,页面总宽度为100vw,页面宽度的1% = 1vw |
vh | 相对单位。相对页面视口的高度,页面总高度为100vw,页面高度的1% = 1vh |
vmin | 相对单位。相对于页面视口的宽度与高度,取其最小值;如页面宽1000px,高1200px,那么1vmin = 10px |
vmax | 相对单位。相对于页面视口的宽度与高度,取其最大值;如页面宽1000px,高1200px,那么1vmin = 12px |
% | 相对单位。相对于父元素的宽度;如父元素宽100px,那么1% = 1px |
in | 绝对单位,寸 |
cm | 绝对单位,厘米 |
mm | 绝对单位,毫米 |
pt | 绝对单位,point,大约 1/72寸 |
pc | 绝对单位,pica,大约6pt,1/6寸 |
16、安装axios
文档:vue-axios|axios中文网 | axios (axios-js.com)
npm install --save axios vue-axios
将下面代码加入入口文件:
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
你可以按照以下方式使用:
Vue.axios.get(api).then((response) => {
console.log(response.data)
})
this.axios.get(api).then((response) => {
console.log(response.data)
})
this.$http.get(api).then((response) => {
console.log(response.data)
})
可以通过向 axios
传递相关配置来创建请求
axios(config)
// 发送 POST 请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// 获取远端图片
axios({
method:'get',
url:'http://bit.ly/2mTM3nY',
responseType:'stream'
})
.then(function(response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
注可能出现错误,所有使用axios的方法更正为:
在 main.js
文件中写:
Vue.prototype.$axios = axios
在需要使用时:
this.$axios.get('url')
.then(response=>{
// data1是已经写好了与json格式一样的变量,用来接收、response.data是接收的数据
this.data1 = response.data
});
17、博客界面初始化:获取所有博客的特点信息
1.要通过 json
格式去获取返回的博文大全的数据,这里暂时没有后端,所以我们写一个json
文件 临时存放数据
注:注意接收的变量中字段名要与 json 中的一样
0、此处是数据库中对应表的字段
- blog_id (自增)、
- title (标题)、
- blog_Summary (摘要)
- blog_content (博客内容)、// 博文内容暂时还不需要
- user_id (表示是哪个用户发的博客)、
- comment_id (对应评论表中id)、
- audit (审核状态:0(通过)、1(未通过)、2(未审核))、
- like (点赞数,可多次点赞)、
- release_time (发布时间)、
- mtime (修改时间)、
- browse (浏览量)、
- top (置顶:0:置顶,1:不置顶)
2、先写好json文件data_blog.json
{
"success": 200,
"blog_data": [
{
"id": "1",
"title": "标题",
"blog_Summary": "摘要",
"user_id": "表示是哪个用户发的博客",
"comment_count": "评论数量,类中没有需要再查",
"like": "点赞数,可多次点赞",
"release_time": "发布时间",
"browse": "浏览量",
"top": "置顶:0:置顶,1:不置顶"
},
{
"id":"2",
"title": "书单",
"blog_Summary": "读书使人得到一种优雅和风味,这就是读书的整个目的,而只有抱着这种目的的读书才可以叫做艺术。一人读书的目的并不是要“改进心智”,因为当他开始想要改进心智的时候,一切读书的乐趣便丧失净尽了。",
"user_id": "2",
"comment_count": "14",
"like": "85",
"release_time": "2019-01-21",
"browse": "9954",
"top": "0"
},
{
"id": "3",
"title": "多态到底有什么用?",
"blog_Summary": "结论:多态可有可无但又至关重要,在不考虑代码今后的发展时,多态是无用的,如果考虑以后代码的修改与增删,多态是能大大提高代码的扩展性与复用的。想搞清楚有什么用之前,先说说什么是多态:多态可以理解为一个事物可以拥有多种形态(自己理解的,后面会讲为啥)规范定义:1.需要有",
"user_id": "3",
"comment_count": "99",
"like": "100",
"release_time": "2022-11-17",
"browse": "170",
"top": "0"
},
{
"id":"4",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id": "5",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id":"6",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id":"7",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id":"8",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id": "9",
"title": "单例模式在多线程下的数据修改问题(即线程不安全),spring中是如何保证单例的线程安全问题的?",
"blog_Summary": "原因:在步骤3进入且准备更改步骤1的值时此时步骤2的线程获取到了步骤1的值且在步骤3赋值完成且输出后线程的值才输出,所以导致了线程在步骤1的值已经修改的情况下还能获取到步骤1的值。面试官原问题是:在单例模式下,类A获取单例对象且修改对象中的属性值,然后类B也获取对象也修",
"user_id": "4",
"comment_count": "9",
"like": "10",
"release_time": "2022-11-15",
"browse": "263",
"top": "1"
},
{
"id": "9",
"title": "十、二、八、十六进制间转换(包含负数间进制转换)原码、补码、反码定义",
"blog_Summary": "十、二、八、十六进制间转换(包含负数间进制转换)0.0、有符号的 1 字节表示范围。u200B 我们知道 1字节 (byte) = 8 位(bit)(即8位二进制,如:11111111)、1024个字节 = 1 K 、1024 K = 1Mu200B 所以,一字节可以表示的范围就有 0 ~ 256 (无符号数),而有符号数表示的范围则是: -128 ~ 127u200B",
"user_id": "10",
"comment_count": "2",
"like": "7",
"release_time": "2021-05-18",
"browse": "3189",
"top": "1"
}
]
}
3、我们要接收到 json 的数据,就要先定义好一个和 json 一模一样格式的变量去接收
data(){
return{
data1:{
success:200,
blog_data:[
{
id:"",
title: "",
blog_Summary:"",
user_id: "",
comment_count:"",
like: "",
release_time:"",
browse: "",
top: ""
}
]
}
}
}
4、编写axios、因为是一进去就要有的数据,所以使用vue的钩子函数:mounted、然后在里面写异步通信:axios函数
mounted() {
// 钩子函数
this.$axios.get('http://localhost:8080/static/data/data_blog.json')
.then(response=>{
// 这句话的意思是,我从返回的数据中选择blog_data的数据赋值给data1变量(response.data代表所有返回的数据)
this.data1 = response.data.blog_data
});
}
5、显示数据、因为 json 数据不止一条,所以我们使用vue的for去输出所有数据、我从项目代码中截取的,加了些样式
<div v-for="i in this.data1">
<!--一篇博客-->
<div style="margin: 0 0 52px 0;padding-top: 49px;">
<!--存储标题和置顶-->
<div>
<!--标题,用 a 标签-->
<a style="font-size: 2.5vw;color: #212121;transition: color .3s ease;
font-family: Merriweather,'Open Sans','Microsoft Jhenghei','Microsoft Yahei',sans-serif;
font-weight:bold;
">{{i.title}}</a>
<!--置顶:里面没有值除非传入,值为 0 时代表需要置顶-->
<span v-if='i.top=="0"' style="font-size: 1vw;color: #fff;background: #6fa3ef;border-radius: 3px;padding: 1px 3px;">置顶</span>
</div>
<!--存储时间,阅读,评论,推荐(推荐值即点赞数)-->
<span style="font-size: 0.9vw;color: #888;">
<!--发表-->
<i></i>◕ 发表于 {{i.release_time}}
<!--阅读-->
<i></i>♒ 阅读:{{i.browse}}
<!--评论-->
<i></i>♩ 评论:{{i.comment_count}}
<!--推荐-->
<i></i>❤ 推荐:{{i.like}}
</span>
<!--存储摘要和阅读全文的文字-->
<div style="font-size:1.1vw;padding-top: 5px;color: #212121">
<!--摘要-->
摘要:
{{i.blog_Summary}}
<!--阅读全文-->
<router-link to="" style="display:block;padding-top: 25px;font-weight: bolder;opacity: 0.9">阅读全文 »</router-link>
</div>
</div>
<!--分割线-->
<hr>
</div>
18、对登陆数据验证
原文:在vue中使用rules的定义和校验规则_蓝木蓝木的博客-CSDN博客_rules vue
在vue中使用rules的定义和校验规则
表单内容里面定义属性:
<Form ref="rulesForm" :model="rulesForm" :label-width="100" :rules="rules">
<FormItem label="名称" prop="name">
<Input v-model="rulesForm.name" placeholder="名称"/>
</FormItem>
</Form>
在data()里面写具体的规则:
rules {
name: [ { type: 'string',required: true,message: "名称必填", trigger: 'blur'}, {max: 30,message: "名称长度不能超过30位" }]
}
rules:与上文 ‘表单内容’ 中 表单的 :rules 属性值相同
prop:与规则中的name属性相同
验证内容:必填,blur是失去焦点验证,max是最大长度验证(min最小长度)
直接用pattern进行匹配验证:
name: [ { pattern: pattern验证, required: true, message: "提示信息", trigger: "blur" }]
前端Vue中常用rules校验规则:
1、是否合法IP地址:
pattern:/^(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5])$/,
2.是否手机号码或者固话
pattern:/^((0d{2,3}-d{7,8})|(1[34578]d{9}))$/,
3.是否身份证号码
pattern:/(d{15}$)|(^d{18}$)|(d{17}(d|X|x)$)/,
4.是否邮箱
pattern:/^([a-zA-Z0-9]+[-_.]?)+@[a-zA-Z0-9]+.[a-z]+$/,
5.整数填写
pattern:/^-?[1-9]d*$/,
6.正整数填写
pattern:/1d*$/,
7.小写字母
pattern:/2+$/,
8.大写字母
pattern:/3+$/,
9.大小写混合
pattern:/4+$/,
10.多个8位数字格式(yyyyMMdd)并以逗号隔开
pattern:/^d{8}(,d{8})*$/,
11.数字加英文,不包含特殊字符
pattern:/5+$/,
12.前两位是数字后一位是英文
pattern:/^d{2}[a-zA-Z]+$/,
13.密码校验(6-20位英文字母、数字或者符号(除空格),且字母、数字和标点符号至少包含两种)
pattern:/(?![d]+$)(?![a-zA-Z]+$)(?![da-zA-Z]+KaTeX parse error: Got function 'u' with no arguments as superscript at position 5: )([^̲u̲4e00-u9fa5s])…/,
14.中文校验
pattern:/6+$/,
19、vue在第二次跳转同一路由跳转数据不更新
原因
使用router-view时,如果在相同路由之间跳转,默认在跳转路由时会采用缓存策略,并不会刷新当前路由组件。即mounted,beforeDestory等钩子函数并不会触发。vue 同一路由跳转不走生命周期,导致数据不更新。
解决办法
办法一:
使用watch 监听路由变化。手动更新数据。
办法二:
使用 <router-view :key="$route.fullPath"/>
就是每次跳转加个随机值,那么就不一样了
办法三:
把你mounted(){} 里面执行的办法在 activated(){} 里面在执行一遍,完美解决。
这里选择第二种、其他方法暂时没搞明白
1、我们一般显示组件是用
<router-view />
2、我们将其修改为:
<router-view :key="key1" />
3、即每次组件跳转都要带key1的值,key1的值使用计算属性去设置,全部代码为:
<template>
<div id="app"> <router-view :key="key1" /> </div>
</template>
<script>
export default {
name: 'App',
data(){
return{
}
},
computed: { // 计算属性
key1(){
return this.$route.path + Math.random(); // 生成以时间为标准的随机数
}
}
}
20、编写登陆界面
注:登陆界面还是自己写吧,丑就丑了点
问题:我想使用别人的登陆界面,或者是一些UI框架,但无一例外,他们使用的css样式中设置了lang="scss"
属性,导致在项目打包发布时会却一些模块,当你安装模块后,又会显示你某些模块的版本有问题,当你一个个改版本后,发现你项目的模块彻底崩盘了,运行不了。所以慎用这个属性
<style lang="scss" scoped>
最后还是白嫖了一个登陆界面,自己就加了一个背景图片
<template>
<div class="building">
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog
title="温馨提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Land",
data() {
return {
form: {
username: '',
password: ''
},
// 表单验证,需要在 el-form-item 元素中增加 prop 属性
rules: {
username: [
{required: true, message: '账号不可为空', trigger: 'blur'}
],
password: [
{required: true, message: '密码不可为空', trigger: 'blur'}
]
},
// 对话框显示和隐藏
dialogVisible: false
}
},
methods: {
onSubmit(formName) {
// 为表单绑定验证功能
this.$refs[formName].validate((valid) => {
if (valid) {
// 使用 vue-router 路由到指定页面,该方式称之为编程式导航
this.$router.push("/");
} else {
this.dialogVisible = true;
return false;
}
});
}
}
}
</script>
<style scoped>
.login-box {
border: 1px solid #DCDFE6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title {
text-align: center;
margin: 0 auto 40px auto;
color: #303133;
}
.building {
background: url("https://imgapi.xl0408.top/index.php") ; /* API接口大全:https://imgapi.xl0408.top/index.php http://prlrr.com/wallpaper-API/ https://img.r10086.com/ https://api.r10086.com/img-api.php?type=动漫综合1 "https://api.ixiaowai.cn/api/api.php" "../assets/test01.jpg"*/
height: 100%;
width: 100%;
position: absolute; /*fixed:绝对定位,absolute:固定在那,滑动时位置改变*/
background-size: 100% 100%;
top: 0; /* 这里是设置与顶部的距离*/
left: 0; /* 这里是设置与左边的距离*/
}
</style>
21、vue 回到上一个页面
参考:[vue项目如何实现返回上一页 - 腾讯云开发者社区-腾讯云 (tencent.com)]
vue 返回上一页有两种方法:
如果使用的是 vue-router ,this.$router.go(-1)
就可以回到上一页。 history.go(-1)
是回到浏览器上一页。
但是由于 Vue 应用是单页应用,浏览器的访问历史未必和 Vue 的浏览历史相同。
还有一点,就是使用 router 跳转的时候,Vue 不会重新加载 CSS 。比如从 A 页面跳到 B 页面,会沿用 A 页面中的 CSS 样式,我在 A 页面中设置了 .content
的上边距是 20px
,B 页面没有设置边距,但如果从 A 页面跳到 B 页面,B 页面中的 .content
也会带有 20px
的上边距。
解决办法就是给 style 标签添加 scope
属性。
Vue的 style 中使用 scope 属性,浏览器渲染后,会给每个组件中的元素增加自定义属性,浏览器渲染样式时会变成 data-v-xxx
这也是 scoped 的工作原理,所以在子组件中写的元素,只有子组件中的自定义属性,而父组件中加的样式,最终浏览器渲染时是找不到对应的元素的,(因为父组件中样式给出的自定义属性是不一致的),所以子组件中的样式没办法在父组件中修改。
这样也就解决了,页面跳转上个页面的 CSS 样式也被带过来的问题。
22、Vue页面跳转后位置不在顶部的解决办法
注:使用方式1好像会导致悬挂的喵失效,方式2就不会
方式1:注:在中间增加下面代码,不要在末尾,会不生效
router.beforeEach((to, from, next) => {
// chrome
document.body.scrollTop = 0
// firefox
document.documentElement.scrollTop = 0
// safari
window.pageYOffset = 0
next()
})
或者下面代码:
// 跳转后返回顶部
router.afterEach((to,from,next) => {
window.scrollTo(0,0);
})
方式二:如果需要某个页面跳转回顶部,仅需要在当前页面(page)合适的位置加入一下代码即可:
// chrome
document.body.scrollTop = 0
// firefox
document.documentElement.scrollTop = 0
// safari
window.pageYOffset = 0
方式三:在路由的index.js中,加入以下代码也可实现路由改变定位到顶部的效果,savedPosition当且仅当通过浏览器的前进/后退按钮触发时才可用,代码如下:
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return {
x: 0,
y: 0
}
}
至此前端告一小段落了,要开始写后端了
后端
- 使用Mysql数据库,使用SpringBoot框架
- 整合 MyBatis
- 使用 Druid 数据库连接池 (有日志监控)
使用 SpringSecurity (Spring 的安全框架,使用其中的身份认证与授权功能,即权限控制 )使用 token- 使用 Swagger 功能,即实时 API 接口文档功能
使用 JWT 进行基于 Token 的前后端分离用户认证(和上面的安全框架择其一,具体再了解)
先写要用到的技术,至于功能慢慢看。
后端编写时问题
1、编写数据库表
1.varchar相当于String类型,能存储的汉字数看是使用什么编码,utf8(2个为一个汉字),所以varchar(30)只能输入15个汉字
2.String 可以存储 4G 的数据
3.微型文本可使用 tinytext 类型 ,2^8-1
4.text 文本串 2^16-1 保存大文本
5.大型文本可使用 mediumtext 类型,百万级别
6.在使用 MyBatis 时,数据库字段名与实体类成员变量名保持一致,但数据库中 user_id
可在实体类中写成 userId
,MyBatis 会自动识别。
注:不能识别,但是在编写sql语句时可以给字段加别名,别名设置为你实体类中对应的成员变量名就行
7.图片类型存储地址,存储在后端中,且前端访问是通过链接,而不是相对地址或者决对地址,相当于前端跨域请求了一个图片
用户表/博客表/标签表/评论表:
注:如下字段不代表最终字段、有些名字改了一点,有些字段属性更改了,具体看GitHub上的源码
/*[15:35:33][0 ms]*/ CREATE DATABASE `blog_springboot_vue`CHARACTER SET utf8 COLLATE utf8_general_ci;
/*[15:35:34][0 ms]*/ SHOW DATABASES;
/*[15:35:34][0 ms]*/ USE `blog_springboot_vue`;
/*[15:35:39][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[15:35:46][0 ms]*/ SHOW CHARSET;
/*[15:36:10][0 ms]*/ SHOW COLLATION;
/*[15:36:10][0 ms]*/ SHOW COLLATION;
/*[15:52:15][511 ms]*/ CREATE TABLE `blog_springboot_vue`.`user`( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户id', `name` VARCHAR(30) NOT NULL COMMENT '用户名', `pwd` VARCHAR(30) NOT NULL COMMENT '密码', `nickname` VARCHAR(30) NOT NULL COMMENT '昵称', `root` INT(4) UNSIGNED NOT NULL DEFAULT 2 COMMENT '权限(0:管理员,1:普通用户)', `email` VARCHAR(30) NOT NULL COMMENT '邮箱', `head` VARCHAR(100) COMMENT '头像地址', `registertime` VARCHAR(30) DEFAULT '0000-00-00' COMMENT '注册时间', `birthday` VARCHAR(30) DEFAULT '0000-00-00' COMMENT '生日', `age` INT(4) UNSIGNED DEFAULT 18 COMMENT '年龄', PRIMARY KEY (`id`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;
/*[15:52:18][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[15:52:18][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[15:52:18][0 ms]*/ SHOW CHARSET;
/*[15:52:18][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'user';
/*[15:52:18][0 ms]*/ SHOW CHARSET;
/*[15:52:18][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`user`;
/*[15:52:18][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`user`;
/*[15:52:18][0 ms]*/ SHOW COLLATION;
/*[15:52:34][0 ms]*/ SHOW COLLATION;
/*[15:52:34][13 ms]*/ SHOW COLLATION;
/*[16:08:19][935 ms]*/ CREATE TABLE `blog_springboot_vue`.`blog`( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '文章id', `title` VARCHAR(100) NOT NULL COMMENT '文章标题', `Summary` TINYTEXT COMMENT '摘要', `content` MEDIUMTEXT COMMENT '文章全文', `user_id` INT(4) NOT NULL COMMENT '哪个用户的文章', `audit` INT(4) NOT NULL COMMENT '审核状态:0(通过)、1(未通过)、2(未审核)', `like` INT(10) NOT NULL COMMENT '点赞数,可多次点赞', `release_time` VARCHAR(30) NOT NULL COMMENT '发布时间', `mtime` VARCHAR(30) DEFAULT '0' COMMENT '修改时间', `browse` INT(10) NOT NULL DEFAULT 0 COMMENT '浏览量', `top` INT(4) NOT NULL DEFAULT 1 COMMENT '0:置顶,1:不置顶', PRIMARY KEY (`id`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;
/*[16:08:21][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'blog';
/*[16:08:21][0 ms]*/ SHOW CHARSET;
/*[16:08:22][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`blog`;
/*[16:08:22][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`blog`;
/*[16:08:22][0 ms]*/ SHOW COLLATION;
/*[16:08:22][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:09:19][1417 ms]*/ ALTER TABLE `blog_springboot_vue`.`user` CHANGE `name` `name` VARCHAR(100) CHARSET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', CHANGE `pwd` `pwd` VARCHAR(100) CHARSET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码', CHANGE `nickname` `nickname` VARCHAR(100) CHARSET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '昵称', CHANGE `email` `email` VARCHAR(100) CHARSET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '邮箱', CHANGE `head` `head` VARCHAR(200) CHARSET utf8 COLLATE utf8_general_ci NULL COMMENT '头像地址', CHANGE `registertime` `registertime` VARCHAR(50) CHARSET utf8 COLLATE utf8_general_ci DEFAULT '0000-00-00' NULL COMMENT '注册时间', CHANGE `birthday` `birthday` VARCHAR(50) CHARSET utf8 COLLATE utf8_general_ci DEFAULT '0000-00-00' NULL COMMENT '生日';
/*[16:09:21][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'user';
/*[16:09:21][0 ms]*/ SHOW CHARSET;
/*[16:09:21][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`user`;
/*[16:09:21][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`user`;
/*[16:09:21][0 ms]*/ SHOW COLLATION;
/*[16:09:21][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:09:40][133 ms]*/ ALTER TABLE `blog_springboot_vue`.`blog` CHANGE `release_time` `release_time` VARCHAR(50) CHARSET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '发布时间', CHANGE `mtime` `mtime` VARCHAR(50) CHARSET utf8 COLLATE utf8_general_ci DEFAULT '0' NULL COMMENT '修改时间';
/*[16:09:42][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'blog';
/*[16:09:42][0 ms]*/ SHOW CHARSET;
/*[16:09:42][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`blog`;
/*[16:09:42][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`blog`;
/*[16:09:42][0 ms]*/ SHOW COLLATION;
/*[16:09:42][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:09:49][0 ms]*/ SHOW CHARSET;
/*[16:09:58][0 ms]*/ SHOW COLLATION;
/*[16:09:58][0 ms]*/ SHOW COLLATION;
/*[16:16:59][1090 ms]*/ CREATE TABLE `blog_springboot_vue`.`comment`( `id` INT(4) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '评论id', `blog_id` INT(4) NOT NULL COMMENT '属于哪个博客', `level` INT(4) NOT NULL COMMENT '1:表示父级为博客,2:表示是评论下的评论(即回复)', `level2` INT(4) NOT NULL DEFAULT -1 COMMENT '默认为-1,如果是二级评论则记录父级评论id', `user_id` INT(4) UNSIGNED NOT NULL COMMENT '发布者id', `time` VARCHAR(50) COMMENT '发布时间', `content` TINYTEXT COMMENT '评论内容', `audit` INT(4) NOT NULL DEFAULT 2 COMMENT '审核状态(0(通过)、1(未通过)、2(未审核)', `like` INT(10) NOT NULL DEFAULT 0 COMMENT '点赞数', PRIMARY KEY (`id`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;
/*[16:17:02][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:17:02][0 ms]*/ SHOW CHARSET;
/*[16:17:02][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'comment';
/*[16:17:02][0 ms]*/ SHOW CHARSET;
/*[16:17:02][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`comment`;
/*[16:17:02][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`comment`;
/*[16:17:02][0 ms]*/ SHOW COLLATION;
/*[16:17:34][0 ms]*/ SHOW COLLATION;
/*[16:17:34][0 ms]*/ SHOW COLLATION;
/*[16:18:24][461 ms]*/ ALTER TABLE `blog_springboot_vue`.`comment` CHANGE `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '评论id', CHANGE `blog_id` `blog_id` INT(10) NOT NULL COMMENT '属于哪个博客', CHANGE `user_id` `user_id` INT(10) UNSIGNED NOT NULL COMMENT '发布者id';
/*[16:18:26][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'comment';
/*[16:18:26][0 ms]*/ SHOW CHARSET;
/*[16:18:26][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`comment`;
/*[16:18:26][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`comment`;
/*[16:18:26][0 ms]*/ SHOW COLLATION;
/*[16:18:26][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:18:45][396 ms]*/ ALTER TABLE `blog_springboot_vue`.`blog` CHANGE `user_id` `user_id` INT(10) NOT NULL COMMENT '哪个用户的文章';
/*[16:18:47][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'blog';
/*[16:18:47][0 ms]*/ SHOW CHARSET;
/*[16:18:47][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`blog`;
/*[16:18:47][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`blog`;
/*[16:18:47][0 ms]*/ SHOW COLLATION;
/*[16:18:47][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:20:35][1413 ms]*/ ALTER TABLE `blog_springboot_vue`.`blog` ADD COLUMN `tag` VARCHAR(100) NULL COMMENT '文章标签' AFTER `top`;
/*[16:20:38][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'blog';
/*[16:20:38][0 ms]*/ SHOW CHARSET;
/*[16:20:38][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`blog`;
/*[16:20:38][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`blog`;
/*[16:20:38][0 ms]*/ SHOW COLLATION;
/*[16:20:38][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:21:34][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:22:08][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
/*[16:22:39][574 ms]*/ CREATE TABLE `blog_springboot_vue`.`tags`( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '标签id', `tag` VARCHAR(30) COMMENT '标签名称', PRIMARY KEY (`id`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;
/*[16:22:42][0 ms]*/ SHOW TABLE STATUS FROM `blog_springboot_vue` LIKE 'tags';
/*[16:22:42][0 ms]*/ SHOW CHARSET;
/*[16:22:42][0 ms]*/ SHOW FULL FIELDS FROM `blog_springboot_vue`.`tags`;
/*[16:22:42][0 ms]*/ SHOW KEYS FROM `blog_springboot_vue`.`tags`;
/*[16:22:42][0 ms]*/ SHOW COLLATION;
/*[16:22:43][0 ms]*/ SHOW FULL TABLES FROM `blog_springboot_vue` WHERE table_type = 'BASE TABLE';
2、创建SpringBoot项目、导入依赖
暂时只导入,mysql、web、jdbc、MyBatis、swagger、druid、log4j。
剩下的需要再补
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zou</groupId>
<artifactId>blog_springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blog_springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<!--swagger接口API-->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--log4j 数据源会使用到此日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--myBatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.zou.BlogSpringbootApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2、设置配置文件
因为功能没开始写,所以有些地方的配置是不对的、如:myBatis,后续会改、配置文件使用 yaml 格式,原配置文件需要删除
spring:
application:
name: blog_springboot
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/blog_springboot_vue?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
server:
port: 8081
# 整合mybatis
mybatis:
type-aliases-package: com.zou.pojo # 实体类的包路径
mapper-locations: classpath:mybatis/mapper/*.xml # xml文件路径,classpath表示resources文件路径
3.编写实体类
略过
4、定义dao层的mapper接口和mapper.xml文件中的sql语句
略过
5、为啥插入,删除,修改、MyBatis的返回值是int
mybatis 执行 insert,update,delete 返回的都是实际操作影响的行数
insert: 插入n条记录,返回影响行数n。(n>=1,n为0时实际为插入失败)
update: 更新n条记录,返回影响行数n。(n>=0)
delete: 删除n条记录,返回影响行数n。(n>=0)
6、只需要部分字段
解决方法:
注:这种方法虽然可以,但如果不需要的字段是基本数据类型,它是可以有默认值的,所以我可以不管它,就不用再通过下面这个方法去写
一、.xml文件中,resultMap的 type改为 java.util.HashMap 即可,其余代码都不用写, 亲测可用,xml文件中代码如下:
<!-- 返回类型 -->
<resultMap id="testRestMap" type="java.util.HashMap">
<result column="province_name" jdbcType="VARCHAR" property="provinceName" />
<result column="area_name" jdbcType="VARCHAR" property="areaName" />
<result column="lane_num" jdbcType="VARCHAR" property="laneNum" />
<result column="road_name" jdbcType="VARCHAR" property="roadName" />
</resultMap>
<!--sql查询 -->
<select id="selTest" resultMap="testRestMap">
select
province_name,
area_name,lane_num,
road_name
from site_info
where 1=1
and del_flag='0'
</select>
7、MyBatis parameterType和resultType区别
1.resultType
用来设置接收数据库返回结果的类型,即使返回多条结果,也只需要设置结果类型即可,多条结果会以List形式返回
一般用来从数据库提取数据
2.parameterType
用于将信息存储进数据库,接收接口方法的参数类型
如:我们通过id查询数据,所以这个parameterType的值设置为id的类型
3、当我们需要通过id查询数据时
要同时设置返回值类型和传入参数类型。
4、为什么dao层是在接口上注解,而service是在实现类上(实例化bean的注解)
问题:我们一般在dao层的接口上使用注解
@Repository // 对象交给spring管理。
而service则是在实现类上注解,为什么呢?不能也在接口上注解?
结果:不能、会报如下错误,具体就是:接口不能实例化,所以我们使用@Autowired
注入的变量会失败,因为bean不存在
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-11-24 21:57:27.819 ERROR 7860 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field blogService in com.zou.controller.BlogController required a bean of type 'com.zou.service.BlogService' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.zou.service.BlogService' in your configuration.
那为什么dao层可以注解到接口上?
1、有MyBatis在,无论你在不在接口上注解,它都会实例化为且给IoC托管
因为:最终发现 MapperScannerConfigurer 帮我们做了实例化bean的工作。
在Spring配置Mybatis的文件中我们可以看到如下代码:
[html] view plain copy 在CODE上查看代码片派生到我的代码片
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.tarena.note.dao">
</property>
MapperScannerConfigurer,让它扫描特定的包,自动帮我们成批地创建映射器。这样就大大减少了配置的工作量。
basePackage属性是让你为映射器接口文件设置基本的包路径。可以使用分号或逗号作为分隔符设置多于一个的包路径。每个映射器都会在指定的包路径中递归地被搜索到。被发现的映射器将会使用spring对自动侦测组件默认的命名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类名。如果发现了@Component或JSR-330@Named注解,它会获取名称。
5、在SpringBoot的xml文件中编写sql语句时字段名需要使用``括起来
不然会报错:
### The error may involve defaultParameterMap
### The error occurred while setting parameters
6、使用 JSONObject 类
是第三方类库,所以要导入、导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
使用:
编码:
public void testJson() {
JSONObject object = new JSONObject();
//string
object.put("string","string");
//int
object.put("int",2);
//boolean
object.put("boolean",true);
//array
List<Integer> integers = Arrays.asList(1,2,3);
object.put("list",integers);
//null
object.put("null",null);
System.out.println(object);
}
解码:
public void testJson2() {
JSONObject object = JSONObject
.parseObject("{"boolean":true,"string":"string","list":[1,2,3],"int":2}");
//string
String s = object.getString("string");
System.out.println(s);
//int
int i = object.getIntValue("int");
System.out.println(i);
//boolean
boolean b = object.getBooleanValue("boolean");
System.out.println(b);
//list
List<Integer> integers = JSON.parseArray(object.getJSONArray("list").toJSONString(),Integer.class);
integers.forEach(System.out::println);
//null
System.out.println(object.getString("null"));
}
7、解决跨域问题
跨域:Web的一种保护机制(同源策略限制),所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port。
所以当我们将前端代码与后端代码放在不同文件夹下且两者都开启运行后,它们拥有不同访问路径,例如:
前端:localhost: 8080
后端:localhost: 8081
此时使用Axios异步通信时就会失败、若只是前端传输基本数据,不传输cookie时,只需要在Springboot中设置一个配置类即可(注:我项目中前后端都配置了跨域才成功,可能和传输的数据有关)
也可以直接在controller层的方法上面加@CrossOrigin注解
@Configuration
public class CrosConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","HEAD","POST","DELETE","OPTIONS","PUT")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
如果需要传输cookie,则需要在vue中设置
在vue项目中config目录下的index.js文件中设置跨域
proxyTable: {
'/apis':{ // 当前端请求以这个开头时
target: 'http://localhost:8089',//这里是你的sprintboot项目中的后台接口
changeOrigin: true,//允许跨域
pathRewrite: {//重写路径
'^/apis':'/apis'//写'/apis'就相当于写'http://localhost:8089/apis'
}
}
8、前端传递json数据给后端
POST类型
前端
methods:{ // 这是vue中定义多个方法的关键字
insertPost(){// 定义一个方法,让点击按钮的响应函数为执行这个方法
this.$axios({
url:'api/blog/addBlog', // 地址要注意,我这是在前端配置了跨域的,真实访问地址为:http://localhost:8081/blog/addBlog
method:'post',
//发送格式为json
data:JSON.stringify( // stringfy是为了将json对象变成字符串
{
"id": "",
"title": "这是前端传入的",
"summary": "这是前端传入的这是前端传入的",
"content": "这是前端传入的这是前端传入的这是前端传入的",
"userId": "1",
"audit": "0",
"like": "99",
"releaseTime": new Date(), // 获取当前时间
"mtime": "",
"browse": "33333",
"top": (Math.random()*100) % 2,
"tag": "前端传入"
}),
headers: {'Content-Type': 'application/json'}// 重要,设置传递的数据为json格式
}).then(rep=>{
console.log(rep.data)
}
)
}
}
后端接收
@RequestBody
注解表示会匹配传递过来的json数据,参数进行匹配,即与Blog中的成员变量匹配并调用set方法进行值注入。
// 控制层进行传输
@RestController//使用的效果是将方法返回的对象直接在浏览器上展示成json格式.
@RequestMapping("/blog")
public class BlogController {
// 插入一篇博客,添加
@PostMapping("/addBlog")
public int insertBlog(@RequestBody Blog blog) {
System.out.println("插入了博客"+(i++));
System.out.println(blog);
// 调用插入数据库的方法
return blogService.insertBlog(blog);
}
}
9、后端传递json给前端
前端使用get请求,因为是后端给前端数据,前端不需要传递数据
后端
// 控制层进行传输
@RestController//使用的效果是将方法返回的对象直接在浏览器上展示成json格式.
@RequestMapping("/blog")
public class BlogController {
// 查询所有博客
@GetMapping("/queryBlog")
public JSONObject selectBlog() {
System.out.println("查询了博客"+(i++));
// 查询数据库并接收数据
List<Blog> blogs = blogService.selectBlog();
// 生成json数据并返回
JsonData jsonData = new JsonData(new JSONObject());
// 放入数据:
return jsonData.json(State.success,blogs);
}
}
后端会传递json的格式
"blog_message": 200,
"blog_data": [
{
"id": "",
"title": "",
"summary": "",
"content": "",
"userId": "",
"audit": "",
"like": "",
"releaseTime": "",
"mtime": "",
"browse": "",
"top": "",
"tag": ""
}
前端
1、先准备一个接收的模板,除了data1的名字可以改以外,剩下的名字要严格与传递过来的json数据的key值一样,注意,下面的所有变量值不能为 null,你可以设置值,或者像我一样,但是不能这样写:id:null,尽量不要这样写,有可能会出错,不是一定会出错。
data(){
return{
data1:{
blog_message:"",
blog_data:[
{
id:"",
title: "",
summary:"",
content:"",
userId: "",
audit:"",
like:"",
releaseTime:""
mtime:"",
browse: "",
top: "",
tag:""
}
]
}
}
}
2、使用Axios接收
// 钩子函数
mounted() {
this.$axios.get('api/blog/queryBlog')
.then(response=>{
this.data1 = response.data.blog_data // 因为我们只需要blog_data里面的数据,所以blog_message数据没取
console.log(this.data1)
});
},
11、使用get方式传递变量值给后端
参考文档:3-6.restFul传递方式/Axios_冥丿城的博客-CSDN博客_axios restful风格传参
1.get 方式传值有两种方法
第一种 restFul 风格:http://localhost:8081/blog/selectIdBlog/1
第二种 正常 :http://localhost:8081/blog/selectIdBlog?id=1
第一种:restFul 风格
前端
注:一定要记得将url地址标记起来的不是单引号 ‘’ 而是 ``、搞错了就无法传递变量,会当成字符串处理,然后记得将变量类型与后端接收的一致
注:传入的变量类型一定要转换,parseInt
是转换为int,password.toString()
是转换为字符串
a(){
// 通过id查询文章并显示
// 需要将id从字符串形式变为int: parseInt()
this.$axios.get(`http://localhost:8081/blog/selectIdBlog/${parseInt(this.$route.params.id)}`).then(
rep=>{
console.log(rep.data)
}
)
}
后端
// 通过 id 查询博客
@GetMapping("/blog/selectIdBlog/{id}")
public Blog selectIdBlog(@PathVariable int id) {
System.out.println("查询的id为:"+id+"次数:"+this.i);
return blogService.selectIdBlog(id);
}
最终url为(假设id为1)http://localhost:8081/blog/selectIdBlog/1
第二种:普通风格
前端
// 通过id查询文章并显示
this.$axios.get('http://localhost:8081/blog/selectIdBlog',{
params:{
id:this.$route.params.id //假设id值为1
}
}).then(
rep=>{
console.log(rep.data)
}
)
最终的url是:http://localhost:8081/blog/selectIdBlog?id=1
后端
// 通过 id 查询博客
@GetMapping("/selectIdBlog") // 这个参数是 required 确定在 API 中的参数中是否必须要输出参数。
public Blog selectIdBlog(@RequestParam(value = "id",required = true) int id) {
System.out.println("查询的id为:"+id+"次数:"+this.i);
return blogService.selectIdBlog(id);
}
10. 前后端传值
掌握前后端传值的正确姿势,是你开始 CRUD 的第一步!
1. @PathVariable
和 @RequestParam
@PathVariable
用于获取路径参数,@RequestParam
用于获取查询参数。
举个简单的例子:
@GetMapping("/klasses/{klassId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
@PathVariable("klassId") Long klassId,
@RequestParam(value = "type", required = false) String type ) {
...
}
如果我们请求的 url 是:/klasses/123456/teachers?type=web
那么我们服务获取到的数据就是:klassId=123456,type=web
。
2. @RequestBody
用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter
或者自定义的HttpMessageConverter
将请求的 body 中的 json 字符串转换为 java 对象。
我用一个简单的例子来给演示一下基本使用!
我们有一个注册的接口:
@PostMapping("/sign-up")
public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {
userService.save(userRegisterRequest);
return ResponseEntity.ok().build();
}
UserRegisterRequest
对象:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterRequest {
@NotBlank
private String userName;
@NotBlank
private String password;
@NotBlank
private String fullName;
}
我们发送 post 请求到这个接口,并且 body 携带 JSON 数据:
{"userName":"coder","fullName":"shuangkou","password":"123456"}
这样我们的后端就可以直接把 json 格式的数据映射到我们的 UserRegisterRequest
类上。
???? 需要注意的是:一个请求方法只可以有一个@RequestBody
,但是可以有多个@RequestParam
和@PathVariable
。 如果你的方法必须要用两个 @RequestBody
来接受数据的话,大概率是你的数据库设计或者系统设计出问题了!
12、路由间传参 id
问题:我要传递id到另一个路由让另一个页面可以根据id查询指定网站
路由传参分为 params 传参与 query 传参,params 传参类似于网络请求中的 post 请求,params 传过去的参数不会显示在地址栏中(但是不能刷新)。params 只能配合 name 使用,如果提供了 path,params 会失效。query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(?name=xx)。query 较为灵活既可以配合 path 使用,也能配合 name 使用(亲测可用)。
name是什么呢?name 是配置路由时给 path 取的别名,方便使用。但要注意的是 “地址栏显示的路径始终是 path 的值”,如图定义路由
有两种方法:
方式一:通过 params 传参
路由处定义:
{ path: '/blogContent', name: 'blogContent', component: Blog_content}
页面一:声明式:router-link to 以对象形式跳转
<router-link :to="{name:'home',params:{name:'路由传参'}}">跳转传递参数</router-link>
页面二:接收用 this.$route.params.+传过来的“name”
console.log('接收到了传过来的值', this.$route.params.name)
通过 params 传参有一个缺点,就是刷新页面就获取不到参数了。
方式二:通过 query 传参(推荐)
路由处定义和上面一样、
页面一:声明式:router-link to 以对象形式跳转
<router-link :to="{name:'home',query:{name:'query路由传参'}}">query路由传参</router-link>
页面二:接收用 this.$route.query.+传过来的“name”
console.log('接收到了传过来的值', this.$route.query.name)
实例:我用for遍历出若干条文章,每条文章有个标签设置,查看文章详情的链接,链接携带文章id跳转到另一个路由,另一个路由通过id在钩子函数中使用axios查询文章全部内容。
路由处:
{ path: '/blogContent', name: 'blogContent', component: Blog_content},
for循环的页面(即包含跳转链接的页面)注:要使用 :to,而不是 to
!--阅读全文 先跳转到详情页并附带id信息,然后详情页去获取文章-->
<router-link :to="{name:'blogContent',query:{id:i.id}}" >阅读全文 »</router-link>
路由跳转的页面:使用this.$route.query.id
拿到数据
mounted() {
// 显示文章
console.log(this.$route.query.id)
this.$axios.get(`http://localhost:8081/blog/selectIdBlog/${parseInt(this.$route.query.id)}`).then(
rep=>{
console.log(rep.data)
}
)
}
可能报错:Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead.
报错代码:我们这里想去 id 的值、但是 to 标签前面没加 :
<router-link to="{{i.id}}" >阅读全文 »</router-link>
正确形式:
<router-link :to="{{i.id}}" >阅读全文 »</router-link>
原因:
报Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead.这样的错误的原因是系统在提示我们属性内的插值已被删除,要改用v-bind或者冒号来进行简写,因此在用到插值表达式的属性前加上:和将{{}}去掉即可,同时也了解了在Vue里面标签的属性中不能直接使用插值表达式。
链式编程
1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
vue中加 冒号:和不加 冒号 有什么区别?
vue中冒号:是v-bind的缩写。加冒号的,说明后面的是一个变量或者表达式;没加冒号的后面就是对应的字符串字面量!
13、使用markdown插件
注:此插件还是有点难用,除非你对样式没有要求,而且目录大概率是要自己写出来的
插件地址:
mavonEditor
1、安装插件
cnpm install mavon-editor --save
2、全局安装,在main.js文件中写入如下代码
// 全局注册
// import with ES6
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
// use
Vue.use(mavonEditor)
3、显示,新定义一个vue的组件,在组件中编写如下内容,则是完成基本的导入
<div>
<mavon-editor v-model="value"/>
</div>
export default {
name: 'markdown',
data(){
return{
value:'博客内容', // 存放你想输出的博客
}
}
注:此插件默认样式为 z-index: 1500 min-height: 300px , min-width: 300px
4、如上操作是边写边展示写的内容,如果仅仅是展示,需要重新设置配置
5、设置配置
-
可以看到,我使用双向绑定的方法去设置配置,所以配置需要在标签中设置,
-
配置标签有的前面使用
:
是为了告诉vue等号后面的值是变量,这样我们可赋值的类型就是任意,如果没有冒号则默认为字符串 -
如下配置并不能做到只展示的效果,可自行修改,此处只作为例子展示
全部代码为:
<template>
<div >
<mavon-editor
// 真正设置配置的地方
v-model="value"
:navigation=navigation
:subfield=subfield
:defaultOpen=defaultOpen
:placeholder=placeholder
:editable=editable
:codeStyle=codeStyle
:toolbarsFlag=toolbarsFlag
:shortCut=shortCut
:autofocus=autofocus
:ishljs=ishljs
style="z-index: 0; min-height: 90vh;width: 100%;bottom: 10px;height: 100%; top: 10px;"
/>
</div>
</template>
<script>
export default {
name: 'markdown',
data(){
return{
value:'是是是', // 存放你想输出的博客
navigation:true, // 要不要显示目录
subfield:true, // true: 双栏(编辑预览同屏), false: 单栏(编辑预览分屏(通过小眼睛切换))
defaultOpen:'', // edit: 默认展示编辑区域 , preview: 默认展示预览区域 , 其他 = edit
placeholder:'开始编辑', // 输入框为空时默认提示文本
editable: true , // 是否允许编辑
codeStyle:'code-github' , //markdown样式: 默认github, 可选配色方案
toolbarsFlag:true, //工具栏是否显示
shortCut:true , //是否启用快捷键
autofocus:true, //自动聚焦到文本框
ishljs:true, // 代码高亮
toolbars: { // 工具栏
bold: true, // 粗体
italic: true, // 斜体
header: true, // 标题
underline: true, // 下划线
strikethrough: true, // 中划线
mark: true, // 标记
superscript: true, // 上角标
subscript: true, // 下角标
quote: true, // 引用
ol: true, // 有序列表
ul: true, // 无序列表
link: true, // 链接
imagelink: true, // 图片链接
code: true, // code
table: true, // 表格
fullscreen: true, // 全屏编辑
readmodel: true, // 沉浸式阅读
htmlcode: true, // 展示html源码
help: true, // 帮助
/* 1.3.5 */
undo: true, // 上一步
redo: true, // 下一步
trash: true, // 清空
save: true, // 保存(触发events中的save事件)
/* 1.4.2 */
navigation: true, // 导航目录
/* 2.1.8 */
alignleft: true, // 左对齐
aligncenter: true, // 居中
alignright: true, // 右对齐
/* 2.2.1 */
subfield: true, // 单双栏模式
preview: true, // 预览
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
API 文档
props
name 名称 | type 类型 | default 默认值 | describe 描述 |
---|---|---|---|
value | String | 初始值 | |
language | String | cn | 语言选择,暂支持 cn: 中文简体 , en: 英文 , fr: 法语 |
scrollStyle | Boolean | true | 开启滚动条样式(暂时仅支持chrome) |
subfield | Boolean | true | true: 双栏(编辑预览同屏), false: 单栏(编辑预览分屏) |
default_open | String | edit: 默认展示编辑区域 , preview: 默认展示预览区域 , 其他 = edit | |
placeholder | String | 开始编辑… | 输入框为空时默认提示文本 |
editable | Boolean | true | 是否允许编辑 |
code_style | String | code-github | markdown样式: 默认github, 可选配色方案 |
toolbarsFlag | Boolean | true | 工具栏是否显示 |
toolbars | Object | 如下例 | 工具栏 |
ishljs | Boolean | true | 代码高亮 |
image_filter | function | null | 图片过滤函数,参数为一个File Object ,要求返回一个Boolean , true 表示文件合法,false 表示文件不合法 |
events
name 方法名 | params 参数 | describe 描述 |
---|---|---|
change | String: value , String: render | 编辑区发生变化的回调事件(render: value 经过markdown解析后的结果) |
save | String: value , String: render | ctrl + s 的回调事件(保存按键,同样触发该回调) |
fullscreen | Boolean: status , String: value | 切换全屏编辑的回调事件(boolean: 全屏开启状态) |
readmodel | Boolean: status , String: value | 切换沉浸式阅读的回调事件(boolean: 阅读开启状态) |
htmlcode | Boolean: status , String: value | 查看html源码的回调事件(boolean: 源码开启状态) |
subfieldtoggle | Boolean: status , String: value | 切换单双栏编辑的回调事件(boolean: 双栏开启状态) |
previewtoggle | Boolean: status , String: value | 切换预览编辑的回调事件(boolean: 预览开启状态) |
helptoggle | Boolean: status , String: value | 查看帮助的回调事件(boolean: 帮助开启状态) |
navigationtoggle | Boolean: status , String: value | 切换导航目录的回调事件(boolean: 导航开启状态) |
imgAdd | String: filename, File: imgfile | 图片文件添加回调事件(filename: 写在md中的文件名, File: File Object) |
imgDel | String: filename | 图片文件删除回调事件(filename: 写在md中的文件名) |
如果想上传图片
这个功能慢慢开发,想实现你导入一个markdown文件和存放图片的文件夹,它自动上传文件和图片,这样就不需要一个个将图片地址重新写,虽然有图床也可以如此,但不想。
14、你使用的是 true、但是当做字符串类型
报错说无效的命名数据:“数据”类型检查失败。而且上面说了是Boolean类型错误,要把值改成"true"
这里发现使用的值是"true",但是他执行时是按照String类型执行,而不是Boolean去执行的
解决方法:加 :
,即让那个标签的值是当做变量而不是字符串
<mavon-editor
:navigation=true // 只有加 : 后 true 才会被当做 布尔型 使用
/>
15、在父组件中传值给子组件
既能传值也可以将值赋值给子组件中的变量
父组件: 在要使用的子组件上定义一个 :information=2
、information
和等于号后面的值自由设置
<template>
<markdownLook :information=this.data1.content></markdownLook>
</template>
<script>
import markdownLook from "../components/markdownLook"; // 先导入子组件
components: {
// 注册组件以使用
,markdownLook
},
</script>
子组件
<template>
<div>{{information}}</div>
</template>
export default {
name: "markdownLook",
props: ['information']
}
16、处理 mavon-editor 插件的边框、阴影、背景颜色
1、因为我们使用的插件在展示时默认是白色背景,有白色边框以及灰色的工具栏,还有阴影。我们不需要这些
解决方法:
- 阴影有它提供的参数去设置
boxShadow:false , // 去除背景阴影
boxShadowStyle:"", // 边框阴影样式
- 背景颜色、边框
就只能在 <mavon-editor>
标签里面重新设置背景颜色,可以设置和你插入的背景颜色一样、边框设置为 border: none;
注:设置背景颜色时需要把插件的两个参数设置为空(最好)
toolbarsBackground:"" ,//工具栏背景颜色
previewBackground:"" , //预览框背景颜色
17、JavaScript的三种弹出提示框(alert、confirm、prompt)
链接:JavaScript的三种弹出提示框(alert、confirm、prompt)_牛哄哄的柯南的博客-CSDN博客_alert()的功能
- alert () :正常弹
- confirm()
- prompt ()
alert ()
alert()方法是显示一条弹出提示消息和确认按钮的警告框。
需要注意的是 :alert()是一个阻塞的函数,如果我们不点确认按钮,后面的内容就不会加载出来。
alert("想要提示的文本内容")
2、confirm()
confirm()方法是显示一个含有指定消息和确认和取消按钮的确认框。
如果点击"确定"返回true,否则返回false。
使用方式:
不接收返回值:
confirm("这样写可以直接显示,不接收返回值。")接收返回值:
接收返回值:
var x;
var r=confirm("请按下按钮!");
if (r==true){
x="你按下的是"确定"按钮。";
}
else{
x="你按下的是"取消"按钮。";
}
document.write(x)
3、prompt ()
prompt()方法是显示提示用户进行输入的对话框。
这个方法返回的是用户输入的字符串。
使用方式:
不显示默认文本:
prompt("开心吗?"); // 这个显示内容也可以不写,但就没有交互的意义了。
显示默认文本:
var x;
var name=prompt("请输入你的名字","Keafmd");
if (name!=null && person!=""){
x="你好! " + name + "。";
document.write(x)
}
18、数据库字段与成员变量名不一致
在写sql语句时对字段起别名,别名与成员变量一致
19、设置vue全局变量
1、定义一个js文件:url.js
export default {
URL: "http://localhost:8081/"
}
2、在main.js中注册
import url from "./url";
Vue.prototype.$URL=url // 定义全局变量url
3、在其他组件中使用 this.$URL.URL
this.$axios.get( this.$URL.URL +`user/selectUserName/${name.toString()}`)
20、使用Axios进行同步请求
axios本身是不能同步请求的,这里需要借助:用到的是ES7的async
和await
async
用于声明一个函数是异步的,await
用于声明在一个异步函数中等待语句执行完毕。也就是说await
只能在async
函数中使用。
注意事项
如果同步请求是封装在其他函数中,那么每一个函数都需要做成异步函数,示例如下:
Vue调用的是fun3,但是因为中间做了几次判断,最终在fun1中进行请求,那么涉及到的每一个函数都需要异步化
示例:
methods: {
// 此处使用async
async zc(){
// 注册
var name=prompt("请输入用户名与密码,两者用英文 `,` 逗号分隔","root,1");
if (name!=null){
// 使用axios验证用户名是否被注册了
var str = name.split(',');
// 此处使用await
await this.yz(str[0]);
if (this.flag) return alert("抱歉,用户名已存在!");
else {
// 执行注册程序
return alert("用户名不存在!");
}
}
},
// 此处使用async
async yz(name){
// 验证用户名是否被注册this.$URL.URL+
// 此处使用await
await this.$axios.get(this.$URL.URL+`user/selectUserName/${name.toString()}`)
.then(rep=>{
this.flag = rep.data === 1; // 如果存在则赋值为true,反之false
console.log(this.flag)
console.log(rep.data)
})
}
}
21、通过js操作dom
1、获取指定id的标签并修改样式
// 通过id获取最终输入的标签
document.getElementById('fb').setAttribute('style','opacity:1;');
2、获取输入框的value值
var password = document.getElementById('password').value;
22、使用@PathVariable
接收多个参数
后端
// 验证密码、返回用户信息
@GetMapping("/selectUserPwd/{name}/{pwd}")
public JSONObject selectUserPwd(@PathVariable("name") String name,@PathVariable("pwd") String pwd) {}
前端:注意调用全局变量的方式this.$URL.URL
,注意url拼接时不要少或者多 /
注意变量格式转换,注意变量拼接
this.$axios.get(this.$URL.URL+'user/selectUserPwd/'+username.toString()+'/'+password.toString())
.then(rep=>{
alert(rep.data.message)
console.log(rep.data)
})
23、MyBatis 查询用户名为空时返回 null,
24、springboot的视图层获取请求头
有点小问题,没成功
1、HttpServletRequest
@GetMapping("/getParameter")
String getString(HttpServletRequest request) {
//获取一个参数
String name=request.getParameter("appId");
//获取所有参数
Map map=request.getParameterMap();
return name;
}
2.@RequestHeader
@GetMapping("/getParameter")
public String test(@RequestHeader("appId") String appId) {
return appId;
}
3.RequestContextHolder的使用
/两个方法在没有使用JSF的项目中是没有区别的
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
//RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
String appId = request.getHeader("appId");
//从session里面获取对应的值
String str = (String) requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION);
25、console.log()输出问题
//这样括号只有变量时,如果变量是键值对形式(JSON)数据时会以对象形式输出全部数据
console.log(this.data1.message)
// 如果是这样、它默认会将变量变成字符串,也就是说如果变量存储的是键值对(或者JSON)形式的数据,它是直接显示[Object,Object],即使有数据
// 除非变量是字符串,才会正常输出
console.log(""+this.data1.message)
26、论vue的全局变量与token
vue 设置的全局变量作用域是整个会话,简而言之,访问网页后,你只通过网页按钮或者链接去进行跳转那么全局变量的值是一直存在的。
但是,如果你点击网页的刷新按钮或者重新回车访问某网页后、全局变量的值就会恢复为初始值
但是token一般存储在cookie里面,是本地化存储,即使你刷新了我还可以再获取
27、本地化存储数据
第一种使用cookie
使用 js-cookie 这个插件,相当于使用cookie、具体使用自行百度
第二种使用:localStorage
- 是永久化存储,除非手动删除
- 本质上是保存字符串,如果需要保存JSON则需另外操作一下
- 一般的浏览器能存储的是5MB左右。
- localStorage作用域是协议、主机名、端口。(理论上,不人为的删除,一直存在设备中)
- localStorage是window上的。所以不需要写this.localStorage,vue中如果写this,是指vue实例。会报错
保存:
//对象
const info = { name: 'hou', age: 24, id: '001'};
//字符串
const str="haha";
localStorage.setItem('hou', JSON.stringify(info));
localStorage.setItem('zheng', str);
============================================
获取
var data1 = JSON.parse(localStorage.getItem('hou'));
var data2 = localStorage.getItem('zheng');
====================
删除
//删除某个
localStorage.removeItem('hou');
//删除所有
localStorage.clear();
=============================
更改
和保存一样
=============
监听
Storage 发生变化(增加、更新、删除)时的 触发,同一个页面发生的改变不会触发,只会监听同一域名下其他页面改变 Storage
window.addEventListener('storage',function(e) {
console.log('key', e.key);
console.log('oldValue', e.oldValue);
console.log('newValue', e.newValue);
console.log('url', e.url);})
=====================
判断是否存在localStorage
if(localStorage.length>0){// 长度大于0代表有东西,但不一定是我们需要的
var mydata = localStorage.getItem('projectId'); // 获取我们需要的
if(mydata!=null){// 判断我们需要的是不是空的
...
}
}
28、刷新页面 vue
vue 中一般是组件跳转,是不会重新刷新页面的,如果我们执行添加操作后想重新访问页面已刷新数据就需要用到
// 跳转到首页
this.$router.push('/')
// 跳转后会刷新当前页
this.$router.go(0)
29、按钮背景透明
#mybutton{
background:transparent; /*按钮背景透明*/
border-width:0px; /*边框透明*/
outline:none; /*点击后没边框*/
}
30、边框设置
属性:border,可以为元素这是上右下左四条边框(顺序很重要)
取值有三个:
1.border-width:边框宽度,默认3px,可以手动设置,取像素值
2.border-style:边框样式,必填项。可取: solid (实线边框) dashed(虚线边框) dotted(点线边框) double(双线边框)
3.border-color:边框颜色,取颜色值,默认黑色
31.圆角设置
设置一个值:
border-radius:10px;
设置四个值:
border-radius: 左上 右上 右下 左下;
设置两个值:
border-radius:左上右上 右下左下;
32、空格
33、获取屏幕参数
网页可见区域宽:document.body.clientWidth
网页可见区域高:document.body.clientHeight
网页可见区域宽:document.body.offsetWidth (包括边线的宽)
网页可见区域高:document.body.offsetHeight (包括边线的宽)
网页正文全文宽:document.body.scrollWidth
网页正文全文高:document.body.scrollHeight
网页被卷去的高:document.body.scrollTop
网页被卷去的左:document.body.scrollLeft
网页正文部分上:window.screenTop
网页正文部分左:window.screenLeft
屏幕分辨率的高:window.screen.height
屏幕分辨率的宽:window.screen.width
屏幕可用工作区高度:window.screen.availHeight
屏幕可用工作区宽度:window.screen.availWidth
HTML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth
scrollHeight: 获取对象的滚动高度。
scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离
scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
scrollWidth:获取对象的滚动宽度
offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度
offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置
offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置
event.clientX 相对文档的水平座标
event.clientY 相对文档的垂直座标
event.offsetX 相对容器的水平坐标
event.offsetY 相对容器的垂直坐标
document.documentElement.scrollTop 垂直方向滚动的值
event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
IE,FireFox 差异如下:
IE6.0、FF1.06+:
clientWidth = width + padding
clientHeight = height + padding
offsetWidth = width + padding + border
offsetHeight = height + padding + border
IE5.0/5.5:
clientWidth = width - border
clientHeight = height - border
offsetWidth = width
offsetHeight = height
(需要提一下:CSS中的margin属性,与clientWidth、offsetWidth、clientHeight、offsetHeight均无关)
网页可见区域宽: document.body.clientWidth
网页可见区域高: document.body.clientHeight
网页可见区域宽: document.body.offsetWidth (包括边线的宽)
网页可见区域高: document.body.offsetHeight (包括边线的高)
网页正文全文宽: document.body.scrollWidth
网页正文全文高: document.body.scrollHeight
网页被卷去的高: document.body.scrollTop
网页被卷去的左: document.body.scrollLeft
网页正文部分上: window.screenTop
网页正文部分左: window.screenLeft
屏幕分辨率的高: window.screen.height
屏幕分辨率的宽: window.screen.width
屏幕可用工作区高度: window.screen.availHeight
屏幕可用工作区宽度: window.screen.availWidth
34、回到顶部函数
调用 clickQuotation
就行
var heightFlag = false//全局变量,如果是在data()return{}里面定义,可以设置成 heightFlag:false
clickQuotation() {
if (this.heightFlag) return
const orangeHeight = window.pageYOffset//点击事件后,页面移动至页面左上角的距离,现在为0(顶部);const orangeHeight = window.pageYOffset-100(指距顶部下移100)
//
let i = 0
this.heightFlag = true
this.interval = setInterval(() => {
const next = Math.floor(this.heightChange(10 * i, orangeHeight, -orangeHeight, 500))
if (next <= 0) {
window.scrollTo(0, 0)//第二个0表示滚动距离
clearInterval(this.interval)
this.heightFlag = false
} else {
window.scrollTo(0, next)
}
i++
}, 16.7)
},
heightChange(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b
return -c / 2 * (--t * (t - 2) - 1) + b
}
35、分页
注:此处分页采用了分页优化,即不直接使用limit而是使用子查询获取需要数据中的开头一条id、然后再通过此id进行分页,具体后面有详细介绍
前端
前端无非是传递页面值给后端,后端根据页数进行查询。
<!--上一页,下一页按钮-->
<div style="margin-left: 75%;color: #93999f;font-size: 15px;" >
<button @click="previous" style="background:transparent; border-width:1px;border-style:solid;border-color: #93999f;color: #93999f;font-size: 15px;border-radius:3px;">上一页</button>
{{paging}}
<button @click="next" style="background:transparent; border-width:1px;border-style:solid;border-color: #93999f;color: #93999f;font-size: 15px;border-radius:3px;">下一页</button>
</div>
export default{
data(){
return{
paging:1 // 分页参数
}
},
methods:{
previous(){
// 上一页
if (this.paging<=1){
alert("大哥,后退不了了吧")
}else {
// 如果有上一页,就将分页变量的值减一
this.paging=this.paging-1
// 将减一后的分页变量作为参数传递给后端,后端根据变量去查询
this.$axios.get(this.$URL.URL+'blog/queryBlog/'+this.paging)
.then(rep=>{
// 接收数据
this.data1 = rep.data
});
}
},
next(){
// 下一页
// 因为下一页不一定有,所以我们先查,如果有再下一步操作,没有就提醒
this.$axios.get(this.$URL.URL+'blog/queryBlog/'+(this.paging+1))
.then(rep=>{
// 先判断有没有数据,如果有数据,length的值是大于0的
if (rep.data.data.length!==0){
this.paging=this.paging+1
this.data1 = rep.data
}else {
alert("真的没有的,求放过!")
}
});
},
}
}
后端
1.sql语句为
<!--每次只查询10条数据,会传入一个paging变量,表示当它为1时,表示查询第一条到第10条,为2代表第11到20条-->
<!--如果是 2 就先通过where=10找到第 10 条数据,然后通过limit分页去取后面10条,这样大大提高性能-->
<!--显示数据库查询结果的从第一条到第五条的数据,0:代表初始的位置,5:代表每次显示的条数-->
<select id="selectBlog" resultType="Blog" parameterType="int">
select
`id`,`title`,`summary`,`user_id` as userId,`audit`,`like`,`release_time` as releaseTime,`mtime`,`browse`,`top`,`tag`
from blog
where id>=#{paging}
LIMIT 0,10
</select>
意思是:比如说要查询第11条到20条这10条数据,那么我们先通过where语句定位到第11条(即id>=11,注:这里之所以是大于,是有可能因为删除过数据的原因,表中的id会缺少,如你删除的id为 10 的数据后,那么你where语句还用等于号时就会出错),我们定位到第11条后再通过limit去之后的10条
因此,但前端传递的是页数,不是id值,所以我们需要修改一下:
当我们查询第1到10条时,id应该为1,
当我们查询第11到20条时,id应该为11,
所以页数需要通过如下操作变成合适的id值传入:((页数-1)*10)+1
为什么要这么做?而不是直接使用limit查询?
原因:
分页优化:如查询出50条数据,显示第40到50条的数据,Mysql分页是获取 前40条+设定要显示的条数(即10),然后再抛弃前40条后去返回40到50条的数据。(即Mysql没有跳过前40行的数据)这样当数据特别大时,无疑会照成更多开销,
为什么会多开销?因为查询了很多不需要的列,且这些列大概率是回表查询(列字段不是索引情况下),即前40条的列都要查询,那么有可能都要回表,所以会浪费时间
解决方法就是通过子查询获取到偏移位置的 id 即第40条,然后只取10条且 limit
参数为0 10
,这样就只有 id 列被查询了而且大概率不会回表,id 一般都是会建立索引。
例:子查询会查询第100000条
注:使用*
代表所有列会导致不能使用索引,尽量写出所有字段
例1:
select * from orders_history where type=8 and
id>=(select id from orders_history where type=8 limit 100000,1)
limit 100;
例2:
SELECT `id`,`title`,`audit` FROM blog WHERE audit=1 AND
id>=(SELECT id FROM blog WHERE audit=1 LIMIT 1,1) // 子查询是查询第一个符号条件的id
LIMIT 10;
后端视图层代码
// 查询所有博客
@GetMapping("/blog/queryBlog/{paging}")
public JSONObject selectBlog(@PathVariable int paging) {
//因为我们传递的paging是页数,真正要查询的起始位置是((页数-1)*10)+1,所以我们传递的参数要改一下
// 查询数据库并接收数据
List<Blog> blogs = blogService.selectBlog(((paging-1)*10)+1);
// 生成json数据并返回
// 放入数据:
return JsonData.json(jsonObject,"查询成功!共有"+blogs.size()+"条数据",blogs,State.success);
}
36、myBatis 多参数查询
在我们定义mapper接收中的方法时使用:zhuti
为对象,另一个是字符串
int updateByIdNew(@Param("zhu")Zhuti zhuti,@Param("zhuti") String zhutiName);
使用:
<update id="updateByIdNew">
update ${zhuti}
set
credit_level = #{zhu.creditLevel},zhuti_update = #{zhu.zhutiUpdate}
where zhuti_id = #{zhu.zhutiId}
</update>
38、vue,将id传入按钮的响应函数
定义按钮
<vs-button danger @click="sc(1)" style="display: inline-block;">删除</vs-button>
定义方法:此时id的值为1
sc(id){
console.log(id)
}
39、让标签使用绝对定位, bottom不生效问题
position:fixed; // 一定要结合这个一起使用
bottom:0;
40、在mybatis中使用like
参考文档:Mybatis中Like 的使用方式以及一些注意点 - 阿飞云 - 博客园 (cnblogs.com)
方式1:
<!--有sql注入问题-->
这种会有sql注入的问题,需要明白在 Mybatis中 $ 和 # 使用的区别。这种写法也不能加jdbcType=VARCHAR,否则也会报错。
<select id="findUserByLikeName1" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like '%${name}%'
</select>
注意:排序的字段也容易出现这个问题,在使用的时候也一定要注意。
order by ${orderBy}
第一种方式在实际开发过程中千万要注意,不要写成这样了。
方式2:
<!--直接在代码中拼接%, 不存在sql注入-->
<select id="findUserByLikeName2" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like #{name,jdbcType=VARCHAR}
</select>
在代码中加 %
@Test
public void findUserByLikeName2(){
String name = "Cloud";
List<User> test = userMapper.findUserByLikeName2("%" +name+"%");
// select * from t_user where name like ?
// %Cloud%(String)
System.out.println(test.size());
}
这种方式在一些项目中也会看到。如果没有使用如Mybatis等ORM框架,直接写sql查询就这样拼接了。
方式3:
<!--concat Mysql和 Oracle区别 ,不存在sql注入-->
<select id="findUserByLikeName3" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%')
</select>
小注意
当使用方式三的时候,如果查询的关键字就是%
,那情况会是什么? 初始化数据中name
有9条数据中包含%
。
查询的sql如下:
select * from t_user where name like concat(‘%’,‘%’,‘%’)
查出来全部的数据,并不是只包含了%
的数据,如果查询_
也是一样的。
那这种情况肯定是不满足查询需求的,则需要调整。
①在代码中进行转义
@Test
public void findUserByLikeName3(){
String name = "%";
name = name.replaceAll("_", "\\_");
name = name.replaceAll("%", "\\%");
List<User> test = userMapper.findUserByLikeName3(name);
System.out.println(test.size());
}
②使用ESCAPE
<select id="findUserByLikeName4" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%') ESCAPE '/'
</select>
小总结
1、不要写方式1的这种模糊查询,容易发生sql注入!
建议使用第三种方式进行模糊查询
2、上面这三种模糊查询,都是使用%关键字%
,这种方式是不会走索引的,大数据量时候有查询效率问题
看情况,可以使用全文索引;或者使用ES进行
说明:网上有一些优化like的查询的,但是亲测后没啥用
3、注意关键词中有%、_这些特殊字符如何处理。
1、业务上不允许输入这些字符,直接过滤(前台、后台过滤)
2、使用上面的ESCAPE或者转义
41.vue中的v-if里面使用全局变量或者return里面定义的变量时出错
1、出错代码
<span v-if='this.$user.user.data.id' >我的</span> // this.$user.user.data.id:是全局变量
<span v-if='this.id' >我的</span> // this.id:是return定义的变量
使用上述if等号后面的变量进行判断时会报:变量未定义的错误:
TypeError: Cannot read properties of undefined (reading '$user')
需要改成:即不需要前面的this,不知道为啥
<span v-if='$user.user.data.id' >我的</span>
<span v-if='id' >我的</span> // this.id:是return定义的变量
42.增加正计时/倒计时
参考:js倒计时,正计时_Billow_lamb的博客-CSDN博客_js 正计时
// 倒计时
<div id="box"></div>
<script>
function counDownTime() {
let a = '2021-2-26 00:00:00'
let s = new Date(a).getTime()
let timeDtart = new Date().getTime()
let start = s - timeDtart
//3600 代表1个小时60分钟 1分60秒
let day = Math.floor(start / (3600 * 24 *1000))
let shi = Math.floor(start / (1000 * 3600) % 24) // 取余一天24小时
let fen = Math.floor(start / (1000 * 30) % 60) // 取余一小时60分钟
let m = Math.floor(start / (1000 ) % 60)// 取余一分钟60秒
return day + "天" + shi + ":" + fen + ":" + m
}
setInterval(()=>{
document.getElementById('box').innerHTML = counDownTime()
},1000)
</script>
// 正计时
<div id="box1"></div>
<script>
function timingTime(){
let start = '2021-2-24 10:00:00'
let startTime = new Date(start).getTime()
let currentTime = new Date().getTime()
let difference = currentTime - startTime
let m = Math.floor(difference / (1000))
let mm = m % 60 // 秒
let f = Math.floor(m / 60)
let ff = f % 60 // 分钟
let s = Math.floor(f/ 60) // 小时
let ss = s % 24
let day = Math.floor(s / 24 ) // 天数
return day + "天" + ss + "时" + ff + "分" + mm +'秒'
}
setInterval(()=>{
document.getElementById('box1').innerHTML = timingTime()
},1000)
</script>
43、Cannot set properties of null (setting ‘innerHTML’) 错误
每次报错弄不出来,然后也没有管他,只是换用别的方法,这一次终于是忍不住了
错误原因为document中的innerHTML为空,也就是说在加载js文件时,找不到其中调用的对象
因此需要将js文件引用放置在调用的对象的后面加载就可以了
但是,在vue项目中,有时候却不方便这样处理
所以只需要:
if(proportionUpdate){
proportionUpdate.innerHTML = arrUpdate.length + '/3'
}
44、插入数据时数据库编码出错Incorrect string value: ‘xF0x9Fx91x89 xE9…’ for column ‘content’ at row 1
通常情况,Mysql数据编码格式为“utf-8”,对于汉字来说足够;Mysql中utf8占3个字节,但是,3个字节对于表情符号是不够的,需4个字节;此时使用utf8,会出现‘xF0x9Fx8Dx83xF0x9F’的问题。
解决方法:
utf8mb4编码是utf8编码的超集,兼容utf8,并且能存储4字节的表情字符。
核对选择:utf8mb4_general_ci
在sqlyong中取消勾选隐藏语言选项就能对单个字段设置编码
云服务器
至此、程序开发完成一小段路,虽然还有许多功能未完善,但时不我待。
所以下一步就是购买云服务器且部署我们的网站。
因为之前买过腾讯云的服务器,所以这次就只能买阿里云的,感觉没腾讯划得来。
云服务器有两种:轻量级应用服务器和云服务器。两者区别大概就是云服务器就是一台没软件只有系统的电脑,轻量级应用服务器会允许你预装一些东西,如环境,应用之类,像我们部署vue需要用到的node.js。
后补
1.一把辛酸泪,虽然搞过一次,但是使用的技术不同,所以难免要走弯路。我尽量讲讲
2.购买服务器后后端springboot代码打成jar包后,上传至服务器且服务器设置好jdk8,然后运行jar包即可,至此,后端部署完成。
3.前端(坑!):正常开发环境我们只需要用到cil脚手架以及容器node.js就可以完成打包 发布 访问、在服务器也可以这样,但是访问地址永远只能是本地,外部不能通过ip+端口访问(我感觉能改,但是搞了很久,遂放弃),选择第二种,加一层(经典java思维,不行就加一层(笑))具体就是需要用到nginx容器,它就是为了外部能通过固定ip与端口去访问我们的前端代码,至于怎么设置,搞好了简单一匹,但是门外汉就很难受了。具体就是下载,安装,然后我们需要将vue通过npm run build
命令打包代码形成一个dist
文件夹,这个文件夹名字是你设置的,里面存放两个东西,index.html和static文件夹,打包完后就只有这两个东西,然后我们把文件夹dist放到你下载的nginx的目录下的html目录下,再然后设置conf的nginx.conf文件中的配置:
设置下面这些东西:
listen:外部访问时需要加上的端口号,不设置访问时就只需要ip地址,设置了就需ip+端口
server_name:设置你的公网ip
root html/dist; # 打包的文件存放路径(就是这个路径下要有你打包后的index.html文件即可,路径随便改,只有有html文件,即你项目的入口,我吗vue项目都会有一个index.html文件作为入口)
index index.html index.htm:应该是入口文件全名(没验证,猜测)
try_files $uri $uri/ /index.html;# —解决页面刷新404问题,如果不设置这个,你只要访问根目录以外的任何路径都会404
server {
#listen 8080; #前端访问时需要的端口
server_name 8.134.81.146;#前端访问时需要的ip,默认127.0.0.1或localhost
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist; # 打包的文件存放路径
index index.html index.htm;
try_files $uri $uri/ /index.html;# ---解决页面刷新404问题
#try_files $uri $uri/ @router; #需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
}
...
}
最后,记得Mysql也要下载且导入sql、路漫漫其修远兮、如果你有幸看到此处,此语赠你,java路难走,屎难吃,现在2022.12.2,我好像无幸再继续为我喜欢的java走下去了,我确实菜,也见了比我学校好也比我努力,比我聪明的,java好想越来越平民化又好想越来越高门槛了,时代如此、理解是理解、只是不甘,我也深知工作是工作、生活是生活、我可以委屈工作,但生活还是要留有一席之地,前路未卜、瞒,瞒,瞒。
1.购买
最主要的是便宜:轻量级应用服务器比云服务器便宜,所以我选购了阿里云的这一款:
但好像,49的也够用。
然后我们再来详细讲一下配置
2核4g
2核:只cpu同时能运行多少线程,4g是内存的意思
每月流量:这个就是不太好的地方,轻量级应用服务器会有流量限制,超了会额外扣费,云服务器没有
ESSD:相当于硬盘、它会让你至少预留40g,但你真正安装后系统就占用十几个g而已
4 Mbps:带宽,同一时间能够接纳的流量的吞吐量,相当于网速
地区:就是服务器在哪里待着
镜像:可以理解为你要预装的东西,如果是你的电脑的组装起来的,那么刚开始是没有window系统的,需要你通过优盘或者其他方式装系统,这个镜像就是这个意思,应用镜像就是前面我说的可以预装应用的意思。这里我没有选择预装应用,而是只装了一个window系统,后面自己装应用会熟悉点,因为时间紧迫就直接选择window系统了,熟悉一点,加上上一次也是win
然后就是购买。。。
2.配置
之后会来到这个页面:
做两件事:
1.重置密码(必做)
2.使用下面的远程登陆方式登陆远程电脑(不做也行)
之后你就能登陆到服务器上了,这里就不放页面了
3.如何上传文件至服务器
这里选择最简单的方法:使用我们电脑自带的远程连接去连接服务器。
1.电脑直接搜索:远程桌面连接 这个应用
2.打开
1.用户名就是你使用Workbench远程连接
时,要你输入的用户名,你点击连接后的密码就是远程连接时的密码
2.之后选择本地资源选项:勾选剪贴板(一定要勾选不然没用)
3.成功连接上后,直接使用复制粘贴文件的功能即可将本地文件上传至服务器的指定文件夹
4.安装Mysql
过程省略,只说遇到的问题
问题
1.mysql安装时显示MSVCP20.dll没有
(1)这是因为没有安装微软常用运行库合集64位导致的,点击如下链接
去对应网站下载就好:Download Visual C++ Redistributable Packages for Visual Studio 2013 from Official Microsoft Download Center
64位的系统就选x64的、
2、安装可视化软件SQLyog(可选)
3、安装jdk8
4、从idea中导出项目的jar包
(2条消息) 在云服务器如何上部署springboot项目_@zzy的博客-CSDN博客
5、启动jar包,关闭jar包(window下)
到jar所在文件夹下运行cmd然后运行命令:java -jar blog_springboot-0.0.1-SNAPSHOT.jar
如果是Windows环境,要如何在后台执行呢
新建一个bat文件,输入:
- 普通的启动: java -jar xxx.jar
- 后台启动:javaw -jar xxx.jar
系统需要退出的时候,可以查看任务管理器。从命令行中找到你启动的项目(javaw.exe),进而杀掉。
该方式的优缺点是,启动便捷,但在看日志只能在窗口中,并且一旦关闭窗口,程序便关闭服务。
如果需要另外打印日志,可在后面增加 >out.log 2>&1 &
java -jar *.jar >out.log 2>&1 &
bat启动方式
@echo off
%1 mshta vbscript:CreateObject("WScript.Shell").Run("%~s0 ::",0,FALSE)(window.close)&&exit
java -jar demo-0.0.1-SNAPSHOT.jar >StartupLog.log 2>&1 &
exit
6.在阿里云控制台里设置端口能被访问
不设置外面的电脑就不能访问。
具体操作可以点击工作台里的常用文档,然后搜索开放端口
6、安装node.js
7、安装vue-cli
nginx
查看nginx的版本号:nginx -v
启动nginx:start nginx
快速停止或关闭nginx:nginx -s stop
正常停止或关闭nginx:nginx -s quit
配置文件nginx.conf修改重装载命令:nginx -s reload
npm run build
Vue项目打包后 opacity百分比被渲染为1%
打包后 opacity渲染为1%
背景
项目打包部署后opacity渲染为1%,本地localhost的透明度为50%无异常
解决
搜索资料发现opacity设置为百分比的时候,打包后数值被强制改成了1%
进一步查看 MDN发现属性值时0-1.0之间的数字值,
方案
将属性值改为0.5打包后就没问题
nginx 中只能访问根目录,无法访问路由(404)
在:nginx.conf
文件中的service 中添加 try_files $uri $uri/ /index.html;# ---解决页面刷新404问题
server {
listen 8080; #前端访问时需要的端口
server_name 8.134.81.146;#前端访问时需要的ip,默认127.0.0.1或localhost
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist; # 打包的文件存放路径
index index.html index.htm;
try_files $uri $uri/ /index.html;# ---解决页面刷新404问题
#try_files $uri $uri/ @router; #需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
}
nginx 只使用ip地址访问
将上面的 #listen 8080; #前端访问时需要的端口
注释即可
上传GitHub
1、不能使用QQ邮箱注册
注册GitHub邮箱收不到验证码无法完成注册,需要在QQ邮箱把GitHub设置成邮箱白名单
参考文档:
(4条消息) 解决注册Github邮箱用qq邮箱无法收到验证码的问题_淘姿淘梦的博客-CSDN博客_qq邮箱收不到github验证码
2、下载git
参考步骤:
(4条消息) 在github上上传本地项目步骤(两种方式)_在线打码的博客-CSDN博客_github上传本地项目
3、按照步骤上传即可
1-9 ↩︎
a-z ↩︎
A-Z ↩︎
A-Za-z ↩︎
a-zA-Z0-9 ↩︎
u0391-uFFE5A-Za-z ↩︎
最后
以上就是优雅棒棒糖为你收集整理的从0到1编写个人博客项目使用springboot+vue(前后端分离) 到 购买服务器上传项目 到 GitHub开源项目、此过程下所遇问题及解决方法,至少你帮你少走70%弯路个人博客编写后记前端问题后端后端编写时问题云服务器问题上传GitHub的全部内容,希望文章能够帮你解决从0到1编写个人博客项目使用springboot+vue(前后端分离) 到 购买服务器上传项目 到 GitHub开源项目、此过程下所遇问题及解决方法,至少你帮你少走70%弯路个人博客编写后记前端问题后端后端编写时问题云服务器问题上传GitHub所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复