Vue3基礎介紹大全


vue3.0正式版本已經發布超過一年,社區生態已經完善,所以,是時候開始使用vue3.0了

目前已支持 vue3 的UI組件庫

  • ant-design-vue
    https://antdv.com/docs/vue/introduce-cn/
    ant-design-vue 是 Ant Design 的 Vue 實現,組件的風格與 Ant Design 保持同步
    目前支持 Vue 3.0 的 2.0.0 測試版 已發布
  • element-plus
    https://element-plus.gitee.io/#/zh-CN
    Element Plus,一套為開發者、設計師和產品經理准備的基於 Vue 3.0 的桌面端組件庫
  • vant
    https://vant-contrib.gitee.io/vant/v3/#/zh-CN
    輕量、可靠的移動端 Vue 組件庫
    Vant 是有贊前端團隊開源的移動端組件庫,於 2016 年開源,已持續維護 4 年時間
    目前 Vant 已完成了對 Vue 3.0 的適配工作,並發布了 Vant 3.0 版本

Vue3帶來的新變化

  1. 性能提升
  • 首次渲染更快
  • diff算法更快
  • 內存占用更少
  • 打包體積變小
  1. 更好的Typescript支持
  2. Composition API (重點)

在使用vue2.x版本開發較復雜的組件時,邏輯難以復用,組合式api的出現可以解決此類問題

相關閱讀:

  1. Vue3 中文文檔  https://vue3js.cn/docs/zh/
  2. Vue3 設計理念  https://vue3js.cn/vue-composition/ 破壞性語法更新

vue3.0對於2.0版本的大部分語法都是可以兼容的,但是也有一些破壞性的語法更新,需要格外注意

  1. 實例方法$on移除   (eventBus現有實現模式不再支持 可以使用三方插件替代)
  2. 過濾器filter移除 (插值表達式里不能再使用過濾器 可以使用methods替代)
  3. .sync語法移除  (和v-model語法合並)

Vue3開發環境搭建

項目搭建成功后,可以看一下package.json文件,在dependencies配置項中顯示,當前使用的版本為3.0.0

"dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0"
}

項目環境可以查看我寫的另一篇文章:https://www.cnblogs.com/panwudi/p/16086040.html

然后打開main.js 入口文件,發現Vue的實例化發生了一些變化,由先前的new關鍵詞實例化,轉變為createApp方法的調用形式

// vue2.x
new Vue({
  el: '#app',
  render: h => h(App)
})

// vue3.x
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

打開一個單文件組件發現,vue3.0的單文件組件中不再強制要求必須有唯一根元素

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

組合式API

組合式api(Composition API)算是vue3對開發者來說非常有價值的一個api更新,先不關注具體語法,先對它有一個大的感知

composition vs options

options API開發出來的vue應用如左圖所示,它的特點是理解容易,因為各個選項都有固定的書寫位置,比如響應式數據就寫到data選擇中,操作方法就寫到methods配置項中等,應用大了之后,相信大家都遇到過來回上下找代碼的困境

composition API開發的vue應用如右圖所示,它的特點是特定功能相關的所有東西都放到一起維護,比如功能A相關的響應式數據,操作數據的方法等放到一起,這樣不管應用多大,都可以快讀定位到某個功能的所有相關代碼,維護方便,設置如果功能復雜,代碼量大,還可以進行邏輯拆分處理

特別注意:

  1. 選項式api和組合式api倆種風格是並存的關系 並不是非此即彼
  2. 需要大量的邏輯組合的場景,可以使用compition API進行增強

案例對比

1.案例1

倆個獨立的功能:

  1. 通過點擊按鈕來控制div的顯示和隱藏
  2. 通過點擊按鈕控制div內字體顏色的變化

vue2.x option Api版本

<template>
  <div>
    <!-- 功能一模板 -->
    <button @click="show">顯示</button>
    <button @click="hide">隱藏</button>
    <div v-if="showDiv">一個被控制顯隱的div</div>
  </div>
  <div>
    <!-- 功能二模板 -->
    <button @click="changeRed">紅色</button>
    <button @click="changeYellow">藍色</button>
    <div :style="`color:${fontColor}`">一個被控制字體顏色的的div</div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      showDiv: true, // 功能一數據
      fontColor: '' // 功能二數據
    }
  },
  methods: {
    // 功能一方法
    show() {
      this.showDiv = true
    },
    hide() {
      this.showDiv = false
    },
    // 功能二方法
    changeRed() {
      this.fontColor = 'red'
    },
    changeYellow() {
      this.fontColor = 'blue'
    }
  }
}
</script>

vue3.0 composition api版本

<template>
  <div>
    <!-- 功能一模板 -->
    <button @click="show">顯示</button>
    <button @click="hide">隱藏</button>
    <div v-if="showDivFlag">一個被控制顯隱的div</div>
  </div>
  <div>
    <!-- 功能二模板 -->
    <button @click="changeRed">紅色</button>
    <button @click="changeBlue">藍色</button>
    <div :style="`color:${fontColor}`">一個被控制字體顏色的的div</div>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'App',
  setup() {
    // 功能一
    const showDivFlag = ref(true)
    function show() {
      showDivFlag.value = true
    }
    function hide() {
      showDivFlag.value = false
    }
    // 功能二

    const fontColor = ref('')
    function changeRed() {
      fontColor.value = 'red'
    }
    function changeBlue() {
      fontColor.value = 'blue'
    }
    return { showDivFlag, show, hide, fontColor, changeRed, changeBlue }
  }
}
</script>

composition api版本優化

<script>
import { ref } from 'vue'
function useShow() {
  const showDivFlag = ref(true)
  function show() {
    showDivFlag.value = true
  }
  function hide() {
    showDivFlag.value = false
  }
  return { showDivFlag, show, hide }
}

function useColor() {
  const fontColor = ref('')
  function changeRed() {
    fontColor.value = 'red'
  }
  function changeBlue() {
    fontColor.value = 'blue'
  }
  return { fontColor, changeRed, changeBlue }
}
export default {
  name: 'App',
  setup() {
    // 功能一
    const { showDivFlag, show, hide } = useShow()
    // 功能二
    const { fontColor, changeRed, changeBlue } = useColor()
    return { showDivFlag, show, hide, fontColor, changeRed, changeBlue }
  }
}
</script>

通過定義功能函數,把倆個功能相關的代碼各自抽離到一個獨立的小函數中,然后通過在setUp函數中再把倆個小功能函數組合起來,這樣一來,既可以把setup函數變得清爽,又可以方便維護快速定位功能位置

setup入口函數

  1. setup 函數是一個新的組件選項,作為組件中組合式API 的起點(入口)
  2. setup 中不能使用 this, this 指向 undefined
  3. setup函數只會在組件初始化的時候執行一次
  4. setup函數在beforeCreate生命周期鈎子執行之前執行
export default {
  setup () {
    console.log('setup執行了')
    console.log(this)
  },
  beforeCreate() {
    console.log('beforeCreate執行了')
    console.log(this)
  }
}

響應式系統API

reactive 函數

作用:reactive是一個函數,接收一個普通的對象傳入,把對象數據轉化為響應式對象並返回

使用步驟

  1. 從vue框架中導入reactive函數
  2. 在setup函數中調用reactive函數並將對象數據傳入
  3. 在setup函數中把reactive函數調用完畢之后的返回值以對象的形式返回出去
<template>
  <div>{{ state.name }}</div>
  <div>{{ state.age }}</div>
  <button @click="state.name = 'yxx'">改值</button>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup () {
    const state = reactive({
      name: 'yx',
      age: 18
    })
    return {
      state
    }
  }
}
</script>

ref 函數

作用:ref是一個函數,接受一個簡單類型或者復雜類型的傳入並返回一個響應式且可變的 ref 對象

使用步驟

  1. 從vue框架中導出ref函數
  2. 在setup函數中調用ref函數並傳入數據(簡單類型或者復雜類型)
  3. 在setup函數中把ref函數調用完畢的返回值以對象的形式返回出去
  4. 注意:在setup函數中使用ref結果,需要通過.value  訪問,模板中使用不需要加.value
<template>
  <div>{{ money }}</div>
  <button @click="changeMondy">改值</button>
</template>

<script>
import { ref } from 'vue'
export default {
  setup() {
    let money = ref(18)
    console.log(money.value)
    return {
      money
    }
  }
}
</script>

總結說明:

  1. ref 函數可以接收一個簡單類型的值,返回一個可改變的 ref 響應式對象,從而彌補reactive函數不支持簡單類型的問題
  2. reactive和ref函數都可以提供響應式數據的轉換,具體什么時候需要使用哪個API社區還沒有最佳實踐,大家暫時可以使用自己熟練的API進行轉換
  3. 推薦一種寫法:只有明確知道要轉換的對象內部的字段名稱我們才使用reactive,否則就一律使用ref,從而降低在語法選擇上的心智負擔

toRefs 函數

注意:經過reactive函數處理之后返回的對象,如果給這個對象解構或者展開,會讓數據丟失響應式的能力,為了解決這個問題需要引入toRefs函數,使用 toRefs函數 可以保證該對象展開的每個屬性都是響應式的

使用之前reactive函數的例子,如果想在模板中省略到state,直接書寫name和age,你可能會想到,那我在return出去的時候把state中的屬性解構.

<template>
  <div>{{ name }}</div>
  <div>{{ age }}</div>
  <button @click="name = 'yxx'">改值</button>
</template>
<script>
import { reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'yx',
      age: 18
    })
    return {
      ...state
    }
  }
}
</script>

點擊改值按鈕,發現視圖已經不發生變化了,如果解構reactive的返回值,將破壞調用響應式特性,這時就需要使用toRefs方法進行處理了

經過toRefs包裹處理會發現,數據恢復了響應式特性

<template>
  <div>{{ name }}</div>
  <div>{{ age }}</div>
  <button @click="name = 'yxx'">改值</button>
</template>

<script>
import { reactive,toRefs } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'yx',
      age: 18
    })
    return {
      ...toRefs(state)
    }
  }
}
</script>

computed計算屬性

作用:根據現有響應式數據經過一定的計算得到全新的數據

下面講介紹如何在vue3中使用計算屬性

使用步驟

  1. 從vue框架中導入computed 函數
  2. 在setup函數中執行computed函數,並傳入一個函數,在函數中定義計算公式
  3. 把computed函數調用完的執行結果放到setup的return值對象中
<template>
  {{ list }}
  {{ filterList }}
  <button @click="changeList">change list</button>
</template>

<script>
import { computed, ref } from 'vue'
export default {
  setup() {
    const list = ref([1, 2, 3, 4, 5])
    // 輸入大於3的數字
    const filterList = computed(() => {
      return list.value.filter(item => item > 3)
    })
    // 修改list的函數
    function changeList() {
      list.value.push(6, 7, 8)
    }
    return {
      list,
      filterList,
      changeList
    }
  }
}
</script> 

watch 偵聽器

作用:基於響應式數據的變化執行回調邏輯,和vue2中的watch的功能完全一致

  1. 普通監聽
  2. 立即執行
  3. 深度監聽

下面講介紹如何在vue3中使用偵聽器

使用步驟

  1. 從vue框架中導入watch函數
  2. 在setup函數中執行watch函數開啟對響應式數據的監聽
  3. watch函數接收三個常規參數
    1. 第一個參數為函數,返回你要監聽變化的響應式數據
    2. 第二個參數為響應式數據變化之后要執行的回調函數
    3. 第三個參數為一個對象,在里面配置是否開啟立刻執行或者深度監聽

(1)普通監聽

<template>
  {{ age }}
  <button @click="age++">change age</button>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const age = ref(18)
    watch(() => {
      // 返回你想要監聽的響應式屬性(ref產生的對象必須加.value)
      return age.value
    }, () => {
      // 數據變化之后的回調函數
      console.log('age發生了變化')
    })
    return {
      age
    }
  }
}
</script> 

(2)開啟立即執行

watch的效果默認狀態下,只有監聽的數據發生變化才會執行回調,如果你需要在一上來的時候就立刻執行一次,需要配置一下immediate屬性

<template>
  {{ age }}
  <button @click="age++">change age</button>
</template>
<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const age = ref(18)
    watch(() => {
      // 返回你想要監聽的響應式屬性(ref產生的對象必須加.value)
      return age.value
    }, () => {
      // 數據變化之后的回調函數
      console.log('age發生了變化')
    },{ immediate: true})
    return {
      age
    }
  }
}
</script> 

(3)開啟深度監聽

當我們監聽的數據是一個對象的時候,默認狀態下,對象內部的屬性發生變化是不會引起回調函數執行的,如果想讓對象下面所有屬性都能得到監聽,需要開啟deep配置

<template>
  {{ name }}
  {{ info.age }}
  <button @click="name = 'pink'">change name</button>
  <button @click="info.age++">change age</button>
</template>

<script>
import { reactive, toRefs, watch } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'yx',
      info: {
        age: 18
      }
    })
    watch(() => {
      return state
    }, () => {
      // 數據變化之后的回調函數
      console.log('age發生了變化')
    }, {
      deep: true
    })
    return {
      ...toRefs(state)
    }
  }
}
</script> 

使用watch的時候,盡量詳細的表明你到底要監聽哪個屬性,開啟deep深度監聽后,會無腦監聽當前對象的全部屬性,避免使用deep引起的性能問題,比如我僅僅只是想在state對象的age屬性變化的時候執行回調

<template>
  {{ name }}
  {{ info.age }}
  <button @click="name = 'pink'">change name</button>
  <button @click="info.age++">change age</button>
</template>

<script>
import { reactive, toRefs, watch } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'yx',
      info: {
        age: 18
      }
    })
    watch(() => {
      // 詳細的告知你要監聽誰
      return state.info.age
    }, () => {
      // 數據變化之后的回調函數
      console.log('age發生了變化')
    })
    return {
      ...toRefs(state)
    }
  }
}
</script> 

生命周期函數

使用步驟

  1. 先從vue中導入以on打頭的生命周期鈎子函數
  2. 在setup函數中調用生命周期函數並傳入回調函數
  3. 生命周期鈎子函數可以調用多次
<template>
  <div>生命周期函數</div>
</template>

<script>
import { onMounted } from 'vue'
export default {
  setup() {
    // 時機成熟 回調函數自動執行
    onMounted(() => {
      console.log('mouted生命周期執行了')
    })
     onMounted(() => {
      console.log('mouted生命周期函數又執行了')
    })
  }
}
</script> 

選項式API

組合式API

beforeCreate

不需要(直接寫到setup函數中)

created

不需要(直接寫到setup函數中)

beforeMount

onBeforeMount

mounted

onMounted

beforeUpdate

onBeforeUpdate

updated

onUpdated

beforeDestroyed

onBeforeUnmount

destroyed

onUnmounted

父子通信

在vue3的組合式API中,父傳子的基礎套路完全一樣,基礎思想依舊為:父傳子是通過prop進行傳入,子傳父通過調用自定義事件完成

實現步驟

  1. setup函數提供倆個參數,第一個參數為props,第二個參數為一個對象context
  2. props為一個對象,內部包含了父組件傳遞過來的所有prop數據,context對象包含了attrs,slots, emit屬性,其中的emit可以觸發自定義事件的執行從而完成子傳父

代碼落地

app.vue

<template>
  <son :name="name" @get-msg="getMsg"></son>
</template>

<script>
import { ref } from 'vue'
import Son from './components/son'
export default {
  components: {
    Son
  },
  setup() {
    const name = ref('cp')
    function getMsg(msg) {
      console.log(msg)
    }
    return {
      name,
      getMsg
    }
  }
}
</script>
components/son.vue
<template>
  <div>
    {{name}}
    <button @click="setMsgToSon">set</button>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String
    }
  },
  emits: ['get-msg'], // 聲明當前組件觸發的自定義事件
  setup(props,{emit}) {
    console.log(props.name)
    function setMsgToSon(){
      emit('get-msg','這是一條來自子組件的msg信息')
    }
    return {
      setMsgToSon
    }
  }
}
</script>

provide 和 inject

通常我們使用props進行父子之間的數據傳遞,但是如果組件嵌套層級較深,一層一層往下傳遞將會變的非常繁瑣,有沒有一種手段可以把這個過程簡化一下呢,有的,就是我們馬上要學習的provide 和 inject,它們配合起來可以方便的完成跨層傳遞數據

1. 基礎使用

來個需求: 爺組件中有一份數據 傳遞給孫組件直接使用

實現步驟:

  1. 頂層組件在setup方法中使用provide函數提供數據
  2. 任何底層組件在setup方法中使用inject函數獲取數據

代碼落地

爺爺組件 - app.vue

<template>
  <father></father>
</template>

<script>
import Father from '@/components/Father'
import { provide } from 'vue'
export default {
  components: {
    Father
  },
  setup() {
    let name = '你想吃火鍋嗎'
    // 使用provide配置項注入數據 key - value
    provide('name', name)
  }
}
</script>
孫組件 - components/Son.vue
<template>
  我是子組件
  {{ name }}
</template>

<script>
import { inject } from 'vue'
export default {
  setup() {
    const name = inject('name')
    return {
      name
    }
  }
}
</script>

事實上,只要是后代組件,都可以方便的獲取頂層組件提供的數據

2. 傳遞響應式數據

provide默認情況下傳遞的數據不是響應式的,也就是如果對provide提供的數據進行修改,並不能響應式的影響到底層組件使用數據的地方,如果想要傳遞響應數據也非常簡單,只需要將傳遞的數據使用ref或者reactive生成即可

掌握如何通過provide/inject傳遞響應式數據

app.vue

<template>
  <father></father>
  <button @click="changeName">change name</button>
</template>

<script>
import Father from '@/components/Father'
import { provide, ref } from 'vue'
export default {
  components: {
    Father
  },
  setup() {
    // 使用ref轉換成響應式再傳遞
    let name = ref('你想吃火鍋嗎')
    function changeName(){
      name.value = 'pink'
    }
    provide('name', name)
    return {
      changeName
    }
  }
}
</script>

模板中 ref 的使用

在模板中使用ref,我們都很清楚,它一般有三種使用場景

  1. ref + 普通dom標簽  獲取真實dom對象
  2. ref + 組件標簽  獲取組件實例對象
  3. ref + v-for 獲取由dom對象(實例對象)組成的數組  (不經常使用

實現步驟

  1. 使用ref函數傳入null創建 ref對象 =>  const hRef = ref(null)
  2. 模板中通過定義ref屬性等於1中創建的ref對象名稱建立關聯  =>  <h1 ref="hRef"></h1>
  3. 使用 => hRef.value

代碼落地

components/RefComponent.vue

<template>
  我是一個普通的組件
</template>
app.vue
<template>
  <h1 ref="h1Ref">我是普通dom標簽</h1>
  <ref-comoonent ref="comRef"></ref-comoonent>
</template>

<script>
import { onMounted, ref } from 'vue'
import RefComoonent from '@/components/RefComponent'
export default {
  components: {
    RefComoonent
  },
  setup() {
    const h1Ref = ref(null)
    const comRef = ref(null)
    onMounted(() => {
      console.log(h1Ref.value)
      console.log(comRef.value)
    })
    // 必須return
    return {
      h1Ref,
      comRef
    }
  }
}
</script>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM