vue使用總結

上乾貨

從如下幾個方面整理:html

  • vue 用法
  • vue-router 用法
  • webpack 打包
  • 遇到的坑

1、Vue 用法

1. 一般寫的 .vue 文件,實則是在寫 vue 的配置文件,最終將執行 new Vue() 的操做,傳入這些配置。

new Vue({
  data(){
    return {
      name:'xx'
    }
  }
})

// 常編寫的.vue文件
export default {
  data(){
    return {
      name:'xx'
    }
  }
}

2. 經常使用組件能夠在全局註冊。

  • 組件代碼,TestComp.vue
<template>
    <div>
    test component
  </div>
</template>
<script>
export default {}
</script>
  • 註冊組件,Vue.component 註冊
// main.js
import Vue from 'vue';
import TestComp from './TestComp.vue'
Vue.component('test-comp',TestComp);
以上 main.js 中也能夠經過 Vue.use 註冊,其實實質仍是調用 Vue.Component,對應的 use 的須要有一個 install 函數,使用 Vue.use 將觸發 install 執行,而後在 install 中執行 Vue.component 操做。
// my-components.js
import TestComp from './TestComp.vue'
export default {
    install(Vue){
    Vue.component('test-comp',TestComp)
    // 這裏還能夠有多個註冊 vue.component vue.directive, vue.filter 等
  }
}
// main.js
import Vue from 'vue';
import MyComponents from './my-components.js';
Vue.use(MyComponents)

3. 用的比較多的指令,過濾器,mixin,能夠在全局配置

/* 一、 註冊指令 */
Vue.directive('')

/* 二、註冊過濾器 */
Vue.filter('filterName',(val,...args)=>{
  console.log(args); 
  return val;
})
// 過濾器使用 {{ 'abc' | filterName(1,2,3) }}
// value = 'abc' args 爲 [1,2,3]

/* 三、全局 mixin (不建議) */
Vue.mixin({
  mounted(){
    console.log('每一個組件中都將執行這裏')
  }
})

4. keep-alive 的使用,能夠緩存組件,能夠經過 exclude 排除不需緩存的組件,include 包含須要緩存的,max 定義最大緩存的組件數。exclude,include 傳入一個數組,元素對應組件的 name 屬性,max 傳入數字。

<keep-alive :incude="['home','list']" :max="3">
  <router-view></router-view>
</keep-alive>

5. 在頁面跳轉時,新打開的頁面滾動條可能緩存了滾動條位置,在中間的位置,進入頁面或者在路由鉤子的 afterEach 中執行 window.scroll(0,0) 重置到頂部。

6. 非直接父子組件之間的通訊,使用一個空 vue 對象實現通訊。

// Bus.js
import Vue from 'vue'
export const Bus = new Vue()

// a.vue
import Bus from './bus.js'
Bus.$emit('event_add_cart',1)

// b.vue
import Bus from './bus.js'
Bus.$on('event_add_cart',(num)=>{
  this.cartNum += num
})
a.vue 和 b.vue,引用 bus.js ,Bus 爲同一個實例,並不會重複建立,bus.js 就至關於一個調度中心,能夠無數個組件,都和它創建連接,發佈事件,而後 bus 將發佈到給每個創建連接的組件。

7. computed 只有在依賴的 data 裏面的值改變時,纔會改變,優於直接在模板裏執行表達式和方法,由於模板中的方法和模板,在每次 Vue 刷新視圖時,都將從新執行一遍。

8. data 須要是一個函數返回一個對象,若是直接賦值一個對象時,作不到隔離同一個組件的數據效果,都將公用同一份數據。

9. 在 vue 組件銷燬(beforeDestroy)前,清理定時器和 DOM 事件綁定。

10. 在循環中使用 key,提高渲染性能,key 儘可能不用數組下標,有 key 則 diff 算法就比較 key 值,最大程度上使用原組件,而不是銷燬原來的組件後重建。

11. v-for 能夠和 v-if 同時使用,先循環再判斷,因此能夠有選擇的渲染數組中的元素,而 angular 中是不支持同一個標籤上同時存在 for 和 if 指令的。可能 eslint 會提示報錯不建議混合使用,eslint 建議使用改變數組源數據的方式來實現,能夠在 computed 中使用數組的 filter 過濾掉不想要的數據。

12. 模板中 template 的使用,當幾個同級的元素,須要依賴同一個指令時,又不想添加額外的標籤將這些元素包裹時,可使用 template,template 將不會渲染。在 template 使用 v-for 時,key 值須要添加在子元素上。

<template>
    <div>
    <template v-for="(item,index) in [1,2,3]">
      <div :key="index">
        {{item}}
      </div>
    </template>
        <template v-if="2>1">
            <div>
        2大於1
      </div>
            <div>
        2>1
      </div>    
        </template>
  </div>
</template>

13. 在 data 初始化時,對象類型的數據類型,若是不給初始化,單獨改變值時,頁面是不會響應該值的變化的。

<template>
    <div>
    {{info.name}}
    {{info.age}}
    <div v-for="(item,index) in arr"
         :key="index">{{item}}</div>
  </div>
</template>
<script>
 export default {
   data(){
     return {
       info:{ age:1 },
       arr:[],
         }
   },
   mounted(){
     this.info.name = 'xx'; // 並不能觸發頁面更新
     this.info.age = 2; // 這樣子能夠觸發頁面更新
     this.info = {name:'xx'}; // 這樣子能夠觸發頁面更新
     
     // 數組同理
     this.arr[0] = 1; // 並不能觸發頁面更新
     this.arr = [1]; // 這樣子能夠觸發頁面更新
   }
 }
</script>
由於 Vue 在初始化時須要對 data 進行使用 defineProperty 進行 set 和 get 的劫持,若是對象中的值爲空,那就不會存在相應的 set 和 get,因此二者方式,一個給對象裏面設置初值,二個將對象改成一個新對象,而不是直接在上面添加屬性和值。

基於以上,還有一個使用技巧,則是,一些表單若是依賴後臺返回的一些數據初始化選擇列表等,那麼能夠在賦值前,先在返回的數組中,加上一個屬性,例如 isChecked,而後再賦值給 datavue

<template>
    <div>
   <template v-for="(item,index) in checkboxs">
      <input type="checkbox"
             v-model="item.isChecked"
             :key="index">
    </template>
  </div>
</template>
<script>
export default {
    data(){
    return {
      checkboxs:[]
        }
  },
  methods:{
    getData(){
      // 請求過程略 
      let data = [{name:'zs',name:'ls'}] // 原請求返回數據
      this.checkboxs = data.forEach(item=>Object.assign(item,{isChecked:false}))
    }
    }
}
</script>

14. 開發組件時,能夠用 slot 佔位,內容在使用組件的地方填充,使組件更加靈活。

15. 在開發管理後臺時,能夠將通用的數據和方法,寫在 mixin 裏面,而後在頁面中注入,如通用表格的頁碼配置數據,加載中狀態值,還有獲取上一頁,下一頁的方法。

16. 在查看大圖的場景中,點開大圖,安卓物理鍵返回時,須要關閉大圖,而不進行頁面的跳轉,則是能夠在點開大圖的時候,pushState 在 history 中添加一個路由,若是頁面須要點擊關閉的功能,則須要在點關閉按鈕時,手動觸發 history.go(-1) 一下;

17. 若是在例如一個操做結果頁(好比支付完成),而後又不能手動配置分享內容的時候,又須要分享的是購買的商品時,則能夠經過 replaceState 方法改變路由,可是又不會觸發頁面刷新,再分享時,app或者瀏覽器則會自動抓取到替換後的地址分享出去了。

18. 父組件能夠經過 @hook:created @hook:mounted 等監聽到子組件的生命週期。

19. errorCaptured 捕獲子孫組件的錯誤。

20. slot 插槽的使用

  • 匿名插槽
// child.vue
<template>
<header>
  <slot>all header</slot>
  </header>
</template>

// parent.vue
<template>
<child>
  <div>this is custom header</div>
  </child>
</template>
slot 中能夠有默認值
  • 具名插槽
// child.vue
<template>
  <header>
    <slot name="left">left</slot>
    <slot name="right">right</slot>
  </header>
</template>

// parent.vue
<template>
  <child>
    <div slot="left">custom left</div>
    <div slot="right">custom right</div>
    <div slot="right">custom right2</div>
  </child>
</template>
具名插槽能夠有一個以上一樣 name 的填充。注意組件中用 slot + name ,使用時用 slot=name ,這裏容易搞混。
  • 帶值傳遞插槽,slot-scope,也就是子組件的值傳遞給父組件
// child.vue
<template>
  <header>
    <slot :user="userinfo"
    :address="address"></slot>
  </header>
</template>
<script>
export default {
  data() {
    return {
      userinfo: { name: 'haokur' },
      address: { city: 'guangzhou' },
    }
  },
}
</script>

// parent.vue
<template>
  <div>
    <Child>
      <template slot-scope="row">
      {{JSON.stringify(row)}} => {"user":{"name":"haokur"},"address":{"city":"guangzhou"}}
      </template>
    </Child>
  </div>
</template>
  • 帶值傳遞插槽的使用場景,在 element-ui 中使用的較多,當在非父組件中的循環時,而是向子組件傳遞值,子組件去遍歷時,父組件中的插槽沒法拿到遍歷的當前值,須要子組件在遍歷的時候,把值又給附加在 slot 上。
// List.vue
<template>
  <ul class="table">
    <li class="row"
    v-for="(item,index) in dataList"
    :key="index">
      <slot :row="item">
        {{item}}
      </slot>
    </li>
  </ul>
</template>
<script>
export default {
  props: ['dataList'],
}
</script>

/// parent.vue
<template>
  <div>
    <TestList :dataList="[1,2,3,4,5,6]">
      <template slot-scope="scope">
        {{ scope.row * 2 }}
      </template>
    </TestList>
  </div>
</template>
因而就實現了,子組件反向又像父組件傳遞值

21. v-once 進行渲染優化,v-once 只會初始化一次,以後頁面數據發生變化,v-once 內的內容也不會發生變化。

<template>
  <div>
    <span v-once>{{dateNow}}</span>
    <span>{{dateNow}}</span>
  </div>
</template>
<script>
export default {
  data(){
    return {
      dateNow: Date.now()
      }
  },
  mounted(){
    setInterval(() => {
      this.dateNow = Date.now()
    }, 1000)
  }
}
</script>
測試可看到只有沒有加 v-once 的時間在變。

22. 關於在 created 仍是在 mounted 生命鉤子裏發送數據請求。有些說法是放在 created 中會好一些,能夠避免多渲染一次?其論據多是數據若是加載得快,那麼就不render默認初始值,而是直接拿到獲取到的數據,而後render?先給出個人結論,created 和 mounted 中基本無差,二者的渲染次數是一致的。

<script>
export default {
  created(){
    this.name = 'hello';
    setTimeout(() => {
      this.name = 'haokur'
    }, 0)
  },
  mounted(){
    // this.name = 'xiao'
    //setTimeout(() => {
    //  this.name = 'haokur'
    //}, 0)
  },
  render(h){
    console.log('執行渲染',this.name)
    return h('div',{},this.name)
    }
}
</script>
以上測試可知, 執行渲染 將進行兩次,也就是再快的數據返回,也是要進行兩次 render 的。由於請求和setTimeout 同樣都是異步的,因此它的執行結果是在事件隊列中等着的,而 render 是當前執行棧中的同步方法,它是執行在事件隊列中的方法以前的。

註釋 created,放開mounted ,render 方法則會執行三遍。webpack

可是 created 和 mounted 中直接賦值則是有差異的,由於 render 會發生在 mounted 以前一次。也就是初始化時,created =》render =》 mounted =》(如有更改,再次 render) =》 請求返回 =》 再次 rendergit

因此最佳的處理方式是,同步更改數據的,放 created 中,這樣一些初值在第一次渲染就能正確呈現,且比在 mounted 中少執行一遍 render ,異步更改的無所謂。github

23. is 的使用,在一些特定的結構中使用組件,如 ul 下的 li

// UserItem.js
<template>
    <li>this is useritem</li>
</template>

// container.js
<template>
    <ul>
    <li is="user-item"></li>
  </ul>
</template>
<script>
import UserItem from './UserItem.js'
export default {
    components:{
    'user-item':UserItem
  }
}
</script>

24. Vue 雙向數據綁定實現

25. Proxy 實現

26. 三大框架對比

27. vue 的 functional 屬性

2、Vue-Router 的使用

1. 路由的簡單使用

import Vue from 'vue'
import VueRouter from 'vue-router'

import App from './app.vue'
import Home from './home.vue'

Vue.use(VueRouter); // 註冊 router-view,router-link 組件

new Vue({
  el:'#app',
  router:new VueRouter({
    routes:[
      path:'/home',
      component:Home
    ]
  }),
  render:h=>h(App)
})
以上僅是簡單使用,在實際項目中,能夠將 new VueRouter() 路由配置提出去,而後入口頁引入。
// router.config.js
export const RouterConf = new VueRouter({
  mode:'hash', // hash 默認,切換使用 # 模式,#/home; history 沒有 # ,須要服務器配合;abstract 暫未知
  routes:[
     path:'/home',
     component:Home
  ]
})
// 以上還能夠把 routes 的值數組單獨提出去放一個文件

// main.js
import { RouterConf } from './router.config.js'
new Vue({
  router:RouterConf
})

2. 路由守衛的使用

全局路由守衛

  • beforeEach,全部頁面進入前,能夠在這裏作權限管理,保存滾動條位置,保存當前頁面數據狀態,調起顯示頁面加載動畫等。
// 接上面👆代碼,router.config.js
import RouterConf from './router.config.js';

// to ,將要跳轉的地址信息;now,如今的路由配置信息;next 執行跳轉的方法,next(false)和沒有next頁面都將不跳轉,能夠 next('/login') 驗證用戶是否登陸而後跳轉登陸,也能夠 next(false) 阻止用戶進入沒有權限的頁面。
RouterConf.beforeEach((to,now,next)=>{
  let token = localStorage.getItem('token')
  if(!token && to.name!=='login'){ // 防止路由登陸頁也一直跳轉登陸頁
    next('/login')
  }
  else{
     next();
    }
})
  • afterEach,全部頁面進入後。能夠作一些頁面標題的設置,初始化回到頂部等
import Vue from 'vue';

// 接上面👆代碼,router.config.js
import RouterConf from './router.config.js';

// now 表示當前路由配置對象,from 表示上一個路由配置對象
RouterConf.afterEach((now,from)=>{
   document.title = now.meta.pageTitle;
    // 若是須要在頁面渲染完以後處理一些事情
  Vue.nextTick(()=>{
    window.scroll(0,0);
  })
})

組件路由守衛

  • beforeRouteEnter(to,from,next),組件加載進入時
  • beforeRouteUpdate(to,from,next),路由地址更新後執行,好比 #/home => #/home?a=1
  • beforeRouteLeave(to,from,next),能夠經過 next(false) 阻止頁面離開,比較好用,相見恨晚。
<script>
// home.vue
export default {
  mounted(){
    console.log('home mounted')
  },
  beforeRouteUpdate(to,from,next){
    next();
    },
  beforeRouteEnter(to,from,next){
    next();
    },
  beforeRouteLeave(to,from,next){
    // 能夠彈個窗提示,用戶是否確認離開,確認才離開當前頁面,更嚴謹一點,須要判斷是日後退仍是push新頁面提交等等具體狀況具體分析使用
    let confirmStatus = this.confirmStatus
    confirmStatus && next();
    },
}
</script>
切記beforeRouteUpdate,beforeRouteLeave,都須要手動調 next 纔會進行下一步跳轉。beforeRouteUpdate 暫未遇到什麼使用場景。
  • 番外,沒有發現 beforeRouteLeave 時,頁面返回須要確認時的處理邏輯。
  1. 進入頁面後,先用 pushState 在路由中添加一個記錄,pushState 不會刷新頁面用戶無感知
  2. 而後監聽路由變化,即 window.addEventListener('hashchange',function(){ },false) ;
  3. hashchange 回調中,判斷是否須要彈窗提示
  4. 在表單點擊提交,再也不須要保存當前頁的數據時,先 history.go(-1) ,而後再移除hashchange 監聽,再進行提交表單頁面跳轉等操做。

3. 路由組件使用

router-view

定義路由容器,路由引發的變化的內容都將在這個容器之中,配合 keep-alive 使用可緩存頁面。web

<template>
    <div id="app">
    <div>
        不會隨路由變化而變化的內容,能夠存放一些全局使用內容,如提示信息等,經過使用 vuex 傳遞消息,或是 BusService 傳遞消息,使得內容顯示或者隱藏以及具體顯示內容
      </div>
    <keep-alive :includes="['home','list']">
          <router-view></router-view>
      </keep-alive>
  </div>
</template>

router-link

定義路由跳轉,傳入 to 屬性,爲一個對象,能夠有 name(字符串),path(字符串),query(對象),params(對象)算法

<!-- 假若有配置路由 {
    name:'detail',
    path:'detail/:id'
} -->
<template>
  <router-link :to="{name:'detail',params:{id:1},query:{name:'xx'}}"></router-link> 
  <!-- 上面渲染出的連接: #/detail/1?name=xx -->
  <router-link :to="{path:'/detail',params:{id:1},query:{name:'xx'}}"></router-link>
  <!-- 上面渲染出的連接: #/detail?name=xx -->
  <router-link :to="{name:'detail',path:'/detail',params:{id:1},query:{name:'xx'}}"></router-link>
  <!-- 上面渲染出的連接: #/detail/1?name=xx -->
</template>
注意使用 name 時,vue 將根據路由配置找到對應的 name,而後還原出 path,這時候若是路由配置的 path 是 '/detail/:id' 相似的形式,則將按這規則,還原完整的地址。

也就是當有 path 時,只照顧 query 參數。vue-router

有 name 時,對應路由配置信息找出 path,而後用 params 填充 path,再拼上 queryvuex

name 和 path 共存時,按 name 的規則走。(儘可能避免)element-ui

4. 路由方法的使用,$router & $route

$router,經常使用的如下幾個方法

  • push,參數能夠是字符串,表示要跳轉的地址,或者一個對象,相似 router-link 的 to 的值
  • replace,替換一個地址,參數和 push 同
  • back,回退上一個頁面
  • go,回退或前進 n 個頁面
export default {
  mounted(){
    this.$router.push('/detail/1'); // 直接字符串形式
    this.$router.push({
      name:'detail',
      query:{a:1},
      params:{id:1}
    }); // 使用規則和 router-link 相似
    // this.replace 相似 push
    this.$router.back()
    this.$router.go(-2) // 後退
    this.$router.go(2) // 前進
  }
}

$route,路由參數信息對象(響應式)

組件中獲取路由參數,經過 this.$route 獲取

  • params,對應路由中的 'detail/:id' 配置,路由爲 #detail/1 , 則 this.$route.params.id 爲 1;
  • query,對應路由中的 ?後的值,如路由爲 #detail/1?name=xx,則 this.$route.query.name 爲 xx;
// 能夠在 created 週期內獲取
export default {
  data(){
    return {
      id:'',
      name:''
    }
  }
  created(){
    let { id } = this.$route.params;
    let { name } = this.$route.query;
    this.id = id || ''
    this.name = name || ''
  }
}

5. 路由懶加載配置

之前使用 require.ensure 實現路由懶加載,如今能夠直接 ()=>import 實現了
// router.config.js
export const RouterConf = [
  {
    path:'/home',
    component:()=> import(/* webpackChunkName: "home" */ './home.js')
  }
]

打包時,將以 home 命名。

待續

示例代碼

整理完以後,將發佈在 github 上
https://github.com/haokur/

相關文章
相關標籤/搜索