• 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即可

  • 相关阅读:
    【python】Excel从源表提取相应信息到目标表格
    Vue.config.productionTip 关闭生产提示
    [elementui]多行confirm
    [vue]防抖(debounce) 和 节流(throttling)
    C#的面向对象之继承与多态
    C#中接口与抽象类
    为iPhone开发iPad风格的弹出窗口
    新闻资讯APP开发流程(五) MainView.js
    titanium开发实例社交APP一之登录窗口
    titanium开发实例社交APP二之注册窗口
  • 原文地址:https://www.cnblogs.com/zhyp/p/16478830.html
Copyright © 2020-2023  润新知