自我简介:4年导游,10年程序员,最近6年一直深耕低代码领域,分享低代码和AI领域见解。
什么是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 = trueVue.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; // 服务器返回redirectlet local_redirect = getQueryVariable("redirect"); // 本地的redirectpath_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: "", // 顶部 logotitle: "系统名称", // 左侧标题description: "", // 左侧描述left_img: "", // 左侧图片system_name: "", // 右侧子系统名称system_logo: "", // 右侧子系统 logoapi_login: "/api/auth/login", // 登录请求地址},router, // 暂时必填store, // 暂时必填
});
附加
当插件使用时,可以直接在项目里使用这些方法了,不需要每次都重新配置。把自己的包放到 npm. 就可以做版本维护和全局使用了。当然也可以在本地打包成.tgz
文件在本地安装使用 cnpm i /path/to/winyh.tgz
this.$post()
this.$get()
…
AI时代,对各行业的冲击力只会越来越大,随着AI大模型的竞赛,越来越多强悍的AI模型都会涌现,像软件开发行业的很多工作都会被取代。软件将不再是程序员的专属产物,会由AI创建很多的软件产品。
4年导游,10年程序员,深耕低代码领域6年,持续分享低代码和AI领域领域有价值的思考和沉淀,欢迎关注:winyh5
后续会推出:【挑战365天做 100 套常见的互联网系统】系列文章,让大家可以真实感受到低代码快速落地项目的可行性