Vue 基本使用知識點整理

Vue 基本使用

指令,插值

  • 插值,表達(dá)式
  • 指令,動態(tài)屬性
  • v-html:會有 XSS 風(fēng)險,會覆蓋子組件
<template>
    <div>
        <p>文本插值 {{message}}</p>
        <p>JS 表達(dá)式 {{ flag ? 'yes' : 'no' }} (只能是表達(dá)式,不能是 js 語句)</p>

        <p :id="dynamicId">動態(tài)屬性 id</p>

        <hr/>
        <p v-html="rawHtml">
            <span>有 xss 風(fēng)險</span>
            <span>【注意】使用 v-html 之后,將會覆蓋子元素</span>
        </p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            message: 'hello vue',
            flag: true,
            rawHtml: '指令 - 原始 html <b>加粗</b> <i>斜體</i>',
            dynamicId: `id-${Date.now()}`
        }
    }
}
</script>

computed 和 watch

  • computed 有緩存,data 不變則不會重新計算
  • computed 可以設(shè)置 set 和 get 方法
  • watch 深度監(jiān)聽
    • watch 默認(rèn)不是深度監(jiān)聽
    • 啟動深度監(jiān)聽的話需要設(shè)置 deep 為 true
  • watch 監(jiān)聽引用類型,拿不到 old value

computed demo

<template>
    <div>
        <p>num {{num}}</p>
        <p>double1 {{double1}}</p>
        <input v-model="double2"/>
    </div>
</template>

<script>
export default {
    data() {
        return {
            num: 20
        }
    },
    computed: {
        double1() {
            return this.num * 2
        },
        double2: {
            get() {
                return this.num * 2
            },
            set(val) {
                this.num = val/2
            }
        }
    }
}
</script>

watch demo

<template>
    <div>
        <input v-model="name"/>
        <input v-model="info.city"/>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: '吉良吉影',
            info: {
                city: '上海'
            }
        }
    },
    watch: {
        name(oldVal, val) {
            console.log('watch name', oldVal, val) // 值類型,可正常拿到 oldVal 和 val
        },
        info: {
            handler(oldVal, val) {
                console.log('watch info', oldVal, val) // 引用類型,拿不到 oldVal 。因為指針相同,此時已經(jīng)指向了新的 val
            },
            deep: true // 深度監(jiān)聽
        }
    }
}
</script>

class 和 style

注意事項

  • 使用動態(tài)屬性
  • 使用駝峰式寫法

代碼演示

<template>
    <div>
        <p :class="{ black: isBlack, yellow: isYellow }">使用 class</p>
        <p :class="[black, yellow]">使用 class (數(shù)組)</p>
        <p :style="styleData">使用 style</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            isBlack: true,
            isYellow: true,

            black: 'black',
            yellow: 'yellow',

            styleData: {
                fontSize: '40px', // 轉(zhuǎn)換為駝峰式
                color: 'red',
                backgroundColor: '#ccc' // 轉(zhuǎn)換為駝峰式
            }
        }
    }
}
</script>

<style scoped>
    .black {
        background-color: #999;
    }
    .yellow {
        color: yellow;
    }
</style>

條件渲染

  • v-if , v-else 的用法,可使用變量,也可使用 === 表達(dá)式
  • v-if 和 v-show 的區(qū)別?
    • v-if 會直接銷毀和加載 DOM
    • v-show 修改 display 屬性為顯示或隱藏
  • v-if 和 v-show 的使用場景?
    • 頻繁切換顯示隱藏場景使用 v-show
    • 非頻繁切換顯示隱藏場景(如切換注冊/登錄)可使用 v-if

代碼演示

<template>
    <div>
        <p v-if="type === 'a'">A</p>
        <p v-else-if="type === 'b'">B</p>
        <p v-else>other</p>

        <p v-show="type === 'a'">A by v-show</p>
        <p v-show="type === 'b'">B by v-show</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            type: 'a'
        }
    }
}
</script>

循環(huán)(列表)渲染

注意事項

  • v-for 可遍歷對象
  • key 不可亂寫(如 random 或者 index)
  • v-for 和 v-if 不建議一起使用!

代碼演示

<template>
    <div>
        <p>遍歷數(shù)組</p>
        <ul>
            <li v-for="(item, index) in listArr" :key="item.id">
                {{index}} - {{item.id}} - {{item.title}}
            </li>
        </ul>

        <p>遍歷對象</p>
        <ul >
            <li v-for="(val, key, index) in listObj" :key="key">
                {{index}} - {{key}} -  {{val.title}}
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    data() {
        return {
            flag: false,
            listArr: [
                { id: 'a', title: '標(biāo)題1' }, // 數(shù)據(jù)結(jié)構(gòu)中,最好有 id ,方便使用 key
                { id: 'b', title: '標(biāo)題2' },
                { id: 'c', title: '標(biāo)題3' }
            ],
            listObj: {
                a: { title: '標(biāo)題1' },
                b: { title: '標(biāo)題2' },
                c: { title: '標(biāo)題3' },
            }
        }
    }
}
</script>

事件

  • event參數(shù),自定義參數(shù)
    • 在不傳自定義參數(shù)的情況下,event 對象可以直接在函數(shù)形參獲取
    • 傳入自定義參數(shù)情況下,需要將 $event 傳入
    • 此 event 是原生的 event 對象
  • 事件修飾符,按鍵修飾符
    • stop 阻值點擊事件繼續(xù)傳播
    • prevent 提交事件不再重載頁面
    • v-on:click.stop.prevent 修飾符可以串聯(lián)使用
  • 【觀察】事件被綁定到哪里?
    • Vue 的事件是被掛載到當(dāng)前元素的

代碼演示

<template>
    <div>
        <p>{{num}}</p>
        <button @click="increment1">+1</button>
        <button @click="increment2(2, $event)">+2</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            num: 0
        }
    },
    methods: {
        increment1(event) {
            console.log('event', event, event.__proto__.constructor) // 是原生的 event 對象
            console.log(event.target)
            console.log(event.currentTarget) // 注意,事件是被注冊到當(dāng)前元素的,和 React 不一樣
            this.num++

            // 1. event 是原生的
            // 2. 事件被掛載到當(dāng)前元素
            // 和 DOM 事件一樣
        },
        increment2(val, event) {
            console.log(event.target)
            this.num = this.num + val
        },
        loadHandler() {
            // do some thing
        }
    },
    mounted() {
        window.addEventListener('load', this.loadHandler)
    },
    beforeDestroy() {
        //【注意】用 vue 綁定的事件,組建銷毀時會自動被解綁
        // 自己綁定的事件,需要自己銷毀?。?!
        window.removeEventListener('load', this.loadHandler)
    }
}
</script>

表單

  • v-model 雙向數(shù)據(jù)綁定
  • 常見表單項 textarea checkbox radio select
  • 修飾符 lazy(類似防抖效果) number(只能輸入數(shù)字) trim(去除兩邊空格)

代碼演示

<template>
    <div>
        <p>輸入框: {{name}}</p>
        <input type="text" v-model.trim="name"/>
        <input type="text" v-model.lazy="name"/>
        <input type="text" v-model.number="age"/>

        <p>多行文本: {{desc}}</p>
        <textarea v-model="desc"></textarea>
        <!-- 注意,<textarea>{{desc}}</textarea> 是不允許的?。?! -->

        <p>復(fù)選框 {{checked}}</p>
        <input type="checkbox" v-model="checked"/>

        <p>多個復(fù)選框 {{checkedNames}}</p>
        <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
        <label for="jack">Jack</label>
        <input type="checkbox" id="john" value="John" v-model="checkedNames">
        <label for="john">John</label>
        <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
        <label for="mike">Mike</label>

        <p>單選 {{gender}}</p>
        <input type="radio" id="male" value="male" v-model="gender"/>
        <label for="male">男</label>
        <input type="radio" id="female" value="female" v-model="gender"/>
        <label for="female">女</label>

        <p>下拉列表選擇 {{selected}}</p>
        <select v-model="selected">
            <option disabled value="">請選擇</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>

        <p>下拉列表選擇(多選) {{selectedList}}</p>
        <select v-model="selectedList" multiple>
            <option disabled value="">請選擇</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: '吉良吉影',
            age: 33,
            desc: '自我介紹',

            checked: true,
            checkedNames: [],

            gender: 'male',

            selected: '',
            selectedList: []
        }
    }
}
</script>

其他

  • v-once 讓某元素標(biāo)簽只渲染一次
  • ref 寫在標(biāo)簽上可以通過 this.$refs 獲取操作 DOM 節(jié)點
    • ref 還可以寫在子組件上,從而獲取子組件的引用

組件

props 和 $emit

  • props 父組件向子組件傳遞信息
  • $emit 子組件向父組件觸發(fā)一個事件
  • 實現(xiàn)父子組件之間通訊

父組件

<template>
    <div>
        <Input @add="addHandler"/>
        <List :list="list" @delete="deleteHandler"/>
    </div>
</template>

<script>
import Input from './Input'
import List from './List'

export default {
    components: {
        Input,
        List
    },
    data() {
        return {
            list: [
                {
                    id: 'id-1',
                    title: '標(biāo)題1'
                },
                {
                    id: 'id-2',
                    title: '標(biāo)題2'
                }
            ]
        }
    },
    methods: {
        addHandler(title) {
            this.list.push({
                id: `id-${Date.now()}`,
                title
            })
        },
        deleteHandler(id) {
            this.list = this.list.filter(item => item.id !== id)
        }
    },
    created() {
        console.log('index created')
    },
    mounted() {
        console.log('index mounted')
    },
    beforeUpdate() {
        console.log('index before update')
    },
    updated() {
        console.log('index updated')
    },
}
</script>

Input 組件

<template>
    <div>
        <input type="text" v-model="title"/>
        <button @click="addTitle">add</button>
    </div>
</template>

<script>
import event from './event'

export default {
    data() {
        return {
            title: ''
        }
    },
    methods: {
        addTitle() {
            // 調(diào)用父組件的事件
            this.$emit('add', this.title)

            // 調(diào)用自定義事件
            event.$emit('onAddTitle', this.title)

            this.title = ''
        }
    }
}
</script>

List 組件

<template>
    <div>
        <ul>
            <li v-for="item in list" :key="item.id">
                {{item.title}}

                <button @click="deleteItem(item.id)">刪除</button>
            </li>
        </ul>
    </div>
</template>

<script>
import event from './event'

export default {
    // props: ['list']
    props: {
        // prop 類型和默認(rèn)值
        list: {
            type: Array,
            default() {
                return []
            }
        }
    },
    data() {
        return {

        }
    },
    methods: {
        deleteItem(id) {
            this.$emit('delete', id)
        },
        addTitleHandler(title) {
            console.log('on add title', title)
        }
    },
    created() {
        console.log('list created')
    },
    mounted() {
        console.log('list mounted')

        // 綁定自定義事件
        event.$on('onAddTitle', this.addTitleHandler)
    },
    beforeUpdate() {
        console.log('list before update')
    },
    updated() {
        console.log('list updated')
    },
    beforeDestroy() {
        // 及時銷毀,否則可能造成內(nèi)存泄露
        event.$off('onAddTitle', this.addTitleHandler)
    }
}
</script>

自定義事件

  • 創(chuàng)建一個 event.js (如下圖)
  • 通過在 event 上綁定自定義事件,調(diào)用自定義事件實現(xiàn)兄弟組件之間通訊(上圖代碼所示)
// event.js
import Vue from 'vue'

export default new Vue()

生命周期

單個組件

生命周期圖示

帶有父子組件的生命周期

  • 初始化父子組件執(zhí)行順序

先觸發(fā)父元素,然后子元素先進(jìn)行,父元素收尾。

父beforeCreate => 父created => 父beforeMount => 子beforeCreate => 子created => 子beforeMount

=> 子mounted => 父mounted

先父組件創(chuàng)建虛擬dom,再子組件創(chuàng)建虛擬dom 。初始化先保證父組件初始化完再初始化子組件。(外 => 內(nèi))

然后子組件渲染,再父組件渲染 。渲染先保證子組件渲染完再渲染父組件 (內(nèi) => 外)

最先開始創(chuàng)建的是最外層組件,但是最先創(chuàng)建完的是最內(nèi)層組件。 => 類似先進(jìn)后出(棧)。

  • 刪除子組件觸發(fā)生命周期順序

先觸發(fā)父元素,然后子元素先進(jìn)行,父元素收尾。

父beforeUpdate => 子beforeDestroy => 子destroyed => 父updated

  • 更新子組件觸發(fā)生命周期順序

先觸發(fā)父元素,然后子元素先進(jìn)行,父元素收尾。

父beforeUpdate => 子beforeUpdate => 子updated => 父updated

首先是修改父組件的data,所以觸發(fā)父組件的beforeUpdate,它要把更新后數(shù)據(jù)再傳遞給給子組件,子組件更新props,因此再觸發(fā)子組件的beforeUpdate,緊接著渲染頁面觸發(fā)子組件的updated,只有子組件渲染完頁面后,這個時候父組件也就渲染完,觸發(fā)父組件的updated。

Non-Props

  • 父組件向子組件傳值,子組件沒有通過 props 接收,那么子組件會把該屬性變成子組件最外層的 DOM 上的一個屬性
  • 如果不希望發(fā)生此特性那么可以在子組件上加 一條屬性:inheritAttrs: false
  • 子組件可以通過 this.$attrs 手動獲取 non-props 屬性

插槽

  • slot 中使用的作用域的問題

    • 父模板里調(diào)用的數(shù)據(jù)屬性,使用的都是父模板里的數(shù)據(jù)
    • 子模板里調(diào)用的數(shù)據(jù)屬性,使用的都是子模板里的數(shù)據(jù)
  • 具名插槽

    • 父組件傳遞時加上屬性 v-slot="..." 可簡寫為 #...
    • 子組件<slot name="..."></slot>
  • 作用域插槽

    • 代碼演示:

    • const app = Vue.createApp({
          template: `
              <List v-slot="slotProps">   // 可簡寫為 ES6 解構(gòu)形式 v-slot="{ item }", 則下面直接用 item 即可
                  <span>{{ slotProps.item }}</span>   // 2. slotProps.item 就是子組件傳入的數(shù)據(jù)
              </List>
          `
      })
      
      app.component('List', {
          data() { return { list: [1, 2, 3] } },
          template: `
              <div>
                  <slot v-for="item in list" :item=item />    // 1. 將item傳給父組件
              </div>
          `
      })
      
    • 分析上面代碼:

    • 父組件調(diào)子組件的時候傳入 slot 進(jìn)來

    • 子組件通過 :item=item 把數(shù)據(jù)傳給父組件

    • 作用域插槽解決了什么問題?

      • 當(dāng)子組件渲染的內(nèi)容要由父組件決定的情況
      • 通過作用域插槽,可以讓使父組件調(diào)用子組件里的數(shù)據(jù)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容