LEEYANGY

LEEYANGY 关注TA

拼搏百天,我要上湖工大!

LEEYANGY

LEEYANGY

关注TA

拼搏百天,我要上湖工大!

  •  Wuhan/HuBei
  • 学生
  • 写了322,476字

该文章投稿至Nemo社区   Js、Css、Html  板块 复制链接


前端技术学习

发布于 2022/11/24 12:28 560浏览 0回复 19,684

最后更新时间 2023/03/24 00:53 ---添加 前/后/独享/组件路由

---- 华丽的时间分割线 ----


h5页面课程设计1(自己寻找目标页面,要求大致轮廓):

基本语法:晚些时候补上,通过这次页面编写,加深了对html一些布局属性的影响,毕竟以前我很抵触前端(?),现在慢慢有感觉了

h5部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>仿csdn博客布局</title>
</head>
<link rel="stylesheet" type="text/css" href="static/css/index.css">
<body>

<header>
<nav class="menu_content">
<ul>
<li><a href="#">网站logo</a></li>
<li><a href="#">首页</a></li>
<li><a href="#">地图</a></li>
<li><a href="#">博客</a></li>
<li><a href="#">关于我</a></li>
</ul>
</nav>

</header>

<div class="main">
<hr>
<!-- 左边栏-->
<aside class="left">
<div class="l0"><p>博主信息</p></div>
<div class="info">

<img src="static/img/mysql.jpg" width="50" height="50">
<p class="username"><a href="#">***</a></p> <br>
<p class="fans"><a href="#">粉丝数量:100w+</a></p>
<p class="subscribe"><a href="#">订阅数量:50+</a></p>
</div>


<div class="l1">
<p>热门文章</p>
<div class="article">
<p><a href="#">mysql从入门到精通</a></p>
<p><a href="#">java从入门到精通</a></p>
<p><a href="#">kali从入门到入狱</a></p>
</div>
</div>
</aside>
<!-- 右边栏-->
<aside class="right">
<div class="r0">分类专栏</div>
<div class="r1">
<p><a href="#"><img src="static/img/mysql.jpg" width="20" height="20"> mysql相关 5篇</a></p>
<p><a href="#"><img src="static/img/mysql.jpg" width="20" height="20"> tomcat相关 2篇</a></p>
<p><a href="#"><img src="static/img/mysql.jpg" width="20" height="20"> nginx相关 3篇</a></p>
<p><a href="#"><img src="static/img/mysql.jpg" width="20" height="20"> redis相关 4篇</a></p>
</div>
<div class="r2">
<p>留言</p>
</div>
<div class="r3">
<p>***:你好博主 回复 </p>
<p>***:你好博主 回复 </p>
<p>***:你好博主 回复 </p>
<p>***:你好博主 回复 </p>
</div>
</aside>
<!-- 中间栏-->
<aside class="middle">
<article class="arts">
<h1>MySQL的N种优雅食用方法</h1>
<article class="describe">
<h2>在这里为大家介绍MySQL的一些优雅使用方法</h2>
<p>总所周知MySQL是一种关系型数据库......</p>
</article>
</article>

<article class="arts">
<h1>MySQL的N种优雅食用方法</h1>
<article class="describe">
<h2>在这里为大家介绍MySQL的一些优雅使用方法</h2>
<p>总所周知MySQL是一种关系型数据库......</p>
</article>
</article>

<article class="arts">
<h1>MySQL的N种优雅食用方法</h1>
<article class="describe">
<h2>在这里为大家介绍MySQL的一些优雅使用方法</h2>
<p>总所周知MySQL是一种关系型数据库......</p>
</article>
</article>

<article class="arts">
<h1>MySQL的N种优雅食用方法</h1>
<article class="describe">
<h2>在这里为大家介绍MySQL的一些优雅使用方法</h2>
<p>总所周知MySQL是一种关系型数据库......</p>
</article>
</article>

<article class="arts">
<h1>MySQL的N种优雅食用方法</h1>
<article class="describe">
<h2>在这里为大家介绍MySQL的一些优雅使用方法</h2>
<p>总所周知MySQL是一种关系型数据库......</p>
</article>
</article>
</aside>


<hr>
</div>

<!--底部栏-->
<footer class="footer_content">
<p>本站为非盈利性个人网站,所有文章版权均属于其源作者所有。</p>
<p>如有侵权,敬请来信联系我们,我们立即删除。</p>
<p>站长邮箱:***</p>
<p>Copyright © 2020-2023 All Rights Reserved. <a href="https://beian.miit.gov.cn/">备案号:***</a></p>
</footer>

</body>
</html>


css部分
body {
background-color: #f7f7f7;
}

/*头部布局*/
.menu_content ul li {
width: 150px;
height: 35px;
background-color: #f7f7f7;
list-style: none;
text-align: center;
line-height: 35px;
overflow: hidden;

padding: 10px;
margin-top: 20px;
margin-left: 20px;

/* 1float */
float: left;
}

.menu_content li:nth-child(2n) {
/*display: block;*/
/*background-color: lemonchiffon;*/
}

.menu_content ul li:hover {
background-color: lemonchiffon;
}

.menu_content a {
color: royalblue;
text-decoration: none;
}

/*中间布局*/

.main {
/*不跟随浮动*/
clear: both;
padding-top: 10px;
}

.left {
float: left;
/*使用颜色做出区分*/
/*background-color: #dac7c7;*/
width: 250px;
/*去上边和下边空隙*/
margin: 0;
}

.l0 {
text-align: center;
/*border: px;*/
}

.info, first-child {
text-align: left;
border: 1px solid;
}

.middle {
background-color: snow;
/*width: 524px;*/
text-align: center;
/*position: relative;*/
/*padding-left: 300px;*/
left: 300px;

/*自适应变化*/
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/*padding-left: 10px;*/
}

.right {
float: right;
/*background-color: gray;*/
width: 250px;
margin: 0;
}

/*内容布局*/

.r1, p, a {
margin: 0;
text-decoration: none;
}


.r1, first-child {
font-size: 20px;
padding-left: 20px;
background-color: snow;
}

.l0, .l1, .r0, .r2, .info {
text-align: center;
/*background-color: #43bff5;*/
opacity: 0.2;
background: transparent;
border-top: 1px solid black;
/*padding-top: 20px;*/
}

.l1, .r2 {
padding-top: 20px;
}

.info{
padding-left: 20px;
}
.info, .username, .fans, .subscribe {
display: inline-block;

}


.r3, .first-child {
font-size: 20px;
padding-left: 20px;
}

.footer_content {
text-align: center;

}

/*height*/
.left, .right {
height: 200px;
}

.middle {
height: 650px;
}

.arts {
margin: 0;
padding: 0.3rem;
background-color: #eee;
font: 1rem 'Fira Sans', sans-serif;
}

.arts > h1,
.describe {
margin: 0.5rem;
padding: 0.3rem;
font-size: 1.2rem;
}

.describe {
background-color: white;
}

.describe > h2,
.describe > p {
margin: 0.2rem;
font-size: 1rem;
}



首先是教程网址:
vue2  介绍 — Vue.js (vuejs.org)

vue3 Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

vue脚手架

element-ui

为了方便笔记 以下除了代码内部,再提到的vue2,vue3  均简写v2和v3,vue.js包引入,随便百度就能搜到,不做提及

原先html
<div>{{msg}}</div> <<!-- 这个是容器 -->>

<script>
// vue2创建应用实例 方法一 // var vm =new Vue({
// el:"#root",
// data:{
// message:"hello world",
// age:22
// }
// }) 方法二 这种方式是需要返回data中的数据 new Vue({
el: '#root',
data: function () {
return {
message: "hello",
age: "22"
}

}
})
</script> 需要注意的是,这个创建方法是vue2的,vue3见下(官方例子) ```html```

<div id="app"> <button @click="count++"> Count is: {{ count }} </button> </div>


```js```

import { createApp } from 'vue' createApp({ data() { return { count: 0 } } }).mount('#app')



v2:

MVVM模型

1.M 模型(Model):对应data中数据

2.V 视图 (View):模板 对应html中的 {{message}} (即是View)

3.VM 视图模型(ViewModel):Vue实例对象

{{message}} 中可以写的内容 vue对象中出现的内容都可以用(f12能看到的对象)全都能在模板中用 v-on,v-bind....绑定....比如原先html中的 onclick事件,使用vue之后可以简写 v-on:click="method_name" ==>v-on:click="showInfo"
v-on 简写语法 @click ==> @click="showInfo"
##数据代理
<button v-on> 事件处理 new Vue({ el:'', data:{ }, methods:{ 可以写入多个事件 // method_name(){            },     showInfo(){ } } }) vue中事件修饰符 1.prevent:阻止默认事件 2.stop:阻止事件冒泡 (e.stopproprgation()) vue中写法 @onclick.stop 3.once:事件只触发一次 4.capture:使用事件的捕获模式 5.self:只有event,target是当前操作的元素是自己才触发事件 6.passive:当前默认行为立即执行,无需等待事件回调执行完毕 键盘事件 @keydown @keyup 回车keycode==13

@keyup.enter 按下回车

vue中常用按键别名

                enter,delete,esc,space,tab(必须配合keydown使用,ctrl,alt,shift,meta(win键/command键)),up,down,left,right

组件 (入门) new Vue 是创建vm视图模型,div.id绑定view视图

<div id="root">
<Xuesheng></Xuesheng>
</div>

<script>
Vue.config.productionTip = false
// 组件
// 定义一个学生组件
const student = Vue.extend({
template: `
<div>
<h1>{{ name }}</h1>
<h1>{{ age }}</h1>
</div>
`,
data() {
return {
name: "lee",
age: 10
}
}
})
new Vue({
el: "#root",
components: {
xuesheng: student
}
})
</script>




---- 华丽的时间分割线 ----

ajax

使用jQuery发送ajax请求

//使用Ajax向后台发送请求案例,用户注册时候,我们可以通过当前失去焦点,在不经意间对用户名做一个查询,并反馈给用户
//对于一个账号来说,是不允许出现同一用户名的,所以我们需要在用户注册时候进行校验
$(function () {
// jQuery选中id
$("#name").blur(function () {
// 测试是否获取成功,成功即弹窗
// alert("aaaa")
// 获取输入的用户名
var value = $("#name").val();
// 测试是否正常获取值,成功即弹窗
// alert(value)
// 发出ajax请求,验证用户名是否存在
$.ajax({
url: "/user/checkName",
data: {
// 请求值
"name": value
},
// 请求方法
type: "post",
// 返回值方式 json
dataType: "json",
success: function (data) {
// alert("data:"+data)
if (data) {
// 用户存在 data=false
$("#warn").html("用户名已存在,请更换")
alert("真值"+data)
} else {
// 用户不存在 data=false
$("#warn").html("用户名可用")
}
}
})
});
})


---- 华丽的时间分割线 ----

axios

在vue中使用axios

npm(cnpm) install --save axios

复写axios request api

// 对axios进行二次封装
import axios from "axios"
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import "nprogress/nprogress.css"

// 利用axios对象方法create,创建axios实例
const requests = axios.create({ //参照vue官方教程 baseURL:"/api",
//等待时间 timeout:10000,
});

// 请求拦截
requests.interceptors.request.use((config)=>{
// 进度条开始动
nprogress.start();
// config:headers请求头
return config
});

// 响应拦截器
requests.interceptors.response.use((res)=>{
// 进度条结束
nprogress.done();
// 成功返回回调函数,服务器响应返回来数据后,响应拦截可以检测,做其它事情
return res.data;
},(error)=>{
nprogress.done();
// 响应失败回掉函数
return Promise.reject(new Error('fail'));
});


// 对外暴露
export default requests;


测试向后台服务器发送请求

// api统一管理
import requests from "@/api/request";

export const getUserInfo = () => {
// 发请求
return requests({
url:'/user/getUserInfo',
method:'get'
})
}

此时出现了一个跨域问题,我们需要处理跨域,(跨域是指,出现了端口,ip,不一致得情况,此时需要解决跨域)

在webpack项目中,我们需要改动   名称忘了 ,在vue中 ,需要改动vue.config.js文件,该文件同等于webpack中的配置文件

// 代理跨域
devServer:{
port:8080,
proxy:{
// /api 指 匹配请求url中携带有/api的才发送代理请求,否则不适用代理服务器
'/api':{
// 数据来自那台服务器?
target:'http://localhost:8081/',
// 路径重写
pathRewrite:{'^/api':''},
}
}
},


实现效果如下:


---- 华丽的时间分割线 ----

jwt(JSON Web Tokens - jwt.io)更多信息详见官网

java部分,需要在用户登录成功后将生成的jwt响应给前端,前端进行存储,下次请求需要携带此jwt,后端决定其是否有对应权限,获得登录信息之类的校验

jwt utils工具类

package xyz.leeyangy.framework.jwt;

//import org.springframework.boot.context.properties.ConfigurationProperties;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import io.jsonwebtoken.*;
import lombok.Data;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import xyz.leeyangy.framework.constants.JwtConstant;

import java.util.Date;

@Data
@Slf4j
@Component
//@ConfigurationProperties(prefix = "leeyangy.jwt")
public class JwtUtils {

private String header;

/**
* 生成jwt token
*/
public String generateToken(long userId) {
Date nowDate = new Date();
//过期时间
Date expireDate = new Date(nowDate.getTime() + 604800 * 1000);

return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userId+"")
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, JwtConstant.TokenSingKey)
.compact();
}

public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(JwtConstant.TokenSingKey)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
log.debug("validate is token error ", e);
return null;
}
}

/**
* token是否过期
* @return true:过期
*/
public boolean isTokenExpired(Date expiration) {
return expiration.before(new Date());
}

}




vuex && Pinia(介绍 | Pinia 中文文档 (web3doc.top))

Pinia 是 Vue 的存储库,它允许跨组件/页面共享状态

# 安装
yarn add pinia
# 或者使用 npm
npm install pinia # 在main.js中引入 v3

import { createPinia } from 'pinia'

app.use(createPinia())


# v2

import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
el: '#app',
// 其他选项...
// ...
// 注意同一个 `pinia` 实例可以在多个 Vue 应用程序中使用
// 同一个页面
pinia,
})


路由缓存:

作用:让不展示的路由组件保持挂载,不被销毁

在需要路由的组件添加keep-alive标签

<!-- 单个缓存路由 -->
<keep-alive inclue="you_compent_name">
    <router-view></router-view>
</keep-alive>

<!-- 多个缓存路由 -->

<keep-alive :inclue="['1','2','...']">
<router-view></router-view>
</keep-alive>


路由守卫:

    1.全局前置:

router.beforeEach( (to,from,next)=>{
console.log(to,from)
// 使用if判断,自行添加条件
// 放行
next()
})


    2.全局后置:  在路由组件中使用 meta

// meta:{false}

....

{
path: '/articles/add',
component: ArticleAdd,
name: `articleAdd`,
meta:{isAuth:false},

},


router.afterEach( (to,from,next)=>{
console.log(to,from)
// 使用if判断,自行添加条件
// 放行
next()
})


3.独享路由

......略......

4.组件路由


一些vue3路由传参:

vue3的setup中如何使用this.$route获取内容?

// v2原来是这样的       value是你需要拿到的值比如说 id,就把value换成id
this.$route.params.value
// 在v3中,我需要的路由传参,需要这样拿到值
// router,这是自己定义的,
router.currentRoute.value.params.id


v3中获取响应头中内容

封装axios,添加拦截器

import axios from 'axios'
import router from '../router'
import store from '../store'
import nprogress from "nprogress";
import {Dialog} from "vant";


axios.defaults.baseURL = "/api"

// 设置请求头
let reqInstance = axios.create({
headers: {
Authorization: 'Bearer ${localStorage.getItem("access_token")'
}
})


// 前置拦截
axios.interceptors.request.use(config => {
nprogress.start();
return config
})

axios.interceptors.response.use(response => {
let res = response.data;

console.log("=================")
console.log(res)
console.log("=================")

if (res.code === 200) {
nprogress.done();
// Element.Message.success(res.message, {duration: 3 * 1000})
return response
} else {
// Element.Message.error(res.message, {duration: 3 * 1000})
return Promise.reject(response.data.msg)
}
},
error => {
// console.log(error)
if (error.response.data) {
error.message = error.response.data.msg
}

if (error.response.status === 401) {
store.commit("REMOVE_INFO")
router.push("/user/login")
}

Element.Message.error(error.message, {duration: 3 * 1000})
return Promise.reject(error)
}
)


对需要引入的组件进行引入

最终达成的效果如下:

初始化:


最终效果:

最后就是将上述结果持久化到本地,如果不持久化的话,浏览器一刷新,数据就清掉了



我的pinia

import {defineStore} from 'pinia'

// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useUserStore = defineStore('auth', {
// other options...
state: () => {
return {
isLogin: 0,
isAuthenticated: false,
Authorization: localStorage.getItem('USER_TOKEN'),
user: localStorage.getItem('USER_INFO')
}
},
// computed修饰一些值
getters: {
// 通过getter对state状态修改
getAuthenticated: state => state.isAuthenticated,
getUser: state => state.user,
getAuthorization: state => state.Authorization,
},
// method 可以同步异步做提交state
actions: {
// 设置认证,登录状态
setAuth(isAuth: boolean) {
if (isAuth) {
//在pinia中this指代state
this.isAuthenticated = isAuth;
} else {
this.isAuthenticated = false;
}

},
// 保存用户信息
setUser(user: any) {
if (user) {
this.user = user;
} else {
this.user = {};
}
},
setAuthorization(Authorization){
if(Authorization){
this.Authorization = Authorization;
}else {
this.Authorization = '';
}
},
setIsLogin(){
if(this.Authorization!=null && this.user!=null){
this.isLogin = 1;
}else {
this.isLogin = 0;
}
}
}
})


接着在需要持久化的组件上添加pinia订阅,使其能够监听值的变化

// 持久化
this.store.$subscribe((mutation, state) => {
localStorage.setItem("USER_TOKEN", this.store.getAuthorization)
localStorage.setItem("USER_INFO", this.store.getUser)
localStorage.setItem('USER_LOGIN',this.store.isLogin)
});





nodejs报错,版本问题

package.json添加

"scripts": {
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"
},





vue生命周期  暂略

---- 华丽的时间分割线 ----


v2转v3脚手架


vite4.2.x 官方配置代理服务器example

export default defineConfig({
server: {
proxy: {
// 字符串简写写法:http://localhost:5173/foo -> http://localhost:4567/foo
'/foo': 'http://localhost:4567',
// 带选项写法:http://localhost:5173/api/bar -> http://jsonplaceholder.typicode.com/bar
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
// 正则表达式写法:http://localhost:5173/fallback/ -> http://jsonplaceholder.typicode.com/
'^/fallback/.*': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/fallback/, ''),
},
// 使用 proxy 实例
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
configure: (proxy, options) => {
// proxy 是 'http-proxy' 的实例
}
},
// 代理 websockets 或 socket.io 写法:ws://localhost:5173/socket.io -> ws://localhost:5174/socket.io
'/socket.io': {
target: 'ws://localhost:5174',
ws: true,
},
},
},
})



点赞(0)

上一个文章:Hadoop生态圈

下一个文章:springboot && springcloud alibaba

点了个评