天天看點

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

文章目錄

  • 5.指令學習
    • 5.7.計算屬性
    • 5.8.watch
  • 6.元件化
    • 6.1.全局元件
    • 6.2.元件的複用
    • 6.3.局部元件
    • 6.4.元件通信
      • 6.4.1.props(父向子傳遞)
      • 6.4.2.props驗證
      • 6.4.3.動态靜态傳遞
      • 6.4.4.子向父的通信:$emit
  • 7.路由vue-router
    • 7.1.場景模拟
      • 7.1.1.編寫父元件
      • 7.1.2.編寫登入及注冊元件
      • 7.1.3.在父元件中引用
      • 7.1.5.問題
    • 7.2.vue-router簡介和安裝
    • 7.3.快速入門

5.指令學習

5.7.計算屬性

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

但是如果表達式的内容很長,就會顯得不夠優雅,而且後期維護起來也不友善,例如下面的場景,我們有一個日期的資料,但是是毫秒值:

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

我們在頁面渲染,希望得到yyyy-MM-dd的樣式:

<h1>您的生日是:{{
    new Date(birthday).getFullYear() + '-'+ new Date(birthday).getMonth()+ '-' + new Date(birthday).getDay()
    }}
</h1>
           

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

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

var vm = new Vue({
    el:"#app",
    data:{
        birthday:1429032123201 // 毫秒值
    },
    computed:{
        birth(){// 計算屬性本質是一個方法,但是必須傳回結果
            const d = new Date(this.birthday);
            return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay();
        }
    }
})
           
  • 計算屬性本質就是方法,但是一定要傳回資料。然後頁面渲染時,可以把這個方法當成一個變量來使用。

頁面使用:

<div id="app">
       <h1>您的生日是:{{birth}} </h1>
    </div>
           

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

我們可以将同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果确實是完全相同的。然而,不同的是計算屬性是基于它們的依賴進行緩存的。計算屬性隻有在它的相關依賴發生改變時才會重新求值。這就意味着隻要

birthday

還沒有發生改變,多次通路

birthday

計算屬性會立即傳回之前的計算結果,而不必再次執行函數。

5.8.watch

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

示例:

<div id="app">
    <input type="text" v-model="message">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    var vm = new Vue({
        el:"#app",
        data:{
            message:""
        },
        watch:{
            message(newVal, oldVal){
                console.log(newVal, oldVal);
            }
        }
    })
</script>
           

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

6.元件化

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

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

在vue裡,所有的vue執行個體都是元件

6.1.全局元件

我們通過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必須是一個函數,不再是一個對象。

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

6.2.元件的複用

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

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

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

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

元件的data屬性必須是函數!

當我們定義這個

<counter>

元件時,它的data 并不是像之前直接提供一個對象:

data: {
  count: 0
}
           

取而代之的是,一個元件的 data 選項必須是一個函數,是以每個執行個體可以維護一份被傳回對象的獨立的拷貝:

data: function () {
  return {
    count: 0
  }
}
           

如果 Vue 沒有這條規則,點選一個按鈕就會影響到其它所有執行個體!

6.3.局部元件

一旦全局注冊,就意味着即便以後你不再使用這個元件,它依然會随着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執行個體中使用

6.4.元件通信

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

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router
  • 頁面首先分成了頂部導航、左側内容區、右側邊欄三部分
  • 左側内容區又分為上下兩個元件
  • 右側邊欄中又包含了3個子元件

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

6.4.1.props(父向子傳遞)

  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>
           

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

6.4.2.props驗證

我們定義一個子元件,并接收複雜資料:

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

當 prop 驗證失敗的時候,(開發環境建構版本的) Vue 将會産生一個控制台的警告。

我們在父元件中使用它:

<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: 'php'},
            {id:3, name: 'ios'},
        ]
    }
})
           

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

type類型,可以有:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

注意:子元件模闆有且隻有一個根标簽

6.4.3.動态靜态傳遞

給 prop 傳入一個靜态的值:

給 prop 傳入一個動态的值: (通過v-bind從資料模型中,擷取title的值)

靜态傳遞時,我們傳入的值都是字元串類型的,但實際上任何類型的值都可以傳給一個 props。

<!-- 即便 `42` 是靜态的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個JavaScript表達式而不是一個字元串。-->
<blog-post v-bind:likes="42"></blog-post>

<!-- 用一個變量進行動态指派。-->
<blog-post v-bind:likes="post.likes"></blog-post>
           

6.4.4.子向父的通信:$emit

來看這樣的一個案例:

<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進行加或減操作

我們嘗試運作,好像沒問題,點選按鈕試試:

在這裡插入圖檔描述

子元件接收到父元件屬性後,預設是不允許修改的。怎麼辦?

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

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()函數,用來調用父元件綁定的函數

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

7.路由vue-router

7.1.場景模拟

現在我們來實作這樣一個功能:

一個頁面,包含登入和注冊,點選不同按鈕,實作登入和注冊頁切換:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

7.1.1.編寫父元件

為了讓接下來的功能比較清晰,我們先建立一個檔案夾:src

然後建立一個HTML檔案,作為入口:index.html

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

然後編寫頁面的基本結構:

<div id="app">
    <span>登入</span>
    <span>注冊</span>
    <hr/>
    <div>
        登入頁/注冊頁
    </div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
    var vm = new Vue({
        el:"#app"
    })
</script>
           

樣式:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

7.1.2.編寫登入及注冊元件

接下來我們來實作登入元件,以前我們都是寫在一個檔案中,但是為了複用性,開發中都會把元件放入獨立的JS檔案中,我們建立一個user目錄以及login.js及register.js:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

編寫元件,這裡我們隻寫模闆,不寫功能。

login.js内容如下:

const loginForm = {
    template:'\
    <div>\
    <h2>登入頁</h2> \
    使用者名:<input type="text"><br/>\
    密碼:<input type="password"><br/>\
    </div>\
    '
}
           

register.js内容:

const registerForm = {
    template:'\
    <div>\
    <h2>注冊頁</h2> \
    用&ensp;戶&ensp;名:<input type="text"><br/>\
    密&emsp;&emsp;碼:<input type="password"><br/>\
    确認密碼:<input type="password"><br/>\
    </div>\
    '
}
           

7.1.3.在父元件中引用

<div id="app">
    <span>登入</span>
    <span>注冊</span>
    <hr/>
    <div>
        <!--<loginForm></loginForm>-->
        <!--
            疑問:為什麼不采用上面的寫法?
            由于html是大小寫不敏感的,如果采用上面的寫法,則被認為是<loginform></loginform>
            是以,如果是駝峰形式的元件,需要把駝峰轉化為“-”的形式
         -->
        <login-form></login-form>
        <register-form></register-form>
    </div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="user/login.js"></script>
<script src="user/register.js"></script>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app",
        components: {
            loginForm,
            registerForm
        }
    })
</script>
           

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

7.1.5.問題

我們期待的是,當點選登入或注冊按鈕,分别顯示登入頁或注冊頁,而不是一起顯示。

但是,如何才能動态加載元件,實作元件切換呢?

雖然使用原生的Html5和JS也能實作,但是官方推薦我們使用vue-router子產品。

7.2.vue-router簡介和安裝

使用vue-router和vue可以非常友善的實作 複雜單頁應用的動态路由功能。

官網:https://router.vuejs.org/zh-cn/

使用npm安裝:

npm install vue-router --save

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

在index.html中引入依賴:

7.3.快速入門

建立vue-router對象,并且指定路由規則:

// 建立VueRouter對象
const router = new VueRouter({
    routes:[ // 編寫路由規則
        {
            path:"/login", // 請求路徑,以“/”開頭
            component:loginForm // 元件名稱
        },
        {
            path:"/register",
            component:registerForm
        }
    ]
})
           
  • 建立VueRouter對象,并指定路由參數
  • routes:路由規則的數組,可以指定多個對象,每個對象是一條路由規則,包含以下屬性:
    • path:路由的路徑
    • component:元件名稱

在父元件中引入router對象:

var vm = new Vue({
    el:"#app",
    components:{// 引用登入和注冊元件
        loginForm,
        registerForm
    },
    router // 引用上面定義的router對象
})
           

頁面跳轉控制:

<div id="app">
    <!--router-link來指定跳轉的路徑-->
    <span><router-link to="/login">登入</router-link></span>
    <span><router-link to="/register">注冊</router-link></span>
    <hr/>
    <div>
        <!--vue-router的錨點-->
        <router-view></router-view>
    </div>
</div>
           
  • 通過

    <router-view>

    來指定一個錨點,當路由的路徑比對時,vue-router會自動把對應元件放到錨點位置進行渲染
  • 通過

    <router-link>

    指定一個跳轉連結,當點選時,會觸發vue-router的路由功能,路徑中的hash值會随之改變

效果:

前端架構——Vue(2)5.指令學習6.元件化7.路由vue-router

注意:單頁應用中,頁面的切換并不是頁面的跳轉。僅僅是位址最後的hash值變化。

事實上,我們總共就一個HTML:index.html