【編程鹿】學Vue.js這一篇就夠了「萬字學會|通俗易懂」上篇

1540394364528

vue概述

mvvm模式

image-20201001110548401
  • M:即Model,模型,包括數據和一些基本操作
  • V:即View,視圖,頁面渲染結果
  • VM:即View-Model,模型與視圖間的雙向操作(無需開發人員干涉)

在MVVM之前,開發人員從後端獲取需要的數據模型,然後要通過DOM操作Model渲染到View中。而後當用戶操作視圖,我們還需要通過DOM獲取View中的數據,然後同步到Model中。

而MVVM中的VM要做的事情就是把DOM操作完全封裝起來,開發人員不用再關心Model和View之間是如何互相影響的:

  • 只要我們Model發生了改變,View上自然就會表現出來。
  • 當用戶修改了View,Model中的數據也會跟着改變。

把開發人員從繁瑣的DOM操作中解放出來,把關注點放在如何操作Model上。

mvvm模式的優勢:

  1. 低耦合

視圖(View)可以獨立於Model變化和修改,一個ViewModel可以綁定到不同的"View"上,當View變化時Model可以不變,當Model變化時View也可以不變

  1. 可重用性

可以把一些視圖邏輯放在一個ViewModel裏面,讓多個View重用這段視圖邏輯代碼

  1. 獨立開發

開發人員可以專注於業務邏輯和數據的開發(ViewModel),設計人員可以專注於頁面設計。

而我們今天要學習的,就是一款MVVM模式的框架:Vue

快速入門

安裝vue

下載安裝

下載地址:https://github.com/vuejs/vue

可以下載2.5.16版本https://github.com/vuejs/vue/archive/v2.5.16.zip

下載解壓,得到vue.js文件。可以在頁面中直接通過script引入vue.js文件

使用cdn

或者也可以直接使用公共的CDN服務:

<!-- 開發環境版本,包含了用幫助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

或者:

<!-- 生產環境版本,優化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

Vue 入門案例(演示用,不做講解)

HTML模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h2>{{name}},非常酷!</h2>
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app"// el即element,要渲染的頁面元素
        data: {
            name"Vue測試"
        }
    });
</script>
</body>
</html>
  • 首先通過 new Vue()來創建 Vue實例
  • 然後構造函數接收一個對象,對象中有一些屬性:
    • name :這裏指定了一個 name屬性
    • el :是 element的縮寫,通過 id 選中要渲染的頁面元素,本例中是一個 div
    • data:數據,數據是一個對象,裏面有很多屬性,都可以渲染到視圖中
  • 頁面中的 h2 元素中,通過 {{name}} 的方式,來渲染剛剛定義的 name 屬性

更神奇的在於,當你修改name屬性時,頁面會跟着變化。

雙向綁定

對剛纔的案例進行簡單修改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="num">
    <h2>
      {{name}},非常酷!
      有{{num}}個酷炫功能!
    </h2>
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app"// el即element,要渲染的頁面元素
        data: {
            name"Vue測試",
            num1
        }
    });
</script>
</body>
</html>
  • data中添加了新的屬性: num
  • 在頁面中有一個 input 元素,通過 v-modelnum 進行綁定
  • 同時通過 {{num}} 在頁面輸出

可以觀察到,輸入框的變化引起了data中的num的變化,同時頁面輸出也跟着變化。

  • inputnum綁定, inputvalue值變化,影響到了 data中的 num
  • 頁面 {{num}} 與數據 num綁定,因此 num值變化,引起了頁面效果變化。

沒有任何dom操作,這就是雙向綁定的魅力。

事件處理

在頁面添加一個按鈕:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="num">
    <button v-on:click="num++">點我</button>
    <h2>
      {{name}},非常酷!
      有{{num}}個酷炫功能!
    </h2>
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app"// el即element,要渲染的頁面元素
        data: {
            name"Vue測試",
            num1
        }
    });
</script>
</body>
</html>

  • 這裏用 v-on 指令綁定點擊事件,而不是普通的 onclick ,然後直接操作 num
  • 普通 onclick是無法直接操作 num

Vue 實例

創建Vue實例

每個 Vue 應用都是通過用 Vue 函數創建一個新的 Vue 實例開始的:

var vm = new Vue({
 // 選項
})

在構造函數中傳入一個對象,並且在對象中聲明各種Vue需要的數據和方法,包括:

  • el
  • data
  • methods
  • … …等

接下來,一一介紹。

模板或元素

每個Vue實例都需要關聯一段Html模板,Vue會基於此模板進行視圖渲染;可以通過el屬性來指定。

例如一段html模板:

<div id="app">

</div>

然後創建Vue實例,關聯這個div

var vm = new Vue({
 el:"#app"
})

這樣,Vue就可以基於idappdiv元素作爲模板進行渲染了。在這個div範圍以外的部分是無法使用vue特性的。

數據

當Vue實例被創建時,它會嘗試獲取在data中定義的所有屬性,用於視圖的渲染,並且監視data中的屬性變化,當data發生改變,所有相關的視圖都將重新渲染,這就是“響應式“系統。

html:

<div id="app">
 <input type="text" v-model="name"/>
</div>

js:

var vm = new Vue({
  el:"#app",
  data:{
   name:"ZHANGSAN"
 }
})
  • name的變化會影響到 input 的值
  • input中輸入的值,也會導致 vm中的 name發生改變

方法

Vue實例中除了可以定義data屬性,也可以定義方法,並且在Vue的作用範圍內使用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button v-on:click="add">點我</button>
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app",
        data: {},
        methods: {
            addfunction ({
                console.log("點我了...233")
            }
        }
    });
</script>
</body>
</html>

生命週期鉤子函數

每個 Vue 實例在被創建時都要經過一系列的初始化過程 :創建實例,裝載模板,渲染模板等。Vue爲生命週期中的每個狀態都設置了鉤子函數(監聽函數)。每當Vue實例處於不同的生命週期時,對應的函數就會被觸發調用。

所有的生命週期鉤子自動綁定 this 上下文到實例中,因此你可以訪問數據,對屬性和方法進行運算。

20200710065238953

生命週期

每個 Vue 實例在被創建之前都要經過一系列的初始化過程

生命週期函數 含義
beforeCreate(vue對象創建前) 組件實例剛被創建,組件屬性計算之前,比如data屬性等
created(創建後) 模板編譯、掛載之前
mounted(載入後) 模板編譯、掛載之後
beforeUpdate(更新前) 組件更新之前
updated(更新後) 組件更新之後
beforeDestroy(銷燬前) 組件銷燬前調用
destroyed(銷燬後) 組件銷燬後調用

vm.$el :Vue 實例使用的根 DOM 元素

vm.$root :當前的 Vue 實例。

Vue在實例化的過程中,會調用這些生命週期的鉤子,給我們提供了執行自定義邏輯的機會。那麼,在這些vue鉤子中,vue實例到底執行了那些操作,我們先看下面執行的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs生命週期</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    {{message}}
</div>
<script type="text/javascript">
    var vm = new Vue({
        el"#app",
        data: {
            message'hello world'
        },
        beforeCreatefunction ({
            console.log(this);
            showData('創建vue實例前'this);
        },
        createdfunction ({
            showData('創建vue實例後'this);
        },
        beforeMountfunction ({
            showData('掛載到dom前'this);
        },
        mountedfunction ({
            showData('掛載到dom後'this);
        },
        beforeUpdatefunction ({
            showData('數據變化更新前'this);
        },
        updatedfunction ({
            showData('數據變化更新後'this);
        },
        beforeDestroyfunction ({
            showData('vue實例銷燬前'this);
        },
        destroyedfunction ({
            showData('vue實例銷燬後'this);
        }
    });

    function showData(process, obj{
        console.log(process);
        console.log('data 數據:' + obj.message)
        console.log('掛載的對象:')
        console.log(obj.$el)
    }
</script>
</body>
</html>

鉤子函數

例如:created代表在vue實例創建後;

可以在Vue中定義一個created函數,代表這個時期的構造函數:

創建示例html頁面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    {{msg}}
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            // 初始化爲空
            msg""
        },
        // 鉤子函數
        created() {
            // this 表示Vue實例
            this.msg = "hello vue.created";
            console.log(this)
        }
    });
</script>
</body>
</html>

this

可以看下在vue內部的this變量是誰,在created的時候,打印this

var app = new Vue({
        el:"#app",
        data:{
            msg""
        },
        //鉤子函數
        created() {
            //this表示vue實例
            this.msg = "hello vue. created.";
            console.log(this);
        }
    });

控制檯的輸出;總結:this 就是當前的Vue實例,在Vue對象內部,必須使用 this 才能訪問到Vue中定義的data內屬性、方法等。

應用場景

  1. 在beforeCreate生命週期函數運行時,可以添加loading動畫

  2. 在created生命週期函數運行時,可以結束loading動畫,還可以做一些初始化,實現函數自執行等操作

  3. 最經常使用的是mounted生命週期函數

可以發起後端數據請求,取回數據

可以接收頁面之間傳遞的參數

可以子組件向父組件傳遞參數等

指令

什麼是指令?

指令 (Directives) 是帶有 v- 前綴的特殊屬性。例如在入門案例中的v-model,代表雙向綁定。

插值表達式

花括號

格式:

{{表達式}}

說明:

  • 該表達式支持JS語法,可以調用js內置函數( 必須有返回值
  • 表達式必須有返回結果。例如 1 + 1,沒有結果的表達式不允許使用,如:var a = 1 + 1;
  • 可以直接獲取Vue實例中定義的數據或函數

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    {{msg}}
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app",
        data: {
            msg"hello vue."
        }
    });
</script>
</body>
</html>

插值閃爍

使用{{}}方式在網速較慢時會出現問題。在數據未加載完成時,頁面會顯示出原始的 {{}} ,加載完畢後才顯示正確數據,稱爲插值閃爍。解決辦法是通過v-text和v-html替換

v-text 和 v-html

使用v-textv-html指令來替代 {{}}

說明:

  • v-text:將數據輸出到元素內部,如果輸出的數據有HTML代碼,會作爲普通文本輸出
  • v-html:將數據輸出到元素內部,如果輸出的數據有HTML代碼,會被渲染

示例,改造原頁面內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <!--{{msg}}-->
    v-text:<span v-text="msg"></span><br/>
    v-html:<span v-html="msg"></span><br/>
</div>
<script type="text/javascript">
    var app = new Vue({
        el"#app",
        data: {
//            msg: "hello vue."
            msg"<h2>hello vue.</h2>"
        }
    });
</script>
</body>
</html>

並且不會出現插值閃爍,當沒有數據時,會顯示空白。

v-model

剛纔的v-textv-html可以看做是單向綁定,數據影響了視圖渲染,但是反過來就不行。

接下來學習的v-model是雙向綁定,視圖(View)和模型(Model)之間會互相影響。

既然是雙向綁定,一定是在視圖中可以修改數據,這樣就限定了視圖的元素類型。

目前v-model的可使用元素有:

  • input
  • select
  • textarea
  • checkbox
  • radio
  • components(Vue中的自定義組件)

基本上除了最後一項,其它都是表單的輸入項。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="checkbox" value="Java" v-model="language">Java<br/>
    <input type="checkbox" value="PHP" v-model="language">PHP<br/>
    <input type="checkbox" value="Swift" v-model="language">Swift<br/>
    <h2>
        你選擇了:{{language.join(",")}}
    </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            language: []
        }
    });
</script>
</body>
</html>
  • 多個 checkbox 對應一個 model時, model的類型是一個數組,單個 checkbox值是boolean類型
  • radio對應的值是input的value值
  • inputtextarea 默認對應的model是字符串
  • select 單選對應字符串,多選對應也是數組

v-on

基本用法

v-on指令用於給頁面元素綁定事件。

語法:

v-on:事件名="js片段或函數名"

簡寫語法:

@事件名="js片段或函數名"

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <!--直接寫js片段-->
    <button @click="num++">增加</button>
    <!--使用函數名,該函數必須要在vue實例中定義-->
    <button @click="decrement">減少</button>
    <h2>
        num={{num}}
    </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            num1
        },
        methods: {
            decrement() {
                this.num--;
            }
        }
    });
</script>
</body>
</html>

事件修飾符

在事件處理程序中調用 event.preventDefault()event.stopPropagation() 是非常常見的需求。

儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。

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

  • .stop :阻止事件冒泡
  • .prevent :阻止默認事件發生
  • .capture :使用事件捕獲模式
  • .self :只有元素自身觸發事件才執行。(冒泡或捕獲的都不執行)
  • .once :只執行一次

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <!--直接寫js片段-->
    <button @click="num++">增加</button>
    <!--使用函數名,該函數必須要在vue實例中定義-->
    <button @click="decrement">減少</button>
    <h2>
        num={{num}}
    </h2>
    <hr>
    事件冒泡測試:<br/>
    <div style="background-color: lightblue;width: 100px;height: 100px" @click="print('div被點擊了')">
        <button @click.stop="print('點擊了button')">點我試試</button>
    </div>
    <br>阻止默認事件:<br>
    <a href="http://www.baidu.com" @click.prevent="print('點擊了超鏈接')">百度</a>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            num1
        },
        methods: {
            decrement() {
                this.num--;
            },
            print(str) {
                console.log(str);
            }
        }
    });
</script>
</body>
</html>

v-for

遍歷數據渲染頁面是非常常用的需求,Vue中通過v-for指令來實現。

遍歷數組

語法:

v-for="item in items"
  • items:要遍歷的數組,需要在vue的data中定義好。
  • item:循環變量

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="user in users">
            {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            users: [
                {"name""小明""age"13"gender""男"},
                {"name""小紅""age"13"gender""女"},
                {"name""小綠""age"4"gender""男"}
            ]
        }
    });
</script>
</body>
</html>

數組角標

在遍歷的過程中,如果需要知道數組角標,可以指定第二個參數:

語法:

v-for="(item,index) in items"
  • items:要迭代的數組
  • item:迭代得到的數組元素別名
  • index:迭代到的當前元素索引,從0開始。

示例:

<div id="app">
    <ul>
        <li v-for="(user,index) in users">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>
</div>

遍歷對象

v-for除了可以迭代數組,也可以迭代對象。語法基本類似

語法:

v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
  • 1個參數時,得到的是對象的值
  • 2個參數時,第一個是值,第二個是鍵
  • 3個參數時,第三個是索引,從0開始 示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="user in users">
            {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>

    <hr>

    <ul>
        <li v-for="(user,index) in users">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>

    <hr>

    <ul>
        <!--1個參數時,得到的是對象的值-->
        <li v-for="value in person">
            {{value}}
        </li>
    </ul>
    <hr>
    <ul>
        <!--2個參數時,第一個是值,第二個是鍵-->
        <li v-for="(value,key ) in person">
            {{value}}--{{key}}
        </li>
    </ul>
    <hr>
    <ul>
        <!--3個參數時,第三個是索引,從0開始-->
        <li v-for="(value,key,index ) in person">
            {{value}}--{{key}} -- {{index}}
        </li>
    </ul>

</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            users: [
                {"name""小明""age"13"gender""男"},
                {"name""小紅""age"13"gender""女"},
                {"name""小綠""age"4"gender""男"}
            ],
            person: {"name""zhangsan""age"13"gender""男""address""中國"}
        }
    });
</script>
</body>
</html>

key

當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用“就地複用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。

如果使用key這個功能可以有效的提高渲染的效率;key一般使用在遍歷完後,又增、減集合元素的時候更有意義。

但是要實現這個功能,你需要給Vue一些提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要爲每項提供一個唯一 key 屬性。理想的 key 值是每項都有的且唯一的 id。也就是key是該的唯一標識。

示例:

<ul>
 <li v-for="(item,index) in items" :key="index"></li>
</ul>
  • 這裏使用了一個特殊語法: :key="" 後面會講到,它可以讓你讀取vue中的屬性,並賦值給key屬性
  • 這裏綁定的key是數組的索引,應該是唯一的

v-if 和 v-show

基本使用

v-if,顧名思義,條件判斷。當得到結果爲true時,所在的元素纔會被渲染。

語法:

v-if="布爾表達式"

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">

    <button @click="show = !show">點我</button>
    <h2 v-if="show">
        hello vuejs.
    </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            showtrue
        }
    });
</script>
</body>
</html>

與 v-for 結合

v-ifv-for出現在一起時,v-for優先級更高。也就是說,會先遍歷,再判斷條件。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">

    <button @click="show = !show">點我</button>
    <h2 v-if="show">
        hello vuejs.
    </h2>
    <hr>
    <ul>
        <li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            showtrue,
            users: [
                {"name""北京大學""age"8"gender""男"},
                {"name""清華大學""age"12"gender""女"},
                {"name""復旦大學""age"4"gender""男"},
                {"name""南開大學""age"2"gender""女"}
            ]
        }
    });
</script>
</body>
</html>

v-else

可以使用 v-else 指令來表示 v-if 的“else 塊”:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">

    <button @click="show = !show">點我</button>
    <h2 v-if="show">
        hello vuejs.
    </h2>
    <hr>
    <ul v-if="show">
        <li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
        <li v-else style="background-color: blue">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            showtrue,
            users: [
                {"name""北京大學""age"8"gender""男"},
                {"name""清華大學""age"12"gender""女"},
                {"name""復旦大學""age"4"gender""男"},
                {"name""南開大學""age"2"gender""女"}
            ]
        }
    });
</script>
</body>
</html>

v-else 元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,否則它將不會被識別。v-else-if ,顧名思義,充當 v-if 的“else-if 塊”,可以連續使用:

<div v-if="type === 'A'">
 A
</div>
<div v-else-if="type === 'B'">
 B
</
div>
<div v-else-if="type === 'C'">
 C
</div>

<div v-else>
 Not A/B/C
</div>

類似於 v-elsev-else-if 也必須緊跟在帶 v-if 或者 v-else-if 的元素之後。

v-show

另一個用於根據條件展示元素的選項是 v-show 指令。用法大致一樣:

<h1 v-show="ok">Hello!</h1>

不同的是帶有 v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS 屬性display

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click="show = !show">點我</button>
    <h2 v-if="show">
        hello vuejs.
    </h2>
    <hr>
    <ul v-if="show">
        <li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
        <li v-else style="background-color: blue">
            {{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
        </li>
    </ul>
    <hr>
    <h2 v-show="show">
        你好,世界!
    </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            showtrue,
            users: [
                {"name""北京大學""age"8"gender""男"},
                {"name""清華大學""age"12"gender""女"},
                {"name""復旦大學""age"4"gender""男"},
                {"name""南開大學""age"2"gender""女"}
            ]
        }
    });
</script>
</body>
</html>

v-bind

屬性上使用vue數據

看這樣一個案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
    <style type="text/css">
        div {
            width100px;
            height100px;
            color: white;
        }

        .red {
            background-color: red;
        }

        .blue {
            background-color: blue;
        }
    
</style>
</head>
<body>
<div id="app">
    <button @click="color='red'">紅色</button>
    <button @click="color='blue'">藍色</button>
    <div :class="">
        點擊按鈕改變背景顏色
    </div>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            color"red"
        }
    });
</script>
</body>
</html>

解讀:

  • 頁面有兩個按鈕,點擊時,會改變Vue實例中的color值,這個值與前面定義的CSS樣式一致。
  • 目前div的 class爲空,希望實現點擊按鈕後,div的class樣式會在.red和.blue之間切換

該如何實現?

大家可能會這麼想,既然color值會動態變化爲不同的class名稱,那麼我們直接把color注入到class屬性就好了,於是就這樣寫:

<div class="{{color}}"></div>

這樣寫是錯誤的!因爲插值表達式不能用在標籤的屬性中。

此時,Vue提供了一個新的指令來解決:v-bind,語法:

v-bind:屬性名="Vue中的變量"

例如,在這裏我們可以寫成:

<div v-bind:class="color"></div>

不過,v-bind太麻煩,因此可以省略,直接寫成:屬性名='屬性值' ,即:

<div :class="color"></div>

class屬性的特殊用法

上面雖然實現了顏色切換,但是語法卻比較囉嗦。

Vue對class屬性進行了特殊處理,可以接收數組或對象格式

對象語法:可以傳給 :class 一個對象,以動態地切換 class:

<div :class="{ red: true,blue:false }"></div>
  • 對象中,key是已經定義的class樣式的名稱,如本例中的:red 和 blue
  • 對象中,value是一個布爾值,如果爲true,則這個樣式會生效,如果爲false,則不生效。

之前的案例可以改寫成這樣:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>
    <style type="text/css">
        div {
            width100px;
            height100px;
            color: white;
        }

        .red {
            background-color: red;
        }

        .blue {
            background-color: blue;
        }
    
</style>
</head>
<body>
<div id="app">
    <button @click="color='red'">紅色</button>
    <button @click="color='blue'">藍色</button>
    <!--v-bind 簡寫爲 :-->
    <div :class="color">
        點擊按鈕改變背景顏色
    </div>
    <hr>
    <br>
    <button @click="bool=!bool">點我改變下面色塊的顏色</button>
    <div :class="{red:bool, blue:!bool}">
        點擊按鈕改變背景顏色
    </div>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            color"red",
            bool:true
        }
    });
</script>
</body>
</html>

首先class綁定的是一個對象:{red:bool, blue: !bool}

red和blue兩個樣式的值分別是bool和!bool,也就是說這兩個樣式的生效標記恰好相反,一個生效,另一個失效。

bool默認爲true,也就是說默認red生效,blue不生效

現在只需要一個按鈕即可,點擊時對bool取反,自然實現了樣式的切換

計算屬性

在插值表達式中使用js表達式是非常方便的,而且也經常被用到。

但是如果表達式的內容很長,就會顯得不夠優雅,而且後期維護起來也不方便,例如下面的場景,有一個日期的數據,但是是毫秒值:

data:{
 birthday:1429032123201 // 毫秒值
}

在頁面渲染,希望得到yyyy-MM-dd的樣式則需要如下處理:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>

</head>
<body>
<div id="app">
    <h2>
    你的生日是:
    {{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
 </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            birthday1429032123201
        }
    });
</script>
</body>
</html>

雖然能得到結果,但是非常麻煩。

Vue中提供了計算屬性,來替代複雜的表達式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>

</head>
<body>
<div id="app">
    <h2>
    你的生日是:
    {{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
    </h2>
    <hr>
    <h2>
    computed計算方式;你的生日爲:{{birth}}
    </h2>

</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            birthday1429032123201
        },
        computed: {
            birth() {
                const date = new Date(this.birthday);
                return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDay();
            }
        }
    });
</script>
</body>
</html>

計算屬性本質就是方法,但是一定要返回數據。然後頁面渲染時,可以把這個方法當成一個變量來使用。

過濾器

說明

官方說法:Vue.js 允許你自定義過濾器,可被用於一些常見的==文本格式化==。過濾器可以用在兩個地方:雙花括號插值和 v-bind 表達式 (後者從 2.1.0+ 開始支持)。過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符號指示。

通俗的來說:過濾器是對即將顯示的數據做進一步的篩選處理,然後進行顯示,值得注意的是過濾器並沒有改變原來的數據,只是在原數據的基礎上產生新的數據。

過濾器的種類:

  • 全局過濾器
  • 局部過濾器

過濾器的使用步驟

定義過濾器

  • 全局過濾器

    Vue.filter('過濾器名稱'function (value[,param1,...] {  
    //邏輯代碼
    })
  • 局部過濾器

    new Vue({       
      filters: {      
         '過濾器名稱'function (value[,param1,...] 
             // 邏輯代碼     
           } 
        }    
    })

應用過濾器

{{ 表達式 |  過濾器名字}}

過濾器不帶參數

案例1:日期格式化

<body>
    <div id="myDiv">
        {{birthday | dateFormat }}
    </div>
    <script src="../js/vue.js"></script>
    <script src="../js/moment.js"></script>  // 需要引入一個日期處理的一個工具類
    <script type="text/javascript">

       // 定義過濾器
        Vue.filter("dateFormat",function(value){
            return moment(value).format("YYYY-MM-DD HH:mm:ss");
        });

        const app = new Vue({
            el"#myDiv",
            data: {
                birthdaynew Date()
            }
        });
    
</script>
</body>

案例2:文本格式化

<body>
    <div id="myDiv">
        {{message | messageFormat}} <br/>
        {{message }}       // 值並沒有改變
    </div>
    <script src="../js/vue.js"></script>
    <script type="text/javascript">

        Vue.filter("messageFormat",function(value){
            return value.replace("很棒","優秀");
        });

        const app = new Vue({
            el"#myDiv",
            data: {
                message"HelloWorld,是一個很棒的青年"
            }
        });
    
</script>
</body>

過濾器帶參數

<body>
    <div id="myDiv">
        {{birthday | dateFormat }}     <br/>  // 不傳遞格式化標準就用完整寫法
        {{birthday | dateFormat("YYYY-MM-DD") }} // 傳入了格式化標準就用傳遞的寫法進行格式化
    </div>
    <script src="../js/vue.js"></script>
    <script src="../js/moment.js"></script>
    <script type="text/javascript">

        Vue.filter("dateFormat",function(value,pattern = "YYYY-MM-DD HH:mm:ss"){
            return moment(value).format(pattern);
        });

        const app = new Vue({
            el"#myDiv",
            data: {
                birthdaynew Date()
            }
        });
    
</script>
</body>

watch

監控

watch可以讓我們監控一個值的變化。從而做出相應的反應。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>

</head>
<body>
<div id="app">
    <input v-model="message">
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            message"hello vue"
        },
        watch: {
            message(newValue, oldValue) {
                console.log("新值:" + newValue + ";舊值:" + oldValue)
            }
        }
    });
</script>
</body>
</html>

深度監控

如果監控的是一個對象,需要進行深度監控,才能監控到對象中屬性的變化,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vuejs測試</title>
    <script src="node_modules/vue/dist/vue.js"></script>

</head>
<body>
<div id="app">
    <input type="text" v-model="message">
    <hr>
    <br>
    <input type="text" v-model="person.name"><br>
    <input type="text" v-model="person.age">
    <button @click="person.age++">+</button>
    <h2>
        姓名爲:{{person.name}};年齡爲:{{person.age}}
    </h2>
</div>
<script type="text/javascript">
    let app = new Vue({
        el"#app",
        data: {
            message"hello vue",
            person: {"name""zhangsan""age"12}
        },
        watch: {
            message(newValue, oldValue) {
                console.log("新值:" + newValue + ";舊值:" + oldValue)
            },
            person: {
                // 開啓深度監控,可以監控到對象屬性值的變化
                deeptrue,
                // 監控的處理辦法
                handler(obj) {
                    console.log("name = " + obj.name + ", age=" + obj.age);
                }
            }
        }
    });
</script>
</body>
</html>

變化:

以前定義監控時,person是一個函數,現在改成了對象,並且要指定兩個屬性:

  • deep:代表深度監控,不僅監控person變化,也監控person中屬性變化
  • handler:就是以前的監控處理函數

組件化

在大型應用開發的時候,頁面可以劃分成很多部分。往往不同的頁面,也會有相同的部分。例如可能會有相同的頭部導航。

但是如果每個頁面都獨自開發,這無疑增加了我們開發的成本。所以我們會把頁面的不同部分拆分成獨立的組件,然後在不同頁面就可以共享這些組件,避免重複開發。

全局組件

我們通過Vue的component方法來定義一個全局組件。

<div id="app">
    <!--使用定義好的全局組件-->
    <counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    // 定義全局組件,兩個參數:1,組件名稱。2,組件參數
    Vue.component("counter",{
        template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
        data(){
            return {
                count:0
            }
        }
    })
    var app = new Vue({
        el:"#app"
    })
</script>
  • 組件其實也是一個Vue實例,因此它在定義時也會接收:data、methods、生命週期函數等
  • 不同的是組件不會與頁面的元素綁定,否則就無法複用了,因此沒有el屬性。
  • 但是組件渲染需要html模板,所以增加了template屬性,值就是HTML模板
  • 全局組件定義完畢,任何vue實例都可以直接在HTML中通過組件名稱來使用組件了。
  • data必須是一個函數,不再是一個對象。

組件的複用

定義好的組件,可以任意複用多次:

<div id="app">
    <!--使用定義好的全局組件-->
    <counter></counter>
    <counter></counter>
    <counter></counter>
</div>

你會發現每個組件互不干擾,都有自己的count值。怎麼實現的?

組件的data屬性必須是函數

當我們定義這個counter組件時,它的data 並不是像這樣直接提供一個對象:

data: {
  count0
}

取而代之的是,一個組件的 data 選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝:

data: function ({
  return {
    count0
  }
}

如果 Vue 沒有這條規則,點擊一個按鈕就會影響到其它所有實例!

局部註冊

一旦全局註冊,就意味着即便以後你不再使用這個組件,它依然會隨着Vue的加載而加載。

因此,對於一些並不頻繁使用的組件,我們會採用局部註冊。

我們先在外部定義一個對象,結構與創建組件時傳遞的第二個參數一致:

const counter = {
    template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
    data(){
        return {
            count:0
        }
    }
};

然後在Vue中使用它:

var app = new Vue({
    el:"#app",
    components:{
        counter:counter // 將定義的對象註冊爲組件
    }
})
  • components就是當前vue對象子組件集合。
    • 其key就是子組件名稱
    • 其值就是組件對象的屬性
  • 效果與剛纔的全局註冊是類似的,不同的是,這個counter組件只能在當前的Vue實例中使用

組件通信

通常一個單頁應用會以一棵嵌套的組件樹的形式來組織:

image-20201001195026231
  • 頁面首先分成了頂部導航、左側內容區、右側邊欄三部分
  • 左側內容區又分爲上下兩個組件
  • 右側邊欄中又包含了3個子組件

各個組件之間以嵌套的關係組合在一起,那麼這個時候不可避免的會有組件間通信的需求。

父向子傳遞

  1. 父組件使用子組件時,自定義屬性(屬性名任意,屬性值爲要傳遞的數據)
  2. 子組件通過props接收父組件屬性

父組件使用子組件,並自定義了title屬性:

<div id="app">
    <h1>打個招呼:</h1>
    <!--使用子組件,同時傳遞title屬性-->
    <introduce title="父組件傳遞的消息"/>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    Vue.component("introduce",{
        // 直接使用props接收到的屬性來渲染頁面
        template:'<h1>{{title}}</h1>',
        props:['title'// 通過props來接收一個父組件傳遞的屬性
    })
    var app = new Vue({
        el:"#app"
    })
</script>

傳遞複雜數據

我們定義一個子組件,並接受複雜數據:

    const myList = {
        template'\
        <ul>\
         <li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
        </ul>\
        '
,
        props: {
            items: {
                typeArray,
                default: [],
                requiredtrue
            }
        }
    };
  • 這個子組件可以對 items 進行迭代,並輸出到頁面。
  • props:定義需要從父組件中接收的屬性
    • type:限定父組件傳遞來的必須是數組
    • default:默認值
    • required:是否必須
    • items:是要接收的屬性名稱

我們在父組件中使用它:

<div id="app">
    <h2>百知已開設如下課程:</h2>
    <!-- 使用子組件的同時,傳遞屬性,這裏使用了v-bind,指向了父組件自己的屬性lessons -->
    <my-list :items="lessons"/>
</div>
var app = new Vue({
    el:"#app",
    components:{
        myList /
/ 當key和value一樣時,可以只寫一個
    },
    data:{
        lessons:[
            {id:1, name: 'java'},
            {id:2, name: 'python'},
            {id:3, name: 'ui'},
        ]
    }
})

子向父的通信

來看這樣的一個案例:

<div id="app">
    <h2>num: {{num}}</h2>
    <!--使用子組件的時候,傳遞num到子組件中-->
    <counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    Vue.component("counter", {// 子組件,定義了兩個按鈕,點擊數字num會加或減
        template:'\
            <div>\
                <button @click="num++">加</button>  \
                <button @click="num--">減</button>  \
            </div>'
,
        props:['num']// count是從父組件獲取的。
    })
    var app = new Vue({
        el:"#app",
        data:{
            num:0
        }
    })
</script>
  • 子組件接收父組件的num屬性
  • 子組件定義點擊按鈕,點擊後對num進行加或減操作

我們嘗試運行,好像沒問題,點擊按鈕試試:

image-20201001200426862

子組件接收到父組件屬性後,默認是不允許修改的。怎麼辦?

既然只有父組件能修改,那麼加和減的操作一定是放在父組件:

var app = new Vue({
    el:"#app",
    data:{
        num:0
    },
    methods:{ // 父組件中定義操作num的方法
        increment(){
            this.num++;
        },
        decrement(){
            this.num--;
        }
    }
})

但是,點擊按鈕是在子組件中,那就是說需要子組件來調用父組件的函數,怎麼做?

我們可以通過v-on指令將父組件的函數綁定到子組件上:

<div id="app">
    <h2>num: {{num}}</h2>
    <counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>

在子組件中定義函數,函數的具體實現調用父組件的實現,並在子組件中調用這些函數。當子組件中按鈕被點擊時,調用綁定的函數:

  Vue.component("counter", {
            template:'\
                <div>\
                    <button @click="plus">加</button>  \
                    <button @click="reduce">減</button>  \
                </div>'
,
            props:['count'],
            methods:{
                plus(){
                    this.$emit("inc");
                },
                reduce(){
                    this.$emit("dec");
                }
            }
        })
  • vue提供了一個內置的this.$emit()函數,用來調用父組件綁定的函數

❤️ 帥氣的你又來看了我

如果你覺得這篇內容對你挺有有幫助的話:

  1. 點贊支持下吧,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)

  2. 歡迎在留言區與我分享你的想法,也歡迎你在留言區記錄你的思考過程。

  3. 覺得不錯的話,也可以關注 編程鹿 的個人公衆號看更多文章和講解視頻(感謝大家的鼓勵與支持🌹🌹🌹)


本文分享自微信公衆號 - 鹿小洋的Java筆記(lulaoshiJava)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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