vue-element-admin项目中可能遇到的一些问题

10,022 阅读9分钟

这篇文章记录我用vue写项目的过程中所有遇到的一些问题, 模板使用的是vue-element-admin

1. 项目中的环境问题

.env文件主要作用是存储环境变量, 也就是会随着环境变化的东西。 因为这些信息应该是和环境绑定的, 不应该随代码的更新而变化, 所以一般不会把.env文件放到版本控制中

.env 全局默认配置文件
.env.development 开发环境下的配置文件
.env.production 生产环境下的配置文件

运行不同的脚本打包, 会生成对应的环境

  • package.json
"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build", // 本地
    "build:prod": "vue-cli-service build --mode production", // 生产
}
  • 测试环境 .env.development
.env.development
NODE_ENV='production'
VUE_APP_PROFILE='test'
VUE_APP_URL='test'
  • 生产环境 .env.production
.env.prod
NODE_ENV='production'
VUE_APP_PROFILE='prod'
VUE_APP_URL='prod'

2. el-image本地图片无法加载

首先, 如果使用el-image 报 Unknown custom element: <el-image>这种错 。说明element-ui版本太低了,el-image是后续版本才加上的属性。需要卸载后重新安装更高版本 。下面为安装步骤
步骤1 :npm uninstall element-ui ,步骤2: npm i element-ui -S, 步骤3: main.js中default修改为theme-chalk。

而本地路径无法显示是因为 element 组件上使用的是相对路径, webpack 并不会对路径进行处理,导致找到了一个无效的路径。 解决方法:

  • 方法1: 使用绝对路径
<el-image :src="'https://fuss10.elemecdn.com/e/5d/1.jpg'"></el-image>
  • 方法2: 使用require
<el-image :src="require('@/assets/images/icon/file/1.jpg')"></el-image>
// 或者
<template>
  <el-image :src="imgUrl">
</template>
<script>
export default {
  data:function(){
    return {
      imgUrl: require("@/assets/images/icon/file/1.jpg")
    }
  }
}
</script>
  • 方法3: 使用import
<template>
  <el-image :src="imgUrl">
  </el-image>
</template>
<script>
import jpg from '@/assets/images/icon/file/1.png'
export default {
  data:function(){
    return {
      imgUrl: jpg
    }
  }
}
</script>

4. 父子组件传值和互相调用彼此的方法

  • 1. 父传值给子
    子组件 通过props接收, 通过this.属性名使用
//父  
<child-comp :sendMsg="msg"></child-comp> 
import childComp from './childComp'
components:{
    childComp
},
//子  这是简单写法
props: ['sendMsg']  
// 推荐写法
props: {
    type: [String, Number],
    default: 'sendMsg'
}

父组件中, 不加:,代表传递的是固定值, 加:,代表传递的是变量。 如果想传递数字, 需要加:, 否则传递的是字符串

  • 2. 子传值给父
  1. 子组件通过 $emit通知父组件的方法,并将值传递过去。父组件通过函数去处理子组件传过来的值
//父  注意 这里是`@` ,别写成`:` 了  <child-comp @中间件="父方法"></child-comp>
<child-comp @func="getChildValue"></child-comp>
getChildValue(data){
  console.log(data); // 我是子传过来的值
}
// 子
<button @click="childClick">我是子组件的button</button>
childClick() {
  this.$emit('func','我是子传过来的值')
}
  1. 使用 :val.syncupdate:val 传值

这种方法其实就是子传父$emit传值, @接收值的语法糖 。 下面的例子是一个select选择框公共组件, 父通过:val.syncsearchObj.pushFlag的值传给子。 子通过props接收, 注意, 不能直接使用传给子的val,否则报错。 在data中先将值赋给value。 最后通过select的change事件, 将值传给父。 父中的值就能直接改变了

// 父
<issued-select :val.sync="testData"></issued-select>

// 子
<el-select
 @change="selectChange"
 v-model="value"
/>

props: {
   val: {
     type: [Number, String],
     default: ""
   }
}

data() {
   return {
       value: this.val
   }
}

selectChange(val) {
    this.$emit("update:val", val);
}

5. 端口号被占用而无法正常访问后台数据

  • 核心代码 lsof -i :端口号 , kill -9 PID号

  • 还原场景: 我的项目,开始启动时的端口号是9527,如果按ctrl + z。项目占用的端口号不停止, 所以下次npm run dev的时候, 会依次往后+1 端口号。这样就造成了,访问后台URL时端口号是9528,会报错。正确结束端口的命令是ctrl + c。 如果想杀死某个端口占用,首先根据端口号找到是哪个进程占用的。 然后根据这个进程的PID ,杀死这个进程。

  • 根据端口号查找进程的命令: lsof -i :9527

  • 根据返回的进程PID号, 杀死这个进程的命令: kill -9 3485

  • 如果没有任何报错,或者命令不存在等提示, 就证明已经杀死了这个进程

6. 页面跳转缓存锚点, 刷新将锚点丢弃

当前页跳转到其他页时, 缓存当前页目前处于的高度。 以后重新返回当前页时, 直接锚点跳转到缓存的高度。 如果刷新页面, 将缓存清掉。

// 离开当前页时记录距离顶部的位置,存到sessionStorage里,方便回来时取用
beforeRouteLeave(to, from, next) {
    sessionStorage.setItem('historyScollTop', window.pageYOffset);
    next();
},

// 在当前页的所有异步请求完毕之后。 进行锚点跳转操作
const historyScollTop = sessionStorage.getItem('historyScollTop');
if(historyScollTop){
    this.$nextTick(()=>{
        window.scrollTo(0, historyScollTop);
    })
};

// 刷新页面将记录的页面高度缓存清除。 在mounted生命周期函数里给window添加事件。 `beforeunload钩子`。 在这里把`historyScollTop`清除
mounted(){
    window.addEventListener("beforeunload", function(e) {
      sessionStorage.removeItem('historyScollTop');
    });
}

7. 关闭eslint校验规则

直接查看vue-element-admin文档。 其实很多问题,查文档都是有解决办法的。所以,碰到这类问题,最好能第一时间查文档。

如果你不想使用 ESLint 校验(不推荐取消),只要找到 vue.config.js 文件。 进行如下设置 lintOnSave: false 即可

提交代码git commit -m "test"的时候,如果由于代码校验而无法提交输入以下命令 git commit --no-verify -m "test"来关闭代码校验

12. 在单独引入js文件中使用ui的组件, 以element-ui为例。 其他ui组件类似

import { Message } from 'element-ui';
// import { Toast } from 'vant';  手机端vant提示

export function warningMsg(str) {
    Message({
        message: `${str}`,
        type: 'warning'
    }); 
}

其他更多组件的引入看element-ui官网
element.eleme.cn/#/zh-CN/com…

13. 创建全局变量

原理: Vue内部有data属性, 可以将自己定义的变量赋值给data属性, 然后通过$root.xxx进行访问。

// globalVar.js 自己变量的js文件。 
const gData = {
    testObj: {
        name: 'andy',
        age: '33'
    },
    gender: '男'
}

export default gData;

// main.js 在main.js中引入定义的变量并写入new Vue对象中
import gData from './utils/globalVar'
new Vue({
  router,
  store,
  data:gData,
  render: h => h(App)
}).$mount("#app");

// use.vue 在.vue文件中使用
<div>{{$root.testObj.name}}</div>
getAge(){
	return this.$root.testObj.age
}

14. 创建全局公共组件(重要)

vue项目中, 如果需要使用一些比较常用的组件。 那么最好能注入到vue全局上。否则在每个vue文件中都引入一遍,是比较低效的做法。 这种全局组件推荐前边在文件前加个g进行区分。 比如gHeader, gDialog。 下面推荐2种写法。

方法1(推荐): 直接把公共组件.vue文件全放在components文件夹下, 新建importGlobalComp.js,用正则匹配所有的这个文件夹下.vue结尾的文件,然后用js方法去遍历这个文件夹下的所有文件注入到vue实例上, 最后在main.js中引入importGlobalComp.js就可以在全局使用了。
注意: 这种方式components下的所有.vue文件都会被注册为全局组件。 要想只让components下的子文件注册到全局, 把require.context中是否查询其子目录设置为false即可。

// importGlobalComp.js

// 全局注册全局组件
import Vue from 'vue';

const requireComponent = require.context(
    // 其组件目录的相对路径
    // 这块是存放公共组件的文件目录, 是需要根据相应的路径更改的
    '@/components',
    // 是否查询其子目录
    true,
    // 匹配基础组件文件名的正则表达式
    /\.vue$/
);

requireComponent.keys().forEach((fileName) => {
    // 获取组件配置
    const componentConfig = requireComponent(fileName);
    // 获取组件的 PascalCase 命名
    let componentName =
        // 获取和目录深度无关的文件名
        fileName.replace(/^\.\//, '').replace(/\.\w+$/, '');
    // 递归遍历文件下的.vue文件, 获取其文件名。 
    let reverseName = componentName.split('').reverse().join('')
    componentName = reverseName.split('/')[0].split('').reverse().join('');
    // 全局注册组件
    Vue.component(
        componentName,
        // 如果这个组件选项是通过 `export default` 导出的,
        // 那么就会优先使用 `.default`,
        // 否则回退到使用模块的根。
        componentConfig.default || componentConfig
    );
});
// main.js中引入js文件
import '@/utils/importGlobalComp'

方法2: 在global.js中引入组件, 然后注入到vue的install内部。 把global.js导出后, 在main.js中引入,并通过Vue.use(global)注入到vue的原型上使用

// global.js
import vuePicturePreview from "vue-picture-preview";
import gTest from "@/components/gTest"

const GlobalComp = {
    install: function(Vue){
        Vue.component('Previewer', vuePicturePreview)
        Vue.component('gTest', gTest)
    }
}

export default GlobalComp

// main.js
import global from './utils/global'
Vue.use(global)

// 在vue文件中使用
<preview></preview>
<g-test></g-test>

封装的一些组件

15. 在项目中添加过滤器filter

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

在项目中可以使用下面2种方式来添加filter过滤器函数。 假设我们需要添加2种过滤器函数。 1,用来添加千分位符。 2, 格式化日期。

方法1. 在filters.js中写导出函数, 在main.js中引入并遍历使用 (推荐)

// filters.js
import moment from 'moment'
export function thousand(num) {
    return parseFloat(num).toFixed(2).replace(/\d(?=(?:\d{3})+\b)/g, `$&,`)
}

export function date(str, formatStr = 'YYYY-MM-DD HH:mm:ss') {
    return moment(str).format(formatStr)
}

// main.js
import * as filters from '@/utils/filters.js'

Object.keys(filters).forEach(key=>{
  Vue.filter(key, filters[key])
})

// test.vue 使用
<h4>{{198324 | thousand}}</h4> // 198,324.00
<h4>{{new Date() | date}}</h4> // 2020-07-01 21:07:14

方法2. 直接filters.js中写过滤函数

// filters.js
import Vue from 'vue'
import moment from 'moment'

Vue.filter('date', (res, formatStr = 'YYYY-MM-DD HH?mm?ss') => {
    return moment(res).format(formatStr)
})

Vue.filter('thousand', (str) => {
	if(!str) {
    	return str;
    };
    return parseFloat(str).toFixed(2).replace(/\d(?=(?:\d{3})+\b)/g, `$&,`);
})

// 在main.js  引入
import '@/utils/filters.js'

// test.vue 使用
<h4>{{198324 | thousand}}</h4> // 198,324.00
<h4>{{new Date() | date}}</h4> // 2020-07-01 21:07:14

16. 在项目中使用Vue.directive自定义指令

自定义指令的5个生命周期(均为可选),分别是 bind, inserted, update, componenUpdated, unbind

// 函数简写
Vue.directive('color-swatch', function (el, binding) {
  el.style.backgroundColor = binding.value
})

/**
 * @description 例子1
 * 自动聚焦
 */
Vue.directive("focus",{
    // 获取光标在inserted中操作,此时元素已经插入到父节点了
    inserted(el){
        el.focus();
    }
});

/**
 * @description 例子2
 * 实现当前使用了v-color这个指令的元素的文本颜色是和 data中定义好的color中的颜色值一致
 */
Vue.directive('color', (el, binding) => {
    el.style.color = binding.expression || 'blue';
})

// .vue文件
<div v-color>字体颜色为默认蓝色</div>  // 字体颜色为blue
<div v-color="red">字体颜色为自定义颜色</div>  //字体颜色为红色

17. 初始化css, Vue项目初始样式重置

步骤1: normalize.css插件, 步骤2: 在main.js中引入

// 在terminal中安装依赖
cnpm i normalize.css -S

// 在main.js中引入css
import 'normalize.css/normalize.css'

18. vue router路由点击自身报错

路由首次点击没问题, 如果再点击这个页面的导航就会报上述的错误 解决办法:

  1. 升级vue-router. (推荐)
cnpm i vue-router@3.0 -S
  1. 在main.js中添加以下代码
import Router from 'vue-router'
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

19. vue项目中使用sass

vue的webpack项目中需要安装上node-sass、sass-loader和style-loader。 然后在.vue文件的style中假如lang="scss"就可以使用sass语法了

// 在terminal中安装sass所需要的依赖
npm i node-sass sass-loader -S
// 在.vue文件中使用
<style lang="scss" scoped></style>

20. 点击按钮刷新当前页面

  • 方法1: 最直接整个页面重新刷新 location.reload() // 使用h5原生的reload方法 this.$router.go(0) // 使用vue路由go方法 // 这两种都可以刷新当前页面的,缺点就是相当于按ctrl+F5 强制刷新那种,整个页面重新加载,会出现一个瞬间的空白页面,体验不好

  • 方法2: 建一个空白页面supplierAllBack.vue 点击按钮的时候先跳转到这个空白页,然后在supplierAllBack.vue文件中, 通过组件内路由做判断, 返回from来的路由。 这个方式,相比第一种不会出现一瞬间的空白页,只是地址栏有个快速的切换的过程

  • 方法3: (推荐✅) provide / inject 组合 首先,要修改下你的App.vue, 通过声明reload方法,控制router-view的显示或隐藏,从而控制页面的再次加载,这边定义了isRouterAlive //true or false 来控制, 然后在需要当前页面刷新的页面中注入App.vue组件提供(provide)的 reload 依赖,然后直接用this.reload来调用就行

// App.vue
<template>
  <div id="app">
    <router-view v-if="isRouterAlive" />
  </div>
</template>

<script>
export default {
  name: 'App',
  provide() {
    return {
      reload: this.reload
    }
  },
  data() {
    return {
      isRouterAlive: true
    }
  },
  methods: {
    reload() {
      this.isRouterAlive = false;
      this.$nextTick(function() {
        this.isRouterAlive = true;
      })
    }
  }
}
</script>

// 刷新页面, 就两行代码。 注入, 和函数中调用this.reload()
inject: ['reload'],
 
clickToRefresh(){
	this.reload();
} 

21. 操作当前元素相关dom节点(父,子, 兄弟节点等)

使用原生js的api来操作

// html相关
<div class="parentEle" >
  <div>
    <p>1.1</p>
    <p id="string">1.2</p>
    <p>1.3</p>
  </div>
  <div @click = "clickTwo($event)">
    <p>2.1</p>
    <p id="string">2.2</p>
    <p>2.3</p></div>
  <div>第3个元素</div>
</div>

// js相关
clickTwo(e) {
  // e.target 指向触发事件监听的对象。
  console.log(e.target);
  // e.currentTarget 指向添加监听事件的对象
  console.log(e.currentTarget);
  // 获得点击元素的前一个元素
  console.log(e.currentTarget.previousElementSibling.innerHTML); 
  // 获得点击元素的第一个子元素
  console.log(e.currentTarget.firstElementChild); 
  // 获得点击元素的下一个元素
  console.log(e.currentTarget.nextElementSibling); 
  // 获得点击元素的父元素
  console.log(e.currentTarget.parentElement);
  // 获得点击元素的前一个元素的第一个子元素的HTML值
  console.log(
    e.currentTarget.previousElementSibling.firstElementChild.innerHTML
  );
}

22. Vue的父组件和子组件生命周期钩子执行顺序

核心答案: 第一次页面加载时 会触发 beforeCreate, created, beforeMount, mounted这几个钩子。

  • 渲染过程: 父组件挂载完成一定是等子组件挂载完成后, 才算是父组件挂载完, 所以父组件的mounted在子组件mounted之后。 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
  • 子组件更新过程: 影响到父组件: 父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated 不影响父组件: 子beforeUpdate -> 子updated
  • 父组件更新过程: 影响到子组件: 父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated 不影响子组件: 父beforeUpdate -> 父updated
  • 销毁过程: 父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

重要: 父组件等待子组件完成后,才会执行自己对应完成的钩子

23. 在vue中优雅的使用第三方库(例如moment)

在 Vuejs 项目中使用 JavaScript 库的一个优雅方式是将其代理到 Vue 的原型对象上去. 按照这种方式, 我们引入 Moment 库:(Lodash, Axios, Async等库都可以使用这种方式引入)

// npm安装moment
npm i moment -S

// main.js
import moment from 'moment'
Object.defineProperty(Vue.prototype, '$moment', { value: moment })

// vue文件中使用
export default {
  created() {
    console.log('The time is ', this.$moment().format('HH:mm'))
  }
}

由于所有的组件都会从 Vue 的原型对象上继承它们的方法, 因此在所有组件/实例中都可以通过 this.$moment(). 的方式访问 Moment 而不需要定义全局变量或者手动的引入

24. provide和inject的使用

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 provide 选项应该是一个对象或返回一个对象的函数

语法:

  • provide: Object | () => Object
  • inject: Array<string> | { [key: string]: string | Symbol | Object }

使用场景: 由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问祖先组件的数据

示例: 比如一种常见用法刷新vue组件

// 祖先 app.vue
// 父组件中返回要传给下级的数据
<template>
  <div
    id="app"
  >
    <router-view
      v-if="isRouterAlive"
    />
  </div>
</template>
// script
  provide () {
    return {
      reload: this.reload
    }
  },
  methods: {
    reload () {
      this.isRouterAlive = false
      this.$nextTick(() => {
        this.isRouterAlive = true
      })
    }
  }
  
// 子孙后代中调用 
inject: ['reload'],
// script methods
refreshProject(){
	this.reload()
}

注意: provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

25. 项目中优雅使用icon, svg的方式

在src目录下新建icons/文件夹,在icons/文件夹下建svg/文件夹,将来我们项目中的svg图标都会统一放在这里。 去阿里图标库下载.svg格式的图片。 然后配置svg图标, 下载svg-sprite-loader插件单独处理svg图标。 它是一个webpack loader,支持将多个svg打包成svg sprites。 npm i svg-sprite-loader -D。 然后在vue.config.js中的chainWebpack函数中配置


// 内置路径包
const path = require("path");

// 定义resolve方法,获取绝对路径
function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  // 一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例
  // 允许对内部的 webpack 配置进行更细粒度的修改
  chainWebpack: config => {
    // 配置svg默认规则排除icons目录中svg文件处理
    config.module
      .rule("svg")
      .exclude.add(resolve("src/icons"))
      .end();

    // 新增icons规则,设置svg-sprite-loader处理icons目录中svg文件
    config.module
      .rule("icons")
      .test(/\.svg$/)
      .include.add(resolve("src/icons"))
      .end()
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({ symbolId: "icon-[name]" })
      .end();
  }
}
  • 初步使用
import "@/icons/svg/qq.svg"

<svg>
  <use xlink:href="#icon-qq"></use>
</svg>
  • 进阶: -svg文件自动引入 做了自动引入操作后, 不用再引入一遍图标svg了,因为我们做了自动化,icons/svg/下的svg后缀图标文件都可被自动引入
// 在icons/index.js中写处理icons图标自动加载的逻辑
const req = require.context("./svg", false, /\.svg$/);
req.keys().map(req);

// 在main.js中引入icons/index.js文件
import "@/icons/index.js";

// 在其他页面直接使用用
<svg>
  <use xlink:href="#icon-qq"></use>
</svg>
  • 进阶最终版 在components/目录下新建SvgIcon/index.vue文件,我们写一个svgicon组件,封装一下再全局注册,这样使用起来就会很方便了!
<template>
  <svg :class="className" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>
<script>
export default {
  name: "SvgIcon",
  props: {
    icon: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: "svg-icon"
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.icon}`;
    },
    svgClass() {
      if (this.className) {
        return "svg-icon " + this.className;
      }
    }
  }
};
</script>
<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

组件写好了之后我们在icons/index.js中进行全局注册,这样我们只引入这一个文件就可以达到自动加载和组件注册两个功能 icons/index.js改进如下:


import Vue from "vue";
import SvgIcon from "@/components/SvgIcon";

// icons图标自动加载
const req = require.context("./svg", false, /\.svg$/);
req.keys().map(req);

// 全局注册svg-icon组件
Vue.component("svg-icon", SvgIcon);

在vue文件中使用图标的方法

  <svg-icon icon="qq" class-name="qq-style"></svg-icon>

fill改变icon颜色。 fill: red这么改变。 如果下载的svg文件内部有fill, 就不会改变fill颜色。 解决办法, 直接在svg中将fill相关代码删除。 width, height改变svg大小。

26. 手机端自适应

在全局css中写上以下内容

html {
    font-size: 41.6667Px;
}

@media screen and (min-width: 320Px) {
    html {
        font-size: 41.6667Px
    }
    body {
        font-size: 12Px
    }
}

@media screen and (min-width: 360Px) {
    html {
        font-size: 46.875Px
    }
    body {
        font-size: 12Px
    }
}

@media screen and (min-width: 375Px) {
    html {
        font-size: 48.828Px
    }
    body {
        font-size: 14Px
    }
}

@media screen and (min-width: 384Px) {
    html {
        font-size: 50Px
    }
    body {
        font-size: 14Px
    }
}

@media screen and (min-width: 400Px) {
    html {
        font-size: 52.0833Px
    }
    body {
        font-size: 14Px
    }
}

@media screen and (min-width: 414Px) {
    html {
        font-size: 53.9063Px
    }
    body {
        font-size: 14Px
    }
}

@media screen and (min-width: 424Px) {
    html {
        font-size: 55.2083Px
    }
    body {
        font-size: 14Px
    }
}

@media screen and (min-width: 480Px) {
    html {
        font-size: 62.5Px
    }
    body {
        font-size: 15.36Px
    }
}

@media screen and (min-width: 540Px) {
    html {
        font-size: 70.3125Px
    }
    body {
        font-size: 17.28Px
    }
}

@media screen and (min-width: 720Px) {
    html {
        font-size: 93.75Px
    }
    body {
        font-size: 23.04Px
    }
}

@media screen and (min-width: 750Px) {
    html {
        font-size: 97.6563Px
    }
    body {
        font-size: 24Px
    }
}

@media screen and (width: 768Px) {
    html {
        font-size: 100Px
    }
    body {
        font-size: 24Px
    }
}

@media screen and (min-width: 769Px) {
    html {
        font-size: 50Px
    }
    body {
        font-size: 14Px
    }
    .fixed-bottom {
        position: fixed !important;
        bottom: 0;
        max-width: 768Px;
        width: 100%;
        left: auto;
        margin-left: auto;
    }
}

在package.json中引入以下文件

"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-pxtorem": "^5.1.1",
"postcss-url": "^7.2.1",

新建.postcssrc.js文件, 内容如下

// https://github.com/michael-ciniawsky/postcss-load-config

module.exports = {
  "plugins": {
    "postcss-import": {},
    "postcss-url": {},
    // to edit target browsers: use "browserslist" field in package.json
    "autoprefixer": {},
    'postcss-pxtorem': {
      rootValue: 48.828,//结果为:设计稿元素尺寸/16,比如元素宽320px,最终页面会换算成 20rem
      propList: ['*'],
      "selectorBlackList":[".mint"]
    }
  }
}

27. js中使用用css变量

  • 设置css变量: document.body.style.setProperty("--themeColor", "blue")
  • js中使用这个css变量: getComputedStyle(document.body).getPropertyValue('--themeColor')
  • css中使用这个css变量: color: var(--themeColor)
  • 删除变量document.body.style.removeProperty('--themeColor');
:root {
  --bg: red;
}

#test{
  background:var(--bg);
}

/* 背景图片不支持这种写法 --bgUrl:url('@/assets/bg1.jpg'); 需要换背景图片可在js中用 字符串模板 + require 设置。
如:document.body.style.setProperty('--bgUrl',`url(${require('@/assets/bg1.jpg')})`); */

:root{
  --bg:red;
  --color:#000;
  --fontSize:16px;
  --fill:red;
}
$color:var(--color);
#test {
  background:var(--bg);
  height:500px;
  width: 400px;
  font-size: var(--fontSize);
  color: $color;
  .users{
    fill: var(--fill);
    font-size: 100px;
  }
}

28. Vue监听缓存

不建议去进行这种缓存监听, 麻烦且管理起来难度不小。 尽量使用vuex去实现这类业务需求

  1. 首先在 main.js 中给 Vue.protorype 注册一个全局方法,然后创建一个 StorageEvent 方法,当我在执行sessionStorage.setItem(k, val) 的时候,初始化事件并派发事件。
Vue.prototype.resetSetItem = (key, newVal) => {
  if (key === 'websocketHistory') {
    // 创建一个StorageEvent事件
    let newStorageEvent = document.createEvent('StorageEvent');
    const storage = {
      setItem: (k, val) => {
        localStorage.setItem(k, val);
        // 初始化创建的事件
        newStorageEvent.initStorageEvent('setItem', false, false, k, null, val, null, null);
        // 派发对象
        window.dispatchEvent(newStorageEvent);
      }
    };
    return storage.setItem(key, newVal);
  }
};
  1. 在其他页面写入缓存
this.resetSetItem('websocketHistory', data);
  1. 然后在需要监听的页面中,created生命周期中使用
window.addEventListener('setItem', () => {
  console.log(JSON.parse(localStorage.getItem('websocketHistory')));     
});