Vue.js學(xué)習(xí)總結(jié)

一简肴、什么是Vue.js

1. vue是一種數(shù)據(jù)驅(qū)動的前端框架

this.msg="我愛你"贮缅,通過改變數(shù)據(jù)榨咐,然后自動渲染到綁定的DOM節(jié)點(diǎn)上

2. jQuery就是一種結(jié)構(gòu)驅(qū)動的前端框架

$(#app).text('你真好'),先獲取結(jié)構(gòu),然后在修改數(shù)據(jù)來更新結(jié)構(gòu)

二谴供、搭建環(huán)境

首先保證你的電腦上有node和npm块茁,版本越新越好

  1. npm install -g vue-cli
    全局安裝腳手架工具,安裝的時候可以指定版本
  2. vue init webpack myProject
    用webpack工具初始化vue項目桂肌,myProject是項目名稱数焊,可以自己命名
  3. cd myProject
    進(jìn)入創(chuàng)建的vue項目目錄
  4. npm install
    安裝項目所需要的依賴,也就是安裝一些必要的插件
  5. npm run dev
    開始運(yùn)行項目

三崎场、Vue核心知識點(diǎn)

1. Vue實例

1.1

vue實例被創(chuàng)建時佩耳,它會自動的將data中的屬性,methods中的方法綁定到Vue實例對象上,也就是Vue實例代理了data對象上的所有屬性
調(diào)用時: vm.msg = "hello world"

1.2

data中存在的屬性才是響應(yīng)式的谭跨,新添加的不算
比如在瀏覽器端添加: vm.b = 1,那么b的變化不會對頁面進(jìn)行渲染

1.3

Object.freeze(),會阻止修改現(xiàn)有的屬性干厚,響應(yīng)系統(tǒng)無法再追蹤變化
var data = {msg: 1} ; Object.freeze(data)

1.4

Vue實例還暴露了一些有用的實例屬性和方法,有前綴饺蚊,_,Vue實例無法代理這些屬性萍诱,為了與用戶自定義的屬性區(qū)分開 `vm.el vm.$data vm.$props……

3. Vue模板語法

2.1 動態(tài)參數(shù)

<a v-bind:[attrName] = 'url'></a>
如果data中有一個屬性attrName值為 href,那么這個綁定v-bind:href = 'url'
v-on:[eventName] = 'doSomething'></a>
同樣,在data中有eventName的值為focus函數(shù)時污呼,v-on:focus = 'doSomething'

注意:
2.1.1. 動態(tài)參數(shù)值是字符串類型裕坊,值為null,用于解除綁定燕酷,其他任何非字符串類型的值都會觸發(fā)一個警告
2.1.2 <a v-bind:['foo' + bar] = 'value'></a>
無效,要使用沒有空格和引號的表達(dá)式籍凝,或者用計算屬性代替

2.2 修飾符

.prevent告訴v-on指令對于觸發(fā)的事件調(diào)用event.preventDefault()
<form v-on:submit.prevent = 'onSubmit'></form>

3. 計算屬性周瞎、方法和偵聽器

3.1 計算屬性

計算屬性有緩存,依賴改變才會重新計算

<div id="app">    
       {{fullName}}
        {{age}}
 </div>
<script>
var app = new Vue({
            el: '#app',
            data: {
                firstName: 'Dell',
                lastName: 'Lee',
                age: 28
            },
            computed: {
                //這個計算屬性依賴于this.firstName this.firstName這兩個變量饵蒂,只要他們不發(fā)生
               //改變声诸,那么這個計算屬性就不會進(jìn)行重新計算
              //vm.age = 27,頁面重新渲染,但計算屬性不會重新計算
              //vm.firstName = 'Mike',會重新計算
               fullName () {
                  console.log('打印了一次')
                  return this.firstName + ' ' + this.firstName
              }
           }
</script>            

3.1.1 計算屬性中的 getter和setter

      computed: {
                fullName: {
                    //自動執(zhí)行退盯,獲取 fullName 的值
                    //并且 get 依賴的變量發(fā)生改變時彼乌,get就會重新進(jìn)行計算
                    get () {
                        return this.firstName + " " + this.lastName 
                    },
                    //當(dāng)重新設(shè)置fullName的值的時候,set函數(shù)就會執(zhí)行 vm.fullName = 'Mike Wang'
                    //value就是 vm.fullName = 'Mike Wang'
                    //set函數(shù)里渊迁,重新設(shè)置 fullName 改變了firstName lastName慰照,觸發(fā)了get 進(jìn)行計算
                    set (value) {
                        var arr = value.split(' ')
                        this.firstName = arr[0]
                        this.lastName = arr[1]
                    }
                }
            }

3.2 方法

            methods: {
                //沒有緩存,只要頁面上有內(nèi)容變動琉朽,就會執(zhí)行
                fullName () {
                    console.log('打印了一次')
                    return this.firstName + ' ' + this.lastName 
                }
            },

3.3 偵聽器

            watch: {
                //有緩存毒租,只有監(jiān)聽的屬性變量發(fā)生改變,才會執(zhí)行函數(shù)內(nèi)的內(nèi)容
                firstName () {
                    console.log('打印了一次')
                    return this.fullName =  this.firstName + ' ' + this.lastName 
                },
                lastName () {
                    console.log('打印了一次')
                    return this.fullName =  this.firstName + ' ' + this.lastName
                }
            },

注意: 計算屬性和偵聽器

  • 計算屬性的鍵名是計算出來的箱叁,所以鍵名是一個新的名稱墅垮,在data和props中都是不存在的,計算屬性一般可以監(jiān)聽多個值的變化
  • watch偵聽器就是已有值發(fā)生變化的時候執(zhí)行的操作,所以偵聽器的鍵名是data或者props中已經(jīng)存在的

4.class與style的綁定

4.1 class的綁定

<style>
        .divStyle{
            background-color: red;
            widows: 100px;
            height: 100px;
        }
        .borderStyle{
            border: 10px solid black;
        }
        .btnBackground{
            background: red;
        }
        .active{
            background-color: yellow;
            widows: 100px;
            height: 100px;
        }
        .error{
            border: 10px solid red;
        }
    </style>
<div id="app">
        綁定class對象語法:對象鍵是類名耕漱,值是布爾值 <br>
        意思就是isActive是true算色,那么就綁定類名為divStyle的這個類
        <div v-bind:class= '{divStyle:isActive,borderStyle:isBorder}'>1223454</div>
        <button v-bind:class = '{btnBackground:isBackground}' v-on:click='changeColor'>哈哈,快點(diǎn)我</button>
        綁定class數(shù)組語法:數(shù)組中的成員直接對應(yīng)類名
        意思是綁定的類由 activeClass 這個變量來決定孤个,若為空剃允,就綁定空,若是active齐鲤,就綁定類名為active的這個類
        <div v-bind:class='[activeClass,errorClass]'>hhhhhh</div>
    
    </div>
<script>
    
        var app = new Vue({
            el:'#app',
            data:{
                isActive: true,
                isBorder: true,
                isBackground: true,
                activeClass: 'active',
                errorClass: 'error'
            },
            methods:{
                changeColor(){
                    this.isBackground = !this.isBackground
                }
            }
        })
    </script>

4.2 style的綁定

    綁定內(nèi)聯(lián)樣式:鍵代表的是style的屬性(color,size屬性等)斥废,值就是屬性值啦
    切記: 在vue中,只要是大寫字母给郊,就會轉(zhuǎn)變?yōu)? -小寫
    fontSize ----> font-size
    <div id="app">
        <div v-bind:style="{'color':color,'fontSize':fontSize}">你丫真傻啊</div>
    </div>
    <script src="https://cdn.bootcss.com/vue/2.6.6/vue.min.js"></script>
    <script>
    
        var app = new Vue({
            el:'#app',
            data:{
              color:'red',
              fontSize: '20px'
            },
           
        })
    </script>

5. 條件渲染

5.1 v-if

v-if綁定的變量若為true牡肉,則該元素就會出現(xiàn)在DOM中,并且在頁面中渲染出來淆九;若為false统锤,則將該元素從DOM中移除

5.1.1 v-if使用

<div id="app">
       <div v-if='show'>{{msg}}</div>
    </div>
   <script>
       var app = new Vue({
           el: '#app',
           data:{ 
              show: false,
              msg: 'hello world' 
           },
           methods: {

            }
        })
   </script>

5.1.2 v-if v-else-if v-else的使用

<div id="app">
       v-if v-else-if v-else一定要連在一起寫,中間不能有其他標(biāo)簽分隔開
       <div v-if='this.show === "a"'>this is A</div>
       <div v-else-if='this.show === "b"'>this is B</div>
       <div v-else>this is others</div>
    </div>
   <script>
       var app = new Vue({
           el: '#app',
           data:{ 
              show: 'a',
            //   msg: 'hello world' 
           },
           methods: {

            }
        })
   </script>

5.1.3 key值的使用

Vue 會盡可能高效地渲染元素炭庙,通常會復(fù)用已有元素而不是從頭開始渲染饲窿,所以如果是完全一樣的文本框,vue會完全借用上一個文本框焕蹄,如果不想復(fù)用逾雄,可用不同的key值區(qū)分。

注意:元素復(fù)用并不局限與文本框,其他元素都會復(fù)用的鸦泳,這會極大的提高Vue的渲染效率

<div id="app">
        <div v-if='show'>
            用戶名:<input type="text" key="userName">
        </div>
        <div v-else>
            lalala: <input type="text" key="userName"> //會復(fù)用
            密碼名:<input type="text" key="passwordName"> //不會復(fù)用
        </div>

    </div>
   <script>
       var app = new Vue({
           el: '#app',
           data:{ 
            show: true
           },
        })
   </script>

5.2 v-show

v-show綁定的變量無論是true還是false银锻,它只是控制這個DOM元素的display:'none'這個style屬性而已,這個元素一直在DOM中

6.列表渲染

6.1數(shù)組渲染

這種是工作中常用的循環(huán)做鹰,要加上key值击纬,可以提高vue的性能,理想的 key 值是每項都有的唯一 id钾麸。key不建議使用index

注意:

  • Vue有一組觀察數(shù)組的變異方法更振,所以他們也會觸發(fā)視圖更新

方法如下:

  1. push()----在數(shù)組末尾加上一項 vm.list.push({id:'004',text:'啦啦啦'})
  2. pop()----將數(shù)組的最后一個元素移除
  3. shift()----刪除數(shù)組的第一個元素
  4. unshift()----在數(shù)組的第一個元素位置添加一個元素
  5. splice()----可以添加或者刪除數(shù)組中的一個或多個元素—返回刪除的元素

三個參數(shù):

  1. 表示開始操作的位置
  2. 要操作的長度,為0喂走,就是不刪除
  3. 為可選參數(shù)殃饿,可以把要添加的元素或者數(shù)組對象寫在這里
    arr.splice(4,1,{ll:true})
  1. sort()----排序
  2. reverse()----數(shù)組反轉(zhuǎn)
  • 當(dāng)我們想要修改數(shù)組中的元素的時候谋作,不能通過下標(biāo)的方式進(jìn)行改變芋肠,只能通過Vue提供的幾個數(shù)組變異方法來實現(xiàn)。
    比如想要在數(shù)組添加一項內(nèi)容:
    vm.list[4]={id: '005',text:'hello world'}其實list數(shù)組中已經(jīng)添加上了這一項遵蚜,只是無法在頁面中渲染出來
    比如在數(shù)組第二項后面添加一項內(nèi)容:
    vm.list.splice(1,0,{id:'001',text:'第三項'})

  • 當(dāng)我們想要修改數(shù)組中的元素的時候帖池,還可以通過改變數(shù)組的引用,就是對數(shù)組進(jìn)行重新賦值

  • 當(dāng)我們想要修改數(shù)組中的元素的時候吭净,還可以通過set語法
    Vue.set(vm.list,1,{id:'007',text: '你真傻'})或者vm.$set(vm.list,1,{id:'007',text: '你真傻'}),中間數(shù)字為操作元素的index值

<div id="app">
        <div v-for='(item,index) of list' :key = 'item.id'>{{item.text}}----{{index}}</div>
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                list: [{
                    id: '001',
                    text: 'xiaoliu'
                },{
                    id: '002',
                    text: 'xiaoxv'
                },{
                    id: '003',
                    text: 'xiaofang'
                }]
            }
        })
    </script>

如果我們想要通過一個list循環(huán)兩項睡汹,比如div和span

兩個循環(huán)完全獨(dú)立,不符合預(yù)期
<div id="app">
        <div v-for='(item,index) of list'  :key='item.id'>{{item.text}}----{{index}}</div>
        <span v-for='(item,index) of list'  :key='item.id'>{{item.text}}----{{index}}</span>
    </div>
外面包一層div,效果可以寂殉,但我們并不想要包裹的div出現(xiàn)在DOM中
<div id="app">
        <div v-for='(item,index) of list' :key='item.id'>
            <div>{{item.text}}----{{index}}</div>
            <span>{{item.text}}----{{index}}</span>
        </div>
    </div>
滿足預(yù)期囚巴,且在DOM中不會出現(xiàn)template標(biāo)簽
<div id="app">
        <template v-for='(item,index) of list' :key='item.id'>
            <div>{{item.text}}----{{index}}</div>
            <span>{{item.text}}----{{index}}</span>
        </template>
    </div>

6.2 對象渲染

參數(shù)順序:拿到value key index 的寫法   v---k---i  外開
<div id="app">
        <div v-for = '(item,key,index) of userInfo'>{{key}}: {{item}}--{{index}}</div>
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                userInfo: {
                    name: 'Dell',
                    sex: 'man',
                    age: 18
                }
            }
        })
    </script>

注意:
對象里面要想添加某一項,用點(diǎn)語法是行不通的
比如:vm.userInfo.address = 'ZhengZhou',在頁面中渲染不出來

  • 通過改變對象引用友扰,就是為對象重新賦值的方式來改變對象內(nèi)容
  • 通過set語法 Vue.set(vm.userInfo,'address', 'beijing')或者vm.$set(vm.userInfo,'adress','beijing')

7.組件使用中的一些細(xì)節(jié)

7.1 table中的問題

正常情況下如此
<div id="root">
        <table>
            <tbody>
                <tr><td>1</td></tr>
                <tr><td>2</td></tr>
                <tr><td>3</td></tr>
            </tbody>
        </table>
    </div>
每一行都引入一個組件彤叉,但是渲染的時候發(fā)現(xiàn)渲染出來的 row 并不在 tbody 標(biāo)簽里面
因為 tbody 標(biāo)簽里面只能寫 tr,其他標(biāo)簽他識別不了
<div id="root">
        <table>
            <tbody>
                <row></row>
                <row></row>
                <row></row>
            </tbody>
        </table>
    </div>
    <script>

        Vue.component('row',{
            template: '<tr><td>this is a row</td></tr>'
        })

        new Vue({
            el: '#root',
        })
    </script>
使用is屬性可以解決這個問題
<table>
            <tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>
比如:這幾種情況最好也不要直接寫組件,用is屬性來實現(xiàn)
 <ul>
      <li is="row"></li>
 </ul>
  <ol>
      <li is="row"></li>
  </ol>
  <section>
      <option is="row"></option>
  </section>

7.2 組件中的data

組件都是可以復(fù)用的村怪,所以組件中data也得是一個獨(dú)立的對象秽浇,那么組件之間的data才不會相互干擾

<div id="root">
        <table>
            <tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>      
    </div>
    <script>
        Vue.component('row',{
            data () {
                return {
                    content: 'this is a row'
                }
            },
            template: '<tr><td>{{content}}</td></tr>'
        })
        new Vue({
            el: '#root',
            data: {
            }
        })
    </script>

7.3 ref的使用

ref在dom標(biāo)簽上使用,指向的是這個dom節(jié)點(diǎn)

<div id="root">
        <div ref="hello"
        @click = 'handleClick'
        >
        hello world
    </div>
    </div>
    <script>
        new Vue({
            el: '#root',
            data: {

            },
            methods: {
                handleClick () {
                    console.log(this.$refs.hello)//就是指向的上面的div節(jié)點(diǎn)
                    console.log(this.$refs.hello.innerHTML)//hello world
                }                
            }
        })
    </script>

ref在一個組件上使用甚负,實際上是對這個組件的一個引用

<div id="root">
        <counter ref="one" @change = 'sumNumber'></counter>
        <counter ref="two" @change = 'sumNumber'></counter>
        <div>{{sum}}</div>
    </div>
    <script>
        Vue.component('counter',{
            template:  '<div @click="handleClick">{{number}}</div>',
            data () {
                return {
                    number: 0
                }
            },
            methods: {
                handleClick () {
                    this.number ++
                    this.$emit('change',this.number)
                }
            }
        })
        new Vue({
            el: '#root',
            data: {
                sum: 0
            },
            methods: {
                sumNumber (number) {
                    this.sum = this.$refs.one.number + this.$refs.two.number
                }
            }
        })
    </script>

8. 父子組件間數(shù)據(jù)傳遞

8.1 父組件向子組件傳遞數(shù)據(jù)

注意:

  1. 父組件向子組件傳值時柬焕,傳值參數(shù)前最好加上v-bind,以下面為例:
    count="1"就是傳遞的字符串1
    :count="1"就是數(shù)字1梭域,
    不加:斑举,那么傳遞參數(shù)就是參數(shù)后的那個值
    加:,那么傳遞參數(shù)就是引號里面的js表達(dá)式
  2. 子組件接收到父組件傳過來的值病涨,是不能修改的富玷,不要在子組件中修改props中的值,vue中有單向數(shù)據(jù)流的概念,即父組件向子組件傳值時凌彬,父組件中可以隨意修改所要傳遞地數(shù)據(jù)沸柔,子組件不能反過來修改父組件傳遞來的數(shù)據(jù)

如果你在子組件中修改了props:

  1. 如果傳過來的是基本類型,那么會有警告铲敛,但在頁面中正常渲染
  2. 如果是引用類型(對象褐澎,數(shù)組),vue會檢測不到變化伐蒋,頁面中也不會渲染

如何修改傳遞過來的值工三?

  • 把傳遞過來的值賦值給子組件data的一個屬性里,就可以修改啦
<div id="root">
    <counter :count = "1"></counter>
    <counter :count = "2"></counter>
   </div>
   <script>
       var counter = { //局部組件先鱼,需要在父組件中注冊
           props: ['count'],
           template: '<div @click = "handleClick">{{this.count}}</div>',
           methods: {
               handleClick () {
                this.count ++
               }
           }
       }
       var vm = new Vue({
           el: '#root',
           data: {

           },
           components: {
               counter
           }
       })
   </script>

//正確寫法
var counter = { //局部組件俭正,需要在父組件中注冊
           props: ['count'],
           data () {
            return {
                number: this.count
            }
           },
           template: '<div @click = "handleClick">{{number}}</div>',
           methods: {
               handleClick () {
                this.number ++
               }
           }
       }
      

8.2 子組件向父組件傳遞數(shù)據(jù)

<div id="root">
    <counter :count = "1" @change = 'numberChange'></counter>
    <counter :count = "2" @change = 'numberChange'></counter>
    <div>{{total}}</div>
   </div>
   <script>
       var counter = { //局部組件,需要在父組件中注冊
           props: ['count'],
           data () {
            return {
                number: this.count
            }
           },
           template: '<div @click = "handleClick">{{number}}</div>',
           methods: {
               handleClick () {
                this.number = this.number + 2
                this.$emit('change',2) // 2是步長
               }
           }
       }      
       var vm = new Vue({
           el: '#root',
           data: {
            total: 3
           },
           components: {
               counter, 
           },
           methods: {
            numberChange (step) {
                this.total += step
            }
           }
       })
   </script>

8.3 組件參數(shù)校驗與非props特性

以下是props的參數(shù)校驗的幾種寫法焙畔,以及參數(shù)意思

props特性和非props特性

  1. props特性就是父組件傳遞過去掸读,子組件有接收,接收后這個特性就不會出現(xiàn)在DOM結(jié)構(gòu)中
  2. 非props特性就是父組件傳遞過去宏多,子組件沒有接收儿惫,會在DOM結(jié)構(gòu)中出現(xiàn)(了解)
<div id="root">
        <child content="hello world"></child>
   </div>
   <script>
       Vue.component('child',{
        //    props: ['content'],
            props: {
                // content: [ String, Number ]
                content: {
                    type: String,
                    required: true,//要求父組件必須向子組件傳content,不傳會有提示
                    default: 'default value',//如果不傳內(nèi)容伸但,那么這個內(nèi)容就默認(rèn)顯示
                    validator (value) {//校驗器肾请,value就是傳過來的數(shù)據(jù)
                        return (value.length < 5)
                    }
                }
            },
           template: '<div>{{content}}</div>'
       })      
       var vm = new Vue({
           el: '#root',
           data: {
            
           },         
        })
   </script>

8.3 自定義事件和原生事件

  1. 自定義事件:子組件在父組件中使用時,直接綁定在子組件上的事件就是自定義事件更胖,必須經(jīng)過子組件的觸發(fā)才能執(zhí)行
  2. 原生事件:直接在子組件里的模板上綁定的事件铛铁,子組件引入后是可以直接使用的
  3. 怎么在父組件的子組件里直接綁定原生事件,不用子組件的再次觸發(fā)呢却妨?

直接在綁定的自定義事件后加上修飾符.native

 <div id="root">
        <child @click = 'handleClick'></child> //在這里綁定的是自定義事件饵逐,必須經(jīng)過子組件的觸發(fā)才能執(zhí)行
    </div>
    <script>
        Vue.component('child', {
            template: '<div @click = "handleChildClick">click</div>',//這里綁定的是原生事件
            methods: {
                handleChildClick () {
                    alert('child click')
                    this.$emit('click')//觸發(fā)自定義事件
                }
            }
        })

        var vm = new Vue({
            el: '#root',
            methods: {
                handleClick () {
                    alert('click')
                }
            }
        })
    </script>
<div id="root">
        <child @click.native = 'handleClick'></child> 
    </div>
    <script>
        Vue.component('child', {
            template: '<div @click = "handleChildClick">click</div>'//這里綁定的是原生事件
        })
        var vm = new Vue({
            el: '#root',
            methods: {
                handleClick () {
                    alert('click')
                }
            }
        })
    </script>

9. 非父子組件之間的傳值

bus/總線/發(fā)布訂閱模式/觀察者模式
步驟:

  1. 建立總線 Vue.prototype.bus = new Vue()
  2. 在子組件中觸發(fā) this.bus.$emit('change',this.selfContent)
  3. 在子組件mounted生命周期鉤子函數(shù)中監(jiān)聽 this.bus.$on('change',(value)=>{})value就是傳過來的值,可以進(jìn)行操作
<div id="root">
        <child content="Dell" ></child>
        <child content="Lee"></child>
    </div>
    <script>
        Vue.prototype.bus = new Vue()
        Vue.component('child',{
            props: {
                content: String
            },
            data () {
                return {
                    selfContent: this.content
                }
            },
            template: '<div @click = "handleClick">{{selfContent}}</div>',
            methods: {
                handleClick () {
                   this.bus.$emit('change',this.selfContent)
                }
            },
            mounted () {
                this.bus.$on('change',(value)=>{
                    // alert(this.content)
                    this.selfContent = value
                })
            }
        })
        var vm = new Vue({
            el: '#root'
        })
    </script>

10. 插槽

10.1 沒有插槽前的傳值方式

父組件向子組件傳值管呵,采用props傳值梳毙,可以傳少量的值,但如果數(shù)值較多的話捐下,代碼可讀性會很低

<div id="root">
        <child content="<p>Dell</p>"></child>
    </div>
    <script>

        Vue.component('child',{
            props: ['content'],
            // template: "<div><p>hello</p>{{content}}</div>"http://content無法轉(zhuǎn)義
            template: `<div>
                            <p>hello</p>
                            <div v-html='this.content'></div> //可以轉(zhuǎn)義账锹,但是content外包了一個div
                        </div>`
        })

        var vm = new Vue({
            el: '#root'
        })
    </script>

插槽

 <div id="root">
        <child>
            <p>Dell</p>
        </child>
    </div>
    <script>

        Vue.component('child',{
            template: `<div>
                            <p>hello</p>
                            <slot>默認(rèn)數(shù)據(jù)</slot>//父組件中間沒有傳值的時候默認(rèn)顯示
                        </div>`
        })
        var vm = new Vue({
            el: '#root'
        })
    </script>

具名插槽,可以在父組件中插入多個模塊的內(nèi)容

<div id="root">
        <child>
            <div class="header" slot="header">header</div>
            <div class="footer" slot="footer">footer</div>
        </child>
    </div>
    <script>

        Vue.component('child',{
            template: `<div>
                            <slot name="header"></slot>
                            <p>hello</p>
                            <slot name="footer"></slot>
                        </div>`
        })

        var vm = new Vue({
            el: '#root'
        })
    </script>

作用域插槽

  • 執(zhí)行邏輯:
  1. 父組件調(diào)用子組件時坷襟,向子組件傳遞了一個插槽
  2. 子組件通過slot向父組件傳遞數(shù)據(jù)奸柬,比如: :item = item
  3. 插槽是作用域插槽,插槽必須寫在template里面婴程,同時聲明從子組件接收的數(shù)據(jù)都放在props里面
  4. 在template里面寫上模板的信息廓奕,以什么方式進(jìn)行展示
  • 什么時候用作用域插槽?
    子組件做循環(huán)或者有一部分的DOM結(jié)構(gòu)要由外部傳過來的時候
<div id="root">
        <child></child>
    </div>
    <script>
        Vue.component('child',{
            data () {
                return {
                    list: [1,2,3,4]
                }
            },
            template: `<div>
                          <ul>
                            <li v-for='item of list'>{{item}}</li>
                          </ul>
                       </div>`
        })
        var vm = new Vue({
            el: '#root'
        })
    </script>

作用域插槽改寫

<div id="root">
        <child>
            <template slot-scope='props'>
                <h1>{{props.item}}</h1>
            </template>
        </child>
    </div>
    <script>
        Vue.component('child',{
            data () {
                return {
                    list: [1,2,3,4]
                }
            },
            template: `<div>
                          <ul>
                            <slot v-for='item of list' :item = item></slot>
                          </ul>
                       </div>`
        })
        var vm = new Vue({
            el: '#root'
        })
    </script>

11. 動態(tài)組件和v-once指令

  1. 動態(tài)組件

動態(tài)組件中is屬性根據(jù)綁定的組件名的不同會動態(tài)的切換組件

  1. v-once 使用

子組件中加上v-once,也就是第一次執(zhí)行的時候就把子組件放入內(nèi)存桌粉,下次直接復(fù)用即可蒸绩,所以如果子組件內(nèi)容不變的話,加上v-once會提高vue性能

<div id="root">
        <component :is = 'type'></component>
        <child-one v-if='type === "child-one"'></child-one>
        <child-two v-if='type === "child-two"'></child-two>
        <button @click='handleClick'>change</button>
    </div>
    <script>
        Vue.component('child-one',{
            template: '<div v-once>child-one</div>'
        })
        Vue.component('child-two',{
            template: '<div v-once>child-two</div>'
        })
        var vm = new Vue({
            el: '#root',
            data: {
                type: 'child-one'
            },
            methods: {
                handleClick () {
                    this.type = this.type === 'child-one'? 'child-two':'child-one'
                }
            }
        })
    </script>

12.Vue動畫

12.1 css過渡動畫

過渡動畫原理:

  1. 在需要過渡的元素外面包裹上transition標(biāo)簽铃肯,那么vue執(zhí)行代碼的時候就會對被包裹的元素進(jìn)行解析
  2. 以緩動出現(xiàn)為例:
  • 動畫開始前患亿,vue剛開始解析代碼,就會給transition包裹的標(biāo)簽(div)加上v-enter , v-enter-active兩個類(注意可以給transition加name押逼,那么v就換成name名稱)
  • 動畫開始時步藕,去掉fade-enter這個類,添加fade-enter-to這個類
  • 動畫結(jié)束時挑格,所有的類都去掉
  1. 我們可以根據(jù)這些類的添加和刪除為這些類添加一些樣式咙冗,來做出動畫效果
緩慢出現(xiàn)類名圖示.png
緩慢離開類名圖示.png

代碼如下:

<style>
        /*緩動出現(xiàn)動畫 */
        .v-enter {
            opacity: 0; /*將初始狀態(tài)的透明度設(shè)為0*/
        }
        .v-enter-active {
            transition: opacity 1s; /*transition檢測到opacity的變化,1s內(nèi)完成*/
        }
        /*緩慢消失動畫 */
        .v-leave-to {
            opacity: 0;/*將最終狀態(tài)變?yōu)?*/
        }
        .v-leave-active {
            transition: opacity 1s;
        }
    </style>
    <div id="root">
        <transition>
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                }
            }
        })
    </script>

12.2 在Vue中使用animate.css庫

Vue中使用keyframes

v-enter-active, v-leave-active在動畫整個過程都是存在的漂彤,所以可以在這里面寫效果

vue提供的原生類名太長雾消,想換類名怎么辦?

可以在transition里面自己設(shè)置類名 比如:enter-active-class='enter' leave-active-class='leave'显歧,那么這兩個類就可以這樣簡寫啦

<style>
       @keyframes bounce-in {
           0% {
               transform: scale(0)
           }
           50% {
               transform: scale(1.5)
           }
           100% {
               transform: scale(1)
           }
       }
        .v-enter-active {
            transform-origin: left center;/*設(shè)置元素變形的原點(diǎn)帘睦,左邊線的中點(diǎn)*/
            animation: bounce-in 1s;
        }
        .v-leave-active {
            transform-origin: left center;
            animation: bounce-in 1s reverse;
        }
    </style>
    <div id="root">
        <transition>
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                }
            }
        })
    </script>

使用animate.css庫
如何使頁面出現(xiàn)以及刷新的時候也出現(xiàn)動畫呢咕宿?

在transition中加入屬性appear,類appear-active-class='animated swing'

<link rel="stylesheet" href="../animate.css">
<div id="root">
        <transition 
        appear
        enter-active-class = 'animated swing' 
        leave-active-class='animated shake'
        appear-active-class= 'animated swing'
        >
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                }
            }
        })
    </script>

12.3 在vue中同時使用animate.css庫和transition

注意: animate.css動畫默認(rèn)時間是1s,所以如果transition和animate時間不一致枢析?
怎么辦疚脐?

  1. type="transition"用來指定以誰的時間為準(zhǔn)
  2. 自定義時間:duration="5000"
<style>
            .v-enter,.v-leave-to{
                opacity: 0;
            }
    
            .v-enter-active,
            .v-leave-active{
                transition: opacity 3s;
            }
    </style>
    <div id="root">
        <transition 
        //type="transition"
        //:duration="5000"  動畫執(zhí)行時間為5s
         :duration="{enter: 5000,leave: 10000}" //可以設(shè)置進(jìn)出場動畫的時間
        appear
        enter-active-class = 'animated swing v-enter-active' 
        leave-active-class='animated shake v-leave-active'
        appear-active-class='animated swing'
        >
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>

12.4 js 動畫

js動畫鉤子:

  1. before-enter: 動畫開始執(zhí)行前就執(zhí)行啦
  2. enter: 動畫開始的時候執(zhí)行
  3. after-enter: 動畫結(jié)束時執(zhí)行
    離開動畫與進(jìn)入動畫一樣before-leave,leave,after-leave
<div id="root">
        <transition
        name="fade"
        @before-enter="handleBeforeEnter"
        @enter = "handleEnter"
        @after-enter = "handleAfterEnter"
        >
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                },
                handleBeforeEnter (el) {//動畫開始執(zhí)行前調(diào)用
                    // alert('beforeenter')
                    el.style.color="red"http://el就是指transition里面包裹的元素
                },
                handleEnter (el,done) {//動畫開始執(zhí)行時調(diào)用
                    // alert('enter')
                    setTimeout(() => {
                        el.style.color="green"
                    },2000)
                    setTimeout(()=>{
                        done()//只有調(diào)用done這個回調(diào)函數(shù)之后奶躯,才會執(zhí)行after-enter這個鉤子
                    },4000)
                },
                handleAfterEnter (el) {
                    el.style.color= "#000"
                }
            }
        })
    </script>

12.5 Velocity動畫庫的使用

 <div id="root">
        <transition
        name="fade"
        @before-enter="handleBeforeEnter"
        @enter = "handleEnter"
        @after-enter = "handleAfterEnter"
        >
            <div v-if='show'>hello world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                },
                handleBeforeEnter (el) {
                    el.style.opacity= 0
                },
                handleEnter (el,done) {
                    Velocity(el,{opacity: 1},{duration: 1000})
                    //在1s之內(nèi)把el的opacity從0變到1
                },
                handleAfterEnter (el) {

                }
            }
        })
    </script>

12.6 Vue中多個元素或組件的過渡

  • 多個元素的過渡

注意:多個元素的過渡买喧,每個元素要加一個key,如果不加vue中會進(jìn)行dom復(fù)用馋袜,就沒有動畫效果啦

<style>
        .v-enter,.v-leave-to{
            opacity: 0
        }
        .v-enter-active,.v-leave-active {
            transition: opacity 1s
        }
    </style>
    <div id="root">
        <transition mode="out-in">
            <div v-if='show' key="hello">hello world</div>
            <div v-else key="bye">bye world</div>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                }
            }
        })
    </script>
  • 多個組件的過渡
<style>
        .v-enter,.v-leave-to{
            opacity: 0
        }
        .v-enter-active,.v-leave-active {
            transition: opacity 1s
        }
    </style>
    <div id="root">
        <transition mode="out-in">
            <child v-if='show'></child>
            <child-one v-else></child-one>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        Vue.component('child',{
            template: '<div>child</div>'
        })
        Vue.component('child-one',{
            template: '<div>child-one</div>'
        })
        var vm = new Vue({
            el: '#root',
            data: {
                show: true
            },
            methods: {
                handleButton() {
                    this.show = !this.show
                }
            }
        })
    </script>

多個組件可以轉(zhuǎn)化為動態(tài)組件

<style>
        .v-enter,.v-leave-to{
            opacity: 0
        }
        .v-enter-active,.v-leave-active {
            transition: opacity 1s
        }
    </style>
    <div id="root">
        <transition mode="out-in">
            <component :is="type"></component>
        </transition>
        <button @click='handleButton'>切換</button>
    </div>
    <script>
        Vue.component('child',{
            template: '<div>child</div>'
        })
        Vue.component('child-one',{
            template: '<div>child-one</div>'
        })
        var vm = new Vue({
            el: '#root',
            data: {
                type: "child"
            },
            methods: {
                handleButton() {
                    this.type = this.type === "child"?"child-one":"child"
                }
            }
        })
    </script>

12.7 vue中的列表過渡

原理:

<transition-group>
          <div v-for="item of list" :key="item.id">{{item.title}}</div>
</transition-group>

把循環(huán)的每一項都變成了

<transition>
          <div>{{item.title}}</div>
</transition>

在進(jìn)行渲染

<style>
            .v-enter,.v-leave-to {
                opacity: 0
            }
            .v-enter-active,.v-leave-active {
                transition: opacity 1s
            }
    </style>
    <div id="root">
        <transition-group>
            <div v-for="item of list" :key="item.id">{{item.title}}</div>
        </transition-group>
        <button @click="handleAddOne">addOne</button>
    </div>
    <script>
        var count = 0
        var vm = new Vue({
            el: '#root',
            data: {
                list: []
            },
            methods: {
                handleAddOne () {
                    this.list.push({
                        title: "xxx我又想你" + count + "次",
                        id: count++
                    })
                }
            }

        })
    </script>

12.8 vue動畫封裝

<div id="root">
        <fade :show="show">
            <div>hello world</div>
        </fade>

        <fade :show="show">
            <div>I love you</div>
        </fade>

        <button @click='handleButton'>切換</button>
    </div>
    <script>
        Vue.component('fade',{
            props: ['show'],
            template: 
                `<transition @before-enter="handleBeforeEnter"
                @enter="handleEnter"
                >
                    <slot v-if="show"></slot>
                </transition>
                `,
            methods: {
                handleBeforeEnter (el) {
                    el.style.color = "red"
                },
                handleEnter (el,done) {
                    setTimeout(()=>{
                        el.style.color = "green"
                        done()
                    },2000)
                }
            }
        })

13. Vue-router的學(xué)習(xí)

基本寫法:vue本質(zhì)上是一個單頁面應(yīng)用简逮,頁面間跳轉(zhuǎn)就是組件之間的跳轉(zhuǎn)

13.1 vue-router的使用步驟(vue-cli的情況下)

  1. 在src目錄下建立router目錄旨巷,目錄下創(chuàng)建index.js
  2. 在index.js中引入import Vue from 'vue'; import Router from 'vue-router'
  3. 使用Router Vue.use(Router) 因為vue-router是一個插件巨缘,vue中使用插件就要Vue.use()
  4. 定義路由
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'

export const routes = [
    {
      path: '/',
      name: 'Home', 
      component: Home
    },
    {
      path: '/city',
      name: 'City', 
      component: City
    },
]
  1. 創(chuàng)建Router實例,對路由進(jìn)行配置
const router = new Router({
    routes, //routes: routes
    mode: 'history', //默認(rèn)是hash模式(路徑中有#,seo不好)采呐,改成history若锁,可以把路徑中#去掉,不過這樣寫刷新頁面后會出現(xiàn)404斧吐,還需在后端配置
    base: /base/, //會在routes設(shè)置的路徑前面加上/base/(可以自定義)又固,用于區(qū)分一
                 //些頁面,注意/base/不是必須的煤率,去掉它頁面還會正常顯示
    
})
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home', 
      component: Home
    },
    {
      path: '/city',
      name: 'City', 
      component: City
    },
  ]
})

四仰冠、vue-router學(xué)習(xí)

Ⅰ. vue-router在項目中的使用

當(dāng)項目比較大時,可以將route拆分成兩個文件
routes.js

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/',
    redirect: '/app'
  },
  {
    path: '/app',
    component: Todo
  },
  {
    path: '/login',
    component: Login
  }
]

router.js

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
const router = new Router({ // 不推薦這樣做蝶糯,這樣做傳出去的就一個router洋只,做服務(wù)端渲染的時候會內(nèi)存溢出
  routes,
  // mode: 'history', // 可以去掉hash路由的#
  // base: '/base/', // 在routes配置的路前都加上/base/, 里面可以是任何內(nèi)容,
  linkActiveClass: 'active-class', // 可以自定義router-link a 標(biāo)簽里面的類名
  linkExactActiveClass: 'exact-active-class' // 精確匹配路徑
})
export default router

router.js另一種寫法:(做服務(wù)端渲染的時候要使用這種方法,要不然會內(nèi)存溢出)

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
export default () => { // 這樣每次傳出去的就是一個新router
  return new Router({
    routes: routes
  })
}
// 在入口文件中引入
import creatRouter from './config/router'
const router = creatRouter()

然后在項目入口文件中引入router.js识虚,在實例中注冊好肢扯,將router-view放到app.vue中就好啦

Ⅱ、router對象(new Router)中的一些配置項

1. mode: history

作用:將URL中難看的#去掉
分析:

  • 為什么vue-router跳轉(zhuǎn)要有hash和history兩種模式呢担锤?
    vue是單頁面應(yīng)用鹃彻,所以vue-router的核心就在于---改變路由的同時不會向后端發(fā)送請求
    hash模式:hash(#)是URL 的錨點(diǎn),代表的是網(wǎng)頁中的一個位置妻献,單單改變#后的部分蛛株,瀏覽器只會滾動到相應(yīng)位置,不會重新加載網(wǎng)頁育拨,也就是說 #是用來指導(dǎo)瀏覽器動作的谨履,對服務(wù)器端完全無用,HTTP請求中也不會不包括#熬丧;同時每一次改變#后的部分笋粟,都會在瀏覽器的訪問歷史中增加一個記錄,使用”后退”按鈕析蝴,就可以回到上一個位置
    history模式:HTML5 History API提供了一種功能害捕,能讓開發(fā)人員在不刷新整個頁面的情況下修改站點(diǎn)的URL,就是利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面
    注意:但當(dāng)用戶直接在用戶欄輸入地址并帶有參數(shù)時:
    Hash模式:xxx.com/#/id=5 請求地址為 xxx.com,沒有問題闷畸;
    History模式: xxx.com/id=5 請求地址為 xxx.com/id=5尝盼,如果后端沒有對應(yīng)的路由處理,就會返回404錯誤
    所以佑菩,我們使用history模式的時候盾沫,一定要在后臺進(jìn)行配置
    我們在開發(fā)環(huán)境時,使用devServer啟動服務(wù)殿漠,所以可以在devServer中配置
historyApiFallback: {
    index: '/index.html'
  },

2. base: '/base/'

---配置base項赴精,base項的值可以任意寫
作用:在routes配置的路前都加上/base/, 里面可以是任何內(nèi)容,可以用來標(biāo)記一些特殊的頁面绞幌。
注意:這個base配置的值在url中不是必須的蕾哟,去掉url還是正常顯示的

3. linkActiveClass 和 linkExactActiveClass

作用:可以改變router-link形成的 a 標(biāo)簽中的兩個類名
最初類名:
<a data-v-06ebb29e="" href="/base/app" class="router-link-exact-active router-link-active">app</a>
配置自定義類名:

linkActiveClass: 'active-class', 
linkExactActiveClass: 'exact-active-class' // 精確匹配路徑

自定義后的類名:
<a data-v-06ebb29e="" href="/base/app" class="exact-active-class active-class">app</a>
問題:這兩個類有什么作用?他們兩個有什么區(qū)別莲蜘?
作用:用來給激活的鏈接加樣式
區(qū)別:

  1. linkActiveClass: 全局配置 <router-link> 的默認(rèn)“激活 class 類名”
  2. linkExactActiveClass: 全局配置 <router-link> 精確激活的默認(rèn)的 class
    舉例:頁面上兩個鏈接:login谭确,login exact, login 路徑是/login, login exact的路徑是login/exact
    那么當(dāng)我們激活 login exact 的時候,頁面上的類顯示:
    <a data-v-06ebb29e="" href="/base/login" class="active-class">login</a>
    <a data-v-06ebb29e="" href="/base/login/exact" class="exact-active-class active-class">login exact</a>

4. scrollBehavior的配置

作用:配置路由頁面的滾動行為
說明:

  1. to from 都是路由對象菇夸,to將要跳轉(zhuǎn)的路由琼富,from就是當(dāng)前還未跳轉(zhuǎn)的路由
  2. savedPosition就是要跳轉(zhuǎn)的頁面以前滾動保留下的位置,如存在庄新,跳轉(zhuǎn)后到保留的位置鞠眉,不存在薯鼠,就到頁面頂端唄
  3. 當(dāng)然不同頁面的位置可以有to from定制
scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition 
    } else {
      return { x: 0, y: 0 }
    }
  }

5. parseQuery 和 stringifyQuery

作用:url中的經(jīng)常會有查詢參數(shù)(?后面的一串)械蹋,我們需要把它們轉(zhuǎn)化為json對象才能使用出皇,其實vue會幫我們轉(zhuǎn)化它們,不過如果我們有一些特殊的需求的話哗戈,可以用他們配置

parseQuery (query) {
    // 把查詢參數(shù)(string)轉(zhuǎn)化為json對象
  },
  stringifyQuery (obj) {
    // 把對象轉(zhuǎn)化為字符串
  }

6. fallback: true

作用:當(dāng)瀏覽器不支持 history.pushState 控制路由是否應(yīng)該回退到 hash 模式郊艘。默認(rèn)值為 true。

如果你不想退回 hash 模式唯咬,那寫成 false 纱注,改成false之后,那么vue單頁應(yīng)用變成多頁應(yīng)用胆胰,每次router-link跳轉(zhuǎn)都會去后端拿數(shù)據(jù)狞贱,比較耗時
所以一般不要改成false

Ⅲ、routes(路由)配置的的要點(diǎn)

1. name的配置與作用

{
    path: '/app',
    component: Todo,
    name: 'app' // 與path無關(guān)蜀涨,可以定義任何名字r
  }

作用:路由跳轉(zhuǎn)router-link中可以使用
比如:下面它們是等效的

<router-link to="/app">app</router-link>
<router-link :to="{name: 'app'}">app</router-link>

2. meta的配置與作用

作用:保存一下當(dāng)前路由的一些信息的瞎嬉,我們再寫html頁面時,head標(biāo)簽里面的meta里面保存的信息厚柳,我們稱他們?yōu)轫撁娴脑葱畔⒀踉妫@些信息有利于我們處理seo,根據(jù)description里面的內(nèi)容來排列他們的搜索結(jié)果,而在vue組件里面别垮,我們沒有辦法在組件中寫這些東西便监,所以可以在路由里面的meta里面寫我們需要的東西

meta: {
      title: 'this is an app',
      description: 'asddffg'
}

3. children的配置和使用

作用:當(dāng)前路由的子路由
注意:當(dāng)前路由可能不止一個子路由,所以children是一個數(shù)組宰闰,在routes配置好之后茬贵,在當(dāng)前路由需要的位置引入router-view即可

children: [
  {
    path: 'test',
    component: Login
  }
]

4. 路由參數(shù)的配置

作用:通過向像跳轉(zhuǎn)的組件傳參數(shù),來改變組件的行為(類似于props)
分為id和query

// 在routes中配置參數(shù)
{
    path: '/app/:id', // 配置參數(shù)
    component: Todo,
    name: 'app', 
    meta: {
      title: 'this is an app',
      description: 'asddffg'
}

// 向要跳轉(zhuǎn)的路由傳參數(shù)(這個參數(shù)一般都是一個變量)
<router-link to="/app/123">app</router-link>

//接收到參數(shù)的路由怎么使用參數(shù)移袍?
mounted () {
  console.log(this.$route) // this.$route包含著路由匹配成功后路由中的所有信息
  console.log(this.$route.params.id) // 拿到了id值
  console.log(this.$route.query) // 拿到了query值(query不用在routes設(shè)置,傳參的時候有query, 就能拿到)
}

注意:在同一個路由下面this.$route在哪一個組件中都是一樣的
this.$route對象:

Snipaste_2019-06-02_20-00-11.png

使用場景:當(dāng)我們定義路徑的時候老充,有一個列表請求(比如商品列表)葡盗,我們可以通過傳參的方式拿到單個商品的id,進(jìn)而去請求這個商品的詳細(xì)內(nèi)容(類似于props啡浊,都是通過傳來的數(shù)據(jù)觅够,來改變當(dāng)前組件的某些行為)

5. props的配置

注意:盡量使用props方法,少使用$route巷嚣,使組件與路由解耦喘先,提高組件的復(fù)用率

  1. props: true 布爾模式
    作用:可以把要傳遞給要跳轉(zhuǎn)路由的參數(shù)轉(zhuǎn)化成props傳遞
    好處:在組件中使用$route會使之與對應(yīng)路由高度耦合,該組件只能在特定url(路由)下使用廷粒,組件單獨(dú)使用時this.$route會不匹配窘拯,拿不到id的值红且。使用props: true則可以解決這個問題,通過props: ['id']可以接收到外部傳來的id的值涤姊,這其實就是一種解耦暇番。
    上面的代碼就可以改成下面這樣:
// 在routes中配置參數(shù)
{
    path: '/app/:id', // 配置參數(shù)
    props: true,
    component: Todo,
    name: 'app', 
    meta: {
      title: 'this is an app',
      description: 'asddffg'
}

// 向要跳轉(zhuǎn)的路由傳參數(shù)(這個參數(shù)一般都是一個變量)
<router-link to="/app/123">app</router-link>

//接收到參數(shù)的路由怎么使用參數(shù)?
props: ['id']
mounted () {
  console.log(this.id)
}
// 注意:如果是命名視圖思喊,props要包一層
components:{
  default: Todo,
  a: login
},
props: {
  default: true,
  a: true
}
  1. props 對象模式
    作用:如果 props 是一個對象壁酬,它會被按原樣設(shè)置為組件屬性。當(dāng) props 是靜態(tài)的時候有用
props: {
  id: '456' // 不會變化
}
  1. props 函數(shù)模式
    props: (route) => ({ query: route.query.a, id: route.query.b })
    接收route參數(shù)恨课,返回一個對象

Ⅳ舆乔、vue-router的一些高級用法

1. 命名視圖

作用:在router-view上設(shè)置好名字,這樣可以根據(jù)我們的需求在同一個路由下顯示不同的組件
使用場景:可以同時展示多個視圖剂公,而不用嵌套希俩。例如創(chuàng)建一個布局,有 sidebar(側(cè)導(dǎo)航) 和 main (主內(nèi)容) 兩個視圖诬留,這個時候命名視圖就派上用場了斜纪。你可以在界面中擁有多個單獨(dú)命名的視圖,而不是只有一個單獨(dú)的出口文兑。如果 router-view 沒有設(shè)置名字盒刚,那么默認(rèn)為 default
配置和使用:

// 在routes中配置
{
    path: '/login',
    components: {
      default: Login,
      a: Todo
    }
  }
// 在組件中使用
<transition name="fade">
  <router-view></router-view>
</transition>
<Footer></Footer>
<router-view name='a'></router-view>

2. vue-router之導(dǎo)航守衛(wèi)

路由守衛(wèi)
全局守衛(wèi)
---對所有的路由有效果绿贞,只要路由跳轉(zhuǎn)因块,就會觸發(fā)

  1. 全局前置守衛(wèi)
    作用:對一些頁面進(jìn)行校驗,比如:驗證一些頁面是需要用戶登錄才可以顯示的
router.beforeEach((to, from, next) => {
  console.log('before each invoked')
   if (to.fullPath === '/app') {
    next('/login')
  } else {
    next()
  }
})
  1. 全局解析守衛(wèi)
router.beforeResolve((to, from, next) => {
  console.log('before resolved invoked')
  next()
})
  1. 全局后置鉤子
router.afterEach((to, from) => {
  console.log('after each invoked')
})

路由內(nèi)守衛(wèi)
---只對該路由有效果

  1. 路由獨(dú)享的守衛(wèi)
// 在routes中配置
beforeEnter: (to, from, next) => {
      console.log('before enter route')
      next()
}

組件內(nèi)守衛(wèi)
---對本組件進(jìn)入籍铁,離開涡上,以及組件的復(fù)用有效果

  1. beforeRouteEnter
    無法拿到this,可以在next方法里面執(zhí)行一個回調(diào)拿到當(dāng)前組件
beforeRouteEnter (to, from, next) {
    console.log('befor route enter')
    next(vm => {
      console.log(vm.id)
    })
  }
  1. beforeRouteUpdate
    執(zhí)行:同一個組件在不同的路由下面顯示的時候拒名。比如:/app/123和/app/456
    可以拿到 this
beforeRouteUpdate (to, from, next) {
    console.log('before route update')
    next()
  }

注意:當(dāng)我們在兩個相似路徑(/app/123和/app/456)跳轉(zhuǎn)時吩愧,第二次還會不會觸發(fā)mounted鉤子呢?
不會增显,當(dāng)我們進(jìn)入/app/123時雁佳,mounted鉤子執(zhí)行,這時跳到/app/456鉤子同云,mounted鉤子就不再執(zhí)行糖权。所以我們不要把根據(jù)id變化而變化的數(shù)據(jù)寫在mounted里面,可以寫在beforeRouteUpdate里面炸站,也可以寫在watch里面

  1. beforeRouteLeave
    使用場景:控制頁面離開行為星澳。比如你修改了一個表單,還沒有保存旱易,現(xiàn)在你要跳到其他頁面禁偎,可以在這里給你設(shè)置一個提醒
beforeRouteLeave (to, from, next) {
    console.log('before route leave')
    if (global.confirm('are you sure?')) {
      next()
    }
  }

3. 異步組件實現(xiàn)按需加載

作用:每次加載頁面腿堤,就要把所有的業(yè)務(wù)邏輯代碼加載一下,若是項目比較大届垫,勢必會大大影響頁面的初始加載速度释液,若是app.js小于1mb以下,就沒有必要使用異步組件啦
異步組件不僅可以在路由中使用装处,在頁面組件中中只要引入組件的地方都可以使用

// 在路由routes 引入
component: () => import('../views/todo/todo.vue')
component: () => import('../views/login/login.vue')
// 安裝 `babel-plugin-syntax-dynamic-import` 插件
npm i babel-plugin-syntax-dynamic-import
// 在 `babelrc`中配置
"plugins": [
    "transform-vue-jsx", // 解析 jsx 語法
    "syntax-dynamic-import" // 異步加載組件
  ]

五误债、vuex的學(xué)習(xí)

Ⅰ、vuex在項目中的使用

// 安裝
npm install vuex --save
// 在 src 目錄下建立store目錄妄迁,建立store.js 文件
// store.js 文件配置
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    updateCount (state, num) {
      state.count = num
    }
  }
})
export default store
// 在入口文件中引入 store 寝蹈,注冊
// 因為vue是一個樹形結(jié)構(gòu),只有把 store 注冊到入口文件根節(jié)點(diǎn)上登淘,下面那些子節(jié)點(diǎn)才都可以拿到 store對象

在組件中使用store
拿到state中的值

computed: {
    count () {
      return this.$store.state.count
    }
  }
// 通過mutations修改state中的值
mounted () {
    let i = 0
    console.log(this.$store)
    setInterval(() => {
      this.$store.commit('updateCount', i++)
    }, 1000)
  }

在大型項目或者可擴(kuò)展項目中可采用一下目錄結(jié)構(gòu)箫老,把每個模塊都拆分出來形成單獨(dú)的目錄


image.png

Ⅱ、getters的使用

作用:getters的作用就是對state中的原始數(shù)據(jù)做一些處理和封裝黔州,可以讓我們在組件中更好的使用耍鬓,相當(dāng)于組件中的computed
很多時候,后端提供我們的數(shù)據(jù)我們不能直接使用流妻,當(dāng)然我們可以拿到數(shù)據(jù)在組件中通過computed進(jìn)行處理牲蜀,但是很多頁面都需要處理數(shù)據(jù),每個頁面都寫computed就會造成代碼冗余绅这,維護(hù)也困難涣达,getters可以幫我們解決這個問題

export default {
  fullName (state) {
    return `${state.firstName} ${state.lastName}`
  }
}

Ⅲ、map 語法在state和getters中的使用

作用:使組件中屬性方法和store中模塊的屬性方法形成一個映射证薇,可以直接把state和getters中的值拿過來
注意:mapState和mapGetters是訪問度苔,拿到數(shù)據(jù),所以在組件的計算屬性 computed 里面混入

// 首先安裝一個轉(zhuǎn)義器包浑度,是Babel用來翻譯最新的語法的
npm i babel-present-stage-1 -D
// 在 .babelrc 文件中配置
{
  "presets": [
    "env", // babel-present-env 主要對javascript最新的語法糖進(jìn)行編譯寇窑,并不負(fù)責(zé)轉(zhuǎn)譯javascript新增的api和全局對象
    "stage-1" // babel-preset-stage-1,轉(zhuǎn)義器包箩张,要配合env使用疗认,包含一些插件,可以識別最新語法
  ],
  "plugins": [
    "transform-vue-jsx", // 解析 jsx 語法
    "syntax-dynamic-import" // 異步加載組件
  ]
}
// 現(xiàn)在就可以在組件中使用 對象展開運(yùn)算符 啦
import { mapState, mapGetters } from 'vuex'
...mapState(['count']) // 映射 this.count 為 store.state.count
...maoState({
  counter: 'count' 
})
...mapState({
  counter: (state) => state.counte // 通過函數(shù)可以對拿到的數(shù)據(jù)做一些處理
})
...mapGetters(['fullName'])

Ⅳ伏钠、Vuex之mutations和actions

1. mutations

代碼演示:

// mutations.js
export default { // 只有兩個參數(shù)
  updateCount (state, num) {
    state.count = num
  },
  firstName (state, num) {
    state.firstName = num
  }
}
// 多個數(shù)值的話
export default {
  updateCount (state, {num, num2}) {
    console.log(num2)
    state.count = num
  },
  firstName (state, num) {
    state.firstName = num
  }
}
// 組件中
this.$store.commit('updateCount', { // payload,載荷
   num: i++,
   num2: 2
})

注意:state中的值只能通過mutations中修改,其實在組件中可以通過
this.$store.state.count = 2也是可以修改的谨设,但是這樣修改的話熟掂,多人協(xié)作的話就會很困難,數(shù)據(jù)不利于維護(hù)扎拣。
所以我們一般會限制組件中直接修改 state 的值赴肚,做法是:
在開發(fā)環(huán)境下素跺,給 store 實例對象加上嚴(yán)格模式,這樣的話誉券,我們?nèi)绻诮M件中修改 state指厌,會有警告

const isDev = process.env.NODE_ENV === 'development'
export default () => {
  return new Vuex.Store({
    strict: isDev, // 在開發(fā)環(huán)境的時候使用
    state: defaultState,
    mutations: mutations,
    getters
  })
}

2. actions

作用:處理異步操作,或者一個動作要多次修改mutations踊跟,往往也用actions封裝一下

export default {
  updateCountAsync (ctx, data) { // context 上下文踩验, store 實例具有相同方法和屬性的 context 對象
    setTimeout(() => {
      ctx.commit('updateCount', {
        num: data.num
      })
    }, data.time)
  }
}
// 在組件中調(diào)用 actions,用 dispatch 方法
this.$store.dispatch('updateCountAsync', {
      num: 5,
      time: 2000
})

3. mapMutatios 和 mapActions 的用法

注意:這兩個map是操作商玫,所以要寫在 methods 里面
代碼如下:

methods: {
    ...mapActions(['updateCountAsync']), // 將 `this.updateCountAsync()` 映射為 `this.$store.dispatch('updateCountAsync')`
    ...mapMutations(['updateCount'])
  }
// 現(xiàn)在代碼就可以改了
this.$store.dispatch('updateCountAsync', { // store 對象的方法
      num: 5,
      time: 2000
})
this.updateCountAsync({ // 組件自身的方法
      num: 5,
      time: 2000
    })

Ⅴ箕憾、Vuex之模塊(module)

由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象拳昌。當(dāng)應(yīng)用變得非常復(fù)雜時袭异,store 對象就有可能變得相當(dāng)臃腫。
為了解決以上問題炬藤,Vuex 允許我們將 store 分割成模塊(module)御铃。每個模塊擁有自己的 state、mutation沈矿、action上真、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割

1. 模塊中的 state

// 在 store.js 中的store對象中配置如下:
modules: {
      a: {
        state: {
          text: 'a'
        }
      },
      b: {
        state: {
          text: 'b'
        }
      }
    }
// 在組件中調(diào)用
computed: {
    textA () {
      return this.$store.state.a.text
    }
  }

2. 模塊中的 mutations

注意: vuex默認(rèn)的會把全部的mutation都放在全局的命名空間中细睡,所以在不同的模塊也可以直接調(diào)用谷羞。
如果我們想把各個模塊的mutation就在自己的模塊,因為太多的mutation都放在全局溜徙,容易引發(fā)命名上的沖突湃缎,怎么辦?
我們可以給模塊加一個命名空間 namespaced: true,

modules: {
      // namespaced: true,
      a: {
        state: {
          text: 'a'
        },
        mutations: {
          updateText (state, text) {
            state.text = text
          }
        }
      }
    }
// 組件中使用
methods: {
    ...mapMutations([ 'updateText'])
  }
mounted () {
    this.updateText('123')
  }

為模塊加上命名空間蠢壹,如何調(diào)用嗓违?

methods: {
    ...mapMutations([ 'a/updateText'])
  }
mounted () {
    this['a/updateText']('123')
  }

3. 模塊中的 getters

modules: {
      a: {
        namespaced: true,
        state: {
          text: 'a'
        },
        mutations: {
          updateText (state, text) {
            state.text = text
          }
        },
        getters: {
          textPlus (state) {
            console.log(state.text)
            return state.text + 1
          }
        }
      }
    }
// 在項目中使用
computed: {
    // ...mapGetters(['a/textPlus']), // this['a/textPlus']調(diào)用
    ...mapGetters({
      fullName: 'fullName',
      textPlus: 'a/textPlus' // 可以直接在模板中使用 {{textPlus}}
    })
  }

默認(rèn)情況下,模塊內(nèi)部的 action图贸、mutation 和 getter 是注冊在全局命名空間的蹂季,當(dāng)我們加上命名空間后,變成局部的疏日,此時我們?nèi)绾文玫饺值?state 和 getter 呢偿洁?

getters: {
          textPlus (state, getters, rootState, rootGetters) {
          // state 當(dāng)前模塊的state對象
          // getters 當(dāng)前模塊的 getters 對象
          // rootState rootGetters 全局的,不僅可以取到根模塊的值沟优,其他模塊也可以
          //  return state.text + rootState.count
            return state.text + rootState.b.count
          }
        }

4. 模塊中的 actions

modules: {
      a: {
        namespaced: true,
        state: {
          text: 'a'
        },
        mutations: {
          updateText (state, text) {
            state.text = text
          }
        },
        getters: {
          textPlus (state, getters, rootState, rootGetters) {
            console.log(state.text)
            return state.text + rootState.b.text
          }
        },
        actions: {
          add (ctx) { // 上下文
            ctx.commit('updateText', 8)
          }
        }
      }
// 使用
methods: {
    ...mapActions(['updateCountAsync', 'a/add']),
    ...mapMutations(['updateCount', 'a/updateText'])
  }
mounted () {
  this['a/add']()
}

但是我們一般都用 ES6 的結(jié)構(gòu)賦值簡化

// 此時調(diào)用的 updateText 在 a 命名空間下涕滋,如果我們想要調(diào)取全局的mutation怎么辦?
actions: {
  add ({ state, commit, rootState }) { 
    commit('updateText', rootState.count)
  }
}
// 首先調(diào)用的mutation全局里面存在挠阁,加上參數(shù) {root:true}
actions: {
   add ({ state, commit, rootState }) {
     commit('updateCount', { num: '56789' }, { root: true })
   }
}

// 模塊的actions調(diào)用其他模塊的mutation
// b 模塊沒有加命名空間
b: {
        state: {
          text: 'b'
        },
        actions: {
          testAction ({ commit }) {
            commit('a/updateText', 'test action')
          }
        }
      }
methods: {
    ...mapActions(['updateCountAsync', 'a/add', 'testAction']),
    ...mapMutations(['updateCount', 'a/updateText'])
  }
mounted () {
  this.testAction()
}
// b 模塊加上命名空間呢
b: { 
        namespaced: true,
        state: {
          text: 'b'
        },
        actions: {
          testAction ({ commit }) {
            commit('a/updateText', 'test action', { root: true })
          }
        }
      }
methods: {
    ...mapActions(['updateCountAsync', 'a/add', 'b/testAction']),
    ...mapMutations(['updateCount', 'a/updateText'])
  }
mounted () {
  this['b/testAction']()
}

5. 動態(tài)注冊模塊

// 在有store對象的位置宾肺,一般在入口文件里溯饵,注冊
const store = createStore()
store.registerModule('c', {
  state: {
    text: 'ccc'
  }
})
store.unregisterModule('c') // 解綁一個model
// 使用方法與其他一樣

Ⅵ、 熱重載

當(dāng)我們修改store里面的代碼時锨用,我們發(fā)現(xiàn)都是刷新整個頁面進(jìn)行更新的, 在我們做webapp的時候丰刊,狀態(tài)經(jīng)常是變化的,如果因為修改了一下 store 中的數(shù)據(jù)增拥,刷新整個頁面啄巧,使得之前的操作記錄也會消失,浪費(fèi)時間
熱重載

export default () => {
  const store = new Vuex.Store({
    strict: isDev, // 在開發(fā)環(huán)境的時候使用
    state: defaultState,
    mutations: mutations,
    getters,
    actions
  })
  if (module.hot) {
    module.hot.accept([
      './state/state',
      './mutations/mutation',
      './getters/getter',
      './actions/actions'
    ], () => {
      const newState = require('./state/state').default
      const newMutations = require('./mutations/mutation').default
      const newGetters = require('./getters/getter').default
      const newActions = require('./actions/actions').default
      store.hotUpdate({
        state: newState,
        mutations: newMutations,
        getters: newGetters,
        actions: newActions
      })
    })
  }
  return store
}

Ⅶ跪者、vuex之其他一些API和配置

1. store.watch

第一個函數(shù)的返回值發(fā)生變化時棵帽,第二個函數(shù)就會執(zhí)行

store.watch((state) => state.count + 1, (newCount) => {
  console.log(`newCount: ${newCount}`)
})

2. store.subscribe(常用于插件)

監(jiān)聽mutations,調(diào)用的話渣玲,可以得到哪一個mutation變化逗概,以及變化的數(shù)據(jù)

store.subscribe((mutation, state) => {
  console.log(mutation.type)
  console.log(mutation.payload)
})
image.png

3. store.subscribeAction(常用于插件)

store.subscribeAction((action, state) => {
  console.log(action.type)
  console.log(action.payload)
})

4. vuex插件的制作

plugins 在vuex初始化的時候就已經(jīng)執(zhí)行,所以可以在里面使用上面subscribe 和 subscribeAction 訂閱一些內(nèi)容忘衍,來進(jìn)行一些操作

export default () => {
  const store = new Vuex.Store({
    strict: isDev, // 在開發(fā)環(huán)境的時候使用
    state: defaultState,
    mutations: mutations,
    getters,
    actions,
    plugins: [
      (store) => {
        console.log('my plugin invoked')
      }
    ]
})

5. vuex 和 vue-router 圖解

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逾苫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子枚钓,更是在濱河造成了極大的恐慌铅搓,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搀捷,死亡現(xiàn)場離奇詭異星掰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嫩舟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門氢烘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人家厌,你說我怎么就攤上這事播玖。” “怎么了饭于?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵蜀踏,是天一觀的道長。 經(jīng)常有香客問我掰吕,道長果覆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任殖熟,我火速辦了婚禮随静,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己燎猛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布照皆。 她就那樣靜靜地躺著重绷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膜毁。 梳的紋絲不亂的頭發(fā)上昭卓,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音瘟滨,去河邊找鬼候醒。 笑死,一個胖子當(dāng)著我的面吹牛杂瘸,可吹牛的內(nèi)容都是我干的倒淫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼败玉,長吁一口氣:“原來是場噩夢啊……” “哼敌土!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起运翼,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤返干,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后血淌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矩欠,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年悠夯,在試婚紗的時候發(fā)現(xiàn)自己被綠了癌淮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡疗疟,死狀恐怖该默,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情策彤,我是刑警寧澤栓袖,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站店诗,受9級特大地震影響裹刮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庞瘸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一捧弃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦违霞、人聲如沸嘴办。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涧郊。三九已至,卻和暖如春眼五,著一層夾襖步出監(jiān)牢的瞬間妆艘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工看幼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留批旺,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓诵姜,卻偏偏與公主長得像汽煮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子茅诱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容