Vue - 基礎筆記梳理

備戰秋招,複習基礎。若有錯誤,歡迎批評指正,共同進步!html

基本概念

構建用戶界面的漸進式框架前端

只關注圖層,自底向上增量開發(增量是什麼TBC!!!)vue

代碼只須要關注邏輯層,DOM操做由VUE處理。node

經過儘量簡單的API實現響應的數據綁定和組合的組件視圖。ios

實現原理

資料參考:Vue原理解析——本身寫個Vuevue-router

資料參考:剖析Vue原理&實現雙向綁定MVVMvuex

  1. 經過創建虛擬dom樹document.createDocumentFragment(),方法建立虛擬dom樹。
  2. 一旦被監測的數據改變,會經過Object.defineProperty定義的數據攔截,截取到數據的變化。
  3. 截取到的數據變化,從而經過訂閱 — 發佈者模式,觸發Watcher(觀察者),從而改變虛擬dom的中的具體數據。
  4. 最後,經過更新虛擬dom的元素值,從而改變最後渲染dom樹的值,完成雙向綁定

虛擬DOM

vue的vnode代碼部分位於項目的src/core/vdom文件夾下,vue的Virtual DOM是基於snabbdom修改的,vnode類的數據結構以下npm

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component’s scope
  functionalContext: Component | void; // only for functional component root nodes
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.functionalContext = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child (): Component | void {
    return this.componentInstance
  }
}
複製代碼

數據雙向綁定

vue原理

  1. 實現一個數據監聽器Observer,可以對數據對象的全部屬性進行監聽,若有變更可拿到最新值並通知訂閱者
  2. 實現一個指令解析器Compile,對每一個元素節點的指令進行掃描和解析,根據指令模板替換數據,以及綁定相應的更新函數
  3. 實現一個Watcher,做爲鏈接Observer和Compile的橋樑,可以訂閱並收到每一個屬性變更的通知,執行指令綁定的相應回調函數,從而更新視圖
  4. mvvm入口函數,整合以上三者

實現observer

Object.defineProperty

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

將須要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter和getter 這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就能監聽到了數據變化。axios

對其更底層對象屬性的修改或獲取的階段進行了攔截

var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // 哈哈哈,監聽到值變化了 kindeng --> dmq

function observe(data) {
    if (!data || typeof data !== 'object') {
        return;
    }
    // 取出全部屬性遍歷
    Object.keys(data).forEach(function(key) {
        defineReactive(data, key, data[key]);
    });
};

function defineReactive(data, key, val) {
    observe(val); // 監聽子屬性
    Object.defineProperty(data, key, {
        enumerable: true, // 可枚舉
        configurable: false, // 不能再define
        get: function() {
            return val;
        },
        set: function(newVal) {
            console.log('哈哈哈,監聽到值變化了 ', val, ' --> ', newVal);
            val = newVal;
        }
    });
}
複製代碼

發佈-訂閱者模式

維護一個數組,用來收集訂閱者,數據變更觸發notify,再調用訂閱者的update方法。

在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。

// ... 省略
function defineReactive(data, key, val) {
    var dep = new Dep();
    observe(val); // 監聽子屬性

    Object.defineProperty(data, key, {
        // ... 省略
        set: function(newVal) {
            if (val === newVal) return;
            console.log('哈哈哈,監聽到值變化了 ', val, ' --> ', newVal);
            val = newVal;
            dep.notify(); // 通知全部訂閱者
        }
    });
}

function Dep() {
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub) {
        this.subs.push(sub);
    },
    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update();
        });
    }
};
複製代碼

實現Compile

compile主要作的事情是解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖

實現watcher

Watcher訂閱者做爲Observer和Compile之間通訊的橋樑,主要作的事情是:

  1. 在自身實例化時往屬性訂閱器(dep)裏面添加本身
  2. 自身必須有一個update()方法
  3. 待屬性變更dep.notice()通知時,能調用自身的update()方法,並觸發Compile中綁定的回調,則功成身退。

實現MVVM

MVVM做爲數據綁定的入口,整合Observer、Compile和Watcher三者,經過Observer來監聽本身的model數據變化,經過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變動的雙向綁定效果。

!!!太複雜了!!!還要再研究研究!!!QAQ

4種狀況不觸發vue響應式更新

  • 不能檢測到的數組變更是:
    • 一、當利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue;
    • 二、當修改數組的長度時,例如:vm.items.length = newLength;
  • 不能檢測到的對象變更是:
    • 三、向響應式對象添加屬性;
    • 四、向響應式對象刪除屬性;
  • 解決方法總結:
    • 一、建立新的數組替換原有數組值
    • 二、使用JavaScript的數組操做函數,這些方法都會返回一個新數組,也是數組替換原理;
    • 三、使用vue自帶的 vue.set(object , key , value );向響應式對象添加屬性;
    • 四、使用vue自帶的 vue.delete(object , key );向響應式對象刪除屬性;
    • 五、對象添加屬性還可使用Object.assign({},obj1,obj2)返回獲取的新對象替換原有對象;

基礎語法

var vm = new Vue({
    el:'至關於id',
    data:{...},
    template:{...},
    methods:{...},函數調用,不能緩存  → {{methodTest()}}
    computed:{...},屬性調用,具備緩存功能,依賴於data中的數據,只有在它的相關依賴數據發生改變時纔會從新求值  → {{computedTest}}
    watch:{...},監測Vue實例上的數據變更
    render:{...} 渲染優先級:render → template → outerHTML
    })
    
data() {
      return {
        demo: {
          name: ''
        },
        value: ''
      };
    },
computed: {
      newName() {
        return this.demo.name;
      }
    },
watch: {
      newName(val) {
        this.value = val;
      }
    }
複製代碼

文本插值<p>{{message}}</p>

HTML插值<div v-html:"message></div>

屬性v-bind:class="{'class1':use}" → 動態賦值

指令v- → 如:v-if v-else

用戶輸入v-model → 實現數據雙向綁定

事件監聽 v-on:click

v-if v-show v-for區別

  • v-for: 遍歷整個數組 item in items
  • v-if:操控一個dom元素的建立與銷燬(決定一個元素的存在與否),有更高的切換開銷。至關於原生js中的if
  • v-show:控制一個dom的顯示隱藏(經過操做dom的display:block/none)達到效果,有更高的初始渲染開銷。
  • 若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不多改變,則使用 v-if 較好。

處理邊界

用處 範例
訪問根實例 this.$root.foo
訪問父級組件實例 this.$parent
訪問子組件實例(非響應式) 子<input ref="input"> 父this.$refs.input.focus()
依賴注入(訪問任意上級) 父 provide:funtion(){ return{getMap:this.getMap}} 子 inject:['getMap']

$:

  1. 表示屬性和方法存在於vue實例的原型上
  2. 字符串中插入變量 ${...}

只有當實例被建立時data中存在的屬性纔是響應式的!即:要先初始化,哪怕爲空也行

實例方法

vm.$el === document.getElementById('example')
vm.$watch('a',function(new,old){在vm.a改變時調用}
複製代碼

不要在選項屬性或回調函數中使用箭頭函數!

兄弟組件傳值

  • 子傳父,而後父傳子
  • vuex
  • 事件總線:以新建一個Vue實例看成事件總線,觸發on和emit

生命週期

參考資料:手把手教Vue--生命週期

方法名 狀態 含義 用法
beforeCreate creating 狀態 實例建立以前調用 加 Loading 事件
created creating 狀態 實例建立成功,此時 data 中的數據顯示出來了 頁面沒有加載完成就請求數據,結束 Loading
beforeMount mounting 狀態 數據中的 data 在模版中先佔一個位置
mounted mounting 狀態 模版中的 data 數據直接顯示出來了 發起異步服務端請求
beforeUpdate updating 狀態 當 data 數據發生變化調用,發生在虛擬 DOM 從新渲染和打補丁以前
updated updating 狀態 數據更改致使的虛擬 DOM 從新渲染和打補丁
beforeDestroy destroying 狀態 在 vue 實例銷燬以前調用,此時實例任然可用 彈出確認刪除
destroyed destroying 狀態 在 vue 實例銷燬以後調用,vue實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬 關閉定時器,中止監聽屬性,清除相關數據達到資源的有效利用

Vue路由

HTML裏

<div id = "app">
    <router-link to="/foo">Go to Foo</router-link>
    <router-view></router-view>
</div
複製代碼

JS裏

1 定義路由組件 const Foo = {template:'<div>foo</div>'}
2 定義路由     const routes = [{path:'/foo',component:Foo}]
3 建立router實例 const router = new VueRouter({routes})
4 建立掛載根實例 const app = new Vue({router}}).$mount('#app')
複製代碼
屬性 用法
to 目標路由的連接
replace 點擊時調用
append 添加相對路徑的基路徑 :to="{path:'relative/path'}" append
tag 渲染成某種標籤 如<li>
active-class 連接激活時使用的CSS類名
exact-active-class 連接精確匹配時的CSS類名
event 觸發導航的事件,如event="mouseover→鼠標移動到上面時導航html內容改變

keep-alive 緩存失活的組件。切換頁面,切換回來,頁面狀態不變

hash和history

參考資料:vue-router的兩種模式(hash和history)及區別

  • hash - 即地址欄URL中的 # 符號(此hash不是密碼學裏的散列運算) 好比這個URL:http://www.abc.com/#/hello,hash的值爲#/hello.它的特色在於:hash雖然出如今URL中,但不會被包括在HTTP請求中,對後端徹底沒有影響,所以改變hash不會從新加載頁面。
1 $router.push() //調用方法
2 HashHistory.push() //根據hash模式調用,設置hash並添加到瀏覽器歷史記錄(添加到棧頂)(window.location.hash= XXX)
3 History.transitionTo() //監測更新,更新則調用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替換當前app路由
6 vm.render() //更新視圖
複製代碼
  • history - 利用了HTML5 History Interface中新增的pushState()replaceState()方法。(須要特定瀏覽器支持)
1.push
與hash模式相似,只是將window.hash改成history.pushState
2.replace
與hash模式相似,只是將window.replace改成history.replaceState
3.監聽地址變化
在HTML5History的構造函數中監聽popState(window.onpopstate)
複製代碼
  • 區別:

    1. pushState()設置的新URL能夠是與當前URL同源的任意URL;而hash只可修改#後面的部分,所以只能設置與當前URL同文檔的URL;
    2. pushState()設置的新URL能夠與當前URL如出一轍,這樣也會把記錄添加到棧中;而hash設置的新值必須與原來不同纔會觸發動做將記錄添加到棧中;
    3. pushState()經過stateObject參數能夠添加任意類型的數據到記錄中;而hash只可添加短字符串;
    4. pushState()可額外設置title屬性供後續使用。
    5. hash 模式下,僅hash符號以前的內容會被包含在請求中,如http://www.abc.com,所以對於後端來講,即便沒有作到對路由的全覆蓋,也不會返回404錯誤。
    6. history模式下,前端的URL必須和實際向後端發起請求的URL一致。如htttp://www.abc.com/book/id。若是後端缺乏對/book/id 的路由處理,將返回404錯誤、

Vue過渡

<transition name="動畫名稱" mode="out-in">
    <div></div>
</transition>
複製代碼

過渡模式:in-out / out-in

屬性 用法
v-enter 進入過渡的開始狀態
v-enter-active 進入過渡生效時的狀態,如{transition:opacity 2s}
v-enter-to 進入過渡的結束狀態
v-leave 離開過渡的開始狀態
v-leave-active 離開過渡生效時的狀態
v-leave-to 離開過渡的結束狀態

混入

mixins:可複用的方法或計算屬性,可包含任意組件選項。

待補充!!!

AJAX

vue-resource

經過XMLHttpRequest或JSONP發起請求並處理響應

可以使用全局對象方式vue.http或在組件內部使用this.$http來發期請求

get:function(){
    this.$http.get('/someUrol').then(
        function(res){
            document.write(res.body);
        },
        function(){
            console.log('error!')
        }
    );
}
複製代碼

API:待補充具體含義~

get(url,[options])
head(url,[options])
delete(url,[options])
jsonp(url,[options])
post(url,[body],[options])
put(url,[body],[options])
patch(url,[body],[options])
複製代碼

axios

基於promise的HTTP客戶端

npm install axios
import axios from 'axios'
複製代碼

get:

axios.get(url).then(function(response){...}).catch(function(error){...});
複製代碼

post:

axios({
    method:'post',
    url:'/user',
    data:{
        firstName:'a',
        lastName:'b'
    }
});
複製代碼

axios.all([func1(),func2()]).then{...} → 執行多個併發請求

API:待補充具體含義~

axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data,[config]])
axios.put(url[,data,[config]])
axios.patch(url[,data,[config]])
複製代碼

axios攔截器

請求攔截

axios.interceptor.request.use(function(config){
    //在發送請求前作的事
    return config;
    },function(error){
        //請求錯誤時作的事
        return Promise.reject(error);
    });
複製代碼

響應攔截

axios.interceptors.response.use(function(response){
    //對響應數據作的事
    return config;
    },function(error){
        //請求錯誤時作的事
        return Promise.reject(error);
    });
複製代碼

Prop驗證

  • 必填:required:true
  • 默認值:default:100
  • 自定義驗證函數:validator:function(value){return ['success,'danger'].indexOf(value)!==-1
  • 類型檢查

插槽

將子組件中<slot></slot>替換爲父組件<></>之間的任意代碼

<slot>當父組件中不提供內容時的後備內容</slot>

具名插槽

父組件:<template v-slot:header></template>
子組件:<header></header>
複製代碼

做用域插槽

父組件:<template v-slot:default="slotProps">
            {{slotProps.user.firstName}}
        </template>
子組件:<slot v-bind:user="user"></slot>

簡寫父組件:<current-user v-slot:"slotProps">slotProps.user.firstName</current-user>

再簡寫父組件:<current-user v-slot:"{user}">{{slotProps.user.firstName}}</current-user>
可定義後備值"{user={firstName:'Guest'}}"
複製代碼

Vue-CLI

快速原型開發:對於單個 *.vue文件

vue serve在開發環境模式下零配置爲.js或.vue文件啓動一個服務器

vue build在生產環境模式下零配置構建一個.js或.vue文件

Preload 預加載首渲內容

Prefetch 預加載將來內容

Vue-cookies

載入

npm install vue-cookies

var Vue = require('vue')
Vue.use(require('vue-cookies')
或
import Vue from 'vue'
import VueCookies from 'vue-cookies'
Vue.use(VueCookies)
複製代碼
用法 範例
設置cookie this.$cookies.set(keyName,value[,expireTime[,path[,domain[,secure]]]])
獲取cookie this.$cookies.get(keyName) // return value
刪除cookie this.$cookies.remove(keyName[,path[,domain]])
存在? this.$cookies.isKey(keyName) //retuen boolean
獲得所有 this.$cookies.keys()

VueX

狀態管理模式 集中式存儲管理應用的全部組件的狀態

  • state:驅動應用的數據源(單一狀態樹)
  • view:以聲明方式將state映射到視圖
  • actions:響應在view上的用戶輸入致使的狀態變化

基本用法

--- main.js ---

import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    //配置...
});

new Vue({
    el: '#app',
    router:router,
    store:store,
    render:h=>{
        return h(App)
    }
});
複製代碼

Vuex裏的數據都是響應式的!!!

state

--- main.js ---
const store = new Vuex.Store({
    state:{
        count:0
    }
});

--- index.vue ---
{{ $store.state.count}}
或
export default{
    computed:{
        count (){
            return this.$store.state.count;
        }
    }
}
複製代碼

mapState

輔助函數,獲取多個狀態,幫助生成計算屬性

computed:{
    localComputed(){
        ...
    },
    ...mapState({
        ...
    })
}
複製代碼

Getter

計算store,返回值會緩存。依賴改變時會從新計算。可經過屬性(緩存)和方法(不緩存)訪問。

--- main.js ---
const store = new Vuex.Store({
    state:{list:[1,5,8,10,30,50]},
    getters:{
        filteredList: state =>{
            return state.list.filter(item => item <10);  ← 以state爲參數
        },
        listCount: (state,getters) =>{
            return getters.filteredList.length;   ← 以state和getters爲參數
        }
    }
})

--- index.vue ---
export default{
    computed:{
        list (){
            return this.$store.getters.filteredList;
        },
        listCount (){
            return this.$store.getters.listCount;
        }
    }
}
複製代碼

mapGetter

輔助函數,映射多個局部計算。

computed:{
    ...mapGetters([...也可另取名字])
}
複製代碼

mutations

mutation改變store中狀態的惟一方法就是提交mutation(同步)

--- main.js ---
const store = new Vues.Store({
    state:{list:[1,5,8,10,30,50]},
    mutation:{
        increment(state,params){
            state.count+=params.count;     ← 直接傳入對象
        },
        descrease (state){
            state.count --;
        }
    }
});

--- index.vue ---
export default{
    methods:{
        handleIncrement (){
            this.$stroe.commit({
                type:'increment',
                count:10                 ← 直接傳入對象
            });
        },
        handleDecrease (){
            this.$store.commit('descrease');
        }
    }
}
複製代碼

Action

提交mutation(而非直接更改狀態),支持異步

--- main.js ---
mutation:{
    increment (state){
        state.count++
    }
},
actions:{
    increment(與store實例具備相同方法和屬性的context對象){
        context.commit('increment')
    }
    異步
    asyncIncrement (context){
        return new Promise(resolve =>{
        setTimeout(()=>{
            context.commit('increment');
            resolve();
        },1000)
    });
}

--- index.vue ---
methods:{
    handleActionIncrement(){
        this.$stroe.dispatch('increment');
    },
    handleAsyncIncrement (){
        this.$store.dispatch('asyncIncrement').then(()=>{  ← 異步調用 返回的是Promise
            console.log('...');
        });
    }
}
複製代碼

mapActions

輔助函數,映射多個dipatch調用

可嵌套:
actionB({dispatch,commit}){
    return dispatch('actionA').then(()={
        commit('other Mutation')
    })
}
複製代碼

可用async / await 組合action! → 一個dispatch觸發多個action

涉及改變數據的,就用mutations,存在業務邏輯的,就用actions

Module

將store分割成模塊

const moduleA = {
    state:{...},
    mutations:{...},
    actions:{...}, ← 可接收rootState 同getter
    getters:{
        sumCount (state,getters,rootState){   ← 在模塊內部調用根節點狀態:rootState
            return state.count + rootState.count;
        }
    }
}
const moduleB = {...}

const store = new Vuex.store({
    modules{
        a:moduleA,
        b:moduleB
    }
})

訪問:
store.state.a
store.state.b
複製代碼

命名空間:

  • namespaced:true 訪問全局需加一些額外操做~
  • 調用時路徑改變

其餘注意

vuex中的state用v-model會報錯!!應綁定事件回調mutation~或用setget雙向綁定!

相關文章
相關標籤/搜索