vue、vue-router 知識梳理

vue

vue生命週期

Vue 實例從建立到銷燬的過程,就是生命週期。html

同時在這個過程當中也會運行一些叫作生命週期鉤子的函數,這給了用戶在不一樣階段添加本身的代碼的機會,利用各個鉤子來完成咱們的業務代碼,鉤子以下:vue

1.beforeCreate
實例初始化以後、建立實例以前被調用,此時數據觀察和事件配置都還沒好準備好
2.created
實例建立完成後被調用,此時data有了,dom尚未,掛載屬性el還沒生成webpack

3.beforeMount
將編譯完成的html掛載到對應的虛擬dom時調用,此時相關的render函數首次被執行,頁面尚未內容,表達式{{ }}的值還未被替換
4.mounted
編譯好的html掛載到頁面完成後調用,此時頁面已經被渲染出數據web

5.beforeUpdate
數據更新時調用,發生在虛擬 DOM 從新渲染和打補丁以前。 你能夠在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。
6.update
因爲數據更改致使的虛擬 DOM 從新渲染和打補丁,在這以後會調用該鉤子。vue-router

7.beforeDestroy
銷燬前調用
8.destroyed
銷燬後調用,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。 該鉤子在服務器端渲染期間不被調用。vuex

🍃 資料1:標註圖+部分舉例聊聊Vue生命週期
🍃 資料2:關於Vue實例的生命週期created和mounted的區別vue-cli

vue指令

內置指令

1.v-text:更新元素的 textContent
2.v-html:更新元素的 innerHTML
3.v-show:根據表達式之真假值,切換元素的 display CSS 屬性
4.v-ifv-else-ifv-else:條件判斷
5.v-for:基於源數據屢次渲染元素或模板塊
6.v-on:(語法糖 @)綁定事件npm

修飾符:segmentfault

  • .stop - 阻止事件冒泡,調用 event.stopPropagation()
  • .prevent - 阻止默認行爲,調用 event.preventDefault()
  • .capture - 添加事件偵聽器時使用 capture 事件捕獲模式
  • .self - 元素自己觸發時才觸發回調
  • .once - 只調用一次該事件

7.v-bind(語法糖 :)當表達式的值改變時,將其產生的連帶影響,響應式地做用於DOM
8.v-model:表單元素實現雙向數據綁定api

修飾符:

  • .trim - 去除兩邊空格
  • .number - 輸入內容轉換爲number類型
  • .lazy - 當焦點離開文本框時,屬性值發生了變化並與文本框內容保持一致

🍃 資料1:Vue.js入門教程-指令

自定義指令

經過directive就能夠在Vue上註冊一個自定義指令(全局註冊、局部註冊)

🌰 舉例註冊一個自定義指令,實現頁面加載自動聚焦到元素

// 1. 註冊一個全局自定義指令 `v-focus`
Vue.directive('focus', {
  // 當被綁定的元素插入到 DOM 中時……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

// 2. 註冊一個局部自定義指令 `v-focus`
directives: {
  focus: {
    // 指令的定義
    inserted: function (el) {
      el.focus()
    }
  }
}

// 使用
<input v-focus>

🍃 資料1:【譯】vue 自定義指令的魅力
🍃 資料2:vue 自定義指令

filter過濾器

過濾器(全局過濾器,局部過濾器)能夠經過管道符號|實現文本數據的格式化,能夠用在表達式 {{}}bind

// 全局過濾器
Vue.filter('toTime', function(value) {
  //value 表示要過濾的內容
})

// 批量註冊全局過濾器
import * as filters from "config/filters"

Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key]);
});

// 局部過濾器
var app = new Vue({
    el: '#app',
    data: {},
    filters: {
        toTime: function(value){
            //value 表示要過濾的內容
        }
    }
})

// 使用
<div :data-time="date | toTime">{{ date | toTime }}</div>

過濾器能夠串聯、接收參數,好比:

{{ message | filterA | filterB }}
{{ message | filterA('p1', 'p2') }}

computed 計算屬性

使用方法:

<p>個人名字:{{ fullname }}</p>

var vm = new Vue({
  el: '#app',
  data: {
    firstname: 'jack',
    lastname: 'rose'
  },
  computed: {
    fullname() {
      return this.firstname + '.' + this.lastname
    }
  }
})

注意:computed中的屬性不能與data中的屬性同名,不然會報錯。

Q: {{ }}computedmethods裏面的方法分別在什麼狀況下使用 ?

`{{}}`經常使用於簡單的運算,當過長或邏輯複雜時會變得難以維護。

`computed`經常使用語複雜邏輯運算,基於依賴緩存。
當計算屬性所依賴的數據發生變化時,纔會從新取值,當遍歷大數組或作大量計算時使用計算屬性比較好

`methods`裏的方法只要從新渲染就會被調用,函數也會被執行。

watch 監測數據

監測屬性watch能夠經過監測數據來響應數據的變化,可在數據變化時須要執行異步或開銷較大的操做時使用。
它是一個對象,鍵是須要觀察的表達式,值是對應回調函數,回調函數接收兩個參數分別是新值和舊值。

⚠️ 注意:watcher回調函數不可用箭頭函數,箭頭函數綁定了父級做用域的上下文,會改變this指向。

new Vue({
  data: { 
    a: 1,
    b: { 
      age: 10 
    } 
  },
  watch: {
    a: function(newVal, oldVal) {
      //若是a發生改變,這個函數就會運行
    },

    /**
     * 監聽對象變化
     * deep - 深度監測,對象內部屬性或方法發生改變,使用它才能監測到該對象的改變
     * immediate - 當即執行,初始化的時候 watch 是不會執行的,若是想在初始化的時候就執行,則需開啓這個屬性
     */
    b: {
      handler: function (val, oldVal) {
        //TODO ...
      },
      deep: true,
      immediate: true
    },

    /**
     * 監聽對象具體某個屬性的變化
     */
    'b.age': function (val, oldVal) {
      //TODO ...
    },
  }
})

Q: watch在任什麼時候候均可以監測的到數據變更嗎?

  • 數組 - 直接用索引設置元素,如 vm.items[0] = {}
  • 數組 - 修改數據的長度,如 vm.items.length = 0
  • 對象 - 不能檢測對象某個(在data中不存在)屬性的添加或刪除

以上三種狀況下均數據發生改變時,watch都不能檢測出來,解決辦法以下:

  • 對於新增或刪除等操做使用vue提供的方法,如$set()、$delete()等
  • 克隆,如Object.assign()JSON.parse(JSON.stringify(obj)){...obj}[...arr]

🍃 資料1:Vue watch選項
🍃 資料2:Vue的computed和watch的細節全面分析
🍃 資料3:Vue中$watch的監聽變化 & vue中正確的使用watch進行監聽

組件註冊

全局註冊

方法:
使用Vue.component(tagName, options)能夠註冊一個全局的組件
全局組件在全部的vue實例中均可以使用

let myButton = Vue.extend({ 
  template: `<button>點擊我</button>`,
  // 組件中的 data 必須是函數 而且函數的返回值必須是對象
  data() {
    return {}
  }
}) 

Vue.component('my-button', myButton)

語法糖:

Vue.component('my-button', { 
    template: `<button>點擊我</button>`
})

使用:

<div id="app"> 
    <my-button /> 
</div>

局部註冊

方法:
使用Vue實例 / 組件實例選項中components註冊
局部註冊的組件只能夠應用在當前實例中。

let myButton = Vue.extend({ 
    template: `<button>點擊我</button>` 
})

let app = new Vue({ 
    el: '#app',
    components: {
        'my-button':myButton
    }
})

語法糖:

let app = new Vue({ 
    el: '#app',
    components: {
        'my-button': {
            template: `<button>點擊我</button>` 
        }
    }
})

使用:

<div id="app"> 
    <my-button /> 
</div>

🍃 資料:Vue.js入門教程-組件註冊

組件通訊

props:父 -> 子

1. 子組件能夠經過props聲明從父組件接收過來的數據,props值分兩種,一種是字符串數組,一種是對象 (須要對值驗證能夠用對象)。

2. 因爲html特性不區分大小寫,當使用dom模板時,駝峯命名的props名稱要轉爲短橫線分隔命名(字符串模板除外)

3. 若是傳遞的數據不是直接寫死的,而是來自父級的動態數據,可使用指令v-bind來動態綁定 props的值,當父組件的數據變化時,也會傳遞給子組件 ,反之不行

4. 業務中常常會遇到兩種須要改變props的狀況:

  • 傳遞初始值進來,子組件將它做爲初始值保存到data中,在本身做用域下能夠隨意使用和修改
  • prop做爲須要被轉變的原始值傳入,這種狀況下可使用計算屬性

可是,以上兩種狀況,針對於引入類型的數據,在子組件中修改值是會影響到父組件的值的,須要注意!

例子:基於vue-cli

// 父組件 - 傳遞 msg1 的值到子組件

<template>
  <div class="parent-box">
    我是父組件
    <child :msg1="msg1"></child>
  </div>
</template>

<script>
import child from "./components/child";  
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg1: "我是msg1 - 父組件傳給子組件的第1個值", 
    };
  }
};
</script>
//子組件child - 接收來自父組件的 msg1 的值

<template>
  <div class="child-box">
    我是子組件
    {{msg1}}
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg1"], //props 接收來自父組件傳遞過來的值
};
</script>

$emit:子 -> 父

當子組件須要像父組件傳遞數據時,就要用到自定義事件。
相似觀察者模式,子組件用$emit來觸發事件,父組件用v-on(語法糖@)來監聽子組件觸發的自定義事件。

用法:
step1:父組件 - 定義一個方法,並經過v-on(語法糖@)綁定給子組件,如:

<child :msg1="msg1" @changeMsg1="changeMsg1"></child>

step2:子組件 - 經過 $emit 觸發已綁定的方法,如:

//參數1 eventName - 方法名
//參數2 [...arg] - 要返給被觸發方法的參數
this.$emit("changeMsg1", '傳給父組件的參數');

例子:

//父組件 - 傳遞給子組件一個函數 changeMsg1 

<template>
  <div class="parent-box">我是父組件
    <child :msg1="msg1" @changeMsg1="changeMsg1"></child>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg1: "我是msg1 - 父組件傳給子組件的第1個值",
    };
  },
  methods: {
    //參數 data 是子組件傳遞過來的參數
    changeMsg1(data) {
      this.msg1 = data;
    }
  }
};
</script>
//子組件child - 給 button 綁定 handleClick 方法,在 handleClick 中經過 $emit 去觸發changeMsg1方法,並傳參過去

<template>
  <div class="child-box">
    {{msg1}}
    <button @click="handleClick">btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg1"],
  methods: {
    handleClick() {
      this.$emit("changeMsg1", '傳給父組件的參數'); 
    }
  }
};
</script>

$parent & $children、$refs

this.$parent 能夠直接訪問該組件父實例或組件(子 -> 父),父組件也能夠經過this.$children訪問全部子組件(數組)(父 -> 子),並且能夠遞歸向上或向下無線訪問,直到根實例或最內層組件。

業務中子組件應該應該儘量避免依賴父組件數據,更不應主動修改父組件數據,這樣會使父子組件耦合嚴重,只看父組件很難理解父組件狀態,由於父組件的值可能被隨意修改了。

這種方式適用於一個頁面單純的拆分紅多個組件組合在一塊兒的狀況,被拆分的組件只有一個公用且肯定了的父組件。

例子:

//父組件 - 經過 $children 改變子組件 tip 值
<template>
  <div class="parent-box">
    我是父組件
    <child :msg="msg"></child>
    <button @click="handleClick">父btn</button>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父組件 msg 的值"
    };
  },
  methods: {
    handleClick() {
      this.$children.map(i => {
        i.tip = "我是父組件,我用 $children 改變了子組件 child 中 tip的值";
      });
    }
  }
};
</script>
//子組件 - 經過 $parent 改變父組件 msg 值
<template>
  <div class="child-box">
    我是子組件
    {{tip}}
    {{msg}}
    <button @click="handleClick">子btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg"],
  data() {
    return {
      tip: "我是子組件的數據,誰能改變我?"
    };
  },
  methods: {
    handleClick() {
      this.$parent.msg = "我是子組件,我用 $parent 改變了父組件中 msg 的值";
    }
  }
};
</script>

當子組件不少時,很難經過$children遍歷出來須要的組件實例,能夠用ref來爲子組件指定一個索引名稱,而後用this.$refs[name]來查找

例子:

//父組件 - 用 ref 給子組件 child 設置索引名,經過 refs 去查找子組件
<template>
  <div class="parent-box">
    我是父組件
    <child ref="component1" :msg="msg"></child>
    <button @click="handleClick">父btn</button>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父組件 msg 的值"
    };
  },
  methods: {
    handleClick() {
      this.$refs.component1.tip = '我是父組件,我要經過 $refs 來查找子組件 child 並修改 它的 tip 值'
    }
  }
};
</script>

EventBus:非父子組件通信

若是涉及到爺孫之間、兄弟之間或更深層次組件之間須要通訊,能夠聲明一個vue實例,把公共方法和屬性放在這個實例上面,讓這個實例充當一條通信橋樑。

範圍:適用於簡單場景,複雜場景請用 vuex

步驟:基於vue-cli

step1:建立一個vue實例,掛載到全局,好比:

//main.js 文件

Vue.prototype.$bus = new Vue();

//或

window.$bus = new Vue();

step2:$on$emit完成通訊

// $on 監聽事件,處理回調
bus.$on('eventName', val => {
  //TODO...
})

// $emit 觸發事件,返回參數
bus.$emit('eventName', val)

例子:讓兩個同級組件通信

//main.js - 聲明一個全局變量來保存 vue 實例

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.prototype.$bus = new Vue();   // $bus

new Vue({
  render: h => h(App),
}).$mount('#app')
// 根組件 - 包含兩個子組件
<template>
  <div class="parent-box">
    我是父組件
    <child></child>
    <child2></child2>
  </div>
</template>

<script>
import child from "./components/child";
import child2 from "./components/child2";

export default {
  name: "app",
  components: { child, child2 }
};
</script>
//child 子組件 - $on 監聽事件,若是有新參數進來當即替換 childMsg 值
<template>
  <div class="child-box">
    我是子組件 child
    {{childMsg}}
  </div>
</template>

<script>
export default {
  name: "child",
  data() {
    return {
      childMsg: "我是child的數據"
    };
  },
  created() {

    //$on 監聽
    this.$bus.$on('changeMsg', data => {
      this.childMsg = data;
    })
  },

};
</script>
//child2 - $emit 觸發事件,並傳遞參數到 changeMsg 方法中
<template>
  <div class="child-box">
    我是子組件 child2
    <button @click="handleClick">child2 btn</button>
  </div>
</template>

<script>
export default {
  name: "child2",
  methods: {
    handleClick() {

      //$emit 觸發
      this.$bus.$emit('changeMsg', '我是 child2 ,我更改了 child 的 childMsg 值')
    }
  }
};
</script>

v-model:雙向數據綁定 - 單個屬性

v-model能夠在 表單控件 或者組件上建立雙向綁定。

表單控價上的雙向綁定,如:

<input v-model="msg">

//至關於:

<input :value="msg" @input="msg = $event.target.value">

如上,能夠看出語法糖v-model的實現過程:將input標籤用v-bind綁定value屬性,v-on綁定input事件。當輸入框的文本發生改變時,自動將屬性value的值替換成目標輸入值。

那麼,換到組件中該怎麼實現呢?

父組件:
經過v-model綁定變量

<child v-model="msg"></child>

//至關於

<child :value="msg" @input="msg = arguments[0]"></child>

子組件:

  • 接收一個value屬性
  • 在有新的value時觸發input事件

舉個栗子:

// 父組件 - 用 v-model 綁定 msg
<template>
  <div class="parent-box">
    我是父組件
    這是個人 msg 值:{{msg}}
    <child v-model="msg"></child>
  </div>
</template>

<script>
import child from "./components/child";

export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父組件的 msg"
    };
  }
};
</script>
//子組件 - 經過 props 接收 value,用 $emit 觸發 input 事件
<template>
  <div class="child-box">
    我是子組件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["value"],
  methods: {
    handleClick() {
      this.$emit("input", "我是 child,這是我傳給父組件的新值");
    }
  }
};
</script>

上例中,若是屬性value被佔用了,或者input事件衝突了,就會引發報錯,因此最好用 model 屬性來定製 v-model

定製方法:model { prop?: string, event?: string }

上例中,child組件重寫爲:

//child - 經過 model 定製 v-model
<template>
  <div class="child-box">
    我是子組件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  model: {
    prop: 'msg',  //代替 value
    event: 'changeMsg' //代替input方法
  },
  props: ["msg"],
  methods: {
    handleClick() {
      this.$emit("changeMsg", "我是 child,這是我傳給父組件的新值");
    }
  }
};
</script>

.sync:雙向數據綁定 - 多個屬性

2.3版本中從新引入的.sync 修飾符做爲語法糖存在,它會被擴展爲自動更新父組件屬性的v-on監聽器,以下:

<child :msg.sync="msg"></child>

//至關於

<child :msg="msg" @update:msg="val => msg = val"></child>

當子組件須要更新msg時,觸發update方法,將舊值替換成新值,以下:

this.$emit('update:name', newVal)

一次性想傳遞多個屬性時,能夠結合v-bind一塊兒使用,以下:

//父組件 - 傳遞一個對象到子組件,會分別爲對象的每一個屬性分配一個 v-on 監聽器

<template>
  <div class="parent-box">
    我是父組件
    {{info.msg1}}
    {{info.msg2}}
    <child v-bind.sync="info"></child>
  </div>
</template>

<script>
import child from "./components/child";

export default {
  name: "app",
  components: { child },
  data() {
    return {
      info: {
        msg1: 1111,
        msg2: 2222
      }
    };
  }
};
</script>
//子組件 - $emit 觸發事件

<template>
  <div class="child-box">
    我是子組件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  methods: {
    handleClick() {
      this.$emit("update:msg1", "33333");
      this.$emit("update:msg2", "44444");
    }
  }
};
</script>

$attrs & $listeners

1.在多級組件通訊中,$attrs & $listeners 分別負責收集父組件中傳遞過來的屬性和事件,其中$attr中收集的屬性不包括組件中已經過props接受的屬性,$listeners中收集的事件不包括有.native修飾符的事件。屬性經過v-bind="$attrs"一級級向下傳遞, 事件經過v-on="$listeners"一級級向下傳遞。

2.$attrs中包含的屬性,默認狀況下將會做爲普通的HTML屬性應用在子組件的根元素上,能夠經過在當前組件中設置inheritAttrs: false 去掉(style, class除外),去掉默認行爲不影響數據的使用。

例如:

未設置 inheritAttrs: false時:

<div class="grandson-box" msg3="333" msg4="444"></div>

設置了 inheritAttrs: false時:

<div class="grandson-box"></div>

3.適用範圍:簡單的多級組件通訊

例子:

// 一級組件 - 經過 v-bind, v-on 傳遞屬性和事件到下級組件

<template>
  <div class="parent-box">
    我是爺爺
    <son v-bind="msg" @event1="event1" @event2.native="event2" @event3="event3" @event4="event4"></son>
  </div>
</template>

<script>
import son from "./components/son";

export default {
  name: "app",
  components: { son },
  data() {
    return {
      msg: {
        msg1: 111,
        msg2: 222,
        msg3: 333,
        msg4: 444
      }
    };
  },
  methods: {
    event1() {
      console.log(1);
    },
    event2() {
      console.log(2);
    },
    event3(data) {
      console.log(3, data); //3, 我是孫子組件grandson傳遞到爺爺那去的參數
    },
    event4() {
      console.log(4);
    }
  }
};
</script>
// 二級組件 - 經過 v-bind="$attrs" v-on="$listeners" 傳遞屬性和事件到下一級組件中

<template>
  <div class="son-box">
    我是兒子 {{$attrs}}
    <grandson v-bind="$attrs" v-on="$listeners"/>
  </div>
</template>

<script>
import grandson from "./grandson";
export default {
  name: "son",
  inheritAttrs: false, //組件根部屏蔽掉 $attrs 的屬性,可是值仍然存在只是不展現在 html 上了
  components: { grandson },
  props: ["msg1", "msg2"], //經過 props 接收的屬性 會被過濾,不存在 $attrs 中
  created() {
    console.log("----- son -------");
    //若是上級組件傳遞過來的事件含有 .native 修飾符,則該事件被過濾, 不存在 $listeners 中
    console.log(this.$listeners); //{event1: ƒ, event3: ƒ, event4: ƒ}
  }
};
</script>
// 三級組件

<template>
  <div class="grandson-box">
    我是孫子
    {{$attrs.msg3}}
  </div>
</template>

<script>
export default {
  name: "grandson",
  created() {
    console.log("------ grandson ------");
    console.log(this.$listeners); //{event1: ƒ, event3: ƒ, event4: ƒ}
    this.$listeners.event3("我是孫子組件grandson傳遞到爺爺那去的參數");
  }
};
</script>

🍃 資料一:vue組件的那點事
🍃 資料二:vue組件通訊全揭祕(共7章)
🍃 資料三:Vue.js 父子組件通訊的十種方式

===================== 分割線 ====================

vue-router

vue-router的使用流程(基於vue-cli):

  • step1: 安裝並註冊vue-router
  • step2: 定義路由組件
  • step3: 配置路由
  • step4: 建立router實例,將配置好的路由傳入
  • step5: 建立並掛載根實例,注入路由

step1: 安裝並註冊vue-router

安裝

yarn add vue-router -S

註冊

1.引入vuevue-router

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

2.註冊

Vue.use(VueRouter)

step2: 定義路由組件

基於vue-cli,咱們在寫好頁面組件以後,一般會經過importrequire引入到管理路由的文件中,普通引入方式以下:

import login from '../page/login';

//或者

const login = require('../page/login').default;

注意:用 require 引入組件時須要加 default,不然報錯!

擴展:exports、module.exports 和 export、export default 究竟是咋回事

這種普通的引入方式有一個缺點,就是在npm run build的時候,全部引入的文件最終會被打包成一個文件,加載的時候會加載整個文件,文件過大會致使頁面加載緩慢,爲了解決這個問題,咱們一般在項目中將它分紅多個小的代碼塊來加載,一般用如下三種方法:

第一種:異步組件

當須要這個組件的時候,會異步加載過來,並將結果儲存以供下次使用。

const login = resolve => require(['../page/login'], resolve)

第二種:懶加載組件

結合Vue的異步組件和Webpack的代碼分割功能,實現路由組件的懶加載。

const login = () => import('../page/login');

注意:若是使用 Babel,須要添加 syntax-dynamic-import 插件

若是想把某個路由下的全部組件都打包在同個異步塊 (chunk) 中。能夠經過命名chunk來實現,以下loginindex 都被打包到了base文件中:

const login = () => import(/* webpackChunkName: "base" */ '../page/login');
const index = () => import(/* webpackChunkName: "base" */ '../page/index');
const list = () => import( /* webpackChunkName: "list" */ '../page/list');

第三種:webpack 代碼分割

webpack打包時,會把其對應的文件拆分到一個單獨的chunk中,此chunk會在須要的時候被異步加載。

//最後一個參數 'login' 能夠指定打包出的 chunk 名稱
const login = r => require.ensure([], () => r(require('../page/login')), 'login');

🍃 資料:vue項目實現按需加載的3種方式:vue異步組件、es提案的import()、webpack的require.ensure()

step3: 配置路由

舉個例子:

const routes = [
    { path: '/', redirect: '/index' },
    { path: '/login', component: login, name: 'login', meta: { windowTitle: '登陸' } },
    { path: '/index', component: index, name: 'index', meta: { windowTitle: '首頁' } },
    { path: '/list/:id', component: list, name: 'list', meta: { windowTitle: '列表'}, props: true}
]

routes 經常使用配置:

path

格式:path: string
概述:當前頁面的路徑

項目中常常會遇到這種狀況,好比有一個 產品列表 list 組件,對於全部id各不相同的產品,都要使用這個組件來渲染,能夠經過配置 動態路徑參數 來匹配這種路徑,例如:path:'/list/:id'

component

格式:component?: Component
概述:路由視圖,經過懶加載等方式引入的組件(見 step2 內容)

name

格式:name?: string
概述:命名路由,可經過 name 名字跳轉頁面,兩邊 name 要保持一致,跳轉方式如:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

//或

router.push({ name: 'user', params: { userId: 123 }})

props:

格式: boolean | Object | Function
概述:組件中使用 $route 會使之與其對應路由造成高度耦合,從而使組件只能在某些特定的 URL 上使用,限制了其靈活性。路徑上的參數可經過配置props: true [將組件和路由解耦][27],沒必要再經過 $route.params 去訪問參數例如:

{ path: '/user/:id', component: User, props: true },

//User - 設置了props:true 以後,route.params 將會被設置爲組件屬性。
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>',
  //template: '<div>User {{ $route.params.id }}</div>'  //不須要這樣獲取值了
}

children

格式:Array<RouteConfig>
概述:嵌套路由

step4: 建立router實例,將配置好的路由傳入

經常使用配置以下:

const router = new VueRouter({

    //頁面配置(見step3)
    routes, 
    
    /**
     * mode - 模式,默認 hash
     * hash,地址會變成有 # 號的那種,很醜
     * history,無 # 號的正常地址
     */
    mode: 'history', 
    strict: process.env.NODE_ENV !== "production",

    //滾動位置
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            //savedPosition - 在按下 後退/前進 按鈕時,就會像瀏覽器的原生表現那樣
            return savedPosition;
        } else {
            //返回到頂部
            return {
                x: 0,
                y: 0
            };
        }
    }
});

step5: 建立並掛載根實例,注入路由

import router from './router' //引入路由

new Vue({
  router, //註冊
  render: h => h(App),
}).$mount('#app')

完整示例:

router.js文件配置

//step1 - 安裝並註冊`vue-router`
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

//step2: 定義路由組件
const login = r => require.ensure([], () => r(require('../page/login')), 'login'); //登陸
const index = r => require.ensure([], () => r(require('../page/index')), 'index'); //首頁
const list = r => require.ensure([], () => r(require('../page/list')), 'list'); //首頁


//step3: 配置路由
const routes = [
    { path: '/', redirect: '/index' },
    { path: '/login', component: login, name: 'login', meta: { windowTitle: '登陸' } },
    { path: '/index', component: index, name: 'index', meta: { windowTitle: '首頁' } },
    {    path: '/list/:id',    component: list, name: 'list', meta: { windowTitle: '列表'}, props: true },
]

//step4: 建立`router`實例,將配置好的路由傳入
const router = new VueRouter({
    routes,
    mode: 'hash', //模式
    strict: process.env.NODE_ENV !== "production",
    //滾動位置
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition;
        } else {
            return {
                x: 0,
                y: 0
            };
        }
    }
});

//路由守衛
router.beforeEach((to, from, next) => {
    const token = localStorage.token || '';
    const userArr = localStorage.userArr ? JSON.parse(localStorage.userArr) : null;
    if (to.meta.windowTitle) document.title = to.meta.windowTitle;

    if (!token && to.name !== 'login') {
        next({name: 'login', replace: true}); //沒有token進入登陸頁面
    } else if (token && userArr && to.name == 'login') {
        next({name: 'index', replace: true}); //有token進入登陸頁面跳轉首頁
    } else if (to.name != 'login' && !userArr) {
        next({name: 'login', replace: true});
    } else {
        //TODO 若是沒有登陸信息 - 保存登陸信息
        // if (!store.state.userArr) {
        //     store.commit('SAVE_USERARR', userArr);
        // }
        next();
    }
})

export default router;

main.js文件配置

import Vue from 'vue'
import App from './App'
import router from './router' //引入路由

Vue.config.productionTip = false

//step5: 建立並掛載根實例,注入路由
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

🍃 資料1:Vue2.0 探索之路——vue-router入門教程和總結
🍃 資料2:vue-router 一些容易被忽略的知識點
🍃 資料3:關於vue-router的beforeEach無限循環的問題
🍃 資料4:Vue的鉤子函數[路由導航守衛、keep-alive、生命週期鉤子

相關文章
相關標籤/搜索