Vue-2 vue-cli,生命週期,computed,watch,自定義指令

Vue-2 vue-cli,生命週期,computed,watch,自定義指令

vue-cli環境配置、項目安裝、目錄樹

我們使用webpack也可以手動安裝配置vue腳手架環境,但是有一套更簡單一點的vue-cli的構建工具,大大降低了webpack的使用難度。

一、配置vue的腳手架環境

npm install -g vue-cli

完成之後查看vue版本:

vue -V

如果終端承認vue命令,那麼腳手架安裝成功,否則要麼腳手架安裝失敗,要麼就是需要配置vue的path變量。

二、生成項目模板

1. 查看所有vue命令

vue -h

運行結果:

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  init           generate a new project from a template
  list           list available official templates
  build          prototype a new project
  create         (for v3 warning only)
  help [cmd]     display help for [cmd]

2. 構建vue項目

vue init webpack my-pro

在安裝過程中,會出現以下一些選擇:

? Project name  輸入項目名稱
? Project description 輸入項目描述

? Author 作者

? Vue build 打包方式,回車就好了

? Install vue-router?  選擇  Y 使用 vue-router,輸入 N 不使用

? Use ESLint to lint your code? 代碼規範,推薦 N

? Setup unit tests with Karma + Mocha? 單元測試,推薦 N

? Setup e2e tests with Nightwatch? E2E測試,N

3. 項目的目錄結構

├── README.md // 項目說明文檔

├── node_modules // 依賴包目錄

├── build // webpack相關配置文件(都已配置好,一般無需再配置)

│       ├── build.js  //生成環境構建

│       ├── check-versions.js  //版本檢查(node,npm)

│       ├── dev-client.js    //開發服務器熱重載 (實現頁面的自動刷新) 

│       ├── dev-server.js    //構建本地服務器(npm run dev)

│       ├── utils.js         // 構建相關工具

│       ├── vue-loader.conf.js   //csss 加載器配置

│       ├── webpack.base.conf.js    //webpack基礎配置

│       ├── webpack.dev.conf.js     // webpack開發環境配置

│       └── webpack.prod.conf.js     //webpack生產環境配置

├── config // vue基本配置文件(可配置監聽端口,打包輸出等)

│       ├── dev.env.js // 項目開發環境配置

│       ├── index.js //   項目主要配置(包括監聽端口、打包路徑等)

│       └── prod.env.js // 生產環境配置

├── index.html // 項目入口文件

├── package-lock.json // npm5 新增文件,優化性能

├── package.json // 項目依賴包配置文件
├── src // 項目核心文件(存放我們編寫的源碼文件)

│       ├── App.vue // 根組件
        
│       ├── assets // 靜態資源(樣式類文件、如css,less,和一些外部的js文件)

│       │       └── css  //樣式

│       │       └── font  //字體
│       │       └── images  //圖片
│       ├── components // 組件目錄

│       │       └── Hello.vue // 測試組件

│       ├── main.js // 入口js文件

│       └── router // 路由配置文件夾

│       └── index.js // 路由配置文件

└── static // 靜態資源目錄(一般存放圖片類)

注:assets和static文件夾的區別
assets目錄中的文件會被webpack處理解析爲模塊依賴,只支持相對路徑形式。例如,在 <img src="./logo.png">background: url(./logo.png)中"./logo.png" 是相對的資源路徑,將由Webpack解析爲模塊依賴。

static/ 目錄下的文件並不會被Webpack處理:它們會直接被複制到最終的打包目錄(默認是dist/static)下。必須使用絕對路徑引用這些文件,這是通過在 config.js 文件中的 build.assetsPublicPathbuild.assetsSubDirectory 連接來確定的。

任何放在 static/ 中文件需要以絕對路徑的形式引用:/static/[filename]

在我們實際的開發中,總的來說:static放不會變動的文件 assets放可能會變動的文件。

4. 安裝好了之後啓動項目

npm run dev

Component的封裝、調用

vue-cli開發的是單頁面的應用,只有一個 html 文件,在開發時,我們將 整個項目按功能劃分成模塊,每個模塊都可以被任意的複用。高效率,低耦合。

模塊的具體呈現形式就是組件,一個模塊就是一個組件,每個組件有自己的數據、函數、週期等。

項目默認安裝好了之後,可以在 src 文件夾中看到有一個默認的HelloWorld.vue文件。這就是默認安裝的組件,一個組件就是一個 vue 文件。

我們在 src 的 components 文件夾中創建組件。

一、組件的基本構成

<template>
    <div class="home">
        <!-- 當前組件中的結構 -->
    </div>
</template>
<script>
    // 當前組件的腳本
    export default {
        // 當前組件的名稱
        name: 'Home',
        data () {
            return {
                // 當前組件中所有的數據
            }
        },
        methods: {
            // 當前組件中所有的函數
        }
    }
</script>
<style>
    /* 當前組件的樣式 */
</style>

注:

  • 組件中的結構必須包含在 <template>標籤中。
  • 組件所有的子結構都必須包含在一個父級標籤中,建議使用block標籤。
  • 組件的<style>標籤中的樣式,是全局的樣式,如果這個組件被其他組件調用,那麼這個組件中的style樣式會作用於當前頁面中的所用同選擇器元素。
  • <style>標籤加了scoped(如<style scoped></style>)屬性之後,其樣式就是局部的,只作用於當前組件。

二、組件的調用

組件的調用方式有多種,作爲子組件被調用,路由調用,插槽調用等,在這裏我們先說傳統的調用方式,即作爲子組件被調用。

調用組建時,需要這麼幾個步驟:

  • 導出當前組件,exprot default {}
  • 在要使用當前組件的父級組件中,導入當前組件。
  • 將當前組件傳入父組件的components中。
  • 在父組件中使用標籤形式調用組件。
<!-- App.vue -->
<template>
  <div id="app">
    <!-- 
        在App.vue中調用Home.vue
        注: App.vue是根組件,它是和index.html有直接關係,在main.js中被實例化
     -->
    <Home/>
  </div>
</template>

<script>
// 導入Home.vue
import Home from './components/home'
export default {
  name: 'App',
  components: {
    // 在此聲明  
    Home
  }
}
</script>

<style></style>

生命週期鉤子函數

每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等,我們把這一系列的過程稱爲組件的生命週期(組件從註冊到銷燬的整個過程)。我們有時候需要在組件生命週期的某個過程中,執行某些代碼,基於此,vue 提供了生命週期鉤子函數,給了用戶在不同階段添加自己的代碼的機會。

但是在此之前,我們要詳細的介紹下組件的聲明週期,以及生命週期中每個階段組件完成和未完成的部分。

一、組件的生命週期

從圖中可以看到,vue 爲生命週期提供了 8 個鉤子函數:

  • beforeCreate: 創建前
  • created: 創建後
  • beforeMount: 掛載前
  • mounted: 掛載後
  • beforeUpate: 更新前
  • upated: 更新後
  • beforeDestoy: 銷燬前
  • destoyed: 銷燬後

二、每個鉤子函數執行時的組件形態

我們可以在組件的生命週期鉤子函數中執行一些代碼。

<template>
  <div id="app">
    <button @click="flag = !flag">點擊</button>
    <Home v-if="flag"/>
  </div>
</template>

<script>
import Home from './components/home'
export default {
    name: 'App',
    components: {
        Home
    },
    data() {
        return {
            flag: true,
            info: 'i am info of component'
        }
    },
    //數據沒有 元素沒有
    beforeCreate:function(){
        console.log("創建前========");
        console.log(this.info);
        console.log(this.$el);
    },
    //數據有 元素沒有
    created:function(){
        console.log("已創建========");
        console.log(this.info);
        console.log(this.$el);
    },
    //數據有 元素有 虛擬dom
    beforeMount:function(){
        console.log("mount之前========");
        console.log(this.info);
        console.log(this.$el);
    },
    //數據有 元素有,真實dom
    mounted:function(){
        console.log("mounted========");
        console.log(this.info);
        console.log(this.$el);
    },
    beforeUpdate:function(){
        console.log("更新前========");
        console.log(this.flag);
        console.log(this.$el);
    },
    updated:function(){
        console.log("更新完成========");
        console.log(this.flag);
        console.log(this.$el);
    },
    beforeDestroy:function(){
        console.log("銷燬前========");
        console.log(this.info);
        console.log(this.$el);
    },
    destroyed:function(){
        console.log("已銷燬========");
        console.log(this.info);
        console.log(this.$el);
    }
}
</script>

<style></style>

注:

  • 因爲 vue 是通過控制數據來控制頁面元素的,所以當數據發生變化時,頁面中的元素也會隨之響應,所以更新發生在數據變化時。beforeUpdate時,數據和節點都已經更新了。
  • 可以在 beforeCreate 中,執行一些loading效果。
  • created中結束loading,還可以做一些初始化操作,可以將一些頁面剛開始加載就執行的代碼放在這裏,但是要注意,這個時候是沒有節點的。
  • mounted:在這發起axios請求,拿回數據,配合路由鉤子做一些事情,這個時候有節點又有數據,可以實例化一些插件效果。
  • beforeDestory: destoryed :當前組件已被刪除,清空相關內容。

computed和watch

computed(計算數據)watch(觀察者) 是 vue 組件中非常重要的兩個功能。

一、computed

創建一個組建時,組件中的數據都聲明在 data 中,但是有些數據需要依賴 data 中的其他數據計算,這個數據會隨着依賴數據的變化而變化(比如購物車中被選中的商品總價,需要通過購物車中的商品列表計算,當購物車中商品發生變化,這個總價也會變化)。

我們把這種需要依賴其他變量進行處理之後產生的數據都聲明在computed(計算屬性)中。 計算屬性允許我們對指定的視圖,複雜的值計算。這些值將綁定到依賴項值,只在需要時更新。

計算屬性示例:

<template>
  <div class="home">
    count: <input type="number" v-model.number="current.count">
    price: <input type="number" v-model.number="current.price">
    <button @click="addGood">添加</button>
    <h3>{{ sum }}</h3>
  </div>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            current: {
                count: 0,
                price: 0
            },
            list: [{
                count: 3,
                price: 39.79
            },{
                count: 1,
                price: 109.34
            },{
                count: 3,
                price: 0.45
            }]
        }
    },
    methods: {
        addGood() {
            this.list.push(this.current)
        }
    },
    computed: {
        sum() {
            let sum_ = 0;
            for(let l of this.list) {
                sum_ += l.count * l.price;
            }
            return sum_;
        }
    }
}
</script>

注:
computed和methods有時候從代碼角度而言,沒有區別,但是底層的機制是完全不同的。
methods中的函數,在每次使用這個數據時都要調用,computed依賴於data中的數據,只有在它的相關依賴數據發生改變時纔會重新求值,如果依賴數據不變,計算屬性會緩存這個數據上次的值,不會重複調用複雜的計算過程。

官方文檔反覆強調對於任何複雜邏輯,都應當使用computed計算屬性

computed和methods的區別示例:

<template>
  <div class="home">
    <button @click="n++">a變化</button>
    <button @click="m++">b變化</button>
    <h3>{{ sum_of_c }}</h3>
    <h3>{{ sum_of_c }}</h3>
    <h3>{{ sum_of_c }}</h3>
    <hr>
    <h3>{{ sum_of_m() }}</h3>
    <h3>{{ sum_of_m() }}</h3>
    <h3>{{ sum_of_m() }}</h3>
  </div>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            n: 0,
            m: 0
        }
    },
    methods: {
        sum_of_m() {
            console.log('methods中的sum執行了');
            return this.n + this.m;
        }
    },
    computed: {
        sum_of_c() {
            console.log('computed中的sum執行了');
            return this.n + this.m;
        }
    }
}
</script>

二、watch

有時候我們需要在 data 中的數據發生變化時做一些操作,那就需要監聽這個數據的變化,vue 提供了 watch(偵聽器),來監聽數據的變化。

computed 計算依賴於某些數據的值。watch 監聽數據本身。

雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。當需要在數據變化時執行異步或開銷較大的操作時,這個方式是最有用的。

方式一:

<template>
  <div class="test2">
    <button @click="num = 1000">點擊</button>
  </div>
</template>

<script>
    export default {
        name: "Test2",
        data() {
            return {
                num: 100
            }
        },
        watch: {
            num(newVal, oldVal) {
                console.log(newVal, oldVal);
            }
        }
    }
</script>

這種寫法也可以改寫成這樣:

// ...
methods: {
    numChange(newVal, oldVal) {
        console.log(newVal, oldVal);
    }
},
watch: {
    num: 'numChange'
}
// ...

方式二:
方式一使用 watch 時有一個特點,就是當值第一次綁定的時候,不會執行監聽函數,只有值發生改變纔會執行。如果我們需要在最初綁定值的時候也執行函數,則就需要用到immediate屬性。

// ...
watch: {
    num: {
        handler(newVal, oldVal) {
            console.log(newVal, oldVal)
        },
        // immediate表示在watch中首次綁定的時候,是否執行handler,值爲true則表示在watch中聲明的時候,就立即執行handler方法,值爲false,則和一般使用watch一樣,在數據發生變化的時候才執行handler。
        immediate: true
    }
}
// ...

watch監聽對象時,對象的屬性和屬性的屬性發生變化時,默認不會觸發對於這個對象的監聽。想要在對象的任意屬性(無論嵌套多複雜)改變時,執行對象的watch函數,需要開啓deep。

watch: {
    obj: {
        handler(newVal, oldVal) {
            // code
        },
        deep: true
    }
}
// ...

只是想監聽對象的某個屬性的變化:

watch: {
    'obj.key': {
        handler(newVal, oldVal) {
            // code
        }
    }
}
// ...

自定義指令

除了核心功能默認內置的指令 (v-model 和 v-show),Vue 也允許註冊自定義指令。注意,在 Vue2.0 中,代碼複用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對普通 DOM 元素進行底層操作,Vue提供了一種更簡單的方式-自定義指令,方便我們對DOM的操作。

自定義指令聲明之後的調用方式和使用方式和 Vue 提供的默認指令一樣。

一、自定義指令的小示例:

全局的自定義指令,在 main.js 中創建

Vue.directive('focus', {
  // 當被綁定的元素插入到 DOM 中時
  inserted: function (el) {
    // 聚焦元素
    el.focus();
  }
});

局部的自定義指令,創建在組件中:

export default {
    name: 'Home',
    directives: {
        focus: {
            // 指令的定義
            inserted: function (el) {
                el.focus()
            }
        }
    }
}

無論是局部的還是全局的,調用方式都相同,只是全局的指令可以被任意組件調用,局部的指令只能被當前組件調用:

<input v-focus type="text">

二、鉤子函數和其參數

一個指令定義對象可以提供如下幾個鉤子函數 (均爲可選):

  • bind:只調用一次,指令第一次綁定到元素時調用。在這裏可以進行一次性的初始化設置。
  • inserted:被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
  • update:所在組件的 VNode 更新時調用,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前後的值來忽略不必要的模板更新 (詳細的鉤子函數參數見下)。
  • componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新後調用。
  • unbind:只調用一次,指令與元素解綁時調用。

指令鉤子函數會被傳入以下參數:

  • el:指令所綁定的元素,可以用來直接操作 DOM 。
  • binding:一個對象,包含以下屬性:
    • name:指令名,不包括 v- 前綴。
    • value:指令的綁定值,例如:v-my-directive=“1 + 1” 中,綁定值爲 2。
    • oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
    • expression:字符串形式的指令表達式。例如 v-my-directive=“1 + 1” 中,表達式爲 “1 + 1”。
    • arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數爲 “foo”。
    • modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象爲 { foo: true, bar: true }。
  • vnode:Vue 編譯生成的虛擬節點。移步 VNode API 來了解更多詳情。
  • oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。

更加具體的示例:

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(', ');
    }
})

調用:

<template>
  <div class="test3" v-demo:foo.a.b="message"></div>
</template>

<script>
    export default {
        name: "Test3",
        data() {
            return {
                message: 'hello!'
            }
        }
    }
</script>

注:

  • 指令的參數可以是動態的。例如,在 v-mydirective:[argument]=“value” 中,argument 參數可以根據組件實例數據進行更新!這使得自定義指令可以在應用中被靈活使用。
  • 在很多時候,你可能想在 bind 和 update 時觸發相同行爲,而不關心其它的鉤子。比如這樣寫:
Vue.directive('color-swatch', function (el, binding) {
    el.style.backgroundColor = binding.value;
})
  • 如果指令需要多個值,可以傳入一個 JavaScript 對象字面量。記住,指令函數能夠接受所有合法的 JavaScript 表達式。
Vue.directive('demo', function (el, binding) {
    console.log(binding.value.color) // => "white"
    console.log(binding.value.text)  // => "hello!"
})
<div v-demo="{ color: 'white', text: 'hello!' }"></div>

練習: 封裝v-on指令

參考文檔
Vue中的computed
Vue中computed和methods中的區別
Vue中的watch詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章