Vue基礎知識

1、vue解決了什麼問題

Vue.js(讀音 /vjuː/, 相似於 view) 是一套構建用戶界面的 漸進式框架。與其餘重量級框架不一樣的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關注視圖層,而且很是容易學習,很是容易與其它庫或已有項目整合。另外一方面,Vue 徹底有能力驅動採用單文件組件和 Vue 生態系統支持的庫開發的複雜單頁應用。javascript

Vue.js 的目標是經過儘量簡單的 API 實現響應的數據綁定和組合的視圖組件。css

輕量級框架:只關注視圖層,是一個構建數據的視圖集合,大小隻有幾十kbhtml

簡單易學:國人開發,中文文檔,不存在語言障礙,易於理解和學習前端

雙向數據綁定:保留了angular的特色,在數據操做方面更爲簡單vue

組件化:保留了react的優勢;實現了html的封裝和重用,在構建單頁面應用方面有着獨特的優點java

視圖,數據,結構分離:是數據的更改更爲簡單,不須要進行邏輯代碼的修改,只須要操做數據就能完成相關操做node

虛擬DOM:dom操做時很是耗費性能的,再也不使用原生的dom操做節點,極大解放dom操做,但具體操做的仍是dom不過是換了另外一種方式react

運行速度更快:像比較與react而言,一樣都是操做虛擬dom,就性能而言,vue存在很大的優點jquery

2、MVVM的理解

MVVM定義:MVVM是Model-View-ViewModel的簡寫。即模型-視圖-視圖模型。【模型】指的是後端傳遞的數據。【視圖】指的是所看到的頁面。【視圖模型】mvvm模式的核心,它是鏈接view和model的橋樑。webpack

它有兩個方向:

一是將【模型】轉化成【視圖】,即將後端傳遞的數據轉化成所看到的頁面。實現的方式是:數據綁定。

二是將【視圖】轉化成【模型】,即將所看到的頁面轉化成後端的數據。實現的方式是:DOM 事件監聽。這兩個方向都實現的,咱們稱之爲數據的雙向綁定。

總結:在MVVM的框架下視圖和模型是不能直接通訊的。它們經過ViewModel來通訊,ViewModel一般要實現一個observer觀察者,當數據發生變化,ViewModel可以監聽到數據的這種變化,而後通知到對應的視圖作自動更新,而當用戶操做視圖,ViewModel也能監聽到視圖的變化,而後通知數據作改動,這實際上就實現了數據的雙向綁定。而且MVVM中的View 和 ViewModel能夠互相通訊

在vue中,Model:指的是js中的數據,如對象,數組等等;View:指的是頁面視圖;viewModel:指的是vue實例化對象。爲何說VUE是一個漸進式的javascript框架, 漸進式是什麼意思?

1.若是你已經有一個現成的服務端應用,你能夠將vue 做爲該應用的一部分嵌入其中,帶來更加豐富的交互體驗;

2.若是你但願將更多業務邏輯放到前端來實現,那麼VUE的核心庫及其生態系統也能夠知足你的各式需求(core+vuex+vue-route)。和其它前端框架同樣,VUE容許你將一個網頁分割成可複用的組件,每一個組件都包含屬於本身的HTML、CSS、JAVASCRIPT以用來渲染網頁中相應的地方。

3.若是咱們構建一個大型的應用,在這一點上,咱們可能須要將東西分割成爲各自的組件和文件,vue有一個命令行工具,使快速初始化一個真實的工程變得很是簡單(vue init webpack my-project)。咱們可使用VUE的單文件組件,它包含了各自的HTML、JAVASCRIPT以及帶做用域的CSS或SCSS。以上這三個例子,是一步步遞進的,也就是說對VUE的使用可大可小,它都會有相應的方式來整合到你的項目中。因此說它是一個漸進式的框架。VUE最獨特的特性:響應式系統VUE是響應式的(reactive),也就是說當咱們的數據變動時,VUE會幫你更新全部網頁中用到它的地方。

3、如何實現一個自定義組件,不一樣組件之間如何通訊的?

Vue.component 來建立組件,這些組件是全局註冊的

// 定義一個名爲 button-counter 的新組件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

組件的使用

<div id="components-demo">
  <button-counter></button-counter>
</div>

Prop 是你能夠在組件上註冊的一些自定義特性。當一個值傳遞給一個 prop 特性的時候,它就變成了那個組件實例的一個屬性。爲了給博文組件傳遞一個標題,咱們能夠用一個 props 選項將其包含在該組件可接受的 prop 列表中

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

經過下面的代碼,便可將數據做爲自定義的特性傳遞進來

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})
<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

如上所示,你會發現咱們可使用 v-bind 來動態傳遞 prop。

定義組件名的方式有兩種:

使用 kebab-case

Vue.component('my-component-name', { /* ... */ })

當使用 kebab-case (短橫線分隔命名) 定義一個組件時,你也必須在引用這個自定義元素時使用 kebab-case,例如 <my-component-name>

使用 PascalCase

Vue.component('MyComponentName', { /* ... */ })

當使用 PascalCase (首字母大寫命名) 定義一個組件時,你在引用這個自定義元素時兩種命名法均可以使用。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。注意,儘管如此,直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的。

局部註冊

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

對於 components 對象中的每一個屬性來講,其屬性名就是自定義元素的名字,其屬性值就是這個組件的選項對象。

注意局部註冊的組件在其子組件中不可用

若是你使用了諸如 Babel 和 webpack 的模塊系統。在這些狀況下,咱們推薦建立一個 components 目錄,並將每一個組件放置在其各自的文件中

而後你須要在局部註冊以前導入每一個你想使用的組件。例如,在一個假設的 ComponentB.js 或 ComponentB.vue 文件中:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

如今 ComponentA 和 ComponentC 均可以在 ComponentB 的模板中使用了。

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

props的類型和傳值:https://cn.vuejs.org/v2/guide/components-props.html

組件通訊見本頁第八個問題

本部分摘自:https://blog.csdn.net/liyunkun888/article/details/83269692

4、nextTick

Vue.nextTick():在下次 DOM 更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM

獲取更新後的DOM言外之意就是什麼操做須要用到了更新後的DOM而不能使用以前的DOM或者使用更新前的DOM會出問題,因此就衍生出了這個獲取更新後的 DOMVue方法。因此放在Vue.nextTick()回調函數中的執行的應該是會對DOM進行操做的js代碼

  • 你在Vue生命週期的created()鉤子函數進行的DOM操做必定要放在Vue.nextTick()的回調函數中。緣由是什麼呢,緣由是在created()鉤子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操做無異於徒勞,因此此處必定要將DOM操做的js代碼放進Vue.nextTick()的回調函數中。與之對應的就是mounted鉤子函數,由於該鉤子函數執行時全部的DOM掛載和渲染都已完成,此時在該鉤子函數中進行任何DOM操做都不會有問題 。
  • 在數據變化後要執行的某個操做,當你設置 vm.someData = 'new value'DOM並不會立刻更新,而是在異步隊列被清除,也就是下一個事件循環開始時執行更新時纔會進行必要的DOM更新。若是此時你想要根據更新的 DOM 狀態去作某些事情,就會出現問題。。爲了在數據變化以後等待 Vue 完成更新 DOM ,能夠在數據變化以後當即使用 Vue.nextTick(callback) 。這樣回調函數在 DOM 更新完成後就會調用。
  • mounted 不會承諾全部的子組件也都一塊兒被掛載。若是你但願等到整個視圖都渲染完畢,能夠用 vm.$nextTick 替換掉 mounted。

5、生命週期

建立→初始化數據→編譯模板→掛載DOM→更新→渲染→銷燬

  • beforeCreate:在實例初始化以後,數據觀測data observer(props、data、computed) 和 event/watcher 事件配置以前被調用。
  • created:實例已經建立完成以後被調用。在這一步,實例已完成如下的配置:數據觀測(data observer),屬性和方法的運算, watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見。
  • beforeMount:在掛載開始以前被調用:相關的 render 函數首次被調用。
  • mounted: el 被新建立的 vm.$el 替換,並掛載到實例上去以後調用該鉤子。
  • beforeUpdate:數據更新時調用,發生在虛擬 DOM 從新渲染和打補丁以前。 你能夠在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。
  • updated:不管是組件自己的數據變動,仍是從父組件接收到的 props 或者從vuex裏面拿到的數據有變動,都會觸發虛擬 DOM 從新渲染和打補丁,並在以後調用 updated
  • beforeDestroy:實例銷燬以前調用。在這一步,實例仍然徹底可用。
  • destroyed:Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。 該鉤子在服務器端渲染期間不被調用。

  注意:created階段的ajax請求與mounted請求的區別:前者頁面視圖未出現,若是請求信息過多,頁面會長時間處於白屏狀態。

單個組件的生命週期

  1. 初始化組件時,僅執行了beforeCreate/Created/beforeMount/mounted四個鉤子函數
  2. 當改變data中定義的變量(響應式變量)時,會執行beforeUpdate/updated鉤子函數
  3. 當切換組件(當前組件未緩存)時,會執行beforeDestory/destroyed鉤子函數
  4. 初始化和銷燬時的生命鉤子函數均只會執行一次,beforeUpdate/updated可屢次執行

本部份內容摘自:https://www.jianshu.com/p/46c9d777cab1

6、虛擬dom的原理

用咱們傳統的開發模式,原生JS或JQ操做DOM時,瀏覽器會從構建DOM樹開始從頭至尾執行一遍流程。在一次操做中,我須要更新10個DOM節點,瀏覽器收到第一個DOM請求後並不知道還有9次更新操做,所以會立刻執行流程,最終執行10次。例如,第一次計算完,緊接着下一個DOM更新請求,這個節點的座標值就變了,前一次計算爲無用功。計算DOM節點座標值等都是白白浪費的性能。即便計算機硬件一直在迭代更新,操做DOM的代價仍舊是昂貴的,頻繁操做仍是會出現頁面卡頓,影響用戶體驗。

Web界面由DOM樹(樹的意思是數據結構)來構建,當其中一部分發生變化時,其實就是對應某個DOM節點發生了變化,

虛擬DOM就是爲了解決瀏覽器性能問題而被設計出來的。如前,若一次操做中有10次更新DOM的動做,虛擬DOM不會當即操做DOM,而是將這10次更新的diff內容保存到本地一個JS對象中,最終將這個JS對象一次性attch到DOM樹上,再進行後續操做,避免大量無謂的計算量。因此,用JS對象模擬DOM節點的好處是,頁面的更新能夠先所有反映在JS對象(虛擬DOM)上,操做內存中的JS對象的速度顯然要更快,等更新完成後,再將最終的JS對象映射成真實的DOM,交由瀏覽器去繪製。

diff算法——深度優先遍歷,記錄差別
在實際代碼中,會對新舊兩棵樹進行一個深度的遍歷,每一個節點都會有一個標記。每遍歷到一個節點就把該節點和新的樹進行對比,若是有差別就記錄到一個對象中。
 

用JavaScript的方法來模擬DOM樹,用新渲染的對象樹去和舊的樹進行對比,記錄下變化的變化,而後應用到真實的DOM樹上,這樣咱們只須要更改與原來視圖不一樣的地方,而不須要所有從新渲染一次。

diff算法只會對相同顏色方框內的DOM節點進行比較,即同一個父節點下的全部子節點。當發現節點不存在,則該節點和子節點會被徹底刪除,不會作進一步的比較。

在實際的代碼中,會對新舊兩棵樹進行深度的遍歷,給每個節點進行標記。而後在新舊兩棵樹的對比中,將不一樣的地方記錄下來。

 

7、雙向綁定的原理?數據劫持?

什麼是雙向數據綁定?Vue是一個MVVM框架,數據綁定簡單來講,就是當數據發生變化時,相應的視圖會進行更新,當視圖更新時,數據也會跟着變化。

實現數據綁定的方式大體有如下幾種:

  • 發佈者-訂閱者模式(backbone.js)

  • 髒值檢查(angular.js)

  • 數據劫持(vue.js)

Vue.js則是經過數據劫持以及結合發佈者-訂閱者來實現的,數據劫持是利用ES5的Object.defineProperty(obj, key, val)來劫持各個屬性的的setter以及getter,在數據變更時發佈消息給訂閱者,從而觸發相應的回調來更新視圖。

 Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

實現數據的雙向綁定,首先要對數據進行劫持監聽,因此咱們須要設置一個監聽器Observer,用來監聽全部屬性。若是屬性發上變化了,就須要告訴訂閱者Watcher看是否須要更新。由於訂閱者是有不少個,因此咱們須要有一個消息訂閱器Dep來專門收集這些訂閱者,而後在監聽器Observer和訂閱者Watcher之間進行統一管理的。

//監聽器
function defineReactive(data,key,val) {
    observe(val);
    var dep = new Dep();
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            if(Dep.target) {   //判斷是否須要添加訂閱者
                 dep.addSub(Dep.target);
            }
            return val;
        },
        set: function(newVal) {
            if (val === newVal) {
                return;
            }
            val = newVal;
            console.log('屬性' + key + '已經被監聽了,如今值爲:「' + newVal.toString() + '」');
            dep.notify(); // 若是數據變化,通知全部訂閱者
        }
    });
}
//訂閱者
function Watcher(vm,exp,cb) {
    this.vm = vm;    //指向SelfVue的做用域
    this.exp = exp;  //綁定屬性的key值
    this.cb = cb;    //閉包
    this.value = this.get();
}

Watcher.prototype = {
    update:function() {
        this.run();
    },
    run:function() {
        var value = this.vm.data[this.exp];
        var oldVal = this.value;
        if(value !== oldVal) {
            this.value = value;
            this.cb.call(this.vm,value,oldVal);
        }
    },
    get:function() {
        Dep.target = this;                   // 緩存本身
        var value = this.vm.data[this.exp];  // 強制執行監聽器裏的get函數
        Dep.target = null;                   // 釋放本身
        return value;
    }
}

本部分參考:https://segmentfault.com/a/1190000014616977?utm_source=channel-hottest    http://www.javashuo.com/article/p-vbvongpy-ea.html

8、組件通訊

父傳子

 父傳子的實現方式就是經過props屬性,子組件經過props屬性接收從父組件傳過來的值,而父組件傳值的時候使用 v-bind 將子組件中預留的變量名綁定爲data裏面的數據便可。

<template>
    <div id="container">
        {{msg}}
    </div>
</template>
<script>
export default {
  data() {
    return {};
  },
  props:{
    msg: String
  }
};
</script>
<style scoped>
#container{
    color: red;
    margin-top: 50px;
}
</style>
<template>
    <div id="container">
        <input type="text" v-model="text" @change="dataChange">
        <Child :msg="text"></Child>
    </div>
</template>
<script>
import Child from "@/components/Child";
export default {
  data() {
    return {
      text: "父組件的值"
    };
  },
  methods: {
    dataChange(data){
        this.msg = data
    }
  },
  components: {
    Child
  }
};
</script>
<style scoped>
</style>

子傳父

子傳父的實現方式就是用了 this.$emit 來遍歷 getData 事件,首先用按鈕來觸發 setData 事件,在 setData 中 用 this.$emit 來遍歷 getData 事件,最後返回 this.msg。

  • 子組件中須要以某種方式例如點擊事件的方法來觸發一個自定義事件
  • 將須要傳的值做爲$emit的第二個參數,該值將做爲實參傳給響應自定義事件的方法
  • 在父組件中註冊子組件並在子組件標籤上綁定對自定義事件的監聽
<template>
    <div id="container">
        <input type="text" v-model="msg">
        <button @click="setData">傳遞到父組件</button>
    </div>
</template>
<script>
export default {
  data() {
    return {
      msg: "傳遞給父組件的值"
    };
  },
  methods: {
    setData() {
      this.$emit("getData", this.msg);
    }
  }
};
</script>
<style scoped>
#container {
  color: red;
  margin-top: 50px;
}
</style>
<template>
    <div id="container">
        <Child @getData="getData"></Child>
        <p>{{msg}}</p>
    </div>
</template>
<script>
import Child from "@/components/Child";
export default {
  data() {
    return {
      msg: "父組件默認值"
    };
  },
  methods: {
    getData(data) {
      this.msg = data;
    }
  },
  components: {
    Child
  }
};
</script>
<style scoped>
</style>

非父子傳值

vue 中沒有直接子對子傳參的方法,建議將須要傳遞數據的子組件,都合併爲一個組件

若是必定須要子對子傳參,能夠先從傳到父組件,再傳到子組件(至關於一個公共bus文件)

爲了便於開發,vue 推出了一個狀態管理工具 vuex,能夠很方便實現組件之間的參數傳遞(詳解見本頁第十三個問題)

其次還能夠經過事件總線eventBus來進行兩個組件間的通訊,EventBus又稱爲事件總線。在Vue中可使用EventBus來做爲溝通橋樑的概念,就像是全部組件共用相同的事件中心,能夠向該中心註冊發送事件或接收事件,因此組件均可以上下平行地通知其餘組件,但也就是太方便因此若使用不慎,就會形成難以維護的災難,所以才須要更完善的Vuex做爲狀態管理中心,將通知的概念上升到共享狀態層次。

eventBus原理是建立一個空Vue實例, 而後在上面掛載通信事件, 在掛載事件時, 能夠認爲這個Vue實例(組件), 是全部組件的父組件, 在觸發事件時, 能夠認爲這個Vue實例, 是全部組件的子組件

//eventBus.js的代碼
import Vue from "vue";
let bus = new Vue();
export default bus;
//在須要通訊的兩個組件中分別import
import bus from  //一個組件中發射 bus.$emit('SUBMITSEARCH_PEOPLE',this.searchContent) 
//另外一個組件中接收 bus.$on('SUBMITSEARCH_PEOPLE',searchContentsearchContent
//存在問題是$on會屢次觸發,解決方法以下
beforeDestroy () {
  bus.$off('SUBMITSEARCH_PEOPLE')
}"../utils/eventBus.js";
() => { this.bgC = ; }

9、Proxy 相比於 defineProperty 的優點

 常見的基於數據劫持的雙向綁定有兩種實現,一個是目前Vue在用的Object.defineProperty,另外一個是ES2015中新增的Proxy。

嚴格來說Proxy應該被稱爲『代理』而非『劫持』,不過因爲做用有不少類似之處,咱們在下文中就再也不作區分,統一叫『劫持』。

數據劫持的優點所在。

  • 無需顯示調用: 例如Vue運用數據劫持+發佈訂閱,直接能夠通知變化並驅動視圖,上面的例子也是比較簡單的實現data.name = '渣渣輝'後直接觸發變動,而好比Angular的髒檢測則須要顯示調用markForCheck(能夠用zone.js避免顯示調用,不展開),react須要顯示調用setState
  • 可精確得知變化數據:仍是上面的小例子,咱們劫持了屬性的setter,當屬性值改變,咱們能夠精確獲知變化的內容newVal,所以在這部分不須要額外的diff操做,不然咱們只知道數據發生了變化而不知道具體哪些數據變化了,這個時候須要大量diff來找出變化值,這是額外性能損耗
Proxy在ES2015規範中被正式發佈,它在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫,咱們能夠這樣認爲,Proxy是 Object.defineProperty的全方位增強版。 vue3.0的改進

Proxy能夠直接監聽對象而非屬性

Proxy能夠直接監聽數組的變化

Proxy返回的是一個新對象,咱們能夠只操做新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改

//proxy數據劫持
observe(data) {
  const that = this;
  let handler = {
   get(target, property) {
      return target[property];
    },
    set(target, key, value) {
      let res = Reflect.set(target, key, value);
      that.subscribe[key].map(item => {
        item.update();
      });
      return res;
    }
  }
  this.$data = new Proxy(data, handler);
}
基本用法:let p = new Proxy(target, handler);
target: 是用Proxy包裝的被代理對象(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)。
handler: 是一個對象,其聲明瞭代理target 的一些操做,其屬性是當執行一個操做時定義代理的行爲的函數。
  • get: 讀取值,
  • set: 獲取值,has: 判斷對象是否擁有該屬性,
  • construct: 構造函數

本部分摘自:https://www.jianshu.com/p/2df6dcddb0d7

10、watch computed區別

1. watch屬性能夠用來監聽data屬性中數據的變化,同時還能夠直接在監聽的function中使用參數來獲取新值與舊值

data:{
    firstname:"",
    lastname:""
 },
methods:{},
watch:{
     firstname:function(){
          console.log(this.firstname)
     }
}

watch:{
    firstname:function(newValue,OldValue){
        console.log(newValue);
        console.log(OldValue);
    }
}

同時Watch還能夠被用來監聽路由router的變化

2. computed屬性的做用與watch相似,也能夠監聽屬性的變化,只是他會根據他依賴的屬性,生成一個屬性,讓vm對象可使用這個屬性

computed:{
     fullname:function(){
          return this.firstname +"-"+this.lastname
     }
}

methods,watch,computed的區別

  • computed 屬性的結果會被緩存,除非依賴的響應式屬性變化纔會從新計算。主要看成屬性來使用;
  • methods 方法表示一個具體的操做,主要書寫業務邏輯;
  • watch 一個對象,鍵是須要觀察的表達式,值是對應回調函數。主要用來監聽某些特定數據的變化,從而進行某些具體的業務邏輯操做;能夠看做是 computed 和 methods 的結合體;

11、vue經常使用指令——v-bind  v-model  v-for  v-if  v-on v-show

 1. 數據渲染 v-text、v-html、v-model、{{}}

 v-text是用於操做純文本,它會替代顯示對應的數據對象上的值。當綁定的數據對象上的值發生改變,插值處的內容也會隨之更新。注意:此處爲單向綁定,數據對象上的值改變,插值會發生變化;可是當插值發生變化並不會影響數據對象的值。其中:v-text能夠簡寫爲{{}},而且支持邏輯運算。

<div id="app">  
  {{ message }}  
</div>  
var app = new Vue({  
   el : '#app',  
   data : {  
    message : 'hello world'      
 }  
}) 

v-html用於輸出html,它與v-text區別在於v-text輸出的是純文本,瀏覽器不會對其再進行html解析,但v-html會將其當html標籤解析後輸出。

<div id="app">  
    <p v-html="html"></p>  
</div>  
var app = new Vue({  
        el: "#app",  
        data: {  
            html: "<b style='color:red'>v-html</b>"  
        }  
    });  

v-model一般用於表單組件的綁定,例如input,select等。它與v-text的區別在於它實現的表單組件的雙向綁定,若是用於表單控件之外標籤是沒有用的。

<div id="app">  
  <input v-model="message " />  
</div>  
var app = new Vue({  
   el : '#app',  
   data : {  
    message : 'hello world'      
 }  
})  

2. 控制模塊的顯示/隱藏 v-if、v-show

v-if 是「真正的」條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
v-show 就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。
通常來講, v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。
所以,若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不太可能改變,則使用 v-if 較好。
<template>
    <div id="app">
        <div v-if="isIf">
            if
        </div>
        <div v-show="ifShow">
            show
        </div>
        <button @click="toggleShow">toggle</button>
    </div>
</template>

<script>
    export default {
        name: 'app',
        data() {
            return {
                isIf : true,
                ifShow : true,
                loginType : "username"
            }
        },
        methods: {
            toggleShow : function(){
                this.ifShow = this.ifShow ? false : true;
                this.isIf = this.isIf ? false : true;
            }
        }
    }
</script>

3. 渲染循環列表 v-for

v-for是一個循環指令,通常跟數組結合起來使用

也能夠在模板中使用 v-for

v-for 能夠經過一個對象的屬性來迭代數據

v-for 經過一個對象的屬性來迭代數據,能夠提供第二個的參數爲鍵名

v-for 經過一個對象的屬性來迭代數據,以第三個參數爲索引

v-for 也能夠循環整數

<p id="wantuizhijia">
 <ol>
  <li v-for="site in sites">
   {{ site.name }}
  </li>
 </ol>
</p>
 
<script>
 new Vue({
  el: '#wantuizhijia',
  data: {
   sites: [
    { name: '網推之家' },
    { name: '谷歌' },
    { name: '淘寶' }
   ]
  }
 })
</script>

4. 事件綁定 v-on

<div id="example-1">
  <button v-on:click="counter += 1">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>

var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})

用於監聽DOM事件,典型的就是v-on:click,處理的方法放在methods屬性裏會默認傳一個參數。

這裏的evt,是標準的鼠標點擊事件,相似jquery的click事件的回調函數中的參數。

<button @click="test">點擊</button>  
  
methods: {  
    test: function (evt) {  
        console.log(evt);  
    }  
}

若是須要給他一個像上面同樣的鼠標點擊事件時,則使用$event做爲參數(他和不傳參數時的默認鼠標事件對象是相同的)

<div id="app">  
    <a href="http://www.baidu.com" @click="test(items, $event)">點擊搜索age</a>  
</div>  
<script>  
    var test = {name: "test"};  
    var vm = new Vue({  
        el: '#app',  
        data: {  
            items: "test"  
        },  
        methods: {  
            test: function (msg, evt) {  
                console.log(msg);  
                evt.preventDefault();//阻止默認動做,好比這裏是頁面跳轉  
            }  
        }  
    })  
</script>  

Vue.js 爲 v-on 提供了事件修飾符。以前提過,修飾符是由點開頭的指令後綴來表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件再也不重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修飾符能夠串聯 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件監聽器時使用事件捕獲模式 -->
<!-- 即元素自身觸發的事件先在此處理,而後才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只當在 event.target 是當前元素自身時觸發處理函數 -->
<!-- 即事件不是從內部元素觸發的 -->
<div v-on:click.self="doThat">...</div>

 

本部分摘自:https://www.jianshu.com/p/a0680cb09c83

12、vue-router(hash, HTML5 新增的 pushState)

1. 單頁應用,如何實現其路由功能---路由原理

隨着前端應用的業務功能起來越複雜,用戶對於使用體驗的要求愈來愈高,單面(SPA)成爲前端應用的主流形式。大型單頁應用最顯著特色之一就是採用的前端路由系統,經過改變URL,在不從新請求頁面的狀況下,更新頁面視圖。

更新視圖但不從新請求頁面,是前端路由原理的核心之一,目前在瀏覽器環境中這一功能的實現主要有2種方式:

  • 利用URL中的hash("#");
  • 利用History interfaceHTML5中新增的方法;

vue-routerVue.js框架的路由插件,它是經過mode這一參數控制路由的實現模式的。

const router=new VueRouter({
    mode:'history',
    routes:[...]
})

隨着ajax流行,異步數據請求交互運行在不刷新瀏覽器的狀況下。而異步交互體驗的更高級版本就是SPA——單頁面應用。單頁面應用不只僅是在頁是無刷新的,連頁面吧跳轉也是無刷新的,爲了實現單頁面應用,因此就有了前端路由。相似於服務端路由,前端路由實現起來其實其實也很簡單,就是匹配不一樣的url路徑,進行解析,而後動態的渲染出該區域的html內容,可是這樣存在一個問題,就是url每次變化的時候,都會形成頁面的刷新,那解決的思路即是在改變url的狀況下,保證頁面不刷新,早2014年以前,你們是經過has來實現路由,url hash就相似於http://www.xxx.com/#/login,這種#後面hash值的變化並不會致使瀏覽器向服務器發送請求,也就不會刷新頁面,另外每次hash值的變化,還會觸發hashchange事件,經過這個事件咱們就能夠知道hash值發生了改變,而後咱們即可以監聽hashchange來實現更新頁面部份內容的操做。

pushState和replaceState,經過這兩個api能夠改變url地址且不會發送請求,同時還有 popstate事件,經過這些就可以用另外一種方式來實現前端路由,監聽popState方法跟hashChange 事件是同樣的,用了H5的實現單頁面跳轉,路由的URL就不會多出來一個#,變得更加美觀,可是由於沒有了#,因此當用戶刷新頁面之類的操做的時候,瀏覽器仍是會給服務器發送請求,爲了不出現這種狀況,因此這個實現要服務端的支持,須要把全部路由都重定向到根頁面。

本部分參考連接:https://segmentfault.com/a/1190000014822765?utm_source=tag-newest

2. vue-router如何作用戶登陸權限等

首先,在項目的App.vue中獲取當前登錄用戶的permissionList

並使用vuex狀態管理,將用戶的權限存入

以後,在nav.vue這個導航文件中用mapGetters獲取permissionList,並根據獲取到的權限設置當前用戶的菜單

store中的定義

// getters
const getters = {
  userInfo: state => state.userInfo,
  pmList: state => state.pmList
}

nav.vue導航文件部分代碼

data () {
    return {
      // pmList: [],
      menuList: [] // 當前用戶擁有的權限菜單
    }
  },
  computed: {
    ...mapGetters({
      user: 'userInfo',
      pmList: 'pmList',
      tabList: 'tabList'
    })
  },

// 根據權限設置當前用戶的菜單
    setUserMenu () {
      const permissions = this.pmList
      const routeList = cloneDeep(this.$router.options.routes[0].children)
      if (permissions && permissions.length) {
        routeList.forEach(rootItem => {
          if (rootItem.isNavMenu && permissions.includes(rootItem.name)) {
            if (rootItem.children.length) {
              rootItem.children = rootItem.children.filter(subItem => {
                return subItem.isTabMenu && permissions.includes(subItem.name)
              })
            }
            this.menuList.push(rootItem)
          }
        })
      } else {
        // this.menuList = routeList.filter(item => {
        //   return item.isNavMenu;
        // });
      }
    },

3. 你在項目中怎麼實現路由的嵌套

{
    path: '/goods',
    icon: 'icon-daohangshangpin-px',
    index: 1,
    isNavMenu: true,
    name: 'MALL_PRODUCT',
    meta: {
      title: '商品',
    },
    component: { template: `<router-view></router-view>` },
    children: goods
  },
{
    path: 'list',
    index: 0,
    isTabMenu: true,
    name: 'MALL_PRODUCT_MANAGER',
    meta: { title: '商品管理' },
    component: () => import('@views/goods/main/list.vue')
  },

  {
    path: 'listAddGood',
    isTabMenu: false,
    meta: { title: '添加商品' },
    component: () => import('@views/goods/main/edit.vue')
  },

十3、vuex的理解

在SPA單頁面組件的開發中 Vue的vuex和React的Redux 都統稱爲同一狀態管理,我的的理解是全局狀態管理更合適;簡單的理解就是你在state中定義了一個數據以後,你能夠在所在項目中的任何一個組件裏進行獲取、進行修改,而且你的修改能夠獲得全局的響應變動。

先聲明一個state變量,並賦值一個空對象給它,裏面隨便定義兩個初始屬性值;而後再在實例化的Vuex.Store裏面傳入一個空對象,並把剛聲明的變量state放裏面。

vuex官方API提供了一個getters,和vue計算屬性computed同樣,來實時監聽state值的變化(最新狀態)

mutattions也是一個對象,這個對象裏面能夠放改變state的初始值的方法,具體的用法就是給裏面的方法傳入參數state或額外的參數,而後利用vue的雙向數據驅動進行值的改變

actions也是個對象變量,最大的做用就是裏面的Action方法 能夠包含任意異步操做,這裏面的方法是用來異步觸發mutations裏面的方法,actions裏面自定義的函數接收一個context參數和要變化的形參,context與store實例具備相同的方法和屬性,因此它能夠執行context.commit(' ')

而在外部組件裏進行全局執行actions裏面方法的時候,你只須要用執行

this.$store.dispatch('hideFooter')

或this.$store.dispatch('showFooter')

以及this.$store.dispatch('getNewNum',6) //6要變化的實參

在組件中獲取vuex中的state

computed: {
     count () {
          return this.$store.state.count
     }
}

那爲何要使用computed而不是data獲取vuex中的state呢?
這是由於data 中的內容只會在 created 鉤子觸發前初始化一次,具體來講就是data中設置count: this.$store.state.count 則count的值是created鉤子執行前this.$store.state.count的值,賦值以後屬性的值就是純粹的字面量,以後this.$store.state.count 如何變化均影響不到count的取值。

vuex模塊化

//index.js

import Vue from 'vue';
import Vuex from 'vuex';
import footerStatus from './modules/footerStatus'
import collection from './modules/collection'
Vue.use(Vuex);

export default new Vuex.Store({
    modules:{
         footerStatus,
         collection
    }
});
//collection.js

const state={
    collects:[],  //初始化一個colects數組
};
const getters={
  renderCollects(state){ //承載變化的collects
    return state.collects;
  }
};
const mutations={
     pushCollects(state,items){ //如何變化collects,插入items
        state.collects.push(items)
     }
 };
const actions={
    invokePushItems(context,item){ //觸發mutations裏面的pushCollects ,傳入數據形參item 對應到items
        context.commit('pushCollects',item);
    }
};
export default {
     namespaced:true,//用於在全局引用此文件裏的方法時標識這一個的文件名
     state,
     getters,
     mutations,
     actions
}
//footerStatus.js
 
const state={   //要設置的全局訪問的state對象
     showFooter: true,
     changableNum:0
     //要設置的初始屬性值
   };
const getters = {   //實時監聽state值的變化(最新狀態)
    isShow(state) {  //承載變化的showFooter的值
       return state.showFooter
    },
    getChangedNum(){  //承載變化的changebleNum的值
       return state.changableNum
    }
};
const mutations = {
    show(state) {   //自定義改變state初始值的方法,這裏面的參數除了state以外還能夠再傳額外的參數(變量或對象);
        state.showFooter = true;
    },
    hide(state) {  //同上
        state.showFooter = false;
    },
    newNum(state,sum){ //同上,這裏面的參數除了state以外還傳了須要增長的值sum
       state.changableNum+=sum;
    }
};
 const actions = {
    hideFooter(context) {  //自定義觸發mutations裏函數的方法,context與store 實例具備相同方法和屬性
        context.commit('hide');
    },
    showFooter(context) {  //同上註釋
        context.commit('show');
    },
    getNewNum(context,num){   //同上註釋,num爲要變化的形參
        context.commit('newNum',num)
     }
};
export default {
    namespaced: true, //用於在全局引用此文裏的方法時標識這一個的文件名
    state,
    getters,
    mutations,
    actions
}

 

本部分參考連接:http://www.javashuo.com/article/p-zwfitfjy-eu.html

十4、Webpack

WebPack 是一個模塊打包工具,你可使用 WebPack管理你的模塊依賴,並編繹輸出模塊們所需的靜態文件。它可以很好地管理、打包Web開發中所用到的 HTMLJavaScriptCSS以及各類靜態文件(圖片、字體等),讓開發過程更加高效。對於不一樣類型的資源, webpack有對應的模塊加載器。 webpack模塊打包器會分析模塊間的依賴關係,最後 生成了優化且合併後的靜態資源。
  • CommonJSAMDES6的語法作了兼容
  • jscss、圖片等資源文件都支持打包
  • 串聯式模塊加載器以及插件機制,讓其具備更好的靈活性和擴展性,例如提供對CoffeeScriptES6的支持
  • 有獨立的配置文件webpack.config.js
  • 能夠將代碼切割成不一樣的chunk,實現按需加載,下降了初始化時間
  • 支持 SourceUrlsSourceMaps,易於調試
  • 具備強大的Plugin接口,大可能是內部插件,使用起來比較靈活webpack 使用異步 IO 並具備多級緩存。這使得 webpack 很快且在增量編譯上更加快

【Loader】:用於對模塊源碼的轉換,loader描述了webpack如何處理非javascript模塊,而且在buld中引入這些依賴。loader能夠將文件從不一樣的語言(如TypeScript)轉換爲JavaScript,或者將內聯圖像轉換爲data URL。好比說:CSS-Loader,Style-Loader等。

【Plugin】:目的在於解決loader沒法實現的其餘事,從打包優化和壓縮,到從新定義環境變量,功能強大到能夠用來處理各類各樣的任務。webpack提供了不少開箱即用的插件:CommonChunkPlugin主要用於提取第三方庫和公共模塊,避免首屏加載的bundle文件,或者按需加載的bundle文件體積過大,致使加載時間過長,是一把優化的利器。而在多頁面應用中,更是可以爲每一個頁面間的應用程序共享代碼建立bundle。

一切皆模塊: 正如js文件能夠是一個「模塊(module)」同樣,其餘的(如css、image或html)文件也可視做模 塊。所以,你能夠require('myJSfile.js')亦能夠require('myCSSfile.css')。這意味着咱們能夠將事物(業務)分割成更小的易於管理的片斷,從而達到重複利用等的目的。

按需加載: 傳統的模塊打包工具(module bundlers)最終將全部的模塊編譯生成一個龐大的bundle.js文件。可是在真實的app裏邊,「bundle.js」文件可能有10M到15M之大可能會致使應用一直處於加載中狀態。所以Webpack使用許多特性來分割代碼而後生成多個「bundle」文件,並且異步加載部分代碼以實現按需加載。

1. 打包原理

webpack只是一個打包模塊的機制,只是把依賴的模塊轉化成能夠表明這些包的靜態文件。並非什麼commonjs或者amd之類的模塊化規範。webpack就是識別你的 入口文件。識別你的模塊依賴,來打包你的代碼。至於你的代碼使用的是commonjs仍是amd或者es6的import。webpack都會對其進行分析。來獲取代碼的依賴。webpack作的就是分析代碼。轉換代碼,編譯代碼,輸出代碼。webpack自己是一個node的模塊,因此webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的)

  • 初始化:啓動構建,讀取與合併配置參數,加載 Plugin,實例化 Compiler

  • 編譯:從 Entry 發出,針對每一個Module串行調用對應的Loader去翻譯文件內容,再找到該Module 依賴的 Module,遞歸地進行編譯處理。

  • 輸出:對編譯後的Module組合成 Chunk,把 Chunk轉換成文件,輸出到文件系統。

2. webpack熱更新原理

熱更新是:使得應用在運行狀態下,不重載刷新就能更新、增長、移除模塊的機制。簡單來講,就是爲了 提高開發效率。

https://www.jianshu.com/p/652fbae768bf

3. 優化構建速度

  • 減小構建的文件,減少文件大小:項目中存在太多的無用的文件和代碼,先刪除這些無用的東西
  • 移除 CommonsChunkPlugin
  • Search with Google
//使用 DllPlugin的配置
const manifest = require('./dll/vendor-manifest.json');

// ... 其餘完美的配置

plugins: [
  new webpack.DllReferencePlugin({
    manifest,
  }),
],

本部分摘自:https://segmentfault.com/a/1190000015088834?utm_source=tag-newest  

十5、v-lazy加載圖片

引入插件

import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: require('@/assets/img/dou_dou.jpg'), //請求失敗後顯示的圖片
  loading: require('@/assets/img/dou_dou.jpg'), //加載的loading過渡圖片
  attempt: 1, // 加載圖片數量
})  listenEvents: [ 'scroll' ]  //監聽的事件

src屬性改爲  v-lazy

<img v-lazyload="imageSrc" >

監聽的事件列表:['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend']  

相關文章
相關標籤/搜索