前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue3+element-plus+router+vuex+axios从零开始搭建(3)

vue3+element-plus+router+vuex+axios从零开始搭建(3)

作者头像
solate
发布2021-06-22 20:57:30
3.5K0
发布2021-06-22 20:57:30
举报

618销售冠军是如何炼成的?揭秘电商“盘活”上亿销售数据的奇招!>>>

vuex+router+axios+mockjs

这一章主要是基础组件安装, 各个组件之间会有使用的关系,需要注意一下。

vuex

使用vuex管理全局状态, Vuex 是什么

现在在store文件夹下面新建四个文件state.js, mutations.js, getters.js, actions.js

state.js

state就是Vuex中的公共的状态, 我是将state看作是所有组件的data, 用于保存所有组件的公共数据.

const state = {
  token: "", //权限验证
};
export default state; //导出
mutations.js

mutations对象中保存着更改数据的回调函数, 改变state的值必须经过mutations

const mutations = {
  //保存token
  setToken(state, object) {
    state.token = object.data.token;
  },
};
export default mutations;
getters.js

我将getters属性理解为所有组件的computed属性,也就是计算属性。vuex的官方文档也是说到可以将getter理解为store的计算属性, getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

const getters = {
  //你要计算的属性
};
export default getters;
actions.js

actions 类似于 mutations,不同在于:

  1. actions提交的是mutations而不是直接变更状态
  2. actions中可以包含异步操作, mutations中绝对不允许出现异步
  3. actions中的回调函数的第一个参数是context, 是一个与store实例具有相同属性和方法的对象
const actions = {};
export default actions;
index.js (store.js)

初始化任务时如果选择了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()]
    })

vue-router

在安装时选择了Router组件后在main.js里会有自动有router, 详细main.js查看上一篇

然后进入router/index.js文件中

  1. 引入文件

这里添加状态管理和进度条组件

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;

axios

1.接口处理我选择的是axios,由于它遵循promise规范,能很好的避免回调地狱。现在我们开始安装

cnpm install axios -S

2.在src目录下新建文件夹命名为api,里面新建两个文件,一个是api.js,用于接口的整合, 另一个是request.js,根据相关业务封装axios请求。

request.js

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);
      });
  });
}
api.js

封装好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) => {
            //出错时要做的事情
        });

mockjs

有了接口以后需要模拟后台返回数据,这个时候就可以使用mock组件

  1. 安装

mockjs文档

# 安装
npm install mockjs
  1. 在src目录下创建mock文件夹

无侵入整合MockJS与Vue3.0实例

utils
  1. formatOptions.js
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 };
}
  1. mock.js
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;
api

这里就可以和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",
  };
});
index.js

这里引入api

//引入Mock接口规则文件
import "./api/login.js";

这里就吧该引入的组件就都引入完成了

views 实现

现在整体结构还没有设计,现在只是用来演示组件整体是正确的

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>

参考

从0到1搭建Element的后台框架

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • vuex+router+axios+mockjs
    • vuex
      • state.js
      • mutations.js
      • getters.js
      • actions.js
      • index.js (store.js)
    • vue-router
      • axios
        • request.js
        • api.js
      • mockjs
        • utils
        • api
        • index.js
      • views 实现
      • 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档