Vue知識點總結

 

Vue環境搭建

    方式1
         全局安裝 vue-cli
        $ npm install --global vue-cli
        # 建立一個基於 webpack 模板的新項目
        $ vue init webpack my-project
        # 安裝依賴
        $ cd my-project
        $ npm install
        $ npm run dev
    方式2:
        直接引入對應的js文件

基礎知識

所謂指令,其實本質就是在模板中出現的特殊標記,根據這些標記讓框架知道須要對這裏的 DOM 元素進行什麼操做。javascript

v-text

  • 解釋:更新元素的 textContent,將數據解析爲純文本
<h1 v-text="msg"></h1>

這裏v是vue的前綴,text是指令ID,msg是expression。 msg做爲ViewModel,當它的值發生改變時,就觸發指令text,從新計算標籤的textContent(innerText)。

Mustache{{}}

  • 解釋:能夠局部更新元素的 textContent,將數據解析爲純文本
<div id="app"> <p>蘋果的價格爲{{ msg }}元</p> </div>
 
 

v-html

  • 解釋:更新元素的 innerHTML,將數據解析成html標籤


<h1 v-html="msg"></h1>

v-bind

  • 做用:綁定屬性
  • 語法:v-bind:title="msg"
  • 簡寫::title="msg"

v-on

  • 做用:綁定事件
  • 語法:v-on:click="say" or v-on:click="say('參數', $event)"
  • 簡寫:@click="say"
  • 說明:綁定的事件從methods中獲取

使用逗號分割綁定多個事件css

<div v-on="click:onClick, keyup:onKeyup, keydown:onKeydown"></div>html

事件修飾符

  • .stop 阻止冒泡,調用 event.stopPropagation() // 好比說一個div下有一個彈窗和一個表單 點擊提交按鈕點擊了<input type="submit" />提交表單信息 點擊彈出層周邊空白區域,關閉彈窗 ,當表單顯示在彈窗層上方時,爲防止彈窗被意外關閉,須要阻止表單提交按鈕冒泡行爲
  • .prevent 阻止默認事件,調用 event.preventDefault() // 好比說點擊了連接標籤a,在跳轉以前,要修改一些URL參數 
  • .capture 添加事件偵聽器時使用事件捕獲模式  // 捕獲階段先於冒泡,若是在儘可能頂層處理事件,而後阻止傳播,能夠略微節約性能開銷。scroll/resize 這類可能連續觸發的事件不冒泡的緣由
  • .self 只當事件在該元素自己(好比不是子元素)觸發時觸發回調
  • .once 事件只觸發一次
  • 按鍵修飾符
 <input v-on:keyup.13="submit"> 
記住全部的 keyCode 比較困難,因此 Vue 爲最經常使用的按鍵提供了別名: <input v-on:keyup.enter="submit"> <input @keyup.enter="submit"> 按鍵別名包括:.enter .tab .delete (捕獲 「刪除」 和 「退格」 鍵) .esc .space .up .down .left .right.ctrl .shift .meta(windows 鍵,mac-command 鍵,)
 
 

 

  • .native 在父組件中給子組件綁定一個原生的事件,不加'. native'事件是沒法觸發的
<div id="app">
  <my-component @click.native="clickFun"></my-component>
</div>
Vue.component('my-component', {
  template: `<a href='#'>click me</a>`
})
new Vue({
  el: '#app',
  methods: {
    clickFun: function(){
      console.log("message: success")
    }
  }
})

v-model

  • 做用:在表單元素上建立雙向數據綁定
  • 說明:監聽用戶的輸入事件以更新數據
<input v-model="message" placeholder="edit me">

<input  type='radio'  v-model="radioVal" value='單選按鈕' placeholder="edit me">

<!-- checkboxArr的值必須爲數組 -->
<input  type='checkbox'  v-model="checkboxArr" value='複選按鈕' placeholder="edit me">

 

v-for

  • 做用:基於源數據屢次渲染元素或模板塊
<div v-for="item in items">
  {{ item.text }}
</div>

<!-- 遍歷數組 item 爲當前項,index 爲索引 -->
<p v-for="(item, index) in list">{{item}} -- {{index}}</p>
<!--遍歷對象 item 爲值,key 爲鍵,index 爲索引 -->
<p v-for="(item, key, index) in obj">{{item}} -- {{key}}</p>
<!-- 遍歷常量 item爲從1開始的遞增值 --> <p v-for="item in 10">{{item}}</p>

v-for的key屬性

  • 推薦:使用 v-for 的時候提供 key 屬性,以得到性能提高。
  • 說明:使用v-for更新已渲染的元素列表時,默認用就地複用策略;列表數據修改的時候,它會根據key值去判斷某個值是否修改,若是修改,則從新渲染這一項,不然複用以前的元素; 咱們在使用的使用常常會使用index(即數組的下標)來做爲key,但其實這是不推薦的一種使用方法;


const list = [
    {
        id: 1,
        name: 'test1',
    },
    {
        id: 4,
        name: '我是插隊的那條數據',
    }
    {
        id: 2,
        name: 'test2',
    },
    {
        id: 3,
        name: 'test3',
    },
]


以前的數據                         以後的數據

key: 0  index: 0 name: test1     key: 0  index: 0 name: test1
key: 1  index: 1 name: test2     key: 1  index: 1 name: 我是插隊的那條數據
key: 2  index: 2 name: test3     key: 2  index: 2 name: test2
                                 key: 3  index: 3 name: test3
// 不推薦
<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>

//推薦
<div v-for="(item, index) in list" :key="item.id" >{{item.name}}</div>

 

v-class和v-style

  • 說明:這兩個都是HTML元素的屬性,使用v-bind,只須要經過表達式計算出字符串結果便可
  • 表達式的類型:字符串、數組、對象
  • 語法:
<!-- 1 對象書寫方式-->
<div v-bind:class="{ active: true }"></div> ===>
<div class="active"></div>

<!-- 2 數組書寫方式-->
<div :class="['active', 'text-danger']"></div> ===>
<div class="active text-danger"></div>

<!-- 3 數組對象結合-->
<div v-bind:class="[{ active: true }, errorClass]"></div> ===>
<div class="active text-danger"></div>


<!-- style -->
<!-- 1 對象書寫方式 -->
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<!-- 2 數組書寫方式-->
<div v-bind:style="[baseStyles, overridingStyles]"></div>

v-if 和 v-show

  • v-if:根據表達式的值的真假條件,銷燬或重建元素 v-if適合條件不大可能改變的場景
  • v-show:根據表達式之真假值,切換元素的 display CSS 屬性,dom元素一直在  v-show適合頻繁切換

v-cloak

  • 這個指令保持在元素上直到關聯實例結束編譯。和 CSS 規則如 [v-cloak] { display: none } 一塊兒用時,這個指令能夠隱藏未編譯的 Mustache 標籤直到實例準備完畢。
  • 防止刷新頁面,網速慢的狀況下出現{{ message }}等數據格式
<div v-cloak>
  {{ message }}
</div>

v-pre

  • 說明:跳過這個元素和它的子元素的編譯過程。能夠用來顯示原始 Mustache 標籤。跳過大量沒有指令的節點會加快編譯。
<span v-pre>{{ this will not be compiled }}</span>

v-once

  • 說明:只渲染元素和組件一次。隨後的從新渲染,元素/組件及其全部的子節點將被視爲靜態內容並跳過。這能夠用於優化更新性能。
<span v-once>This will never change: {{msg}}</span>
 

自定義指令

 

指令定義函數提供了幾個鉤子函數 (可選):前端

  1. bind-只調用一次,指令第一次綁定到元素時調用。
  2. insert-被綁定元素插入父節點時調用。
  3. update-所在組件的 VNode 更新時調用,可是可能發生在其子元素的 VNode 更新以前。
  4. componentUpdated-所在組件的 VNode 及其子元素的 VNode 所有更新時調用。
  5. unbind-只調用一次,指令與元素解綁時調用

這五個鉤子函數中bindupdate是最有用的。vue

他們中的每個都有能夠用的el,bindingvnode參數,除了updatecomponentUpdated以外,還會暴露oldVnode,以區分傳遞的舊值和新值。java

  • el 指令所綁定的元素,能夠用來直接操做 DOM 。
  • binding 一個對象,包含如下屬性:name,value,oldValue,expression,argmodifiers(修飾符對象)
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>


Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})


name:'demo',
value:'hello',
expression:'message',
arg:'foo',
modifiers:{"a":true,"b":true}
  • vnode Vue 編譯生成的虛擬節點。

bindingvnode都是隻讀。node

好比要實現一個設置元素在某個位置的功能webpack

Vue.directive('tack',{
    bind(el,binding,vnode){
        el.style.position = 'fixed';
        el.style.top = binding.value.top+'px';
        el.style.left = binding.value.left+'px';
    }
})

<p v-tack="{top:'40',left:'100'}">我固定在離頂部40px、左側100px的地方</p>

 

 
 

Vue自定義指令優先級順序

  • 系統默認指令會先於自定義指令執行ios

<!-- v-show 先於 v-block 執行 -->
<div v-block v-show="false"></div>
  • 自定義指令在標籤上的位置越靠前就越早執行web

<!-- v-none 先於 v-block 執行 -->
<div v-none v-block></div>

經常使用屬性

1、watch 監聽props或本組件的值

immediate屬性

watch 的一個特色是,最初綁定的時候是不會執行的,要等到數據改變時才執行監聽計算。那咱們想要一開始就讓他最初綁定的時候就執行改怎麼辦呢?咱們須要修改一下咱們的 watch 寫法,修改事後的 watch 代碼以下:
watch: {
  firstName: {
    handler(newName, oldName) {
      this.fullName = newName + ' ' + this.lastName;
    },
    // 表明在wacth裏聲明瞭firstName這個方法以後當即先去執行handler方法
    immediate: true
  }
}

  deep屬性

  默認狀況下 handler 只監聽obj這個屬性它的引用的變化,咱們只有給obj賦值的時候它纔會監聽到,若是咱們須要監聽obj裏的屬性a的值呢?這時候deep屬性就派上用場了!

<div>
      <p>obj.a: {{obj.a}}</p>
      <p>obj.a: <input type="text" v-model="obj.a"></p>
</div>

new Vue({
  el: '#root',
  data: {
    obj: {
      a: 123
    }
  },
  watch: {
    obj: {
      handler(newName, oldName) {
         console.log('obj.a changed');
      },
      immediate: true,
    deep:true, } } })
 

deep的意思就是深刻觀察,監聽器會一層層的往下遍歷,給對象的全部屬性都加上這個監聽器,可是這樣性能開銷就會很是大了,任何修改obj裏面任何一個屬性都會觸發這個監聽器裏的 handler。

優化,咱們能夠是使用字符串形式監聽

watch: {
  'obj.a': {
    handler(newName, oldName) {
      console.log('obj.a changed');
    },
    immediate: true,
    // deep: true
  }
} 

全局watch的註銷方式
調用後會返回一個值,就是方法,你要註銷 watch 只要調用方法就能夠了。
app.$watchunWatchunWatch
const unWatch = app.$watch('text', (newVal, oldVal) => {
  console.log(`${newVal} : ${oldVal}`);
})

unWatch(); // 手動註銷watch


2、computed 相似於過濾器,對綁定到view的數據進行處理。不能與data中定義的變量重名,具備緩存性,頁面從新渲染值不變化,計算屬性會當即返回以前的計算結果,而沒必要再次執行函數

<template>
  <div>
    <h4>測試</h4>
    <div>
      {{didi}}
      {{family}}
    </div>
    <div>
      {{didiFamily}}
    </div>
  </div>

</template>

<script>
   export default {
    data () {
       return {
        didi: 'didi',
        family: 'family'
       }
     },
    computed: {
      didiFamily:{
        //getter
        get:function(){
          return this.didi + ' ' + this.family
        },
        //setter
        set:function(newValue){
          // 這裏因爲該計算屬性被賦值,將被調用
          console.log(newValue)
          this.didi = 123
          this.family = 456
        }
      }
    },
    mounted () {
      // 賦值,調用setter函數
      this.didiFamily = 'John Doe'
    }
  }
</script>
Vue實例中被觀察的數據屬性發生了改變時纔會從新執行getter,可是咱們有時候計算屬性依賴實時的非觀察數據屬性,好比下面例子中的Date.now
<template> <div> <h4>測試</h4> <div> <input type="text" v-model="message" /> <div>{{now}}</div> </div> </div> </template> <script> export default { data () { return { message: 'hello' } }, computed: { now:{ cache: false, get:function(){ return Date.now() + this.message } } }, mounted () { setInterval(() => { // 當緩存開關爲false的時候,定時器每次打印的時間都是不同的 console.log(this.now) }, 500) } } </script>
 
  1. 當一個變量會引發多個變量的變化時,應該用watch,多個變量的變化會引發一個變量變化時,應該用computed
  2. 雖然方法也能實現computed一樣的效果,可是由於計算屬性能夠基於它們的依賴進行緩存,因此選擇計算屬性會比方法更優。
 

過濾器

過濾器是針對一些數據 進行篩選、過濾、格式化等相關的處理,變成咱們想要的數據,過濾器的本質 就是一個帶有參數帶有返回值的方法 ,過濾器只能用在{{}}和v-bind裏面

一、過濾器的建立和使用

1.建立
全局過濾器
 Vue.filter(
  'filterA',
  function(myInput){
     //myInput是在調用過濾器時,管道前表達式執行的結果
     //針對myInput,按照業務需求作處理
     //返回
     return '處理後的結果'
  })
  局部過濾器
  filters: {
       filterB: function (value) {
         if (!value) return ''
         value = value.toString()
         return value.charAt(0).toUpperCase() + value.slice(1)
       }
     }

2.使用
  <any>{{expression | filterA | filterB}}</any>

二、如何在調用過濾器時,完成參數的發送和接受

1.發送
<any>{{expression | myFilter(參數1,參數2)}}</any>

2.接受
Vue.filter('myFilter',function(myInput,參數1,參數2){
    return '處理後的結果'
})

混合mixins和繼承extends

mixins接收對象數組(可理解爲多繼承),extends接收的是對象或函數(可理解爲單繼承)。

在項目中咱們常常會遇到多個組件調用同一個方法的問題,爲了不每次都在.vue文件中定義並調用,咱們可採用vue的mixin的用法,mixins就是定義一部分公共的方法或者計算屬性,而後混入到各個組件中使用,方便管理與統一修改

const extend = {
  data() {
    return {
      name: 'extend',
    }
  },
  created() {
    console.log('extends created')
  },
}
const mixin1 = {
  data() {
    return {
      name: 'mixin1',
    }
  },
  created() {
    console.log('mixin1 created')
  },
  methods: {
    fun() {
      console.log('mixin1 fun')
    },
  },
}
const mixin2 = {
  data() {
    return {
      name: 'mixin2',
    }
  },
  created() {
    console.log('mixin2 created')
  },
  methods: {
    fooB() {
      console.log('fooB')
    },
    fun() {
      console.log('mixin2 fun')
    },
  },
}
export default {
  mixins: [mixin1, mixin2],
  extends: extend,
  name: 'app',
  created() {
    console.log('created')
  },
}

生命週期

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>vue生命週期</title>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
  <div id="app">
    <h1>{{message}}</h1>
  </div>
</body>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      message: 'Vue的生命週期'
    },
    // template: "<h1>{{message +'這是在template中的'}}</h1>", //在vue配置項中修改的
    // render: function(createElement) {
    //     return createElement('h1', 'this is createElement')
    // },
    beforeCreate: function() {
      console.group('------beforeCreate建立前狀態------');
      console.log("%c%s", "color:red" , "el     : " ,this.$el); //undefined
      console.log("%c%s", "color:red","data   : " ,this.$data); //undefined 
      console.log("%c%s", "color:red","message: " + this.message) 
      this.test('beforeCreate');
      
    },
    created: function() {
      console.group('------created建立完畢狀態------');
      console.log("%c%s", "color:red","el     : " ,this.$el); //undefined
      console.log("%c%s", "color:red","data   : " ,this.$data); //已被初始化 
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化
      this.test('created');
    },
    beforeMount: function() {
      console.group('------beforeMount掛載前狀態------');
      console.log("%c%s", "color:red","el     : " + (this.$el)); //已被初始化
      console.log(this.$el);
      console.log("%c%s", "color:red","data   : " ,this.$data); //已被初始化  
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化  
    },
    mounted: function() {
      console.group('------mounted 掛載結束狀態------');
      console.log("%c%s", "color:red","el     : " ,this.$el); //已被初始化
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " ,this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化 
    },
    beforeUpdate: function () {
      console.group('beforeUpdate 更新前狀態===============》');
      console.log("%c%s", "color:red","el     : " ,this.$el);
      console.log(this.$el);   
      console.log("%c%s", "color:red","data   : " ,this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    updated: function () {
      console.group('updated 更新完成狀態===============》');
      console.log("%c%s", "color:red","el     : " ,this.$el);
      console.log(this.$el); 
      console.log("%c%s", "color:red","data   : " ,this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    beforeDestroy: function () {
      alert();
      console.group('beforeDestroy 銷燬前狀態===============》');
      console.log("%c%s", "color:red","el     : " ,this.$el);
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " ,this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    destroyed: function () {
      console.group('destroyed 銷燬完成狀態===============》');
      console.log("%c%s", "color:red","el     : " ,this.$el);
      console.log(this.$el);  
      console.log("%c%s", "color:red","data   : " ,this.$data); 
      console.log("%c%s", "color:red","message: " + this.message)
    },
    methods:{
      test(stage){
        console.log(stage+' called test fun')
      }
    }
  })
</script>
</html>

 

 

每一個階段能夠作的事情

    beforeCreate   能夠在這加個loading事件,
    created        在這裏結束loading事件,發送異步請求
    beforeMount
    mounted        操做dom
    beforeUpdate
    updated        對更新後的數據統一處理
    beforeDestroy  清除定時器,清除無用的全局緩存數據  這一步,實例仍然徹底可用。
    destroyed      Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。

組件化

組件:組件就是可被反覆使用的,帶有特定功能的視圖

所謂的組件化,就像玩積木同樣,把封裝的組件進行復用,把積木(組件)拼接在一塊兒,構成一個複雜的頁面應用程序。

組件樹就是由各個組件構成的一種數據結構,它存在的意義是爲了幫梳理應用程序

一、組件的建立

1.1、直接在template屬性中指定模板內容 1.全局組件 Vue.component

 Vue.component('my-com',{ template:` <h2>it is a global components</h2> ` })

2.局部組件
 new Vue({ components:{ 'my-footer':{ template:'<h1>it is local components</h1>' } } })
1.2、.vue結尾的文件
    <template></template>
    <script></script>
    <style></style>
1.3、單獨指定一個模板內容
    <script  id='myContent' type='text/x-template'>
    </script>

    Vue.component('my-component',{
      template:'#myContent'
    })
二、組件使用 做爲普通的標籤去使用 <my-com></my-com> 三、注意事項 1.組件命名方式使用ComponentName和component-name都可,可是當應用到dom的時候,應該使用短橫線分割命名方式。 2.若是一個組件 要渲染多個元素,將多個元素放在一個頂層標籤中,好比div、form
Vue.component("my-component", {
    template: "<div>
        <h1>我是全局組件元素1</h1>
        <h1>我是全局組件元素2</h1>
    </div>"
  });    
 3.全局組件必須寫在Vue實例建立以前,纔在該根元素下面生效;局部組件只能在父模板中直接調用
 

組件間通訊

1、父與子通訊 (props down)
    1.發送
        <son myName='zhangsan'>
        </son>
    2.接受
        到son組件:
        Vue.component('son',{
          props:['myName'],
          template:`
           <p>{{myName}}</p>
          `
        })
    
2、子與父通訊 (events up)
     1.綁定
    methods:{
     handleEvent:function(msg){
    console.log('子組件傳過來的值:',msg);
} }
<son @customEvent="handleEvent"></son> 2.觸發 子組件內部: this.$emit(‘customEvent’,100); 三、ref(reference 引用/參考) 幫助父組件獲得子組件中的數據、方法。 1.指定ref屬性 <son ref="mySon"></son> 2.根據ref獲得子組件實例 this.$refs.mySon 4、$parent 幫助子組件拿到父組件的實例中的數據,方法 this.$parent獲得父組件的實例 this.$root獲得根組件的實例 5、兄弟組件通訊 1.var bus = new Vue(); 2.接收方 bus.$on('eventName',function(msg){}) 3.發送方 bus.$emit('eventName',123);

路由模塊

路由模塊的本質 就是創建起url和頁面之間的映射關係

一、SPA的基本概念和工做原理

SPA:single page application 單一頁面應用程序,只有一個完整的頁面;
它在加載頁面時,不會加載整個頁面,而是隻更新某個指定的容器中內容。


工做原理:
1.解析地址欄 完整的頁面地址、路由地址
2.根據路由地址 從路由詞典中找到真正的要加載的頁面
3.發起ajax請求  請求要加載的頁面
4.像指定的容器中 插入加載來的頁面

二、路由模塊的基本使用

專業術語: 
    router路由容器 
    route路由
    routes 路由數組(路由詞典)
1.引入vue.js vue-router.js
2.指定一個容器
<router-view></router-view>
3.建立業務所須要用到的組件類
var MyLogin = Vue.component()
4.配置路由詞典
 const myRoutes = [
  {
path:
'base',component:MyLogin,
  // 路由嵌套
  children:[
    {path:'/a',component:A}
  ]
}, {path:
'/login',component:MyLogin} ]; const myRouter = new VueRouter({ routes:myRoutes }) new Vue({
mode:'hash', router:myRouter })
5.測試 修改地址欄中的路由地址,測試看加載的組件是否正確 注意事項: 1.先引入vue,再引入插件 2.必定要指定router-view

三、使用路由模塊來實現頁面跳轉的方式

方式1:直接修改地址欄
方式2:this.$router.push(‘路由地址’);
方式3:<router-link to="路由地址"></router-link>

四、完成參數的傳遞

在頁面之間跳轉的時候,在有些場景下,好比說list --> detail 須要同時指定參數

1.配置接收方的路由地址 /detail --> /detail/:index 
2.發送 
    <router-link to="/detail/20" />
    this.$router.push('/detail/20')
3.接收
    this.$route.params.index

5.傳參生成url

      const { href } = this.$router.resolve(
        {
          path: '/dataQuery/decisionDetail', query: { productCode, applyNo } }); window.open(href, "_blank");
 

6.登錄驗證

router.beforeEach((to, from, next) => {
  if (!cookie.get("isLogin")) {
    to.path !== "/login" ? next({ path: "/login" }) : next();
  }else {
   next();
  }
})

 7.草稿保存

beforeRouteLeave (to, from, next) {
     if(用戶已經輸入信息){
       //出現彈窗提醒保存草稿,或者自動後臺爲其保存
     }else{
       next(true);//用戶離開
     }
   }

 

Vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態。

vuex解決了組件之間同一狀態的共享問題。多個組件依賴於同一狀態。傳參的方法對於多層嵌套的組件將會變得很繁瑣,不一樣組件的行爲須要變動同一狀態,父子組件直接引用或者經過事件來變動和同步狀態的多份拷貝,一般會致使沒法維護代碼。這時就出現了Vuex,這是Vuex出現的背景。,而如今有了vuex,這意味着原本須要共享狀態的更新須要組件之間的通信,如今組件就都和store通信了

 

• Vue Components:Vue組件。HTML頁面上,負責接收用戶操做等交互行爲,執行dispatch方法觸發對應action進行迴應。

• dispatch:操做行爲觸發方法,是惟一能執行action的方法。

• actions:操做行爲處理模塊。負責處理Vue Components接收到的全部交互行爲。包含同步/異步操做,支持多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操做就在這個模塊中進行,包括觸發其餘action以及提交mutation的操做。該模塊提供了Promise的封裝,以支持action的鏈式觸發。

• commit:狀態改變提交操做方法。對mutation進行提交,是惟一能執行mutation的方法。

• mutations:狀態改變操做方法。是Vuex修改state的惟一推薦方法,其餘修改方式在嚴格模式下將會報錯。該方法只能進行同步操做,且方法名只能全局惟一。操做之中會有一些hook暴露出來,以進行state的監控等。

• state:頁面狀態管理容器對象。集中存儲Vue components中data對象的零散數據,全局惟一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。

• getters:state對象讀取方法。圖中沒有單獨列出該模塊,這個方法包含在了render中,Vue Components經過該方法讀取全局state對象。

 

下面來看一下如何使用vuex 

1.   首先創建一個store分支文件,如創建一個city_store.js的文件,一般設計store對象都包含4個屬性:state,getters,actions,mutations。

state (相似存儲全局變量的數據)
getters (提供用來獲取state數據的方法)
actions (提供跟後臺接口打交道的方法,並調用mutations提供的方法)
mutations (提供存儲設置state數據的方法)
export default {
    // 1. state
    state:{
        city:"城市名"
    },

    // // 2. getters
    getters:{
        // 參數列表state指的是state數據
        getCityFn(state){
            return state.city;
        }
    },
    // 3. actions
    // 一般跟api接口打交道
    actions:{
        // 設置城市信息
        // 參數列表:{commit, state}
        // state指的是state數據
        // commit調用mutations的方法 
        // name就是調用此方法時要傳的參數
        setCityName({commit,state}, name){
            // 跟後臺打交道
            // 調用mutaions裏面的方法
            commit("setCity", name);
        }
    },
    // 4. mutations
    mutations:{
        // state指的是state的數據
        // name傳遞過來的數據
        setCity(state, name){
            state.city = name;//將傳參設置給state的city
        }
    }
}

2.在store主文件中引入

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);

import city_store from '../store/modules/city_store.js';//引入某個store對象

export default new vuex.Store({
    modules: {
        dialog: city_store
    }
})
 


3.在入口文件main.js中引用vuex

//vuex
import store from './store'

new Vue({
  el: '#app',
  router,
  store,//使用store
  template: '<App/>',
  components: { App }
})

4.在頁面中使用store中的數據和方法

<template>
    <div class="city">
        <h1>{{city}}</h1>
        <ul>
            <li v-for="(item,index) in cityArr" @click="backFn(index)">
                <h2>{{item}}</h2>
            </li>
        </ul>
    </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
        cityArr:['北京','上海','廣州','深圳','茂名','張家界','清遠','汕頭','佛山']
    }
  },
  computed:{
    city:function() {
      // 經過vuex的getters方法來獲取state裏面的數據
      return this.$store.getters.getCityFn;
    }
  },
  methods:{
      backFn : function(index){
          // 調用vuex的ations設置城市的值
          this.$store.dispatch("setCityName", this.cityArr[index]);

          //返回到首頁
          this.$router.push("/");
      }
  }
}
</script>

mapState、mapGetters、mapActions,mapMutations的做用

不少時候 , this.$store.state.city 、this.$store.dispatch("setCityName", this.cityArr[index]);這種寫法不方便 ,使用 mapState、mapGetters、mapActions ,mapMutations就不會這麼複雜了。

<script>
import { mapGetters, mapActions, mapState, mapMutations } from "vuex";
export default {
  name: 'HelloWorld',
  data () {
    return {
        cityArr:['北京','上海','廣州','深圳','茂名','張家界','清遠','汕頭','佛山']
    }
  },
  computed:{
   ...mapGetters(["getCityFn"]),
    city:function() {
      // 經過vuex的getters方法來獲取state裏面的數據
      return this.getCityFn;
    }
  },
  methods:{
      ...mapActions(["setCityName"]),
      backFn : function(index){
          // 調用vuex的ations設置城市的值
          this.setCityName(this.cityArr[index]);

          //返回到首頁
          this.$router.push("/");
      }
  }
}
</script>

mapGetters  mapState通常寫在 computed 中 , mapMutations, mapActions 通常寫在methods 中。

 

keep-alive屬性

在Vue構建的單頁面應用(SPA)中,路由模塊通常使用vue-router。vue-router不保存被切換組件的狀態,它進行push或者replace時,舊組件會被銷燬,而新組件會被新建,走一遍完整的生命週期。

但有時候,咱們有一些需求,好比跳轉到詳情頁面時,須要保持列表頁的滾動條的深度,等返回的時候依然在這個位置,這樣能夠提升用戶體驗。在Vue中,對於這種「頁面緩存」的需求,咱們可使用keep-alive組件來解決這個需求。

 

假若有下面這個場景:

  • 現有頁面:首頁(A)、列表頁(B)、詳情頁(C),通常能夠從:A->B->C;
  • B到C再返回B時,B要保持列表滾動的距離;
  • B返回A再進入B時,B不須要保持狀態,是全新的。

 

在根頁面中定義keep-alive,並傳入全局的緩存數組:在Vuex中定義一個全局的緩存數組,待傳給include:


// App.vue

<div class="app">
  <!--傳入include數組-->
  <keep-alive :include="keepAliveComponents">
    <router-view></router-view>
  </keep-alive>
</div>

export default {
  computed: {
    ...mapState({
      keepAliveComponents: state => state.keepAliveComponents
    })
  }
}
// global.js

export default {
  namespaced: true,
  state: {
    keepAliveComponents: [] // 緩存數組
  },
  mutations: {
    keepAlive (state, component) {
      // 注:防止重複添加
      !state.keepAliveComponents.includes(component) && 
        state.keepAliveComponents.push(component)
    },
    noKeepAlive (state, component) {
      const index = state.keepAliveComponents.indexOf(component)
      index !== -1 &&
        state.keepAliveComponents.splice(index, 1)
    }
  }
}

緩存:在路由配置頁中,約定使用meta屬性keepAlive,值爲true表示組件須要緩存。在全局路由鉤子beforeEach中對該屬性進行處理,這樣一來,每次進入該組件,都進行緩存:

const router = new Router({
  routes: [
    {
      path: '/B',
      name: 'B',
      component: B,
      meta: {
        title: 'B頁面',
        keepAlive: true // 這裏指定B組件的緩存性
      }
    }
  ]
})

router.beforeEach((to, from, next) => {
  // 在路由全局鉤子beforeEach中,根據keepAlive屬性,統一設置頁面的緩存性
  // 做用是每次進入該組件,就將它緩存
  if (to.meta.keepAlive) {
    store.commit('keepAlive', to.name)
  }
})

取消緩存的時機:對緩存組件使用路由的組件層鉤子beforeRouteLeave。由於B->A->B時不須要緩存B,因此能夠認爲:當B的下一個頁面不是C時取消B的緩存,那麼下次進入B組件時B就是全新的:

export default {
  name: 'B',
  created () {
      // ...設置滾動條在最頂部
  },
  beforeRouteLeave (to, from, next) {
    // 若是下一個頁面不是詳情頁(C),則取消列表頁(B)的緩存
    if (to.name !== 'C') {
        this.$store.commit('noKeepAlive', from.name)
    }
    next()
  }
}

slot 插槽

當某個組件被多個地方使用 , 每一個地方使用該組件時對該組件的內部有一部分須要特殊定製 , 這個時候slot可讓咱們更好的複用組件的同時並對其定製。 插槽,也就是slot,是組件的一塊HTML模板,這塊模板顯示不顯示、以及怎樣顯示由父組件來決定, 插槽顯示的位置卻由子組件自身決定,slot寫在組件template的什麼位置,父組件傳過來的模板未來就顯示在什麼位置。

<template>
  <div id="app">
      <children>
        <span>子組件內部元素</span>
      </children>
  </div>
</template>
 
 
<script>
  export default {
    name: 'hello',
    components: {
      children: {
        template: '<div>這裏是子組件</div>'
      }
    }
  }
</script>

<!--渲染結果-->
<div id="app">
    <div>這裏是子組件</div>
</div>
<template>
  <div id="app">
      <children>
        <span>子組件內部元素</span>
      </children>
  </div>
</template>
 
<script>
  export default {
    name: 'hello',
    components: {
      children: {
        template: '<div><slot><p>默認效果</p></slot>這裏是子組件</div>'
      }
    }
  }
</script>

<!--渲染結果-->
<div id="app">
    <span>子組件內部元素</span>
    "這裏是子組件"
</div>

 具名插槽能夠有多個,匿名插槽只能有一個

<template>
  <div class="father">
    <h3>這裏是父組件</h3>
    <child>
      <div slot="up">
        <span>菜單1</span>
      </div>
      <div slot="down">
        <span>菜單-1</span>
      </div>
      <div>
        <span>菜單->1</span>
      </div>
    </child>
  </div>
</template>
<template>
  <div class="child">
    // 具名插槽
    <slot name="up"></slot>
    // 具名插槽
    <slot name="down"></slot>
    // 匿名插槽
    <slot></slot>
  </div>
</template>

做用域插槽-- 做用域插槽的關鍵之處就在於,父組件能接收來自子組件的slot傳遞過來的參數

<div id="root">
        <child>
            <template slot-scope="props"><!--定義一個插槽,該插槽必須放在template標籤內-->
                <li>{{props.value}}</li>
            </template>
        </child>
    </div>
    <script>
        Vue.component('child',{
            data: function(){
                return {
                    list:[1,2,3,4]
                }
            },
            template: `<div>
                            <ul>
                                <slot v-for="value in list" :value='value'>//使用slot佔位
                                </slot>
                            </ul>
                        </div>`
        })
        var vm=new Vue({
            el: '#root'
        })
    </script>

axios

1.axios的get方法

export const getAjax= function (getUrl,getAjaxData) {
  return axios.get(getUrl, {
    params: {
      'getAjaxDataObj1': getAjaxData.obj1,
      'getAjaxDataObj2': getAjaxData.obj2
    }
  })
}

2.axios的post方法

export const postAjax= function (getUrl,postAjaxData) {
  return axios.post(postUrl, {
    data:{ 
'postAjaxDataObj1': postAjaxData.obj1,    'postAjaxDataObj2': postAjaxData.obj2
}   }) }

3.axios的攔截器
主要分爲請求和響應兩種攔截器,請求攔截通常就是配置對應的請求頭信息(適用與常見請求方法,雖然ajax的get方法沒有請求頭,可是axios裏面進行啦封裝),響應通常就是對reponse進行攔截處理,若是返回結果爲[]能夠轉化爲0

Vue.use(Vuex)
Vue.use(VueAxios, axios)
Vue.use(qs)
// 注:qs,使用axios,必須得安裝 qs,全部的Post 請求,咱們都須要 qs,對參數進行序列化。

在 request 攔截器實現

axios.interceptors.request.use(
 config => {
  config.baseURL = '/api/'
  config.withCredentials = true // 容許攜帶token ,這個是解決跨域產生的相關問題
  config.timeout = 6000
  let token = sessionStorage.getItem('access_token')

   config.headers = {
    'access-token': token,
    'Content-Type': 'application/x-www-form-urlencoded'
   }
return config
 },
 error => {
  return Promise.reject(error)
 }
)
//在 response 攔截器實現

axios.interceptors.response.use(
 response => {
  // 定時刷新access-token
  if (!response.data.value && response.data.data.message === 'token invalid') {
   // 刷新token
   store.dispatch('refresh').then(response => {
    sessionStorage.setItem('access_token', response.data)
   }).catch(error => {
    throw new Error('token刷新' + error)
   })
  }
  return response
 },
 error => {
  return Promise.reject(error)
 }
)

 

踩坑經驗

1.修改了數據某項內容,或修改了對象的新增的某個屬性,視圖沒有同步更新

解決方法:Vue.set( target, key, value )

Vue.set(this.items,0,{message:"Change Test",id:'10'})

2.組件命名與html標籤重名

[Vue warn]: Do not use built-in or reserved HTML elements as component id: Table

不要使用內建或預留的HTML 標籤做爲組件ID,改爲其它名稱

3. action中commit的行爲名稱不能重名,重名會形成mutations動做執行覆蓋

4.vue中使用$refs的時候,有時候要用[0]區獲取,有時候不須要
這是因爲vue中的v-for去讀取ref的是讀取的是domlist,而單獨的ref讀取的是實體單獨的dom

5.vue中使用vue-lazyload的時候數據改變,可是圖片沒有改變,這是因爲懶加載的機制致使的,須要給load的img加上一個:key就會更新了

<el-image :key="url" :src="url" lazy></el-image>

6.vue-router跳轉的時候用path的時候參數傳遞只能用query,而用params傳參的時候只能用name來設置跳轉的鏈接

this.$router.push({  name:'router1',params: { id: status ,id2: status3},query: { queryId:  status2 }});

query刷新不會丟失query裏面的數據, params刷新 會 丟失 params裏面的數據

7.vue加載圖片

一、絕對路徑訪問

<template>
    <div>
        <img :src="src" alt="圖片" />
    </div>
</template>
<script>
    export default{
        data(){
          return {
              src:`/static/img/user.png`
          }
        }
    }
</script>
二、使用require

若是想在不調整目錄結構的狀況下讀取圖片,還可使用require:

data(){
     return {
         src:require('../assets/user.png')    //重點看這裏
    }
}
三、使用import

也能夠用import引入圖片路徑:

<script>
    import userPath from '../assets/user.png'
    export default{
        data(){
          return {
              src:userPath 
          }
        }
    }
</script>

 8.前端爬坑日記之vue內嵌iframe並跨域通訊

vue性能優化

1.非頁面中用到的變量不要定義在data中或者使用freeze屬性

new Vue({
  data: {
    // vue不會對list裏的object作getter、setter綁定
    list: Object.freeze([
      { value: 1 },
      { value: 2 },
  },
  created() {
    // 界面不會有響應
    this.list[0].value = 100
    // 下面兩種作法,界面都會響應       
    this.list = [
      { value: 100 },
      { value: 200 },
    ]        
    this.list = Object.freeze([
      { value: 100 },
      { value: 200 },
    ])    
  } 
})

 

2.組件要使用懶加載

const Foo = resolve => {
  // require.ensure 是 Webpack 的特殊語法,用來設置 code-split point
  require.ensure(['./Foo.vue'], () => {
    resolve(require('./Foo.vue'))
  })
}  

簡寫爲
const Foo = resolve => require(['./Foo.vue'], resolve)
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })

3.引入生產環境的 Vue 文件

開發環境下,Vue 會提供不少警告來幫你對付常見的錯誤與陷阱。而在生產環境下,這些警告語句沒有用,反而會增長應用的體積。有些警告檢查還有一些小的運行時開銷。

4.提取組件的 CSS 到單獨到文件

當使用單文件組件時,組件內的 CSS 會以 <style> 標籤的方式經過 JavaScript 動態注入。這有一些小小的運行時開銷,將全部組件的 CSS 提取到同一個文件能夠避免這個問題,也會讓 CSS 更好地進行壓縮和緩存。

5.使用單文件組件預編譯模板

這種定義組件的方式不能在構建階段使用預處理器,如 Pug (formerly Jade) 和 Babel,模板在運行時才被編譯爲渲染函數,使用單文件組件在構建好的代碼已經包含了編譯出來的渲染函數而不是原始的模板字符串

    Vue.component('my-com',{
      template:`
        <h2>it is a global components</h2>
      `
    })

 6.採用預渲染(Prerender)來優化加載性能

採用vue-router history模式+webpack的PrerenderSpaPlugin插件,生成的html文件有內容和數據

1.無預渲染

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>vue-lz-2.0</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
  </head>
  <body>
    <div id="app"></div>
    <script src="dist/build.js"></script>
  </body>
</html>

2.有預渲染

相關文章
相關標籤/搜索