不變的就是變化本身(Vue學習筆記one)

奮鬥是這麼個過程,當時不覺累,事後不會悔。走一段再回頭,會發現一個更強的自己,宛如新生。
你好,我是夢陽辰!期待與你相遇!
文章較長,切勿心急氣躁,否則將一事無成!

學習Vue前,我們需要先了解一些MVVM,因爲它很重要。
雖然沒有完全遵循 MVVM 模型,但是 Vue 的設計也受到了它的啓發。

01.MVVM

1.什麼是MVVM?
MVVM (Model-View-ViewModel)是一種軟件架構設計模式,由微軟WPF(用於替代
WinForm,以前就是用這個技術開發桌面應用程序的)和Silverlight(類似於Java Applet,簡單點說就是在瀏覽器上運行的 WPF)的架構師Ken Cooper和Ted Peters開發,是一種簡化用戶界面的事件驅動編程方式。由John Gossman(同樣也是WPF和Silverlight的架構師)於2005年在他的博客上發表。

MVVM源自於經典的MVC ( Model-View-Controller)模式。MVVM的核心是ViewModeA層,負責轉換 Model中的數據對象來讓數據變得更容易管理和使用,其作用如下:

該層向上與視圖層進行雙向數據綁定
向下與 Model層通過接口請求進行數據交互

在這裏插入圖片描述
MVVM已經相當成熟了,主要運用但不僅僅在網絡應用程序開發中。當下流行的MVVM框架有Vue.js , Angulars等。
02.爲什麼要使用MVVM

MVVM模式和MVC模式一樣,主要目的是分離視圖(View)和模型(Model),有幾大好處

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

可複用:你可以把一些視圖邏輯放在一個ViewModel裏面,讓很多View重用這段視圖邏輯。

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

可測試:界面素來是比較難於測試的,而現在測試可以針對ViewModel來寫。
3.MVVM的組成部分
在這裏插入圖片描述
View
View是視圖層,也就是用戶界面。前端主要由 HTNL和Css來構建,爲了更方便地展現ViewNodel或者Model層的數據,已經產生了各種各樣的前後端模板語言,比如FreeMarker.Thymeleaf等等,各大 MVVM框架如Vue.js,AngularJS,EJS等也都有自己用來構建用戶界面的內置模板語言。



Model
Model是指數據模型,泛指後端進行的各種業務邏輯處理和數據操控,主要圍繞數據庫系統展開。這裏的難點主要在於需要和前端約定統一的接口規則
ViewModel
ViewModel是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從後端獲取的 Model數據進行轉換處理,做二次封裝,以生成符合View層使用預期的視圖數據模型。


需要注意的是ViewModel所封裝出來的數據模型包括視圖的狀態和行爲兩部分,而Model層的數據模型是隻包含狀態的。

比如頁面的這一塊展示什麼,那一塊展示什麼這些都屬於視圖狀態(展示)。

頁面加載進來時發生什麼,點擊這一塊發生什麼,這一塊滾動時發生什麼這些都屬於視圖行爲(交互)視圖狀態和行爲都封裝在了ViewModel裏。這樣的封裝使得ViewModel可以完整地去描述View層`。由於實現了雙向綁定,ViewModel的內容會實時展現在View層,這是激動人心的,因爲前端開發者再也不必低效又麻煩地通過操縱DOM去更新視圖。
MVVM框架已經把最髒最累的一塊做好了,我們開發者只需要處理和維護ViewModel,更新數據視圖就會自動得到相應更新,真正實現事件驅動編程。

View層展現的不是Model層的數據,而是viewModel 的數據,由ViewModel負責與Model層交互,這就完全解耦了View層和Model層,這個解耦是至關重要的,它是前後端分離方案實施的重要一環。

02.Vue

Vue(讀音/vju/,類似於view)是一套用於構建用戶界面的漸進式框架,發佈於2014年2月。與其它大型框架不同的是,Vue被設計爲可以自底向上逐層應用。Vue的核心庫只關注視圖層,不僅易於上手,還便於與第三方庫(如: vue-router,vue-resource,vuex)或既有項目整合。

在這裏插入圖片描述

漸進式意味着你可以將Vue作爲你應用的一部分嵌入其中,帶來更豐富的交互體驗。

或者如果你希望將更多的業務邏輯使用Vue實現,那麼Vue的核心庫以及其生態系統。

比如Core+Vue-router+Vuex,也可以滿足你各種各樣的需求。

Vue.js(讀音 /vjuː/, 類似於 view) 是一套構建用戶界面的漸進式框架。

Vue 只關注視圖層, 採用自底向上增量開發的設計。

Vue 的目標是通過儘可能簡單的 API 實現響應的數據綁定和組合的視圖組件。

1.MVVM模式的實現者
Model:模型層,在這裏表示JavaScript對象

View:視圖層,在這裏表示 DOM(HTML操作的元素)

ViewModel:連接視圖和數據的中間件,Vue.js 就是 MVVM中的ViewModel層的實現者在MVVM架構中,是不允許數據和視圖直接通信的,只能通過ViewModel來通信,而ViewModel就是定義了一個 Observer觀察者

ViewModel能夠觀察到數據的變化,並對視圖對應的內容進行更新

ViewModel能夠監聽到視圖的變化,並能夠通知數據發生改變

至此,我們就明白了,Vue.js 就是一個MVVM的實現者,他的核心就是實現了DOM監聽與據綁定

Vue有很多特點和Web開發中常見的高級功能

解耦視圖和數據

可複用的組件

前端路由技術

狀態管理

虛擬DOM

2.爲什麼要使用Vue.js
輕量級,體積小是一個重要指標。Vue.js壓縮後有只有20多kb (Angular壓縮後56kb+,React壓縮後44kb+)

移動優先。更適合移動端,比如移動端的Touch事件·易上手,學習曲線平穩,文檔齊全
吸取了Angular(模塊化)和React(虛擬DOM)的長處,並擁有自己獨特的功能,如:計算屬性
開源,社區活躍度高

3.vue初體驗
Vue.js的安裝
方式一:
導入在線的cdn
對於製作原型或學習,你可以這樣使用最新版本:



<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

方式二:到官網下載文件:
https://vuejs.org/js/vue.js

方式三:NPM安裝(重點)
在用 Vue 構建大型應用時推薦使用 NPM 安裝[1]。NPM 能很好地和諸如 webpack 或 Browserify 模塊打包器配合使用。同時 Vue 也提供配套工具來開發單文件組件。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>helloVue</title>
    <!--1.導入vue.js-->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script></head><body><div id="app">
    {{message}}</div><script>
    var vm = new Vue({
        el: "#app",
        data: {
            message: "helloVue"
        }
    });</script></body></html>

爲了能夠更直觀的體驗Vue 帶來的數據綁定功能,我們需要在瀏覽器測試一番,操作流程如下:
1、在瀏覽器上運行第一個Vue應用程序,進入開發者工具

2、在控制檯輸入vm.message = ‘Hello World’,然後回車,你會發現瀏覽器中顯示的內容會直接變成Hello World
此時就可以在控制檯直接輸入vm.message來修改值,中間是可以省略data的,在這個操作中,我並沒有主動操作DOM,就讓頁面的內容發生了變化,這就是藉助了Vue的數據綁定功能實現的;MVVM模式中要求ViewModel層就是使用觀察者模式來實現數據的監聽與綁定,以做到數據與視圖的快速響應。

03.Vue基本語法

創建Vue實例傳入的選項(options)
目前掌握這些選項:
el
√類型: string | HTMLlement
√作用∶決定之後Vue實例會管理哪一個DOM。



data:
√類型:Object | Function(組件中data必須是一個函數)
√作用:Vue實例對應的數據對象。

methods:
√類型∶{ [key: string]: Function }
√作用∶定義屬於Vue的一些方法,可以在其他地方調用,也可以在指令中使用。

Mustache語法:雙括號表達式。
mustache語法中,不僅可以直接寫變量,也可以寫簡單的表達式。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    {{message}}
    {{message + name}}
    {{price*3}}</div><script>
    const app = new Vue({
            el:"#app",
            data:{
                message:"夢陽辰你好!",
                name:"夢陽辰",
                price:1000
            }
        });</script></body></html>

v-once

不隨數據的改變而改變

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    {{message}}
    <h3 v-once>{{name}}</h3><!--不會隨着數據的改變而改變--></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            name:"夢陽辰",
            price:1000
        }
    });</script></body></html>

v-html

服務器返回的是帶html標籤的數據,v-html將數據展示。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <h3 v-html="url"></h3><!----></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            url:'<a href="https://www.baidu.com">百度一下</a>'
        }
    });</script></body></html>

v-text

作用和Mustache比較類似。但mustache更加靈活。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <h3 v-html="url"></h3>
    <h2 v-text="message"></h2></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            url:'<a href="https://www.baidu.com">百度一下</a>'
        }
    });</script></body></html>

v-pre

將標籤中的內容原封不動的輸出。不進行任何解析。

v-cloak

解決js代碼執行慢而導致的內容閃爍問題。(後面採用虛擬dom,就不會存在這個問題)
因爲是先加載標籤,在運行js代碼。

在vue解析之前,div中有一個屬性v-cloak
在vue解析之後,div中沒有一個屬性v-cloak.

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        [v-cloak]{
            display: none;
        }
    </style></head><body><div id="app" v-cloak>
    <h2>{{message}}</h2></div><script>
    setTimeout(function () {
        const app = new Vue({
            el:"#app",
            data:{
                message:"夢陽辰你好!",
                url:'<a href="https://www.baidu.com">百度一下</a>'
            }
        });
    },1000)</script></body></html>

v-bind(綁定元素屬性)

注意我們不再和 HTML 直接交互了。一個 Vue 應用會將其掛載到一個 DOM 元素上 (對於這個例子是 #app) 然後對其進行完全控制。那個 HTML 是我們的入口,但其餘都會發生在新創建的 Vue 實例內部。

除了文本插值,我們還可以像這樣來綁定元素 attribute:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title></head><body><!--view層 模板--><div id="app2">
    <span v-bind:title="message">鼠標懸停幾秒鐘查看此處動態綁定的提示信息!</span></div></body><!--導入js--><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script>
    var vm = new Vue({
        el: "#app2",
        data: {
            message: "hello,vue"
        }
    })</script></html>

語法糖:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        [v-cloak]{
            display: none;
        }
    </style></head><body><div id="app" v-cloak>
    <img :src="imgUrl" alt="風景"></div><script>
    setTimeout(function () {
        const app = new Vue({
            el:"#app",
            data:{
                message:"夢陽辰你好!",
                imgUrl:"https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2853553659,1775735885&fm=26&gp=0.jpg"
            }
        });
    },1000)</script></body></html>

v-bind attribute 被稱爲指令。指令帶有前綴 v-,以表示它們是 Vue 提供的特殊 attribute。可能你已經猜到了,它們會在渲染的 DOM 上應用特殊的響應式行爲。在這裏,該指令的意思是:“將這個元素節點的 title attribute 和 Vue 實例的 message property 保持一致”。

如果你再次打開瀏覽器的 JavaScript 控制檯,輸入 app2.message = ‘新消息’,就會再一次看到這個綁定了 title attribute 的 HTML 已經進行了更新。

綁定classs

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .first{
            color: lawngreen;
        }
    </style></head><body><div id="app">
    <h3 :class="active">{{message}}</h3></div><script>
        const app = new Vue({
            el:"#app",
            data:{
                message:"夢陽辰你好!",
                active:'first'
            }
        });</script></body></html>

案例二(對象語法)

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .active{
            color: lawngreen;
        }
    </style></head><body><div id="app">
    <!--<h3 :class="active">{{message}}</h3>-->
    <h3 :class="{active:isActive,line:isLine}">{{message}}</h3><!--對象-->
    <h3 :class="getClasses()">{{message}}</h3><!--對象-->
    <button v-on:click="btn">改變顏色</button></div><script>
        const app = new Vue({
            el:"#app",
            data:{
                message:"夢陽辰你好!",
                isActive:true,
                isLine:false
            },
            methods:{
                btn:function () {
                    this.isActive=!this.isActive;
                },
                getClasses:function () {
                    return {active:this.isActive,line:this.isLine};
                }
            }
        });</script></body></html>

案例三(數組語法)瞭解

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .active{
            color: lawngreen;
        }
    </style></head><body><div id="app">

    <h3 class="title" :class="[active,line]">{{message}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            active:"active",
            line:"fasd"
        }
    });</script></body></html>

v-bind動態綁定樣式(style)
組件開發時常用。
如果不加單或雙引號會當作變量使用。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .active{
            color: lawngreen;
        }
    </style></head><body><div id="app"><!--<h3 class="title" :style="{key(屬性名):value(屬性值))}">{{message}}</h3>-->
    <h3 class="title" :style="{color:'red'}">{{message}}</h3>
    <h3 class="title" :style="{color:red}">{{message}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            red:"yellow"
        }
    });</script></body></html>

數組語法:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .active{
            color: lawngreen;
        }
    </style></head><body><div id="app">


    <h3 class="title" :style="[style1,style2]">{{message}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            style1:{fontSize:"50px"},
            style2:{color:"green"}
        }
    });</script></body></html>

在這裏插入圖片描述

條件(v-if)

控制切換一個元素是否顯示相當簡單:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title></head><body><!--view層 模板--><div id="app-3">
    <p v-if="seen">現在你看到我了</p></div></body><!--導入js--><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script>
    var app3 = new Vue({
        el: '#app-3',
        data: {
            seen: true
        }
    })</script></html>

繼續在控制檯輸入 app3.seen = false,你會發現之前顯示的消息消失了。

例二:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title></head><body><!--view層 模板--><div id="app">
    <h3 v-if="type==='A'">A</h3>
    <h3 v-else-if="type==='B'">B</h3>
    <h3 v-else>C</h3></div></body><!--導入js--><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script>
    var vm = new Vue({
        el: "#app",
        data: {
            type: "1"
        }
    })</script></html>

登錄切換:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <span v-if="isUser">
        <label for="userName" key="userName">用戶賬號</label>
        <input type="text" id="userName" placeholder="用戶賬號">
    </span>
    <span v-else>
        <label for="email" key="email">用戶郵箱</label>
        <input type="text" id="email" placeholder="用戶郵箱">
    </span>
    <button @click="isUser = !isUser">切換登錄</button></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            isUser:true
        }
    });</script></body></html>

vue在進行Dom渲染時,出於性能的考慮會進行復用已經存在的元素,會根據key的值決定是否複用。

v-show

和v-if作用相同。

區別:
當v-show爲false不顯示時:它的元素還是存在,只不過它的display屬性的值爲none。

而v-if爲false時:它的元素根本就不會存在在dom中。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
  <h3 v-if="isUser">你好,夢陽辰!</h3>
  <h3 v-show="isUser">你好,夢陽辰!</h3></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            isUser:false
        }
    });</script></body></html>

在這裏插入圖片描述

循環(v-for)

遍歷數組:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <ul>
        <li v-for="(item,index) in names">
            {{index+1}}.{{item}}
        </li>
    </ul></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            names:["夢陽辰","mengyangchen"]
        }
    });</script></body></html>

遍歷對象:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <ul>
        <li v-for="(value,key) in info">
            {{key}}.{{value}}
        </li>
    </ul>

    <ul>
        <li v-for="(value,key,index) in info">
            {{key}}:{{value}}---{{index}}
        </li>
    </ul></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            info:{
                name:"夢陽辰",
                age:20,
                height:171
            }
        }
    });</script></body></html>

v-for綁定key與不綁定key區別:
綁定key可以高效的更新虛擬DOM。

在中間插入元素:(diff算法)
綁定的key如果值沒有變化,可以複用。

但是沒有綁定的話,會將指定的值改爲插入的值,後續的值依次改(效率較低)

綁定的key的值應該和顯示的內容一致。

數組哪些方法是響應式的:
數據改變,界面內容也隨之改變。

push()後面添加  
pop()最後一個刪除
shift()第一個刪除
unshift()數組最前面添加元素
splice()刪除/插入/替換元素
splice(start,刪除元素的個數)
splice(start,0,你要插入的元素)
splice(start,替換元素的個數,新的替換值)
sort()排序
reverse()反轉
<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <ul>
        <li v-for="item in names" :key="item" >
            {{item}}
        </li>
    </ul>
    <button @click="way">操作數組</button></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            names:["夢陽辰","mengyangchen"]
        },
        methods:{
            way(){
                this.names.splice(0,1,"你好");
                /*Vue.set(this.names,0,"fafd");*/
            }
        }
    });</script></body></html>

點擊哪個值,哪個值就亮色

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script>
    <style>
        .active{
            color: lawngreen;
        }
    </style></head><body><div id="app">
    <ul>
        <li v-for="(item,index) in moives" :class="{active:currentIndex===index}" @click="way(index)">{{item}}</li>
    </ul></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            moives:["海王","海賊王","魁拔"],
            currentIndex:0
        },
        methods:{
            way(index){
                this.currentIndex=index;
            }
        }
    });</script></body></html>

v-on事件(處理用戶輸入)

爲了讓用戶和你的應用進行交互,我們可以用 v-on 指令添加一個事件監聽器,通過它調用在 Vue 實例中定義的方法:

<!DOCTYPE html><html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script></head><body><div id="app-5">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反轉消息</button></div><script>
    var app5 = new Vue({
        el: '#app-5',
        data: {
            message: 'Hello Vue.js!'
        },
        methods: {
            reverseMessage: function () {
                this.message = this.message.split('').reverse().join('')
            }
        }
    })</script></body></html>

計數器:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body>
    <div id="app">
        <h2>當前計數:{{counter}}</h2>
        <!--<button v-on:click="counter++">+</button>
        <button v-on:click="counter&#45;&#45;">-</button>-->

        <button v-on:click="way1">+</button>
        <button @click="way2">-</button>
    </div>
    <script>
        const app = new Vue({
            el:"#app",
            data:{
                counter:0,
            },
            methods:{
                way1:function () {
                    this.counter++;
                },
                way2:function () {
                    this.counter--;
                }
            }
        });
    </script></body></html>

@click爲v-on:click的簡寫。

methods屬性:定義方法。

注意在 reverseMessage 方法中,我們更新了應用的狀態,但沒有觸碰 DOM——所有的 DOM 操作都由 Vue 來處理,你編寫的代碼只需要關注邏輯層面即可。

當通過methods中定義方法,以供@click調用時,需要注意參數問題:情況一∶如果該方法不需要額外參數,那麼方法後的()可以不添加。

但是注意:如果方法本身中有一個參數,那麼會默認將原生事件event參數傳遞進去

情況二∶如果需要同時傳入某個參數,同時需要event時,可以通過**$event**傳入事件。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <button @click="way1">1</button>
    <button @click="way1('你好')">2</button>
    <button @click="way2('夢陽辰',$event)">3</button></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            counter:0,
        },
        methods:{
            way1:function (abc) {
                alert(abc);
            },
            way2:function (abc,event) {
                alert(event);
            }
        }
    });</script></body></html>

v-on的修飾符:
Vue提供了修飾符來幫助我們方便的處理一些事件

.stop-調用event.stopPropagation()阻止冒泡

.prevent -調用event.preventDefault()

.{(keyCode | kelyAlias}-只當事件是從特定鍵觸發時才觸發回調。

.native -監聽組件根元素的原生事件。
.once -只觸發一次回調。
<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
   <!--.stop-->
    <div @click="divClick">
        aaaaa        <button @click.stop="btnClick">按鈕</button>
    </div>

    <!--2.prevent修飾符-->
    <form action="baidu">
        <input type="submit" value="提交" @click.prevent="submitClick">
    </form>

    <!--監聽某個鍵帽-->
    <input type="text" @keyup.enter="keyUp"></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            counter:0,
        },
        methods:{
            divClick(){
                alert("div觸發")
            },
            btnClick(){
                alert("按鈕觸發")
            },
            submitClick(){
                alert("阻止提交")
            },
            keyUp(){
                alert("它按我了!")
            }
        }
    });</script></body></html>

Vue雙向綁定(v-model)

Vue 還提供了 v-model 指令,它能輕鬆實現表單輸入應用狀態之間的雙向綁定。

1. 什麼是雙向綁定
Vue.js是一個MVVM框架,即數據雙向綁定,即當數據發生變化的時候,視圖也就發生變化,當視圖發生變化的時候,數據也會跟着同步變化。這也算是Vue.js的精髓之處了。

值得注意的是,我們所說的數據雙向綁定,一定是對於UI控件來說的,非UI控件不會涉及到數據雙向綁定。單向數據綁定是使用狀態管理工具的前提。如果我們使用vuex,那麼數據流也是單項的,這時就會和雙向數據綁定有衝突。
2. 爲什麼要實現數據的雙向綁定
在Vue.js 中,如果使用vuex ,實際上數據還是單向的,之所以說是數據雙向綁定,這是用的UI控件來說,對於我們處理表單,Vue.js的雙向數據綁定用起來就特別舒服了。即兩者並不互斥,在全局性數據流使用單項,方便跟蹤;局部性數據流使用雙向,簡單易操作。

3. 在表單中使用雙向數據綁定
你可以用v-model指令在表單 、 及 元素上創建雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。儘管有些神奇,但v-model本質上不過是語法糖。它負責監聽戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理。

自己實現雙向綁定:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app"><!--<input type="text" v-model= "message">--><input type="text" :value="message" @input="valuechange"><!--<input type="text" :value="message" @input= "message =$event.target.value">-->
    <h2 :key="message">{{message}}</h2><!--key表示--></div><script>const app = new Vue({
    el:"#app",
    data: {
        message: "你好"
    },
     methods: {
           valuechange(event){
             this.message = event.target.value;

           }
     }})</script></body></html>

注意:v-model會忽略所有元素的value、checked、selected特性的初始值而總是將Vue實例的數據作爲數據來源,你應該通過JavaScript在組件的data選項中聲明。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script></head><body><div id="app-6">
    <p>{{ message }}</p>
    <input v-model="message"></div><script>
    var app6 = new Vue({
        el: '#app-6',
        data: {
            message: 'Hello Vue!'
        }
    });</script></body></html>

在這裏插入圖片描述
radio:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
        <input type="radio"  value="男" v-model="gender">男        <input type="radio" value="女" v-model="gender">女    <h3 :key="gender">你選擇的是:{{gender}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data: {
            message: "你好",
            gender:""
        }
    })</script></body></html>

select:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script></head><body><!--view層 模板--><div id="app">
    下拉框:    <select v-model="selected">
        <option value="" disabled>-請選擇-</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
    </select>
    <p>value:{{selected}}</p></div></body><script>
    var vm = new Vue({
        el: "#app",
        data: {
            selected: ""
        }
    })</script></html>

在select標籤上加multiple可以選擇多個值。
單選框和多選框:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
   <label for="agree">
       <input type="checkbox" id="agree" v-model="isAgree">同意協議   </label>
    <h3>你選擇的是:{{ isAgree}}</h3>
    <button :disabled=!isAgree>下一步</button>



    <!--多選框--><p>---------------------愛好-------------------</p>
        <input type="checkbox" value="籃球" v-model="hobbies">籃球        <input type="checkbox" value="足球" v-model="hobbies">足球        <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球    <h3 :key="hobbies">你的愛好是:{{hobbies}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data: {
            message: "你好",
            isAgree:false,
            hobbies:[]
        }
    })</script></body></html>

值綁定

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <label v-for="item in originHobbies" >
        <input type="checkbox" :value=item v-model="hobbies">{{item}}
    </label>
    <h3 :key="hobbies">你的愛好是:{{hobbies}}</h3></div><script>
    const app = new Vue({
        el:"#app",
        data: {
            message: "你好",
            isAgree:false,
            hobbies:[],
            originHobbies:['籃球','足球','乒乓球']
        }
    })</script></body></html>

v-model的修飾符

lazy   取消實時綁定,當按下回車鍵或失去焦點時進行數據的更新。

number  v-model默認給變量賦值的時候,賦值爲string類型。加上number修飾符後...

trim   去除左右兩邊的空格

04.組件化應用構建

組件系統是 Vue 的另一個重要概念,因爲它是一種抽象,允許我們使用小型、獨立和通常可複用的組件構建大型應用。仔細想想,幾乎任意類型的應用界面都可以抽象爲一個組件樹:
在這裏插入圖片描述
它提供了一種抽象,讓我們可以開發出一個個獨立可複用的小組件來構造我們的應用。

任何的應業用都會被抽象出一顆組件樹。

有了組件化的思想,我們在之後的開發中就要充分的利用它。儘可能的將頁面拆分成一個個小的、可複用的組件。

這樣讓我們的代碼更加方便組織和管理,並且擴展性也更強。

組件的使用分成三個步驟:
創建組件構造器

註冊組件

使用組件
在這裏插入圖片描述
原始方式(不推薦使用,建議使用語法糖)

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><cpn></cpn><div id="app">
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn></div><script>
    /*es6可以使用`來代替"和'
    * */
    //1.創建組件構造器對象
    const cpn = Vue.extend({
        template:`<div>
                     <h3>我是標題</h3>
                     <p>我是內容1</p>
                     <p>我是內容2</p>
                   </div>`
    })
    //2.註冊組件
    Vue.component('my-cpn',cpn)
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",

        }
    })</script></body></html>

在 Vue 裏,一個組件本質上是一個擁有預定義選項的一個 Vue 實例。在 Vue 中註冊組件很簡單:

語法糖:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script></head><body><ol id="app">
    <!-- 創建一個 todo-item 組件的實例 -->
    <todo-item></todo-item></ol></body><script>
    // 定義名爲 todo-item 的新組件
    Vue.component('todo-item', {
        template: '<li>這是個待辦項</li>'
    });

    var app = new Vue({
        el:"#app"
    });</script></html>

但是這樣會爲每個待辦項渲染同樣的文本,這看起來並不炫酷。我們應該能從父作用域將數據傳到子組件纔對。讓我們來修改一下組件的定義,使之能夠接受一個 prop:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script></head><body><ol id="app">
        <!--
          現在我們爲每個 todo-item 提供 todo 對象
          todo 對象是變量,即其內容可以是動態的。
          我們也需要爲每個組件提供一個“key”,稍後再
          作詳細解釋。        -->
        <todo-item
                v-for="item in groceryList"
                v-bind:todo="item"
                v-bind:key="item.id"
        ></todo-item>
    </ol></body><script>
    Vue.component('todo-item', {
        // todo-item 組件現在接受一個
        // "prop",類似於一個自定義 attribute。
        // 這個 prop 名爲 todo。
        props: ['todo'],
        template: '<li>{{ todo.text }}</li>'
    })


    var app7 = new Vue({
        el: '#app',
        data: {
            groceryList: [
                { id: 0, text: '蔬菜' },
                { id: 1, text: '奶酪' },
                { id: 2, text: '隨便其它什麼人喫的東西' }
            ]
        }
    })</script></html>

我們已經設法將應用分割成了兩個更小的單元。子單元通過 prop 接口與父單元進行了良好的解耦。我們現在可以進一步改進 <todo-item> 組件,提供更爲複雜的模板和邏輯,而不會影響到父單元。

全局組件和局部組件
可以在多個vue實例下使用。

在Vue實例中註冊就是局部組件:

 //1.創建組件構造器對象
    const cpn = Vue.extend({
        template:`<div>
                     <h3>我是標題</h3>
                     <p>我是內容1</p>
                     <p>我是內容2</p>
                   </div>`
    })
    
    var app = new Vue({
        el:"#app",
        components:{//mycpn就是使用組件時的標籤名mycpn:cpn}
    });

父組件和子組件的區別:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">

    <mycpn2></mycpn2></div><script>
    /*es6可以使用`來代替"和'
    * */
    //1.創建組件構造器對象
    //子
    const cpn1 = Vue.extend({
        template:`<div>
                     <h3>我是標題</h3>
                     <p>我是內容1</p>
                     <p>我是內容2</p>
                   </div>`
    })

    //父
    const cpn2 = Vue.extend({
        template:`<div>
                     <h3>我是頭部</h3>
                     <p>我是內容1</p>
                     <p>我是尾部</p>
                    <cpn></cpn>
                   </div>`
        ,
        components:{
            cpn:cpn1        }
    })
    //2.註冊組件
    Vue.component('my-cpn',cpn1);

    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",

        },
        components:{
            mycpn2:cpn2        }
    });</script></body></html>

組件模板分離的寫法

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><cpn></cpn><div id="app">
    <my-cpn></my-cpn></div><!--方法1--><!--<script type="text/x-template" id="cpn">
    <div>
        <h3>我是標題</h3>
        <p>我是內容1</p>
        <p>我是內容2</p>
    </div></script>--><!--方法二--><template id="cpn">
    <div>
        <h3>我是標題</h3>
        <p>我是內容1</p>
        <p>我是內容2</p>
    </div></template><script>
    /*es6可以使用`來代替"和'
    * */

    //2.註冊組件
    Vue.component('my-cpn',{
        template:id='#cpn'
    })
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
        }
    })</script></body></html>

組件可以訪問vue實例的數據嗎?

我們發現不能訪問,而且即使可以訪問,如果將所有的數據都放在Vue實例中,Vue實例就會變的非常臃腫。

結論: Vue組件應該有自己保存數據的地方。

組件對象也有一個data屬性(也可以有methods等屬性,下面我們有用到)只是這個data屬性必須是一個函數

而且這個函數返回一個對象,對象內部保存着數據

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><cpn></cpn><div id="app">
    <my-cpn></my-cpn></div><template id="cpn">
    <div>
        <h3>{{title}}</h3>
        <p>我是內容1</p>
        <p>我是內容2</p>
    </div></template><script>
    /*es6可以使用`來代替"和'
    * */

    //2.註冊組件
    Vue.component('my-cpn',{
        template:`#cpn`,
        data(){
            return{
                title:'有點東西!'
            }
        }
    })
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
        }
    })</script></body></html>

data屬性爲什麼必須是一個函數?
如果是一個屬性(對象),應用重複來利用這個主鍵,但是希望各個組件中的數據是互不影響的,data是屬性就無法做到,但是是函數的話就可以做到各個組件實例互不影響。

父子組件的通信

我們提到了子組件是不能引用父組件或者Vue實例的數據的。但是,在開發中,往往一些數據確實需要從上層傳遞到下層∶

比如在一個頁面中,我們從服務器請求到了很多的數據。

其中一部分數據,並非是我們整個頁面的大組件來展示的,而是需要下面的子組件進行展示。

這個時候,並不會讓子組件再次發送一次網絡請求,而是直接讓大組件(父組件)將數據傳遞給小組件(子組件)。

如何進行父子組件的通信呢?

通過props向子組件傳遞數據

通過事件向父組件發送消息

在這裏插入圖片描述

vue實例可以看作是根組件。
props的使用

父傳子

在組件中,使用選項props來聲明需要從父級接收到的數據。
props的值有兩種方式︰
方式一︰字符串數組,數組中的字符串就是傳遞時的名稱。

方式二∶對象,對象可以設置傳遞時的類型,也可以設置默認值等。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><cpn></cpn><div id="app">
    <mycpn v-bind:cmovies="movies" :cmessage="message"></mycpn></div><template id="cpn">
    <div>
        <p>我是內容1</p>
        <p>我是內容2</p>
        {{cmessage}}
        <ul>
            <li v-for="item in cmovies">{{item}}</li>
        </ul>
    </div></template><script>


    const cpn ={
        template:`#cpn`,
       // props:['cmovies','cmessage'],

        //類型限制
       /* props:{
            cmovies:Array,
            cmessage:String,
        },*/
        //提供默認值
       /* props:{
            cmessage:{
                type:String,
                default:"fasdf",
                required:true//加上後表示必傳的值
            }
        }*/
        //類型是對象或者數組時,默認值必須時一個函數
        props:{
            cmovies:{
                type:Array,
                default(){
                    return []
                }
            }

        }

       ,
        data(){
            return{
                title:'有點東西!'
            }
        },
        methods:{

        }
    }

    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            movies: ["海王","海賊王"]
        },
        components:{
            mycpn:cpn        }
    })</script></body></html>

對於命名問題:
標籤(如img)、屬性名(如class) 均會自動在瀏覽器轉化爲小寫,對大小寫不敏感,所以用駝峯命名對存在問題。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><cpn></cpn><div id="app">
    <mycpn v-bind:c-info="info" ></mycpn></div><template id="cpn">
    <div>
        <p>我是內容1</p>
        <p>我是內容2</p>
        <h3>{{cInfo}}</h3>
    </div></template><script>

    const cpn ={
        template:`#cpn`,
        //類型是對象或者數組時,默認值必須時一個函數
        props:{
            cInfo:{
                type:Object,
                default(){
                    return {}
                }
            }

        },
        data(){
            return{
                title:'有點東西!'
            }
        },
        methods:{

        }
    }

    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            info:{
                name:"夢陽辰",
                age:20,
                gender:"男"
            }
        },
        components:{
            mycpn:cpn        }
    })</script></body></html>

子傳父

當子組件需要向父組件傳遞數據時,就要用到自定義事件了。
我們之前學習的v-on不僅僅可以用於監聽DOM事件,也可以用於組件間的自定義事件。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <mycpn v-bind:c-info="info" v-on:item-click="cpnClick"></mycpn></div><template id="cpn">
    <div>
       <button v-for="item in categories" @click="btn(item)">
           {{item.name}}
       </button>
    </div></template><script>

    /*子組件*/
    const cpn ={
        template:`#cpn`,
        //類型是對象或者數組時,默認值必須時一個函數
        props:{
            cInfo:{
                type:Object,
                default(){
                    return {}
                }
            }
        },
        data(){
            return{
                categories:[
                    {id:"a",name:"熱門推薦"},
                    {id:"b",name:"筆記本電腦"},
                    {id:"c",name:"手機"},
                    {id:"d",name:"化妝品"},
                ]
            }
        },
        methods:{
            btn(item){
               /*將信息傳遞給父組件,自定義事件*/
                this.$emit('item-click',item);
            }
        }
    }

    /*父組件*/
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰你好!",
            info:{
                name:"夢陽辰",
                age:20,
                gender:"男"
            }
        },
        components:{
            mycpn:cpn        },
        methods:{
            cpnClick(item){
               console.log(item)
            }
        }
    })</script></body></html>

父子通信案例

父子互相影響數據

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body>
    <div id="app">
        <cpn :number1="num1" :number2="num2"
        @num1_change="num1_change" @num2_change="num2_change"
        ></cpn>
    </div><template id="cpn">
    <div>
        <h3 :key="number1">{{number1}}</h3>
        <h3 :key="temp_number1">{{temp_number1}}</h3>
        <input type="text" :value="temp_number1" @input="num1Input">
        <h3 :key="number2">{{number2}}</h3>
        <h3 :key="temp_number2">{{temp_number2}}</h3>
        <input type="text" :value="temp_number2" @input="num2Input">
    </div></template><script>
    //子組件

    //父組件
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰",
            num1:0,
            num2:1
        },
        methods:{
        num1_change(num){
            this.num1=parseFloat(num)
        },
        num2_change(num){
            this.num2=parseFloat(num)
        }
    },
    components:{
            cpn:{//子組件
                template:`#cpn`,
                props:{
                    number1:Number,
                    number2:Number                },
                data(){
                    return{
                         temp_number1:this.number1,
                         temp_number2:this.number2,
                    }
                },
                methods:{
                    num1Input(event){
                        this.temp_number1=event.target.value;
                        /*將信息傳遞給父組件,自定義事件*/
                        this.$emit("num1_change",this.temp_number1);
                    },
                    num2Input(event){
                        this.temp_number2=event.target.value;
                        /*將信息傳遞給父組件,自定義事件*/
                        this.$emit("num2_change",this.temp_number2)
                    }
                }
            }
        }
    })</script></body></html>

在這裏插入圖片描述

父訪問子

有時候我們需要父組件直接訪問子組件,子組件直接訪問父組件,或者是子組件訪問根組件。

父組件訪問子組件:使用$children $refs reference(引用)

子組件訪問父組件:使用$parent

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn ref="aa"></cpn>
    <button @click="btn">按鈕</button></div><template id="cpn">
    <div>
        <div>子組件</div>
    </div></template><script>
    //子組件

    //父組件
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰",
        },
        methods:{
            btn(){
                console.log(this.$children);
                this.$children[0].showMessage();
                //alert(this.$children[2].name);//下標會改變,所以儘量不要用下標拿屬性(這種適合遍歷拿所有的)

                /*$refs是對象類型,默認是一個空的對象,必須在組件上加ref屬性*/
                alert(this.$refs.aa.name);//用這種方式最佳
            }
        },
        components:{
            cpn:{//子組件
                template:`#cpn`,
                data(){
                    return{
                        name:"我是夢陽辰!"
                    }
                },
                methods:{
                    showMessage(){
                        alert("你好!")
                    }
                }
            }
        }
    })</script></body></html>

子訪問父

子組件訪問父組件:使用$parent

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <cpn></cpn></div><template id="cpn">
    <div>
        <div>子組件1</div>
        <cpn1></cpn1>
    </div></template><template id="cpn1">
    <div>
        <div>子組件2</div>
        <button @click="btn">按鈕</button>
    </div></template><script>
    //子組件

    //父組件
    const app = new Vue({
        el:"#app",
        data:{
            message:"夢陽辰",
        },
        components:{
            cpn:{//子組件
                template:`#cpn`,
               /*第二個子組件,這裏不建議嵌套,只爲練習使用(因爲耦合度高,複用性差)*/
                data(){
                    return{
                        name:"我是夢陽辰1"
                    }
                },
                components: {
                    cpn1:{
                        template: `#cpn1`,
                        methods:{
                            btn(){
                                //訪問父組件
                                console.log(this.$parent)
                                alert(this.$parent.name)
                                /*訪問根組件*/
                                console.log(this.$root)
                            }
                        }
                    }
                }
            }
        }
    })</script></body></html>

05.計算屬性

計算屬性的重點突出在屬性兩個字上(屬性是名詞),首先它是個屬性其次這個屬性有計算的能力(計算是動詞),這裏的計算就是個函數;簡單點說,它就是一個能夠將計算結果緩存起來的屬性(將行爲轉化成了靜態的屬性),僅此而已;可以想象爲緩存!

模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}</div>

在這個地方,模板不再是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裏是想要顯示變量 message 的翻轉字符串。當你想要在模板中的多處包含此翻轉字符串時,就會更加難以處理。

所以,對於任何複雜邏輯,你都應當使用計算屬性。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title></head><body><!--view層 模板--><div id="app">
    <div>currentTime1: {{currentTime1()}}</div>
    <div>currentTime2: {{currentTime2}}</div></div></body><!--導入js--><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script>
    var vm = new Vue({
        el: "#app",
        data: {
            message: "hello,world!"
        },
        methods: {
            currentTime1: function () {
                return Date.now(); // 返回一個時間戳
            }
        },
        computed: {
            //計算屬性:methods,computed 方法名不能重名,重名字後,只會調用methods的方法
            currentTime2: function () {
                this.message;
                // 返回一個時間戳
                return Date.now();
            }
        }
    })</script></html>

結論:
調用方法時,每次都需要進行計算,既然有計算過程則必定產生系統開銷,那如果這個結果是不經常變化的呢?此時就可以考慮將這個結果緩存起來,採用計算屬性可以很方便的做到這一點,計算屬性的主要特性就是爲了將不經常變化的計算結果進行緩存,以節約我們的系統開銷。

06.Axios異步通信

Vue的生命週期

實例生命週期鉤子
每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做生命週期鉤子的函數,這給了用戶在不同階段添加自己的代碼的機會。

比如 created 鉤子可以用來在一個實例被創建之後執行代碼:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 實例
    console.log('a is: ' + this.a)
  }})// => "a is: 1"

也有一些其它的鉤子,在實例生命週期的不同階段被調用,如 mounted、updated 和 destroyed。生命週期鉤子的 this 上下文指向調用它的 Vue 實例。

不要在選項 property 或回調上使用箭頭函數,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因爲箭頭函數並沒有 this,this 會作爲變量一直向上級詞法作用域查找,直至找到爲止,經常導致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之類的錯誤。

生命週期圖示
Vue 實例有一個完整的生命週期,也就是從開始創建、初始化數據、編譯模板、掛載DOM、渲染→更新→渲染、卸載等一系列過程,我們稱這是Vue的生命週期。通俗說就是Vue 實例從創建到銷燬的過程,就是生命週期。

在Vue的整個生命週期中,它提供了一系列的事件,可以讓我們在事件觸發時註冊JS方法,可以讓我們用自己註冊的JS方法控制整個大局,在這些事件響應方法中的this直接指向的是Vue 的實例。
在這裏插入圖片描述

1. 什麼是Axios
Axios是一個開源的可以用在瀏覽器端和NodeJS 的異步通信框架,它的主要作用就是實現AJAX異步通信,其功能特點如下:

從瀏覽器中創建XMLHttpRequests

node.js創建http請求

支持Promise API [JS中鏈式編程]

攔截請求和響應

轉換請求數據和響應數據

取消請求

自動轉換JSON數據

客戶端支持防禦XSRF (跨站請求僞造)

GitHub: https://github.com/ axios/axios
中文文檔: http://www.axios-js.com/

cdn:

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>

2. 爲什麼要使用Axios
由於Vue.js是一個視圖層框架且作者(尤雨溪) 嚴格準守SoC (關注度分離原則),所以Vue.js並不包含Ajax的通信功能,爲了解決通信問題,作者單獨開發了一個名爲vue-resource的插件,不過在進入2.0 版本以後停止了對該插件的維護並推薦了Axios 框架。少用jQuery,因爲它操作Dom太頻繁 !

測試:
模擬後臺返回的json數據:

{
  "name": "mengyangchen",
  "age": "20",
  "sex": "男",
  "url":"https://www.baidu.com",
  "address": {
    "street": "文苑路",
    "city": "南京",
    "country": "中國"
  },
  "links": [
    {
      "name": "bilibili",
      "url": "https://www.bilibili.com"
    },
    {
      "name": "baidu",
      "url": "https://www.baidu.com"
    },
    {
      "name": "cqh video",
      "url": "https://www.4399.com"
    }
  ]}

vue

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title></head><body><!--view層 模板--><div id="vue">
    <div>{{info.name}}</div>
    <div>{{info.address.street}}</div>
    <a v-bind:href="info.url">點我進入</a></div></body><!--1.導入vue.js--><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><!--導入axios--><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script><script>

    var vm = new Vue({
        el: "#vue",
        data: {
            items: ['Java','Python','Php']
        },
        //data:vm的屬性
        //data():vm方法
        data(){
            return{
                //請求的返回參數,必須和json字符串一樣
                info:{
                    name: null,
                    age: null,
                    sex: null,
                    url: null,
                    address: {
                        street: null,
                        city: null,
                        country: null
                    }
                }
            }
        },
        //鉤子函數,鏈式編程,ES6新特性
        mounted(){
            axios.get("../res/package.json").then(res => (this.info=res.data))
        }
    })</script></html>

07.書籍購物車案例

首頁

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="style.css" >
    <script src="../res/js/vue.js"></script></head><body><div id="app">
    <div v-if="books.length">
        <table>
            <thead>
            <tr>
                <th></th>
                <th>書籍名稱</th>
                <th>出版日期</th>
                <th>價格</th>
                <th>購買數量</th>
                <th>操作</th>
            </tr>
            </thead>

            <tbody>
            <tr v-for="(item,index) in books">
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.date}}</td>
                <td>{{item.price | showPrice}}</td>
                <td>
                    <button @click="way1(index)">+</button>
                    {{item.count}}
                    <button @click="way2(index)" v-bind:disabled="item.count<=1">-</button>

                </td>
                <td><button @click="remove(index)">移除</button></td>
            </tr>
            </tbody>
        </table>
        <h3>總價爲:{{totalPrice | showPrice}}</h3>
    </div>
   <h3 v-else>購物車爲空!</h3></div><script src="main.js"></script></body></html>

js代碼

const app = new Vue({
    el:"#app",
    data:{
        books:[
            {id:23,name:"算法第四版",date:"1999-9-26",price:198,count:5},
            {id:24,name:"計算機基礎",date:"1999-9-26",price:198,count:5},
            {id:25,name:"Java核心卷",date:"1999-9-26",price:198,count:5},
            {id:26,name:"深入理解計算機原理",date:"1999-9-26",price:198,count:5}
        ]
    },
    methods:{
        way1(index){
            this.books[index].count++;
        },
        way2(index){
            this.books[index].count--;
        },
        remove(index){
            this.books.splice(index,1)
        }
    },
    /*過濾器*/
    filters:{
        showPrice(price){
            return '¥'+price.toFixed(2);
        }
    },
    computed:{
        totalPrice(){
            let totalPrice=0;
            for(let i=0;i<this.books.length;i++){
                totalPrice+=this.books[i].price*this.books[i].count;
            }
            return totalPrice;
        }

    }});

css代碼

table {
    border: 1px solid #e9e9e9;
    border-collapse: collapse;
    border-spacing: 0;}th,td {
    padding: 8px 16px;
    border: 1px solid #e9e9e9;
    text-align: left;}th {
    background-color: #f7f7f7;
    color: #5c6b77;
    font-weight: 600;}
for  of直接取出數組的內容
i即代表數組中的每一項。
for(let i of this.books){
}

js的高階函數

filter/map/reduce

filter中的回調函數:必須返回一個boolean值
當返回true時,函數內部會自動將這次回調的n假如到新的數組中。如果爲false,函數內部就會過濾掉這次的n。
<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../res/js/vue.js"></script></head><body><div id="app"></div><script>
    const app = new Vue({
        el:"#app",
        data:{
            message:"你好,夢陽辰!",
            isTrue:1
        }
    });
    const nums=[1,344,5,64,45,78];
    /*filter函數的使用*/
    let newNums = nums.filter(function (n) {
        return n < 100
    })
    console.log(newNums)

    /*map函數的使用,對原數組進行操作*/
    let newNums2 = nums.map(function (n) {
        return n*2;
    })
    alert(newNums2)

    /*reduce函數,對數組中所有的內容進行彙總*/
    let newNums3 = nums.reduce(function (preValue,n) {
        return preValue+n;
    },0)
    alert(newNums3)</script></body></html>

簡潔的寫法(箭頭函數):

let total = nums.filter(n => n <100).map(n=>n*2).reduce((pre,n)=>pre+n);

If you find a path with no obstacles, it probably doesn’t lead anywhere.

在這裏插入圖片描述


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