VUE项目踩坑记录

1、虚拟DOM不渲染数据

问题描述:消息已读和未读的功能,点击消息,消息会变成已读,重新请求数据,在重新请求数据前会先清空旧数据,但是因为两次的数据一样,导致vue的diff算法默认不更新视图,使用 this.$set 和 this.$forceUpdate 等方法都不能解决问题

解决方法:在v-for的父元素加个v-if v-if=“list.length > 0”。 vue 的 diff 算法会监测数组变化,响应式地渲染列表。

2、复杂对象数组去重

使用reduce方法进行复杂对象数组去重

let arr = [
  { id: 1, name: 'zs' },
  { id: 2, name: 'ls' },
  { id: 1, name: 'zs' },
]
const obj = {}
arr = arr.reduce((pre, item) => {
  if (!obj[item.id]) {
    obj[item.id] = true
    pre.push(item)
  }
  return pre
}, [])
console.log(arr) // {id: 1, name: 'zs'}{id: 2, name: 'ls'}

3、正则判断是否数据是否以负号或0-9的数字开头(含小数)

const isSum = /^(-|\+)?\d+(\.\d+)?$/
cosnt sunA = 1console.log(isSum.test(sumA))

4、数字格式化:千分位及并保留两位小数(多用于金额格式化)

moneyFormatter = function (money, num) {
    /*
     * 参数说明:
     * money:要格式化的数字
     * num:保留几位小数
     * */
    num = num > 0 && num <= 20 ? num : 2;
    money = money + '';
    var index = money.indexOf('.') + 1;
    if (index > 1 && money.substring(index, money.length).length > num) {
        money = money.substring(0, index + num);
    }
    money = parseFloat((money + '').replace(/[^\d.-]/g, '')).toFixed(num) + '';
    var l = money.split('.')[0].split('').reverse(), r = money.split('.')[1];
    var t = '', i;
    for (i = 0; i < l.length; i++) {
        t += l[i] + ((i + 1) % 3 == 0 && i + 1 != l.length ? ',' : '');
    }
    return t.split('').reverse().join('') + '.' + r;
}

 以上适用于无负数格式化,如果金额有负数,需要判断是否为负数

moneyFormatter = function(money, num) {
        /*
         * 参数说明:
         * money:要格式化的数字
         * num:保留几位小数
         * */
        num = num > 0 && num <= 20 ? num : 2
        money = money + ''
        var index = money.indexOf('.') + 1
        if (index > 1 && money.substring(index, money.length).length > num) {
          money = money.substring(0, index + num)
        }
        money = parseFloat((money + '').replace(/[^\d.-]/g, '')).toFixed(num) + ''
        var l
        let isNegative = ''
        if (money.startsWith('-')) {
          // 判断为负数  将负号取出
          l = money
            .split('.')[0]
            .substring(1, money.length)
            .split('')
            .reverse() // 整数部分
          isNegative = money.split('.')[0].substring(0, 1) // 负号
        } else {
          l = money
            .split('.')[0]
            .split('')
            .reverse()
        }
        r = money.split('.')[1] // 小数部分
        var t = '',
          i
        for (i = 0; i < l.length; i++) {
          t += l[i] + ((i + 1) % 3 == 0 && i + 1 != l.length ? ',' : '')
        }
        return (
          isNegative +
          t
            .split('')
            .reverse()
            .join('') +
          '.' +
          r
        )
      }

 5、字符串toLocaleString()方法

toLocaleString() 方法可根据本地时间把 Date 对象转换为字符串,并返回结果

var d=new Date();
var n=d.toLocaleString();// 2021/11/29 下午4:24:49

 除此之外:还可以将数字变成千分位格式

let num=12345678;
console.log(num.toLocaleString()); // 12,345,678

 还可以将时间转换为 24 小时制

// 2021/11/29 下午4:25:06
console.log(new Date().toLocaleString() 

// 2021/11/29 16:25:06
console.log(new Date().toLocaleString('chinese',{hour12:false}))

6、ElementUI表格样式调整

ElementUI表格样式需要加载行内以对象的形式存

<el-table :data="tableData" :summary-method="getSummaries" show-summary height="240" :header-cell-style="tableHeader" :row-style="{ height: '2.5rem' }" :cell-style="{ padding: '0' }">

header-cell-style设置表头样式

row-style设置每行的高度

cell-style将默认的padding去除

去掉表格默认的高亮效果

.el-table__row:hover > td {
  background-color: transparent;
}

 修改表格下划线颜色

/deep/.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
  border-bottom: 1px solid #e6e6e6;
}

去除进度条的圆角

/deep/.el-progress-bar__outer {
  border-radius: 0;
}
/deep/.el-progress-bar__inner {
  border-radius: 0;
}
/deep/.el-progress-bar__inner {
  ">#5b8ff9;
}

ElementUI普通表格取消高亮

/deep/.el-table__row:hover > td {
  background-color: transparent;
}

如果表格有使用fixed进行固定,使用上面的方法会导致部分fixed固定的列的高亮无法取消,推荐使用下面的方法

/deep/.el-table__body tr.hover-row > td.el-table__cell {
    background-color: transparent;
  }

7、eventBus传值及累加触发问题、created的值在mounted内获取

1、eventBus在兄弟组件之间传值如果且触发了路由跳转(A页面跳转至B页面)会导致第一次传值失败

原因:B页面没有被创建导致发送失败,如果在B页面creted内使用bus.$on会发生bus.$on先触发,A页面的bus.$emit后触发,导致B页面接收不到参数

解决方法:

// 在发送方A页面使用this.this.$nextTick(()=>{})
this.$nextTick(() => {
        bus.$emit('eleOpen', this.openEle)
        console.log('bus.$emit')
})
// 在接收方B页面正常接收即可

2、bus.$on多次触发的问题

 

这个$on事件是不会自动清楚销毁的,需要我们手动来销毁

// 在B组件页面中添加以下语句,在组件beforeDestory的时候销毁。
  beforeDestroy () {
    bus.$off('get', this.myhandle)
  },

3、在created里面发起请求或接收兄弟组件的参数,在mounted内无法调用到created内参数值

原因:虽然按照生命周期是created在前,mounted在后,但生命周期异步加载需要时间,如果延迟时间是可以获取到数据的,但是问题是不知道需要延迟多久,所以最好不要使用定时器处理。

解决方法:

1.在created生命周期内进行异步数据的请求,且将获取到的数据赋值给this.data

2.此时如果在mounted生命周期里获取this.data是获取不到的。

3.不要在mounted内处理数据在watch内处理即可

// 在data定义数据
data(){
     isOpenDialog: false
}
// 在watch内监听
watch: {
    isOpenDialog() {
      this.$nextTick(() => {
        // 在这里可以获取和处理数据
      })
    }
  }

4、Vue.js中this.$nextTick()的使用

this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

假设我们更改了某个dom元素内部的文本,而这时候我们想直接打印出这个被改变后的文本是需要dom更新之后才会实现的,也就好比我们将打印输出的代码放在setTimeout(fn, 0)中

this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

假设我们更改了某个dom元素内部的文本,而这时候我们想直接打印出这个被改变后的文本是需要dom更新之后才会实现的,也就好比我们将打印输出的代码放在setTimeout(fn, 0)中

8、cascader动态加载次级项视图不更新问题

使用ElementUI的级联选择器时,选中一级动态渲染次级时数据添加进去但是不显示解决办法,使用this.$set方法

handleChange(val) {
      this.options.filter(item => {
        if (item.id === val[0]) {
          // item.children = this.children 此方法可以将数据添加进去但不会更新视图,需要使用this.$set方法
          this.$set(item, 'children', this.children)
        }
      })
      console.log(this.options)
    }

cascader点击文本选中当前label

mounted() {
    setInterval(() => {
      document.querySelectorAll('.el-cascader-node__label').forEach(el => {
        el.onclick = function() {
          if (this.previousElementSibling) this.previousElementSibling.click()
        }
      })
    }, 500)
  },

9、vue 打包报错Failed to load resource: net::ERR_FILE_NOT_FOUND

 解决方法:

在项目根目录创建名为vue.config.js的文件夹

在该文件内输入

 module.exports = {
   publicPath: './'
}
重新打包即可

 10、vue路由传参

项目中很多情况下都需要进行路由之间的传值,可以使用sessionstorage/localstorage/cookie 进行离线缓存存储也可以,用vuex也可以,如果只是简单的传值可以使用vue自带的路由传参方法

参考官方文档:https://router.vuejs.org/zh/guide/essentials/passing-props.html

想要实现点击当前页的某个按钮或链接跳转到另外一个页面去,并将某个参数带过去

<div @click="insurance(123)">我要传参</div>

第一种方法 页面刷新数据不会丢失

methods:{
  insurance(id) {
       //直接调用$router.push 实现携带参数的跳转
        this.$router.push({
          path: `/particulars/${id}`,
        })
}

需要对应路由配置如下:可以看出需要在path中添加/:id来对应 $router.push 中path携带的参数。在子组件中可以使用来获取传递的参数值

{
     path: '/particulars/:id',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

this.$route.params.id

第二种方法 页面刷新数据会丢失 类似post请求

通过路由属性中的name来确定匹配的路由,通过params来传递参数。

methods:{
  insurance(id) {
       this.$router.push({
          name: 'particulars',
          params: {
            id: id
          }
        })
  }

对应路由配置: 注意这里不需要使用:/id来传递参数了,因为组件中,已经使用params来携带参数了。

{
     path: '/particulars',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

this.$route.params.id

第三种方法 query传递的参数会显示在url后面以【?id=?】的形式,类似get请求

使用path来匹配路由,然后通过query来传递参数

methods:{
  insurance(id) {
        this.$router.push({
          path: '/particulars',
          query: {
            id: id
          }
        })
  }

对应路由配置:

{
     path: '/particulars',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

this.$route.query.id

 11、将后台返回的数据进行相邻单元格合并

html内容区

<div class="right_content">
        <el-table :data="skuListInfo" :span-method="objectSpanMethod" border>
          <el-table-column prop="name" label="名称"> </el-table-column>
          <el-table-column prop="storeIds" label="适应门店"> </el-table-column>
          <el-table-column prop="feeTypeInfo" label="费用类型"> </el-table-column>
          <el-table-column prop="productInfo" label="适用产品"> </el-table-column>
          <el-table-column prop="billing" label="计费方法"> </el-table-column>
        </el-table>
</div>

script方法区

<script>
export default {
  data() {
    return {
      skuListInfo: [
        {
          id: 1,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '1',
          productInfo: '日托',
          mergeId: 1,
          feeType: '1',
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 2,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '2',
          productInfo: '晚托',
          feeType: '1',
          mergeId: 1,
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 3,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '3',
          productInfo: '临时托',
          feeType: '1',
          mergeId: 1,
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 4,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '4',
          productInfo: '越拖',
          feeType: '1',
          mergeId: 1,
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 9,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '4',
          productInfo: '全部',
          feeType: '2',
          mergeId: 1,
          feeTypeInfo: '定金',
          billing: '月/季/年制度'
        },
        {
          id: 10,
          name: '普通门店',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '4',
          productInfo: '全部',
          feeType: '3',
          mergeId: 1,
          feeTypeInfo: '学杂费',
          billing: '月/季/年制度'
        },
        {
          id: 5,
          name: '团购',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '5',
          productInfo: '寒假托',
          feeType: '2',
          mergeId: 1,
          feeTypeInfo: '定金',
          billing: '月/季/年制度'
        },
        {
          id: 6,
          name: '团购',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '5',
          productInfo: '日托',
          feeType: '1',
          mergeId: 1,
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 7,
          name: '团购',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '5',
          productInfo: '晚托',
          feeType: '1',
          mergeId: 1,
          feeTypeInfo: '学费',
          billing: '月/季/年制度'
        },
        {
          id: 8,
          name: '大客户',
          storeIds: [1, 2, 3, 4],
          storeIdInfo: '[1, 2, 3, 4]',
          productType: '6',
          productInfo: '暑假托',
          feeType: '3',
          mergeId: 1,
          feeTypeInfo: '学杂费',
          billing: '月/季/年制度'
        }
      ],
      typeNameArr: [],
      typeNamePos: 0,
      storeArr: [],
      storePos: 0,
      feeArr: [],
      feePos: 0,
      hoverOrderArr: []
    }
  },
  mounted() {
    this.merage()
  },
  methods: {
    merageInit() {
      this.typeNameArr = []
      this.typeNamePos = 0
      this.storeArr = []
      this.storePos = 0
      this.feeArr = []
      this.feePos = 0
    },
    merage() {
      this.merageInit()
      for (let i = 0; i < this.skuListInfo.length; i += 1) {
        if (i === 0) {
          // 第一行必须存在
          this.typeNameArr.push(1)
          this.typeNamePos = 0
          this.storeArr.push(1)
          this.storePos = 0
          this.feeArr.push(1)
          this.feePos = 0
        } else {
          // 判断当前元素与上一个元素是否相同,eg:this.typeNamePos 是 this.typeNameArr序号
          // 第一列
          // eslint-disable-next-line no-lonely-if
          if (this.skuListInfo[i].name === this.skuListInfo[i - 1].name) {
            this.typeNameArr[this.typeNamePos] += 1
            this.typeNameArr.push(0)
          } else {
            this.typeNameArr.push(1)
            this.typeNamePos = i
          }
          // 第二列
          if (this.skuListInfo[i].storeIdInfo === this.skuListInfo[i - 1].storeIdInfo && this.skuListInfo[i].name === this.skuListInfo[i - 1].name) {
            this.storeArr[this.storePos] += 1
            this.storeArr.push(0)
          } else {
            this.storeArr.push(1)
            this.storePos = i
          }
          // 第三列
          if (this.skuListInfo[i].feeType === this.skuListInfo[i - 1].feeType && this.skuListInfo[i].storeIdInfo === this.skuListInfo[i - 1].storeIdInfo && this.skuListInfo[i].name === this.skuListInfo[i - 1].name) {
            this.feeArr[this.feePos] += 1
            this.feeArr.push(0)
          } else {
            this.feeArr.push(1)
            this.feePos = i
          }
        }
      }
    },
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      // if (columnIndex === 0) { // 用于设置要合并的列
      //   if (rowIndex % 2 === 0) { // 用于设置合并开始的行号
      //     return {
      //       rowspan: 2, // 合并的行数
      //       colspan: 1, // 合并的猎术, 设置为0则直接不显示
      //     };
      //   }
      //   return {
      //     rowspan: 0,
      //     colspan: 0,
      //   };
      // }
      if (columnIndex === 0) {
        // 第一列的合并方法
        const row1 = this.typeNameArr[rowIndex]
        const col1 = row1 > 0 ? 1 : 0 // 如果被合并了row = 0; 则他这个列需要取消
        console.log(row1)
        return {
          rowspan: row1,
          colspan: col1
        }
      } else if (columnIndex === 1) {
        // 第二列的合并方法
        const row2 = this.storeArr[rowIndex]
        const col2 = row2 > 0 ? 1 : 0 // 如果被合并了row = 0; 则他这个列需要取消
        return {
          rowspan: row2,
          colspan: col2
        }
      } else if (columnIndex === 2) {
        // 第三列的合并方法
        const row3 = this.feeArr[rowIndex]
        const col3 = row3 > 0 ? 1 : 0 // 如果被合并了row = 0; 则他这个列需要取消
        return {
          rowspan: row3,
          colspan: col3
        }
      }
    }
  }
}
</script>

12、axios跨域问题解决方法

1.打开vue.config.js进行配置

 devServer: {
    proxy: {
      '/api': {
        target: 'http://www.xxx.com.cn/', // 要请求的API
        changeOrigin: true, // 是否开启跨域
        pathRewrite: {
          '^/api': '' // 重写路由
        }
      }
    }
  }

2.请求时进行后缀请求,比如请求/menu则请求/api/menu即可

posted on 2022-07-14 18:07  zyp_java_net  阅读(174)  评论(0编辑  收藏  举报

导航