618销售冠军是如何炼成的?揭秘电商“盘活”上亿销售数据的奇招!>>>
这一章主要是基础组件安装, 各个组件之间会有使用的关系,需要注意一下。
使用vuex管理全局状态, Vuex 是什么
现在在store文件夹下面新建四个文件state.js
, mutations.js
, getters.js
, actions.js
state就是Vuex中的公共的状态, 我是将state看作是所有组件的data, 用于保存所有组件的公共数据.
const state = {
token: "", //权限验证
};
export default state; //导出
mutations对象中保存着更改数据的回调函数, 改变state的值必须经过mutations
const mutations = {
//保存token
setToken(state, object) {
state.token = object.data.token;
},
};
export default mutations;
我将getters属性理解为所有组件的computed属性,也就是计算属性。vuex的官方文档也是说到可以将getter理解为store的计算属性, getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
const getters = {
//你要计算的属性
};
export default getters;
actions 类似于 mutations,不同在于:
const actions = {};
export default actions;
初始化任务时如果选择了vuex, 那么就会有index.js,
store.js是vuex模块整合文件,由于刷新页面会造成vuex数据丢失,
这里引入了一个vuex数据持久话插件,将state里面的数据保存到localstorage。 安装vuex-persistedstate, (这个我没装暂时不需要,有需要的可以装)
npm install vuex-persistedstate --save
import { createStore } from "vuex";
import state from "./state";
import mutations from "./mutations";
import actions from "./actions";
import getters from "./getters";
export default createStore({
state,
mutations,
actions,
getters,
modules: {},
});
这个是下面博客使用这个组件的配置。
import Vue from 'vue'
import Vuex from 'vuex'
import state from "./state";
import mutations from "./mutations";
import actions from "./actions";
import getters from "./getters";
//引入vuex 数据持久化插件
import createPersistedState from "vuex-persistedstate"
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
actions,
getters,
plugins: [createPersistedState()]
})
在安装时选择了Router组件后在main.js里会有自动有router, 详细main.js查看上一篇
然后进入router/index.js
文件中
这里添加状态管理和进度条组件
import { createRouter, createWebHistory } from "vue-router";
import store from "@/store/index"; //引入状态管理
import NProgress from "nprogress"; //引入进度条组件 cnpm install nprogress --save
import "nprogress/nprogress.css";
2.路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
/**
*@parma {String} name 文件夹名称
*@parma {String} component 视图组件名称
*/
const getComponent = (name, component) => () =>
import(`@/views/${name}/${component}.vue`);
3.路由配置
const routes = [
{
path: "/",
redirect: "/home",
component: getComponent("login", "index"),
},
{
path: "/login",
name: "login",
component: getComponent("login", "index"),
},
{
path: "/",
component: getComponent("layout", "layout"),
children: [
{
path: "/home",
name: "home",
component: getComponent("home", "index"),
meta: { title: "首页" },
},
],
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
4.本项目存在一个token,来验证权限问题,因此进入页面的时候需要判断是否存在token,如果不存在则跳转到登陆页面
//判断是否存在token
router.beforeEach((to, from, next) => {
NProgress.start();
if (to.path !== "/login" && !store.state.token) {
next("/login"); //跳转登录
NProgress.done(); // 结束Progress
}
next();
});
router.afterEach(() => {
NProgress.done(); // 结束Progress
});
5.导出路由
export default router;
1.接口处理我选择的是axios,由于它遵循promise规范,能很好的避免回调地狱。现在我们开始安装
cnpm install axios -S
2.在src
目录下新建文件夹命名为api
,里面新建两个文件,一个是api.js
,用于接口的整合, 另一个是request.js
,根据相关业务封装axios请求。
1.引入依赖
import axios from "axios";
import router from "@/router/index";
import store from "@/store/index"; //引入vuex
2.编写axios基本设置
axios.defaults.timeout = 60000; //设置接口超时时间
axios.defaults.baseURL = process.env.BASE_URL; //根据环境设置基础路径
axios.defaults.headers.post["Content-Type"] =
"application/x-www-form-urlencoded;charset=UTF-8"; //设置编码
3.编写请求拦截,也就是说在请求接口前要做的事情
/*
*请求前拦截
*用于处理需要请求前的操作
*/
axios.interceptors.request.use(
(config) => {
if (store.state.token) {
config.headers["Authorization"] = "Bearer " + store.state.token;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
4.编写请求响应拦截,用于处理数据返回操作
/*
*请求响应拦截
*用于处理数据返回后的操作
*/
axios.interceptors.response.use(
(response) => {
return new Promise((resolve, reject) => {
const res = response.data;
if (res.errCode === 0) {
resolve(res);
} else {
reject(res);
}
});
},
(error) => {
console.log(error);
//断网处理或者请求超时
if (!error.response) {
//请求超时
if (error.message.includes("timeout")) {
console.log("超时了");
this.$message.error("请求超时,请检查互联网连接");
} else {
//断网,可以展示断网组件
console.log("断网了");
this.$message.error("请检查网络是否已连接");
}
return;
}
const status = error.response.status;
switch (status) {
case 500:
this.$message.error("服务器内部错误");
break;
case 404:
this.$message.error("未找到远程服务器");
break;
case 401:
this.$message.warn("用户登陆过期,请重新登陆");
localStorage.removeItem("token");
setTimeout(() => {
router.replace({
path: "/login",
query: {
redirect: router.currentRoute.fullPath,
},
});
}, 1000);
break;
case 400:
this.$message.error("数据异常");
break;
default:
this.$message.error(error.response.data.message);
}
return Promise.reject(error);
}
);
5.请求相关的事情已经完成,现在开始封装get,post请求,
/*
*get方法,对应get请求
*@param {String} url [请求的url地址]
*@param {Object} params [请求时候携带的参数]
*/
export function get(url, params) {
return new Promise((resolve, reject) => {
axios
.get(url, {
params,
})
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
/*
*post方法,对应post请求
*@param {String} url [请求的url地址]
*@param {Object} params [请求时候携带的参数]
*/
export function post(url, params) {
return new Promise((resolve, reject) => {
axios
.post(url, params)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
封装好axios的业务逻辑之后自然要开始,运用,首先引入get以及post方法,然后封装接口并导出
import { get, post } from "./request";
//登陆
export const login = (params) => post("/api/login", params);
//上传
export const upload = (upload) => get("/api/upload", upload);
那我们如何调用接口呢?以登陆页面为例。
import { login } from "@/api/api"; //引入login
/**
* @oarma {Object} login 接口传递的参数
*/
login(this.postData)
.then((res) => {
//成功之后要做的事情
})
.catch((err) => {
//出错时要做的事情
});
有了接口以后需要模拟后台返回数据,这个时候就可以使用mock组件
# 安装
npm install mockjs
import qs from "qs";
export default function formatOptions(options) {
let { url, type, body } = options;
let params = null;
if (type === "GET" || type === "DELETE") {
let index = url.indexOf("?");
let paramsString = index > -1 ? url.slice(index + 1) : "";
if (paramsString !== "") {
params = qs.parse(paramsString);
}
} else {
params = {};
if (body instanceof FormData) {
for (let [key, value] of body.entries()) {
params[decodeURIComponent(key)] = decodeURIComponent(value);
}
} else {
try {
params = JSON.parse(body);
} catch (e) {
params = qs.parse(body);
}
}
}
if (params !== null && Object.keys(params).length === 0) {
params = null;
}
return { url, type, params };
}
import Mock from "mockjs";
import formatOptions from "./formatOptions";
Mock._mock = Mock.mock;
Mock.mock = function (url, method, resFunc) {
if (arguments.length === 1) {
return this._mock(url);
}
if (arguments.length === 2) {
console.error(
"Function Mock.mock require three params: url, method, resFunc!!!"
);
return;
}
if (arguments.length === 3) {
let methods = ["get", "post", "put", "delete"];
if (!methods.includes(method.toLowerCase())) {
console.error(
"Function Mock.mock's second param should be get, post, put, delete!!!"
);
return;
}
if (typeof resFunc !== "function") {
console.error("Function Mock.mock's third param should be a function!!!");
return;
}
}
// 将注册的 url 转成能匹配查询字符串的正则
if (typeof url === "string") {
url = url.replace(/\//g, "\\/");
url += "(|\\?.*)$";
url = new RegExp(url);
} else if (!(url instanceof RegExp)) {
console.error(
"Function Mock.mock's first param should be a string or regexp!!!"
);
return;
}
this._mock(url, method, function (options) {
// 格式化 options 对象
options = formatOptions(options);
let res = null;
try {
res = resFunc(options);
} catch (err) {
res = err;
}
// 将返回的测试数据打印到控制台
console.groupCollapsed(
`%c${options.type.toLowerCase()} | ${options.url}`,
"color: green;"
);
console.log("%cparams: ", "color: #38f");
console.log(options.params);
console.log("%cresponseData: ", "color: #38f");
console.log(res);
console.groupEnd();
console.log("---------------");
return res;
});
};
export default Mock;
这里就可以和axios里的api对应起来
import Mock from "../utils/mock";
//注册
//登录
Mock.mock("/api/login", "post", (options) => {
console.log(options);
return {
errCode: 0,
data: {
token: "token",
},
message: "token已发送: token",
};
});
这里引入api
//引入Mock接口规则文件
import "./api/login.js";
这里就吧该引入的组件就都引入完成了
现在整体结构还没有设计,现在只是用来演示组件整体是正确的
1.main.js
import { createApp } from "vue";
import ElementPlus from "element-plus";
import "element-plus/lib/theme-chalk/index.css";
import App from "./App.vue";
import router from "./router";
import store from "./store";
//条件引入模拟服务器 MockJS优先级高于域名代理 会导致远程API无法访问
//小心,Boolean('false')等于true 'false'不等于false
eval(process.env.NODE_ENV == "development") && require("@/mock");
const app = createApp(App);
app.use(ElementPlus);
app.use(store);
app.use(router);
app.mount("#app");
2.App.vue
<template>
<router-view />
</template>
<style lang="stylus">
#app
font-family Avenir, Helvetica, Arial, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
text-align center
color #2c3e50
margin-top 60px
</style>
3.home/index.vue
<template>
<div>主页</div>
</template>
<script>
export default {
name: "index",
};
</script>
<style scoped></style>
4.layout/layout.vue
<template>
<div>
样式模块
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "layout",
};
</script>
<style scoped></style>
5.login/index.vue
这里使用了axios, vuex的内容注意一下
<template>
<div>
<h3>登录页</h3>
<el-form :model="postData" :rules="theRules" ref="loginForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="postData.username"></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
type="password"
v-model="postData.password"
autocomplete="off"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('loginForm')">
提交
</el-button>
<el-button @click="resetForm('loginForm')"> 重置 </el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { login } from "@/api/api"; //引入login
export default {
name: "index",
data: function () {
return {
postData: {
password: "admin",
username: "admin",
},
theRules: {},
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
login(this.postData)
.then((res) => {
//提交数据到vuex
this.$store.commit("setToken", res);
this.$message.success("登录成功");
this.$router.push({
path: "/home",
});
})
.catch((err) => {
this.$message("error", err.message);
});
} else {
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style scoped></style>