# vue-pc-template **Repository Path**: guo-tongzhe/vue-pc-template ## Basic Information - **Project Name**: vue-pc-template - **Description**: 基于 `vue-cli3.0` +` webpack 4 `+ `element ui` + `sass` + `rem适配方案` + `axios` 封装的一套极简的vue-admin, vue-element-admin, vue后台, 后台系统, 后台框架, 管理后台, 管理系统 - **Primary Language**: JavaScript - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 0 - **Created**: 2023-11-06 - **Last Updated**: 2024-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: Vue, rem, Element-UI, Sass, Axios ## README # Vue-Pc-Template 基于 `vue-cli3.0` +` webpack 4 `+ `element ui` + `sass` + `rem适配方案` + `axios` 封装 ### ➰ Node版本要求 `Vue CLI` 需要 `Node.js `16.18.4。你可以使用 [nvm](https://github.com/nvm-sh/nvm) 在同一台电脑中管理多个 Node 版本。 ### ➰ 启动项目 ``` npm install npm run serve ``` 目录 - 🉑[Axios 封装及接口管理](#axios) - 🉑[rem适配方案](#rem) - 🉑[配置proxy跨域](#proxy) - 🉑[elementUI按需引入](#element) - 🉑[Message消息提示](#msg) - 🉑[引入本地字体包](#fonts) - 🉑[dayjs处理日期的统一解决方案](#dayjs) - 🉑[eslint+prettier统一开发规范](#prettier) - 🉑[Store-Vue状态管理工具](#store) - 🉑[PubSub-js消息的发布与订阅](#pubsub) ### ✔️ Axios 封装及接口管理 `utils/request.js`、`utils/axios.js` 封装 `axios` ```js // request.js const request = setAjaxHttp({ preFix: process.env.VUE_APP_PREFIX, // 接口前缀 timeout: 60 * 1000, // 接口超时时间配置 // 错误回调函数 Message: (message) => Message({ message, type: "error" }), // 最终结果回调 handleResponse: (res, gainError) => {}, // 用于接收ajax响应拦截里面return出来的数据 }); ``` ```js // 构造axios封装函数 function setAjaxHttp({ preFix, timeout, Message, handleResponse }) { // 定义入口网关 const prefix = preFix || ""; // 实例化一个axios const ajax = axios.create(); let setFileName = false; // 自定义文件下载名称 let gainError = false; // 单独处理错误标记 // 添加请求webpack-serve前缀/接口请求网关 function getBaseUrl(config) { // 单独接口使用 请求自定义 服务器地址、端口、接口网关 if (config.baseUrl) { config.url = config.baseUrl + config.url; delete config.baseUrl; } else { // 否则使用环境变量判断得到的,走代理域名和端口 config.url = prefix + config.url; } } // 设置token function setToken(config) { // ... ajax.thisToken = config.data.setToken; } // 添加token function addToken(config) { // ... config.headers["dn-token"] = ajax?.thisToken || store.state.global.token } // 设置下载文件名称 function setDownFileName(config) { // ... setFileName = config.data.setFileName; delete config.data.setFileName } // 单独处理错误标记 function setGainError(config) { // ... gainError = config.data.gainError; delete config.data.gainError; } // 单独设置超时时间 function setTimeOut(config) { // ... config.timeout = timeout; } // 转换为表单格式请求 function switchToFormData(config) { // ... if (config.data.transform) { // 通过axios提供的transformRequest 修改入参格式 config.transformRequest = [ function(data) { return Qs.stringify(data); }, ]; } } // 请求拦截 - 在发送请求之前做些什么 ajax.interceptors.request.use( (config) => { // `hideloading` 默认 `false`,设置为 `true` 后,不显示 loading ui 交互中有些接口不需要让用户感知 if (!config.hideLoading) { context.$loading.show(); } setLoading(config); // 是否打开loading getBaseUrl(config); // 添加请求webpack-serve前缀/接口请求网关 setToken(config); // 设置token addToken(config); // 添加token setDownFileName(config); // 设置下载文件名称 setGainError(config); // 单独处理错误标记 setTimeOut(config); // 单独设置超时时间 switchToFormData(config); // 转换为表单格式请求 return config; }, (error) => Promise.reject(error), ); // 响应拦截 ajax.interceptors.response.use( (res) => { context.$loading.hide(); // 关闭loading if (res?.config) { // 处理流数据的时候,直接返回结果,用于下载 if (res.config?.responseType === "blob") { return res; } } // 调用传入的参数处理函数 return handleResponse(res?.data, gainError); }, (error = "error") => { if (error.stack.indexOf("timeout") > -1) { Message && Message(error); } else if (error.msg !== undefined) { Message && Message(error); } context.$loading.hide(); return Promise.reject(error); }, ); // post请求下载文件,默认download ajax.download = function(url, params = {}) { return ajax({ method: "post", url, data: { ...params, }, headers: { "X-Requested-With": "XMLHttpRequest", }, responseType: "blob", }) .then(disposeRes) .catch((error) => { return Promise.reject(error); }); }; // 处理下载文件返回数据, // 1.获取/设置文件名。 // 2.将后端返回的二进制流转换为下载链接 // 3.动态生成a标签,使用a标签的download属性完成下载 function disposeRes(res) {} } ``` **接口管理** 在`src/api` 文件夹下统一管理接口 - 你可以建立多个模块对接接口, 比如 `home.js` 里是首页的接口这里讲解 `user.js` - `hideloading` 默认 `false`,设置为 `true` 后,不显示`loading ui` 交互中有些接口不需要让用户感知 - `baseUrl`默认获取当前环境下的`VUE_APP_PREFIX`,为了满足以后多端的情况,允许接口配置不同的接口前缀 - `transForm`将传参方式更改为`form-data`形式,非`get`请求都可以进行转换 ```js import request from "@/utils/request.js"; // 隐藏loading export const getUserInfo = (params) => request.get("/api/getUserInfo", params, { hideLoading: true }); // 更换接口前缀 export const getUserInfo = (params) => request.get("/api/getUserInfo", params, { baseUrl: "/api-prod" }); // 转换formData传参 export const setUserInfo = (params) => request.put("/api/putUserInfo", Object.assgin( params, { transForm: true })); ``` [▲ 回顶部](#top) ### ✔️ rem适配方案 #### 1.什么是rem rem是`CSS3`新增的相对长度单位,是指相对于根元素`html`的font-size计算值的大小。简单可理解为屏幕宽度的百分比。 但是,项目中常见尺寸绘制采用的均是`px`,要改用rem一时半会缓不过来,而且可能还要换算转换,所以使用rem还是比较麻烦的,但是,我们可以通过插件,能够将项目中的`px`转换为rem,还可以自定义基数。 #### 2.介绍 本项目中的样式默认使用`px`作为单位,我所使用的`vue`移动方案是使用`amfe-flexible` 和 `postcss-pxtorem` 结合的方式。 #### 3.安装相关依赖 ``` npm install amfe-flexible // CSS单位自适应转换插件 负责更改根font-size npm install postcss-pxtorem@^5.1.1 // 如果版本过高可以降版本下载5.1.1版本 负责将px转成rem ``` #### 4.引入`amfe-flexible` `amfe-flexible`是`lib-flexible`的升级版 flexible就是根据不同的屏幕算出`html`的font-size,通过`js`来动态写meta标签 实上他做了这几样事情: - 动态改写``标签 - - 给``元素添加`font-size`属性,并且动态改写`font-size`的值 举例说明: 我们知道 `1rem` 等于`html` 根元素设定的 `font-size` 的 `px` 值。我们设置 `rootValue: 192` 你可以看到在浏览器分辨率1920*1080下看到 (`1rem 等于 192px`) ```js ``` 当你按住`ctrl`并滚动鼠标滚轮,更改浏览器的分辨率,根元素的`font-size`属性会动态的发生变化。 浏览器分辨率2560*1240 ```js ``` 在`main.js`中引入`amfe-flexible` ```js import "amfe-flexible" ``` #### 5.配置.postcssrc.js 配置`postcss-pxtorem `,可在`vue.config.js`、`postcsssrc.js`、`postcss.config.js`其中之一配置,权重从左到右降低,没有则新建文件,只需要设置其中一个即可 ```js /*** 注意点: (1)rootValue根据设计稿宽度除以10进行设置,这边假设设计稿为1920,即rootValue设为192; (2)propList是设置需要转换的属性,这边*为所有都进行转换。 ***/ module.exports = () => { return { plugins: { "postcss-pxtorem": { rootValue: 192, //根元素字体大小 propList: ["*"], //可以从px转换为rem的属性,匹配正则 // unitPrecision:5, //允许rem单位增长的十进制数字 // replace:true, //替换包含rems的规则,而不添加后备 // mediaQuery:false, //允许在媒体查询中转换px // minPixelValue:0, //设置要替换的最小像素值 // selectorBlackList:[], //忽略转换正则匹配项 // exclude:/node_modules/i 要忽略并保留为px的文件路径 } } } } ``` #### 6.总结 ##### 不能使用行内样式 对于行内样式,插件并不能将`px`转rem,所以对于需要自适应的样式,如font-size、width、height等请不要写在行内。同理,对于不需要转化的样式可以写在行内,或者使用PX(大写)作为单位。 ##### `echarts`适配 ```js // 用途:echarts字体适配 /** * echarts字体自适应 * @param {*} font 字号大小 */ export function echartGetFontSize(font) { let docEl = document.documentElement, clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; if (!clientWidth) return; let fontSize = clientWidth / 1920; return font * fontSize; } ``` ##### form表单`labelWidth`行内样式适配 ```vue // 在vue.config.js中 定义全局的$px2rem // 转换el-form上的labelWidth 将行内样式转换成rem单位 function px2rem(px) { const html = document.getElementsByTagName("html")[0]; const fontSize = html.style.cssText.split(": ")[1].split("px")[0]; return (px / fontSize).toFixed(7) + "rem"; } Vue.prototype.$px2rem = px2rem; // 在form表单页面进行使用 ... // 利用computed监听labelWidth computed: { labelWidth() { return this.$px2rem(140) } } ``` [▲ 回顶部](#top) ### ✔️ 配置 proxy 跨域 开发环境 在`.env.development`文件中定义接口的前缀`VUE_APP_PREFIX` 前端项目启动时,会在本地启动一个node环境,相关配置也就是我们在`vue.config.js`中配置的`devServer` ```vue // vue.config.js下配置 devServer: { open: false, // 启动时,是否自动打开浏览器 port: "8100", hotOnly: true, // 热更新 overlay: { // 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层 warnings: false, errors: false, }, proxy: { // 接口监听、转发 [process.env.VUE_APP_PREFIX]: { target: "https://test.xxx.com", // 接口的域名 changeOrigin: true, // 开启代理,在本地创建一个虚拟服务端 pathRewrite: { ["^" + process.env.VUE_APP_PREFIX]: "/", }, }, // 静态资源文件的监听、转发 "/static": { target: "https://test.xxx.com", changeOrigin: true, }, }, }, ``` 举例说明: ```js // .env.development文件中定义了 VUE_APP_PREFIX = "/api" // 封装了axios,自动获取process.env.VUE_APP_PREFIX,并拼接到接口传递的url中 config.url = process.env.VUE_APP_PREFIX + config.url; // 这是一个获取用户信息的接口 export const getUserInfo = (params) => request.get("/test/userInfo", params); /** 1.经过系统拼接后,接口的地址变成了"/api/test/userInfo",开始发送请求 2.请求传递到devServer,proxy属性中我们对VUE_APP_PREFIX(`/api`)进行了监听 3.target配置开始执行,接口地址变成"https://test.xxx.com/api/test/userInfo" 4.pathRewrite配置开始执行,接口地址变成"https://test.xxx.com/test/userInfo" 5.开始向后端发送请求 */ ``` [▲ 回顶部](#top) ### ✔️ elementUI按需引入 **错误方式** ```js import ElementUI from "element-ui"; // 799.38kB import "element-ui/lib/theme-chalk/index.css"; // 242.83kB ``` 由上所示,我们在main.js中引入了`elementUI`,大小加起来接近1M,页面的加载时间可能会达到**20秒!** 原因是,我们在build的时候所有的依赖包,都会打包到这个文件里面,比如说我们页面上用到了`element-ui`第三方组件的时候,其组件的一些样式和API函数都会被封装到chunk-vendors.js和chunk-vendors.css里面去,这个封装默认是全量的,所以说在写代码时候要注意的是,尽量别在main.js里面全量引入 ##### 1.Vue2 `elementUI`按需引入 1.借助`babel-plugin-component`,我们可以只引入需要的组件,以达到减小项目体积的目的 ``` npm install babel-plugin-component -D ``` 2.将 `babel.config.js(.babelrc)` 修改为: ```js module.exports = { presets: [ '@vue/cli-plugin-babel/preset', ["@babel/preset-env", { "modules": false }], ], plugins:[ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] } ``` 3.在`src`目录下创建`plugins`文件夹,并创建`element.js`文件 ```js // 按需全局引入 elementUI组件 import Vue from "vue"; import { Button } from "element-ui"; Vue.use(Button); ``` ##### 2.Vue3 `elementUI`按需引入 **如何解决这个问题** 1.先安装两个组件unplugin-vue-components 和 unplugin-auto-import安装命令如下 ``` npm install -D unplugin-vue-components unplugin-auto-import ``` 2.安装成功后在vue.config.js里面添加如下两段代码 ```js const AutoImport = require('unplugin-auto-import/webpack') const Components = require('unplugin-vue-components/webpack') const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') ``` ```js configureWebpack: { // 按需导入elementUI plugins: [ AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }) ] } ``` [▲ 回顶部](#top) ### ✔️Message消息提示 全局导入ElementUI,会为Vue.prototype 添加了全局方法 `$msgbox`、`$notify`、`$message`、`$confirm` 在utils文件夹下,利用这些全局方法定义了八种统一的消息提示类型 ```js export default { error: showError, // 错误提示 msg: showMessage, // 展示msg信息 success: showMessage, // 展示成功信息 warning: showWarning, // 展示警告信息 info: showInfo, // notify: showNotify, // ask: ask, // 询问 del: del, // 删除box alert: alertMsg, // 警告 }; ``` 在main.js中引入 ```js import Msg from "./utils/message"; // 挂载到vue实体上 Vue.prototype.$msg = Msg; ``` [▲ 回顶部](#top) ### ✔️ 引入本地字体包 在前端开发过程中,为了满足UI的实体效果,需要导入大量的字体包。 ##### 1.下载或者找UI要字体包 将字体包放入到`src/assets/font/`文件夹下 ##### 2.定义字体名称 在`src/styles/`文件夹下创建`font.less`,定义`@font-face` ```html @font-face { font-family: "honor-bold"; src: url("../assets/font/HONORSans-Bold.ttf") format("truetype"); } @font-face { font-family: "Microsoft-regular"; src: url("../assets/font/MicrosoftYaHei.ttf") format("truetype"); } ``` ##### 3.使用 为了满足windows与ios下不同字体的适配,在`main.js`下通过判断`userAgent`更改根元素的`font-family`属性 一定要加上`sans-serif`字体,为了防止系统找不到我们导入的字体,默认使用系统字体。 ```js // 不同浏览器环境使用不同字体 const userAgent = window.navigator.userAgent.toLowerCase(); const regex = /windows/; if (regex.test(userAgent)) { // windows系统 document.documentElement.style.fontFamily = "Microsoft-regular, Microsoft-bold, pingFang-bold, Arial, sans-serif"; } else { // 苹果系统 document.documentElement.style.fontFamily = "pingFang-regular, pingFang-bold, Arial, sans-serif"; } ``` 同样的我们在`vue`页面中使用的时候也要加上`sans-serif`字体,防止页面出现`warning` ```hmtl font-family: Microsoft-bold, pingFang-bold, sans-serif; ``` ##### 4.压缩字体包 获取到的字体包包含了全世界的字体,以`微软雅黑`字体为例,一种格式类型的字体大概在15M左右,系统初次加载需要耗时20-30s时间,影响用户体验 所以需要对字体包进行压缩,只获取我们常用的字体即可 用python上面的字库提取工具fontTools可以一步解决这个问题 首先我们得下载常用的字体[[3500字常用字体.txt](https://gist.github.com/MoyuScript/f94d0c594e47113b209156a653aaba93#file-%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A83500%E5%AD%97-txt)] ```json lines // 1.安装 pip install fontTools // 2.在字体包文件夹下打开cmd,直接运行fontTools自带的指令 // subset 待提取的字库 // –text-file 需要提取的字 // –output-file 输出的字库 fonttools subset "song.ttf" --text-file="3500w.txt" --output-file="song2.ttf" ``` [▲ 回顶部](#top) ### ✔️ dayjs日期处理的统一解决方案 dayjs是一个轻量的处理时间和日期的 JavaScript 库 **dayjs好处** - 🕒 和Moment.js有着相同的API和模式 - 💪 不可变、持久性 - 🔥 提供链式调用 - 🌐 国际化标准 - 📦 超小的压缩体积,仅仅有2kb左右 - 👫 极大多数的浏览器兼容 **dayjs安装** ``` npm install dayjs --save ``` **dayjs使用** ```javascript // 全局使用:在main.js文件中直接导入 import dayjs from 'dayjs' // 挂载到vue实例上 Vue.prototype.$dayjs = dayjs; ``` **常用的dayjs方法** ```js // 初始化日期/时间 dayjs().format('YYYY-MM-DD'); // 初始化日期 dayjs().format('YYYY-MM-DD HH:mm:ss'); // 初始化日期时间 // 格式化日期 / 时间 dayjs(value).format('YYYY-MM-DD'); // 初始化日期 dayjs(value).format('YYYY-MM-DD HH:mm:ss'); // 初始化日期时间 // 加 / 减 dayjs().add(7, 'day').format('YYYY-MM-DD'); // 2023-03-09 今天(2022-03-02)加上7天 dayjs().add(1, 'month').format('YYYY-MM-DD'); // 2023-04-02 今天(2023-03-02)加上一月 dayjs().subtract(2, 'year').format('YYYY-MM-DD'); // 2021-03-02 今天(2023-03-02)减去2年 dayjs().subtract(2, 'hour').format('YYYY-MM-DD HH:mm:ss'); // 2023-03-02 14:03:39 今天现在(2023-03-02 16:03:39)减去2小时 // 获取某年某月的第一天: dayjs().startOf('year').format('YYYY-MM-DD HH:mm:ss') // 2022-01-01 00:00:00 => 第一天格式化出来的时分秒都是0 dayjs().startOf('month').format('YYYY-MM-DD') // 2022-04-01 // 获取某年某月的最后一天: dayjs().endOf('year').format('YYYY-MM-DD HH:mm:ss') // 2022-12-31 23:59:59 => 最后时间 格式化出来的时分秒是23:59:59 dayjs().endOf('month').format('YYYY-MM-DD') // 2022-04-30 // 获取星期几 dayjs().day() // 返回0(星期日)到6(星期六)的数字 // 设置时也只能接受 0-6 的数字: dayjs().day(6).format('YYYY-MM-DD') // 获取最近周六的日期 => 2023-03-04 dayjs().day(0).format('YYYY-MM-DD') // 获取最近周日的日期 => 2023-03-05 // 获取毫秒数 dayjs('2019-01-25').valueOf() dayjs().valueOf() // 获取时间差(默认输出的差值单位是毫秒) dayjs('2019-01-25').diff('2018-06-05', 'month'); // 7 dayjs('2019-01-25').diff(dayjs('2018-06-05'), 'month'); // 7 // 获取时、分、秒 // 当前时间:2023-03-02 16:55:55 console.log('-----获取年', dayjs().year()); // ==>> 2023 console.log('-----获取月', dayjs().month()); // 0到11的数字 ==>> 2 console.log('-----获取星期', dayjs().day()); // 0(星期日)到6(星期六)的数字 ==>> 3 console.log('-----获取天', dayjs().date()); // 1到31的数字 ==>> 02 console.log('-----获取小时', dayjs().hour()); // 0到23的数字 ==>> 16 console.log('-----获取分钟', dayjs().minute());// 0到59的数字 ==>> 55 console.log('-----获取秒', dayjs().second()); // 0到59的数字 ==>> 55 console.log('-----获取毫秒', dayjs().millisecond()); // 0到999的数字 ==>> 333 // 9. 将毫秒转为时分秒 const timestr = 1650447800731; // 毫秒值必须是number类型,如果是string,结果可能和你想的不一样 console.log('将毫秒转为年-月-日 时:分:秒', dayjs(timestr).format('YYYY-MM-DD HH:mm:ss')); console.log('获取年', dayjs(timestr).year()); console.log('获取月', dayjs(timestr).month()); console.log('获取天', dayjs(timestr).date()); console.log('获取时', dayjs(timestr).hour()); console.log('获取分', dayjs(timestr).minute()); // 10. 判断一个日期是否在另外一个日期之后 isAfter // day.js 为 2023-03-02 console.log('isAfter', dayjs().isAfter(dayjs('2011-01-01'))) // true console.log('isAfter', dayjs('2022-04-20').isAfter(dayjs('2022-04-21'))) // false console.log('isAfter', dayjs('2022-04-20').isAfter(dayjs('2022-04-20'))) // 相同也为false // 11. 判断一个日期是否在另外一个日期之前 isBefore // day.js 为 2023-03-02 console.log('isBefore', dayjs().isBefore(dayjs('2011-01-01'))) // false console.log('isBefore', dayjs('2022-04-20').isBefore(dayjs('2022-04-21'))) // true console.log('isBefore', dayjs('2022-04-20').isBefore(dayjs('2022-04-20'))) // 日期相同时也为false // 12. 判断两个日期是否相同 isSame // day.js 为 2023-03-02 console.log('isSame', dayjs().isSame(dayjs('2011-01-01'))) // false console.log('isSame', dayjs('2022-04-20').isSame(dayjs('2022-04-21'))) // false console.log('isSame', dayjs('2022-04-20').isSame(dayjs('2022-04-20'))) // true ``` [▲ 回顶部](#top) ### ✔️ eslint+prettier统一开发规范 ##### 1.安装eslint与prettier ``` npm install @babel/eslint-parser@^7.18.2 babel-eslint@^10.1.0 eslint@^8.22.0 eslint-config-prettier@^8.5.0 eslint-plugin-prettier@^4.2.1 eslint-plugin-vue@^9.2.0 prettier@^2.7.1 -D ``` ##### 2.创建`.eslintrc.js`文件 ```js module.exports = { root: true, env: { node: true }, extends: ['plugin:vue/essential', 'plugin:vue/recommended', 'eslint:recommended', 'plugin:prettier/recommended'], parserOptions: { parser: '@babel/eslint-parser' }, rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 关闭名称校验 'vue/multi-word-component-names': 'off' }, } ``` ##### 3.创建`.eslintignore`文件 ```json lines dist/* node_modules/* packages/* public/* ``` ##### 4.创建`.prettierrc`文件 ```html { "arrowParens": "avoid", // 在唯一的箭头函数参数周围包含圆括号 (avoid)尽可能省略parens "bracketSpacing": true, // 在对象字面量的括号之间打印空格 "endOfLine": "crlf", // 回车+换行字符(\r\n), Windows中常见 "htmlWhitespaceSensitivity": "ignore", // 所有标记周围的空白(或没有空白)被认为是无关紧要的 "ignorePath": ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中 "jsxBracketSameLine": false, // 在jsx中把'>' 是否单独放一行 "jsxSingleQuote": false, // 在JSX中使用单引号而不是双引号 "printWidth": 140, // 指定自动换行的行长,默认值80 "quoteProps": "as-needed", // 只在需要的对象属性周围添加引号 "semi": true, // 在语句的末尾打印分号,默认值为true "singleQuote": false, // 使用单引号代替双引号 "tabWidth": 2, // 指定每个缩进级别的空格数,默认值为2个空格 "trailingComma": "all", // 在多行逗号分隔的语法结构中,尽可能打印尾随逗号。(例如,单行数组的后面永远不会有逗号。) "useTabs": false, // 用制表符代替空格缩进行,默认值为false "vueIndentScriptAndStyle": false } ``` ##### 5.创建`.prettierrignore`文件 ```json lines dist/* node_modules/* packages/* public/* ``` [▲ 回顶部](#top) ### ✔️ Store-Vue状态管理工具 ##### 1.store配置步骤 - 首先:在main.js中引入 ```js import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; new Vue({ router, store, render: (h) => h(App), }).$mount("#app"); ``` - 第一步:在src下新建store文件夹,其中新建index.js ```js import Vue from 'vue' import Vuex from 'vuex' import map from './modules/app'; Vue.use(Vuex); export default new Vuex.Store({ modules: { app } }) ``` - 第二步:在store文件夹里,新建modules文件夹,其中新建app.js ```js const state = { count: 3, }; // 同步方法,mutations主要用来操作state const mutations = { SET_COUNT(state, count) { state.count = count; }, }; // 异步方法 const actions = { setCount({ commit }, count) { commit("SET_COUNT", count); }, }; // 计算属性 const getters = { getCount: (state) => state.count, }; export default { state, mutations, actions, getters, namespaced: true, }; ``` - 第三步:在index.vue文件夹里进行引入调用 ```vue {{ count }}---{{ countGetter }} 123123 ``` ##### 2.vuex数据持久化 vue使用状态管理vuex弊端之一就是刷新页面导致数据丢失 这可以使用vuex-persistedstate这一插件来解决 - 下载vuex-persistedstate插件 ``` npm i vuex-persistedstate ``` - store/index.js 引入插件,配置参数 ```js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //引入数据持久化插件vuex-persistedstate import createPersistedState from 'vuex-persistedstate' //这里引入app import app from './modules/app' const store = new Vuex.Store({ //模块home 后面的模块都写在这里 modules:{ app }, //配置数据持久化 这里是所有vuex里数据都持久化 plugins:[createPersistedState()] }) export default store ``` 上面的配置导致vuex里所有的变量都持久化了,这在实际开发中是我们所不希望的。 那么通过进一步的设置 可以实现按需持久化(让指定模块里的数据保持持久化) ```js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //引入数据持久化插件vuex-persistedstate import createPersistedState from 'vuex-persistedstate' //这里引入app import app from './modules/app' const store = new Vuex.Store({ //模块home 后面的模块都写在这里 modules:{ app }, //配置数据持久化 所有vuex里数据持久化 //plugins:[createPersistedState()] //按需配置数据持久化 指定vuex里(home模块)数据持久化 plugins:[createPersistedState({ //默认名称vuex key:'appCount', //默认localStorage 本地储存 这里建议临时储存 也可写成window.sessionStorage storage: window.sessionStorage, //默认vuex全部变量 paths:['app'] })] }) export default store ``` [▲ 回顶部](#top) ### ✔️ PubSub-js消息的发布与订阅 原生的js里面没有办法轻松的实现订阅与发布,推荐的是名为**pubsub-js**的第三方库,这个库你可以在任何框架(不仅是VUE框架)里面都能实现消息的订阅与发布。 ##### 1.下载pubsub-js ``` npm i pubsub-js ``` ```js // 同时,在main.js文件中进行引用,并挂载到vue.prototype上 import pubsub from "pubsub-js"; Vue.prototype.$bus = pubsub; ``` ##### 2.如何使用pubsub ```js // 1.任意一个组件,订阅消息 this.pubID = this.$bus.subscribe('demo', (msg) => { console.log('demo消息被发布,回调函数执行并收到数据:', msg) }) // 2.任意一个组件,发布消息 this.$bus.publish('demo', 666) // 3.在第一个组件中取消订阅(要在beforeDestroy钩子里面执行) // 实例销毁之前取消订阅消息 this.$bus.unsubscribe(this.pubID) ``` [▲ 回顶部](#top)