vue實踐中的常見知識漏洞001

前言

本文主要總結了vue實際開發項目當中應該如何解決一些實際的開發問題,可能你認爲很簡單,但短期內也許你並沒解決思路的。css

建議閱讀時間:15-25minhtml


更多精彩內容請關注我掘金主頁或者 達摩兵的空間博客vue

常見技術解答

for循環中針對ui樣式的特徵性樣式或者事件

  • 針對ui有特定的數據字段進行判斷(也叫數據模型方法)

這種書數據的要求比較高,且要求你可以找到比較好的對應關係,須要針對class進行特徵性的組件渲染。當你須要改變時改變數據便可從新渲染達到改變樣式的目的。webpack

<li v-for="item of list" :key="item.id" :class="item.status?'color':''" @click="changeColor(item.id)">{{item.name}}</li>
return {
list:[
{id:1,status:true,name:1111},
{id:2,status:true,name:222}]
}
methods:{
    changeColor(id){
        this.list.map((item)=>{
            if(item.id==id){
            item.status=!item.status;
            }
            return item;
        })
    }
}
複製代碼
  • 傳入對應的參數以及事件源,能夠進行相應的判斷改變class

特色更加靈活,也能夠根據須要傳入你須要傳入的item屬性參數進行與class的匹配判斷,不用改變接口返回的數據結構。es6

<li v-for="item of list" :key="item.id"  @click="changeColor($event)">{{item.name}}</li>
return {
list:[
{id:1,name:1111},
{id:2,name:222}]
}
changeColor(e){
			let el=e.target;
            if(el.classList.contains("color")){
                el.classList.remove("color")
            }else{
                el.classList.add("color")
            }
}
複製代碼

計算屬性方法的使用

問題描述:若是你的計算屬性依賴於data的部分,而你的data對應的字段在data裏沒有申明,只是在請求接口時進行申明賦值,那麼當接口請求時,雖然數據發生了變化,可是計算屬性的值不會發生更新。web

解決方案 :須要你在data裏申明你計算屬性依賴的字段,哪怕是空或者nullnpm

事件執行順序問題

問題描述 :定義了輸入框blur,再按鈕點擊事件問題,其中默認click的話,執行順序是先執行blur再執行click.若是你須要場景在點擊的時候不執行blur的事件數組

解決方案:bash

1 常規方案 : 須要吧點擊事件變成@mousedown.prevent ,前者會讓點擊優於blur執行,後者會阻止blur執行babel

2 el-input並不生效,能夠用計時器延遲執行 將失去焦點的事件計時器延遲執行,而後點擊事件裏清除定時器,也是能夠只執行點擊事件邏輯的

路由參數變化組件不更新

問題描述 :路由參數變化,可是組件沒有對應的更新,主要是由於通常獲取參數寫在了created路由鉤子函數中,路由參數變化的時候,這個生命週期不會從新執行。

解決方案:watch監聽router

watch: {
 // 方法1
  '$route' (to, from) { //監聽路由是否變化
    if(this.$route.params.articleId){// 判斷條件1  判斷傳遞值的變化
      //獲取文章數據
    }
  }
  //方法2
  '$route'(to, from) {
    if (to.path == "/page") {    /// 判斷條件2  監聽路由名 監聽你從什麼路由跳轉過來的
       this.message = this.$route.query.msg     
    }
  }
}
複製代碼

異步函數中使用this沒法指向vue實例對象

問題描述 : 在定時器或者其餘異步函數中使用傳統的func致使this指向不到vue實例,主要緣由是由於this指向的問題,詳細的能夠參考個人《神奇的this》這篇文章。

解決方案 :用箭頭函數或者指定變量賦值爲this(其餘一些不能用箭頭函數的地方本身也要注意)

定時器在組件銷燬後還在執行

問題描述 :一些耗費性能的計時器或者動畫在組件銷燬以後仍是執行的,致使性能變低。

解決方案 :在銷燬組件的生命週期中銷燬定時器或者一些動畫的js

//組件銷燬前執行的鉤子函數,跟其餘生命週期鉤子函數的用法相同。
beforeDestroy(){
     //我一般是把setInterval()定時器賦值給this實例,而後就能夠像下面這麼中止。
    clearInterval(this.intervalId);
},
複製代碼

組件名與引入時大小寫不一致致使報錯

問題描述:

This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
複製代碼

解決方案 :須要嚴格對應組件的大小寫,避免低級錯誤

動態添加的dom沒有樣式

問題描述:做爲常識咱們知道style中的樣式都會追加scoped,這樣針對模板dom中的樣式就能夠生效,但其生效後的最終樣式並非咱們寫的樣式名,而是編碼後的,因此咱們在js中拼接上的dom結構樣式並不會生效。

解決思路: 1 當添加的部分樣式不會太多,並且是動態加載的,能夠將其設置爲非scopred的

2 將添加dom部分用的樣式放到非scoped樣式標籤中

3 將添加的部分,若是有必要,能夠另外寫一個頁面拆分的vue組件

拓展 : 項目中引入的其餘ui框架的樣式,若是你想覆蓋修改,也是須要不加scoped的,若是你想整個項目覆蓋,就能夠在src/styles下定義customer-element.scss 這樣的來重寫覆蓋樣式。

vue中直接修改數據,頁面視圖不更新

問題描述 :在常規理解中,視圖與數據是雙向綁定的,可是有時候修改data的數組或者對象值,視圖不會更新 。

data() { // data數據
        return {
          arr: [1,2,3],
          obj:{
              a: 1,
              b: 2
          }
        };
      },
   // 數據更新 數組視圖不更新
    this.arr[0] = 'OBKoro1';
    this.arr.length = 1;
    console.log(arr);// ['OBKoro1'];
    // 數據更新 對象視圖不更新
    this.obj.c = 'OBKoro1';
    delete this.obj.a;
    console.log(obj);  // {b:2,c:'OBKoro1'}

複製代碼

解決方案 :因爲js的限制,Vue 不能檢測以上數組的變更,以及對象的添加/刪除,不少人會由於像上面這樣操做,出現視圖沒有更新的問題。

1 this.$set(你要改變的數組/對象,你要改變的位置/key,你要改爲什麼value)

this.$set(this.arr, 0, "OBKoro1"); // 改變數組
this.$set(this.obj, "c", "OBKoro1"); // 改變對象
複製代碼

2 數組原生方法觸發視圖更新: splice()、 push()、pop()、shift()、unshift()、sort()、reverse() 推薦使用splice方法會比較好自定義,由於slice能夠在數組的任何位置進行刪除/添加操做

3 替換數組 比方說:你想遍歷這個數組/對象,對每一個元素進行處理,而後觸發視圖更新。

// 文檔中的栗子: filter遍歷數組,返回一個新數組,用新數組替換舊數組
    example1.items = example1.items.filter(function (item) {
      return item.message.match(/Foo/)
    })
複製代碼

須要無腦重複某內容

<div v-for="n in 5">
        <span>這裏會被渲染5次,渲染模板{{n}}</span>
     </div>
複製代碼

babel-plugin-transform-runtime使用

  • 出現問題:這個插件能夠兼容並轉化大部分的es6語法,可是部分語法也是不能轉化或者存在具體問題的。
  1. 異步加載組件時,會產生 polyfill 代碼冗餘
  2. 不支持對全局函數與實例方法的 polyfill。不支持全局函數(如:Promise、Set、Map),Set 跟 Map 這兩種數據結構應該你們用的也很少,影響較小。可是 Promise 影響可能就比較大了。不支持實例方法(如:'abc'.include('b')、['1', '2', '3'].find((n) => n 等等),這個限制幾乎廢掉了大部分字符串和一半左右數組的新特性。 而兩個問題的緣由均歸因於 babel-plugin-transform-runtime 採用了沙箱機制來編譯咱們的代碼(即:不修改宿主環境的內置對象)。因爲異步組件最終會被編譯爲一個單獨的文件,因此即便多個組件中使用了同一個新特性(例如:Object.keys()),那麼在每一個編譯後的文件中都會有一份該新特性的 polyfill 拷貝。若是項目較小能夠考慮不使用異步加載,可是首屏的壓力會比較大。
  • 解決方案:通常狀況下 babel-plugin-transform-runtime 能知足大部分的需求,當不知足需求時,推薦使用完整的 babel-polyfill。
    • 首先,從項目中移除 babel-plugin-transform-runtime,卸載該依賴: npm un babel-plugin-transform-runtime -D
    • 接着修改 babel 配置文件
      // .babelrc
      {
        //...
        "plugins": [
          // - "transform-runtime"
        ]
        //...
      }
      複製代碼
    • 而後,安裝 babel-polyfill 依賴: npm i babel-polyfill -D
    • 最後,在入口文件中導入
      // src/main.js
      import 'babel-polyfill'
      複製代碼

ES6 import 引用問題

在 ES6 中,模塊系統的導入與導出採用的是引用導出與導入(非簡單數據類型),也就是說,若是在一個模塊中定義了一個對象並導出,在其餘模塊中導入使用時,導入的實際上是一個變量引用(指針),若是修改了對象中的屬性,會影響到其餘模塊的使用。

一般狀況下,系統體量不大時,咱們可使用 JSON.parse(JSON.stringify(str)) 簡單粗暴地來生成一個全新的深度拷貝的 數據對象。不過當組件較多、數據對象複用程度較高時,很明顯會產生性能問題,這時咱們能夠考慮使用 Immutable.js。

鑑於這個緣由,進行復雜數據類型的導出時,須要注意多個組件導入同一個數據對象時修改數據後可能產生的問題。

此外,模塊定義變量或函數時即使使用 let 而不是 const,在導入使用時都會變成只讀,不能從新賦值,效果等同於用 const 聲明。

動態懶加載組件

背景:在webpack的新特性中支持組件的懶加載,也就是說咱們能夠在加載到該路由的時候再把這部分腳本進行加載,同時這個在項目進行打包的時候,對應的文件也會被單獨打包,對於首屏優化以及其餘頁面的資源加載優化都是很是好的。這也要求咱們在每一個頁面組件使用組件的時候儘可能按需引入,提高體驗。

問題場景:那麼咱們須要解決的問題是: 0 webpack是靜態解析路徑的,直接傳入變量並不可行 1 每次都寫一串加載組件的代碼很不方便,是否能夠支持寫成一個加載組件的方法 2 是否支持區分生產和開發環境,由於開發環境使用懶加載會致使熱更新,致使更新變慢,因此開發環境使用全量默認加載,生產環境使用懶加載

解決方案以下 : 1 webpack的路徑使用變量拼接,必須預先給出一個相對路徑,而後把具體的組件路徑在傳入

2 用一個箭頭函數,將須要傳入的組件名或者相對路徑傳入

3 用process.env.NODE_ENV肯定使用哪一種加載方式

代碼以下: 在原來的router/index.js中,定義一個加載組件的_import方法。

// router/index.js 
const _import = require('./_import_' + process.env.NODE_ENV)

//使用時
 {
      path: '/',
      name: 'HelloWorld',
      component: _import('HelloWorld')
    },
    
// router/_import_development.js
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

// router/_import_production.js 若是你加載的vue不是這個路徑 請自定義哦
module.exports = file => () => import('@/views/' + file + '.vue')
複製代碼

vue中的data必須爲函數

場景 :vue入門的人可能在頁面單獨引入vue的時候,直接使用data爲對象類型的,並無問題,可是在spa應用中,若是組件中的data爲對象類型就會報錯。

解決方案 :data換爲函數,返回對象類型的鍵值對。

拓展 :你可能知道要這樣作,這裏稍微科普下緣由,主要是由於根組件只會用一次,因此能夠用對象,而子組件可能在一個應用中被屢次使用,爲了不多個組件使用同一數據互相影響,因此講data約定爲了返回函數類型,返回須要的對象,以此保證子組件在數據渲染的時候不會互相影響。

有父子標籤關係的自定義組件渲染失敗

場景 :在自定義組件的時候,不少時候須要將ul下的li標籤,table下的tr\td標籤進行封裝爲自定義組件,但直接使用自定義組件會致使其最終生成的位置不是咱們想要的。其標籤會渲染到tbody標籤之外。

Vue.component("row",{
  template:'<tr><td>{{content}}</td></tr>',
  data(){
    return {
      content:'this is a row'
    }
  },
})
複製代碼

解決方案 :緣由是由於html會進行標籤解析,tbody下的標籤必須爲tr,其餘的同理。那麼咱們能夠將其子標籤設置爲原來的標籤類型,而後用is="selfComponent" 來解決這個問題。

<tr is="row"></tr>

拓展:

  • 不要將渲染vue的容器元素定位到html或者body上,不然提示:Do not mount Vue to <html> or <body> - mount to normal elements instead.
  • 確保有在vue新建實例的時候將el屬性綁定到一個html模板的標籤上

ref使用

場景 :雖然vue不建議直接操做dom,可是在複雜的場景中,咱們須要進行dom的操做,這時候就能夠藉助ref實現。好比下面咱們舉一個簡單的例子,經過ref獲取dom節點,拿到其內容。

解決方案

<div @click="handleClick" ref="hello">hello world
  </div>
   handleClick(){
      console.log(this.$refs.hello)
    }
複製代碼
  • 拓展案例 :實現計數器加和 場景 :假設咱們有兩個計數器組件的實例,如今須要用ref的方案獲得兩個計數器的加和。

代碼以下:

<counter ref="one" @change="handleChange"></counter>
  <counter ref="two" @change="handleChange"></counter>
  <span>{{total}}</span>
  Vue.component("counter",{
  template:"<div @click='change'>{{number}}</div>",
  data(){
  return {
   number:0}  
	},
   methods:{
       change(){
  		this.number++;
         this.$emit("change")
		}       
      }
})
//app父組件方法
  handleChange(){
      this.total=this.$refs.one.number+this.$refs.two.number
    },
複製代碼
  • 拓展認知 : this.$refs.name中若是是原生標籤,拿到的是原生標籤的節點,若是是組件,拿到的是組件的引用。

  • vueRefDemo使用

參考文檔

相關文章
相關標籤/搜索