1. 程式人生 > 其它 >VUE專案踩坑記錄

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