Vue.js 插件开发__Vue.js
发布于 9 天前 作者 banyungong 145 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

什么是Vue.js插件

按照Vue.js 官网的说法。插件通常用来为 Vue 添加全局功能。Vue 插件

Vue.js 插件可以实现什么功能

  • 添加全局方法或者 property
  • 添加全局资源:指令/过滤器/过渡等
  • 通过全局混入来添加一些组件选项
  • 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现
  • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能

这篇文章说下我自己用插件的方式提供一个包含登录界面的插件,插件内提供 axios 的封装。这样每次新项目就可以按照插件,快速实现登录和 axios 功能了。只是提供一个思路,大家可以在参考下自己做拓展。比如提供一个开箱即用的用户登录,注册,找回密码等功能的插件。插件里也可以注入一个库,实现自己的方法。

功能列表

  • 统一登录页
  • axios 可配置化封装

实现的关键点

  • 暴露一个install 方法
  • 在Vue 原型上做一系列的功能或者函数挂载
  • Vue.use 时传入定制化参数

贴下官网示例代码

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  // 可以通过 options 获取外部传入的参数 options.someOptions = true
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}

使用

import myPlugin from "myplugin"

Vue.use(myPlugin, {
	someOption: true 
})

贴下我的实现

只要是理解思想,实现思路和原理。这样就能实现自己想要的功能了

import axios from "axios";
import qs from "qs";
import { version, winyh, getQueryVariable } from "./util";
import { codeMessage } from "./request";
import user_store from "./store";

import Login from "./components/login";

import Notification from "ant-design-vue/lib/notification";
import "ant-design-vue/lib/notification/style/css";

import Message from "ant-design-vue/lib/message";
import "ant-design-vue/lib/message/style/css";

const components = [Login];

/* 异常处理程序 */
const errorHandler = (error) => {
  const { response } = error;
  if (response && response.status) {
    const errorText = codeMessage[response.status] || response.statusText;
    const { status } = response;
    console.log(`请求错误 ${status}:`, errorText);

    Notification.error({
      message: "系统提示",
      description: `请求错误 ${status}: ${errorText}`,
    });

    if (status === 401) {
      console.log(401);
      if (window.location.pathname !== "/user/login") {
        // 带redirect 到登录页

        // router.replace({
        //     path: "/login",
        //     query: { redirect: router.currentRoute.fullPath },
        //   });

        let redirect = encodeURI(location.href);
        location.href = `/user/login?redirect=${redirect}`;
      }
    }

    if (status === 403) {
      console.log(403);
    }

    if (status === 404) {
      console.log(404);
    }

    if (status === 405) {
      console.log(405);
    }

    if (status === 500) {
      console.log(500);
    }
  } else if (!response) {
    console.log("您的网络发生异常,无法连接服务器");
    Notification.error({
      message: "系统提示",
      description: "您的网络发生异常,无法连接服务器",
    });
  }
  return response;
};

/* 必须暴露 install 方法 */
const install = (Vue, options) => {
  const {
    timeout = 3000,
    tokenKey = "token",
    homeUrl = "/",
    api_user = "/user/info",
    router,
    store,
    baseURL,
    headers,
    namespace = "",
    loginConfig = {},
  } = options;

  /* Login 组件全局变量 */
  Vue.prototype.__loginConfig__ = loginConfig;

  /* Vuex 设置 */

  if (store) {
    store.registerModule("user", user_store);
  }

  router.beforeEach((to, from, next) => {
    /* 必须调用 `next` */
    const token = localStorage.getItem(tokenKey) || "";
    const user = store.state.user.userInfo || "";

    const systemExpireAt = Date.now();
    const localExpireAt = localStorage.getItem("expire_at");

    if (systemExpireAt > localExpireAt) {
      if (to.path === "/user/login") {
        next();
      } else {
        next({ path: "/user/login" });
      }
    }

    if (token) {
      if (!user) {
        Vue.prototype.$getUserInfo();
      }
      next();
    } else {
      if (to.path === "/user/login") {
        next();
      } else {
        localStorage.removeItem(tokenKey);
        next({ path: "/user/login" });
      }
    }
  });

  router.addRoutes([
    {
      path: "/user/login",
      component: Login,
    },
  ]);

  if (install.installed) return;

  components.map((component) => Vue.component(component.name, component));

  const instance = axios.create({
    baseURL: baseURL,
    timeout: timeout,
  });

  /* 请求拦截器 */
  instance.interceptors.request.use(
    (config) => {
      const token = localStorage.getItem(tokenKey) || "";
      if (token) {
        config.headers["Authorization"] = "Bearer " + token;
      }
      if (headers) {
        config.headers = {
          ...config.headers,
          ...headers,
        };
      }

      return config;
    },
    (error) => {
      return Promise.reject(error);
    }
  );

  /* 响应拦截器 */
  instance.interceptors.response.use(
    (response) => {
      return response.data;
    },
    (error) => {
      errorHandler(error);
      return Promise.reject(error);
    }
  );

  /*
   * get请求
   */
  Vue.prototype.$get = (url, params) => {
    return instance(url, { params });
  };

  /*
   * post请求
   */
  Vue.prototype.$post = (url, params, headers) => {
    if (headers) {
      var isUrlencoded =
        headers["content-type"] === "application/x-www-form-urlencoded";
    }
    return instance({
      url,
      method: "post",
      data: isUrlencoded ? qs.stringify(params) : params,
      headers,
    });
  };

  /*
   * put请求
   */
  Vue.prototype.$put = (url, params, headers) => {
    if (headers) {
      var isUrlencoded =
        headers["content-type"] === "application/x-www-form-urlencoded";
    }
    return instance({
      url,
      method: "put",
      data: isUrlencoded ? qs.stringify(params) : params,
      headers,
    });
  };

  /*
   * put请求
   */
  Vue.prototype.$delete = (url, params, headers) => {
    if (headers) {
      var isUrlencoded =
        headers["content-type"] === "application/x-www-form-urlencoded";
    }
    return instance({
      url,
      method: "delete",
      data: isUrlencoded ? qs.stringify(params) : params,
      headers,
    });
  };

  /*
   * Login 方法
   */
  Vue.prototype.$login = (url, params, headers) => {
    return new Promise((resolve, reject) => {
      Vue.prototype
        .$post(url, params, headers)
        .then((res) => {
          if (res.success) {
            let { token, expir, redirect } = res.data;
            let path_to = redirect ? redirect : homeUrl; // 服务器返回redirect
            let local_redirect = getQueryVariable("redirect"); // 本地的redirect
            path_to = local_redirect ? decodeURI(local_redirect) : path_to;
            localStorage.setItem(tokenKey, token);
            localStorage.setItem("expire_at", expir);
            Message.success(res.message);
            router.push(path_to);
            resolve({
              status: true,
            });
          } else {
            Message.warning(res.message);
            resolve({
              status: false,
            });
          }
        })
        .catch((error) => {
          Message.error(error.message);
          reject(error.message);
        });
    });
  };

  /*
   * Logout 方法
   */
  Vue.prototype.$logout = (url, params) => {
    return instance(url, { params });
  };

  /*
   * getUserInfo 方法
   */
  Vue.prototype.$getUserInfo = () => {
    store.dispatch("user/updateUserInfo", { success: 111 });
    instance(api_user)
      .then((res) => {
        if (res.success) {
          let data = res.data;
          store.dispatch(namespace + "updateUserInfo", data);
        } else {
          console.log("获取失败");
          store.dispatch(namespace + "updateUserInfo", { success: 111 });
        }
      })
      .catch((error) => {
        console.log(error);
      });
  };
};

// 判断是否是直接引入文件
if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export { version, winyh };

export default { install, version, winyh };

配置参数可以随意配置自己需要的

Vue.use(plugin, {
  timeout: 1000, // 超时设置
  tokenKey: "token", // token本地存储的键
  whiteList: ["login"], // 白名单->暂未实现
  homeUrl: "/test", // 登录后默认跳转的首页
  api_user: "/dasa", // 获取用户信息的接口地址
  baseURL: "", // 请求前缀
  namespace: "", // vuex 命名空间配置
  headers: {
    // 请求头
    "Content-Type": "application/json",
  },
  loginConfig: { 
    logo: "", // 顶部 logo
    title: "系统名称", // 左侧标题
    description: "", // 左侧描述
    left_img: "", // 左侧图片
    system_name: "", // 右侧子系统名称
    system_logo: "", // 右侧子系统 logo
    api_login: "/api/auth/login", // 登录请求地址
  },
  router, // 暂时必填
  store, // 暂时必填
});

附加

当插件使用时,可以直接在项目里使用这些方法了,不需要每次都重新配置。把自己的包放到 npm. 就可以做版本维护和全局使用了。当然也可以在本地打包成.tgz 文件在本地安装使用 cnpm i /path/to/winyh.tgz

this.$post()

this.$get()

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: winyh 原文链接:https://juejin.im/post/6858423788451987470

回到顶部