Vue
生命周期
Setup()
@date 2021/3/18
@note 今天再看一个开源的系统项目
https://gitee.com/asaasa/element_vue3.0?_from=gitee_search#/asaasa/element_vue3.0/blob/dev/src%5Cplugins%5Cpermission.js
在看到它的登录部分时候,发现该页面不存在onCreate()等生命周期的处理,只是在一个Setup()中有处理,搜罗了一下相关的资料,汇总一下
抛出问题
- 既然有了明确的生命周期分块,干嘛还要整一个Setup()这个东西
为了实现组合式API
关于什么是组合式API看官方的说明:
https://v3.cn.vuejs.org/guide/composition-api-introduction.html#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BB%84%E5%90%88%E5%BC%8F-api
为什么使用组合式API?
当系统变得复杂时,在一个页面中引入了多个组件,组件的初始化在不同的生命周期中,各个组件的逻辑实现代码就会变得这里一块那里一块;类似于如下这张图
上面是一个大型组件的实例,逻辑点按照颜色区分
这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果我们能够将与同一个逻辑关注点相关的代码配置在一起,这样会更好。而这正是组合式 API 使我们能够做到的
如何实现组合式API?
setup位于created 和beforeCreated之前,
由于在执行
setup
时,组件实例尚未被创建,因此在setup
选项中没有this
。这意味着,除了props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。我们从
setup
返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。// src/components/UserRepositories.vue export default { components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList }, props: { user: { type: String, required: true } }, setup(props) { console.log(props) // { user: '' } return {} // 这里返回的任何内容都可以用于组件的其余部分 } // 组件的“其余部分” }
//reactive()函数接收一个普通对象,返回一个响应式的数据对象。
const form = reactive({
userName: “Administrator”,
pwd: “123456”
});
//toRefs()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref()类型的响应式数据
const { userName, pwd } = toRefs(form);
const ref_form = ref(null);
const success = ref(false);
封装mock和axios
@date 2021/3/19
@note 今天看了一下这个Demo的登录模块,下面做一下记录
一步步看,先放一下登录模块的代码
<template>
<common
><div class="ve_container">
<el-card :body-style="{ background: 'rgba(0,0,0,0.15)' }">
<h1>VE-Admin</h1>
<transition name="el-fade-in-linear">
<el-form
:model="form"
:rules="rules"
v-show="!success"
class="ve_form"
ref="ref_form"
:inline="false"
@keyup.enter="onSubmit"
>
<el-form-item prop="userName">
<el-input
v-model.trim="userName"
placeholder="用户名"
></el-input>
</el-form-item>
<el-form-item prop="pwd">
<el-input
v-model.trim="pwd"
show-password
placeholder="密码"
></el-input>
</el-form-item>
<el-form-item>
<el-button
class="ve_submit"
type="primary"
@click="onSubmit"
>登录</el-button
>
</el-form-item>
</el-form>
</transition>
</el-card>
</div></common
>
</template>
<script>
import Common from "@/components/Common";
import { ref, reactive, toRefs } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
const rules = {
userName: [{ required: true, message: "请输入用户名", trigger: "blur" }],
pwd: [{ required: true, message: "请输入密码", trigger: "blur" }]
};
export default {
name: "Login",
components: { Common },
// 关于setup() 的介绍 https://blog.csdn.net/qq_25506089/article/details/114743592?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
setup() {
const store = useStore();
const router = useRouter();
//reactive()函数接收一个普通对象,返回一个响应式的数据对象。
const form = reactive({
userName: "ces",
pwd: "test"
});
//toRefs()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref()类型的响应式数据
const { userName, pwd } = toRefs(form);
const ref_form = ref(null);
const success = ref(false);
//清除缓存
sessionStorage.clear();
//vuex的异步操作
store.dispatch("app/set_token", "");
router.options.isAddDynamicMenuRoutes = false;
const onSubmit = () => {
//validate 表单验证 element ui 的 form控件的用法
ref_form.value.validate(async valid => {
console.log("valid");
console.log(valid);
if (valid) {
//若校验成功
console.log("测试");
//这里对 mock和axios 进行了封装,直接调用 VE_API.user.login()即可访问到 配置的 axios请求(或者mock请求)
const data = await VE_API.user.login(form);
console.log(data);
if (data.code === "00") {
const { token, uname } = data;
store.dispatch("app/set_token", token);
store.dispatch("app/set_uname", uname);
success.value = true;
router.push({ name: "AppMain" });
}
} else {
return;
}
});
};
return {
success,
form,
ref_form,
userName,
pwd,
rules,
onSubmit
};
}
};
</script>
<style lang="scss" scoped>
.ve_container {
position: absolute;
width: 400px;
top: 50%;
left: 100px;
transform: translateY(-50%);
min-height: 273px;
text-align: center;
h1 {
font-size: 36px;
-webkit-transition-duration: 1s;
transition-duration: 1s;
-webkit-transition-timing-function: ease-in-put;
transition-timing-function: ease-in-put;
font-weight: 400;
margin-top: 12px;
}
.ve_form {
.ve_submit {
width: 100%;
}
}
}
</style>
这里有上述讲到的 setup() 以及为什么要从vue中引入 ref, reactive, toRefs
其中 ref, reactive, toRefs 的作用在备注中有所标注,摘要出来:
toRefs : toRefs()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref()类型的响应式数据reactive : reactive()函数接收一个普通对象,返回一个响应式的数据对象
让我们继续往下看
ref_form.value.validate(async valid => {})
这里是element ui 表单控件的使用方法
下面就是到了网络请求的部分了,乍一看乱糟糟的,axios的request()去哪里了,处理请求结果的函数跑到哪去了?
来到 封装的 axios.js 文件下
/* eslint-disable indent */
/*
* @Author: your name
* @Date: 2020-10-16 10:38:49
* @LastEditTime: 2021-03-03 13:47:21
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \vue_3.0_test\src\plugins\axios.js
*/
"use strict";
import axios from "axios";
import Qs from "qs";
import NProgress from "nprogress";
// Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
const install = (app, { router, store, opt }) => {
let config = {
Global: true,
// baseURL: process.env.baseURL || process.env.apiUrl || ""
// timeout: 60 * 1000, // Timeout
// withCredentials: true, // Check cross-site Access-Control
transformRequest: [
function(data) {
// 对 data 进行任意转换处理
if (VE_ENV.MODE === "production") {
//简单来说,qs 是一个增加了一些安全性的查询字符串解析和序列化字符串的库
/*
qs.parse()是将URL解析成对象的形式
qs.stringify()是将对象 序列化成URL的形式,以&进行拼接
*/
return Qs.parse(data);
} else {
return Qs.stringify(data);
}
}
]
};
const _axios = axios.create(config);
let ve_loading;
let ve_message = null;
let loadingCount = 0;
// 请求拦截
_axios.interceptors.request.use(
config => {
console.log(config);
//NProgress 是一个进度条插件
NProgress.done();
if (config.Global) {
NProgress.start();
ve_loading = app.config.globalProperties.$loading({
lock: true,
text: "Loading",
spinner: "el-icon-loading",
background: "rgba(0,0,0,0.1)"
});
}
loadingCount++;
//*请求头添加token
const token = store.getters.token;
token && (config.headers.Authorization = token);
// Do something before request is sent
return config;
},
error => {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
// 响应拦截
_axios.interceptors.response.use(
response => {
// TODO 根据响应头更新token
store.dispatch("app/set_token", new Date().getTime());
loadingCount--;
if (loadingCount <= 0) {
NProgress.done();
ve_loading.close();
}
let type = "success";
if (response.data.code != "00") {
type = "error";
}
if (ve_message) {
ve_message.close();
ve_message = null;
}
ve_message = app.config.globalProperties.$message({
type,
message: response.data.message
});
// Do something with response data
return response.data;
},
error => {
loadingCount--;
if (loadingCount <= 0) {
NProgress.done();
ve_loading.close();
}
if (error && error.response) {
let message = "";
switch (error.response.status) {
case 400:
message = "请求错误";
break;
case 401: {
message = "未授权,请登录";
router.replace({
name: "Login"
});
break;
}
case 403:
message = "没有权限,拒绝访问";
break;
case 404:
message = `请求地址出错`;
break;
case 408:
message = "请求超时";
break;
case 500:
message = "服务器内部错误";
break;
case 501:
message = "服务未实现";
break;
case 502:
message = "网关错误";
break;
case 503:
message = "服务不可用";
break;
case 504:
message = "网关超时";
break;
case 505:
message = "HTTP版本不受支持";
break;
default:
break;
}
if (ve_message) {
ve_message.close();
ve_message = null;
}
ve_message = app.config.globalProperties.$message({
message,
type: "error"
});
}
// Do something with response error
return Promise.reject(error);
}
);
const method = {
post: (url, p, config) => _axios.post(url, p, config),
get: (url, p, config) =>
_axios.get(url, Object.assign(config, { params: p }))
};
let api = {};
// require.context 是webpack中,用来创建自己的(模块)上下文;
/*
需要搜索的文件夹目录(必传)
是否需要搜索它的子孙目录,默认为false
匹配文件名的正则表达式
*/
const files = require.context("../mock/modules", false, /\.js$/);
console.log(files);
files.keys().forEach(key => {
const fileName = key.replace(/(\.\/|\.js)/g, "");
api[fileName] = {};
let obj = files(key);
Object.keys(obj).forEach(item => {
api[fileName][item] = (p, config = {}) =>
method[obj[item].type](obj[item].url, p, config);
});
console.log(obj);
console.log(api);
});
window[opt] = api;
// Object.defineProperties(app.config.globalProperties, {
// [opt]: {
// get() {
// return api;
// }
// }
// });
};
export default { install };
在这里面引入了qs,备注中有解释qs是干嘛用的
简单来说,qs 是一个增加了一些安全性的查询字符串解析和序列化字符串的库
注意:它封装的 axios 类传入了router和store对象
请求拦截器处理token,注意拦截后对其返回
我们继续往下看
然后他对axios配置文件进行处理
首先获取 配置文件夹目录下的所有js文件
const files = require.context("../mock/modules", false, /\.js$/);
然后获取当前文件下的所有API接口
封装(两层):[文件名][配置名] : {get,post}
通过window[opt] = api;
赋给全局的上下文,通过 关键词.文件名.配置名即可进行接口的调用
继续看功能
@date 2021/03/25
@note 今天继续看一下这个的源码,分析拆解一下
可以看到他的这个界面还是挺简约,布局也挺不错的,看了一下源码;它是将整个背景封装成了一个背景组件,然后在登录组件中引入了这个背景组件,其中那个小人以及左边的月球,星星都是使用div标签画出来的,用到了css中动画、定位、div曲率等等属性;难道是不难就是个细活,我在想有没有专门这种使用div画图一键导出css的网站,没有的话可以考虑做一个。
接下来还是去看功能代码
在源码中,很多使用如下的变量定义方式:
const { result, ip } = data;
直接定义 data 中的属性,然后后续直接使用 result 而不再使用 data.result 的方式
我们看到 点击【登录】事件的处理
const onSubmit = () => {
//validate 表单验证 element ui 的 form控件的用法
ref_form.value.validate(async valid => {
console.log("valid");
console.log(valid);
if (valid) {
//若校验成功,执行login api
console.log("测试");
const data = await VE_API.user.login(form);
if (data.state === 0) {
const { result, ip } = data;
store.dispatch("app/set_token", result);
store.dispatch("app/set_uname", ip);
console.log("token",ip);
// success.value = true;
router.push({ name: "AppMain" });
}
} else {
return;
}
});
};
项目
现在在看尚硅谷的项目教程
传送 : https://www.bilibili.com/video/BV1V5411K7rT?p=48
附一下前端项目地址:https://github.com/PanJiaChen/vue-element-admin
接下来都是围绕此项目做一下记录
@Date 2021/04/16
@Note 刚开始看这个项目
在看它的API接口时,发现定义的Base_URL的方式是我没有见过的
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
这个
process.env.VUE_APP_BASE_API
参考博客:
https://blog.csdn.net/qq_42783654/article/details/108842949
是定义在如下几个文件中的