VUE项目踩坑记录

1、虚拟DOM不渲染数据

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

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

2、复杂对象数组去重

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的数字开头(含小数)

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 对象转换为字符串,并返回结果

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

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

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

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

1
2
3
4
5
// 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表格样式需要加载行内以对象的形式存

1
<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去除

去掉表格默认的高亮效果

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

 修改表格下划线颜色

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

去除进度条的圆角

1
2
3
4
5
6
7
8
9
/deep/.el-progress-bar__outer {
  border-radius: 0;
}
/deep/.el-progress-bar__inner {
  border-radius: 0;
}
/deep/.el-progress-bar__inner {
  ">#5b8ff9;
}

ElementUI普通表格取消高亮

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

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

1
2
3
/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页面接收不到参数

解决方法:

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

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

 

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

1
2
3
4
// 在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内处理即可

1
2
3
4
5
6
7
8
9
10
11
12
// 在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方法

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
6
7
8
9
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的文件夹

在该文件内输入

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

 10、vue路由传参

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

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

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

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

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

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

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

1
2
3
4
5
{
     path: '/particulars/:id',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

1
this.$route.params.id

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

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

1
2
3
4
5
6
7
8
9
methods:{
  insurance(id) {
       this.$router.push({
          name: 'particulars',
          params: {
            id: id
          }
        })
  }

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

1
2
3
4
5
{
     path: '/particulars',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

1
this.$route.params.id

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

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

1
2
3
4
5
6
7
8
9
methods:{
  insurance(id) {
        this.$router.push({
          path: '/particulars',
          query: {
            id: id
          }
        })
  }

对应路由配置:

1
2
3
4
5
{
     path: '/particulars',
     name: 'particulars',
     component: particulars
   }

目标页面获取参数方法:

this.$route.query.id

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

html内容区

1
2
3
4
5
6
7
8
9
<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方法区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<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进行配置

1
2
3
4
5
6
7
8
9
10
11
devServer: {
   proxy: {
     '/api': {
       target: 'http://www.xxx.com.cn/', // 要请求的API
       changeOrigin: true, // 是否开启跨域
       pathRewrite: {
         '^/api': '' // 重写路由
       }
     }
   }
 }

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

posted on   zyp_java_net  阅读(180)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示