vue3學(xué)習(xí)

dhmeitu006.jpg

01-為什么學(xué)vue3

Vue3現(xiàn)狀:

  • vue-next (opens new window)2020年09月18日,正式發(fā)布vue3.0版本。但是由于剛發(fā)布周邊生態(tài)不支持,大多數(shù)開發(fā)者處于觀望。
  • 現(xiàn)在主流組件庫都已經(jīng)發(fā)布了支持vue3.0的版本,其他生態(tài)也在不斷地完善中,這是趨勢。

Vue3優(yōu)點(diǎn):

  • 最火框架,它是國內(nèi)最火的前端框架之一,官方文檔 (opens new window)中文文檔(opens new window)
  • 性能提升,運(yùn)行速度事vue2.x的1.5倍左右
  • 體積更小,按需編譯體積比vue2.x要更小
  • 類型推斷,更好的支持Ts(typescript)這個也是趨勢
  • 高級給予,暴露了更底層的API和提供更先進(jìn)的內(nèi)置組件
  • ★組合API (composition api) ,能夠更好的組織邏輯,封裝邏輯,復(fù)用邏輯

02-選項(xiàng)API和組合API

什么是選項(xiàng)API寫法:Options ApI
咱們在vue2.x項(xiàng)目中使用的就是 選項(xiàng)API 寫法

  • 代碼風(fēng)格:data選項(xiàng)寫數(shù)據(jù),methods選項(xiàng)寫函數(shù)...,一個功能邏輯的代碼分散。
  • 優(yōu)點(diǎn):易于學(xué)習(xí)和使用,寫代碼的位置已經(jīng)約定好
  • 缺點(diǎn):代碼組織性差,相似的邏輯代碼不便于復(fù)用,邏輯復(fù)雜代碼多了不好閱讀。
  • 補(bǔ)充:雖然提供mixins用來封裝邏輯,但是出現(xiàn)數(shù)據(jù)函數(shù)覆蓋的概率很大,不好維護(hù)。
<template>
  <div class="container">
    <div>鼠標(biāo)位置:</div>
    <div>X軸:{{x}}</div>
    <div>Y軸:{{y}}</div>
    <hr>
    <div>{{count}} <button @click="add()">自增</button></div>  
  </div>
</template>
<script>
export default {
  name: 'App',
  data () {
    return {
      x: 0,
      y: 0,
      count: 0
    }
  },
  mounted() {
    document.addEventListener('mousemove', this.move)
  },
  methods: {
    move(e) {
      this.x = e.pageX
      this.y = e.pageY
    },
    add () {
        this.count++
    }    
  },
  destroyed() {
    document.removeEventListener('mousemove', this.move)
  }
}
</script>

什么是組合API寫法:Compositon API
咱們在vue3.0項(xiàng)目中將會使用 組合API 寫法

  • 代碼風(fēng)格:一個功能邏輯的代碼組織在一起(包含數(shù)據(jù),函數(shù)...)
  • 優(yōu)點(diǎn):功能邏輯復(fù)雜繁多情況下,各個功能邏輯代碼組織再一起,便于閱讀和維護(hù)
  • 缺點(diǎn):需要有良好的代碼組織能力和拆分邏輯能力,PS:大家沒問題。
  • 補(bǔ)充:為了能讓大家較好的過渡到vue3.0的版本來,也支持vue2.x選項(xiàng)API寫法
<template>
  <div class="container">
    <div>鼠標(biāo)位置:</div>
    <div>X軸:{{x}}</div>
    <div>Y軸:{{y}}</div>
    <hr>
    <div>{{count}} <button @click="add()">自增</button></div>  
  </div>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref, toRefs } from 'vue'
export default {
  name: 'App',
  setup () {
    // 鼠標(biāo)移動邏輯
    const mouse = reactive({
      x: 0,
      y: 0
    })
    const move = e => {
      mouse.x = e.pageX
      mouse.y = e.pageY
    }
    onMounted(()=>{
      document.addEventListener('mousemove',move)
    })
    onUnmounted(()=>{
      document.removeEventListener('mousemove',move)
    })

    // 累加邏輯
    const count = ref(0)
    const add = () => {
      count.value ++ 
    }

    // 返回?cái)?shù)據(jù)
    return {
      ...toRefs(mouse),
      count,
      add
    }
  }
}
</script>

03-組合API-setup函數(shù)

setup 是一個新的組件選項(xiàng),作為組件中使用組合API的起點(diǎn)。
從組件生命周期來看,它的執(zhí)行在組件實(shí)例創(chuàng)建之前vue2.x的beforeCreate執(zhí)行。
這就意味著在setup函數(shù)中 this 還不是組件實(shí)例,this 此時是 undefined
在模版中需要使用的數(shù)據(jù)和函數(shù),需要在 setup 返回。

演示代碼:

<template>
  <div class="container">
    <h1 @click="say()">{{msg}}</h1>
  </div>
</template>
<script>
export default {
  setup () {
    console.log('setup執(zhí)行了')
    console.log(this)
    // 定義數(shù)據(jù)和函數(shù)
    const msg = 'hi vue3'
    const say = () => {
      console.log(msg)
    }

    return { msg , say}
  },
  beforeCreate() {
    console.log('beforeCreate執(zhí)行了')
    console.log(this)
  }
}
</script>

04-組合API-生命周期

回顧vue2.x生命周期鉤子函數(shù):

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

認(rèn)識vue3.0生命周期鉤子函數(shù)

  • setup 創(chuàng)建實(shí)例前
  • onBeforeMount 掛載DOM前
  • onMounted 掛載DOM后
  • onBeforeUpdate 更新組件前
  • onUpdated 更新組件后
  • onBeforeUnmount 卸載銷毀前
  • onUnmounted 卸載銷毀后

演示代碼:

<template>
  <div class="container">
    container
  </div>
</template>
<script>
import { onBeforeMount, onMounted } from 'vue'
export default {
  setup () {
    onBeforeMount(()=>{
      console.log('DOM渲染前',document.querySelector('.container'))
    })
    onMounted(()=>{
      console.log('DOM渲染后1',document.querySelector('.container'))
    })
    onMounted(()=>{
      console.log('DOM渲染后2',document.querySelector('.container'))
    })
  },
}
</script>

05-組合API-reactive函數(shù)

定義響應(yīng)式數(shù)據(jù):

  • reactive是一個函數(shù),它可以定義一個復(fù)雜數(shù)據(jù)類型,成為響應(yīng)式數(shù)據(jù)。

演示代碼:

<template>
  <div class="container">
    <div>{{obj.name}}</div>
    <div>{{obj.age}}</div>
    <button @click="updateName">修改數(shù)據(jù)</button>
  </div>
</template>
<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup () {
    // 普通數(shù)據(jù)
    // const obj = {
    //   name: 'ls',
    //   age: 18
    // }
    const obj = reactive({
      name: 'ls',
      age: 18
    })

    // 修改名字
    const updateName = () => {
      console.log('updateName')
      obj.name = 'zs'
    }

    return { obj ,updateName}
  }
}
</script>

06-組合API-toRef函數(shù)

定義響應(yīng)式數(shù)據(jù):

  • toRef是函數(shù),轉(zhuǎn)換響應(yīng)式對象中某個屬性為單獨(dú)響應(yīng)式數(shù)據(jù),并且值是關(guān)聯(lián)的。

演示代碼:

<template>
  <div class="container">
    {{name}} <button @click="updateName">修改數(shù)據(jù)</button>
  </div>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 響應(yīng)式數(shù)據(jù)對象
    const obj = reactive({
      name: 'ls',
      age: 10
    })
    console.log(obj)
    // 2. 模板中只需要使用name數(shù)據(jù)
    // 注意:從響應(yīng)式數(shù)據(jù)對象中解構(gòu)出的屬性數(shù)據(jù),不再是響應(yīng)式數(shù)據(jù)
    // let { name } = obj 不能直接解構(gòu),出來的是一個普通數(shù)據(jù)
    const name = toRef(obj, 'name')
    // console.log(name)
    const updateName = () => {
      console.log('updateName')
      // toRef轉(zhuǎn)換響應(yīng)式數(shù)據(jù)包裝成對象,value存放值的位置
      name.value = 'zs'
    }

    return {name, updateName}
  }
}
</script>
<style scoped lang="less"></style>

07-組合API-toRefs函數(shù)

定義響應(yīng)式數(shù)據(jù):

  • toRefs是函數(shù),轉(zhuǎn)換響應(yīng)式對象中所有屬性為單獨(dú)響應(yīng)式數(shù)據(jù),對象成為普通對象,并且值是關(guān)聯(lián)的

演示代碼:

<template>
  <div class="container">
    <div>{{name}}</div>
    <div>{{age}}</div>
    <button @click="updateName">修改數(shù)據(jù)</button>
  </div>
</template>
<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 響應(yīng)式數(shù)據(jù)對象
    const obj = reactive({
      name: 'ls',
      age: 10
    })
    console.log(obj)
    // 2. 解構(gòu)或者展開響應(yīng)式數(shù)據(jù)對象
    // const {name,age} = obj
    // console.log(name,age)
    // const obj2 = {...obj}
    // console.log(obj2)
    // 以上方式導(dǎo)致數(shù)據(jù)就不是響應(yīng)式數(shù)據(jù)了
    const obj3 = toRefs(obj)
    console.log(obj3)

    const updateName = () => {
      // obj3.name.value = 'zs'
      obj.name = 'zs'
    }

    return {...obj3, updateName}
  }
}
</script>
<style scoped lang="less"></style>

08-組合API-ref函數(shù)

定義響應(yīng)式數(shù)據(jù):

  • ref函數(shù),常用于簡單數(shù)據(jù)類型定義為響應(yīng)式數(shù)據(jù)
    再修改值,獲取值的時候,需要.value
    在模板中使用ref申明的響應(yīng)式數(shù)據(jù),可以省略.value

演示代碼:

<template>
  <div class="container">
    <div>{{name}}</div>
    <div>{{age}}</div>
    <button @click="updateName">修改數(shù)據(jù)</button>
  </div>
</template>
<script>
import { ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. name數(shù)據(jù)
    const name = ref('ls')
    console.log(name)
    const updateName = () => {
      name.value = 'zs'
    }
    // 2. age數(shù)據(jù)
    const age = ref(10)

    // ref常用定義簡單數(shù)據(jù)類型的響應(yīng)式數(shù)據(jù)
    // 其實(shí)也可以定義復(fù)雜數(shù)據(jù)類型的響應(yīng)式數(shù)據(jù)
    // 對于數(shù)據(jù)未之的情況下 ref 是最適用的
    // const data = ref(null)
    // setTimeout(()=>{
    //   data.value = res.data
    // },1000)

    return {name, age, updateName}
  }
}
</script>

09-組合API-computed函數(shù)

定義計(jì)算屬性:

  • computed函數(shù),是用來定義計(jì)算屬性的,計(jì)算屬性不能修改。

基本使用:

<template>
  <div class="container">
    <div>今年:{{age}}歲</div>
    <div>后年:{{newAge}}歲</div>
  </div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 計(jì)算屬性:當(dāng)你需要依賴現(xiàn)有的響應(yīng)式數(shù)據(jù),根據(jù)一定邏輯得到一個新的數(shù)據(jù)。
    const age = ref(16)
    // 得到后年的年齡
    const newAge = computed(()=>{
      // 該函數(shù)的返回值就是計(jì)算屬性的值
      return age.value + 2
    })

    return {age, newAge}
  }
}
</script>

高級用法:

<template>
  <div class="container">
    <div>今年:{{age}}歲</div>
    <div>后年:{{newAge}}歲</div>
    <!-- 使用v-model綁定計(jì)算屬性 -->
    <input type="text" v-model="newAge">
  </div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 計(jì)算屬性:當(dāng)你需要依賴現(xiàn)有的響應(yīng)式數(shù)據(jù),根據(jù)一定邏輯得到一個新的數(shù)據(jù)。
    const age = ref(16)
    // 得到后年的年齡
    // const newAge = computed(()=>{
    //   // 該函數(shù)的返回值就是計(jì)算屬性的值
    //   return age.value + 2
    // })

    // 計(jì)算屬性高級用法,傳人對象
    const newAge = computed({
      // get函數(shù),獲取計(jì)算屬性的值
      get(){
        return age.value + 2
      },
      // set函數(shù),當(dāng)你給計(jì)算屬性設(shè)置值的時候觸發(fā)
      set (value) {
        age.value = value - 2
      }
    })


    return {age, newAge}
  }
}
</script>

目的:讓計(jì)算屬性支持雙向數(shù)據(jù)綁定。
總結(jié):計(jì)算屬性兩種用法

  • 給computed傳入函數(shù),返回值就是計(jì)算屬性的值
  • 給computed傳入對象,get獲取計(jì)算屬性的值,set監(jiān)聽計(jì)算屬性改變。

10-組合API-watch函數(shù)

定義計(jì)算屬性:

  • watch函數(shù),是用來定義偵聽器的
    監(jiān)聽ref定義的響應(yīng)式數(shù)據(jù)
    監(jiān)聽多個響應(yīng)式數(shù)據(jù)數(shù)據(jù)
    監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù)
    監(jiān)聽reactive定義的響應(yīng)式數(shù)據(jù),某一個屬性
    深度監(jiān)聽
    默認(rèn)執(zhí)行
<template>
  <div class="container">
    <div>
      <p>count的值:{{count}}</p>
      <button @click="add">改數(shù)據(jù)</button>
    </div>
    <hr>
    <div>
      <p>{{obj.name}}</p>
      <p>{{obj.age}}</p>
      <p>{{obj.brand.name}}</p>
      <button @click="updateName">改名字</button>
      <button @click="updateBrandName">改品牌名字</button>
    </div>
  </div>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
  name: 'App',
  setup () {
    const count = ref(0)
    const add = () => {
      count.value++
    }
    // 當(dāng)你需要監(jiān)聽數(shù)據(jù)的變化就可以使用watch
    // 1. 監(jiān)聽一個ref數(shù)據(jù)
    // 1.1 第一個參數(shù)  需要監(jiān)聽的目標(biāo)
    // 1.2 第二個參數(shù)  改變后觸發(fā)的函數(shù)
    // watch(count, (newVal,oldVal)=>{
    //   console.log(newVal,oldVal)
    // })


    const obj = reactive({
      name: 'ls',
      age: 10,
      brand: {
        id: 1,
        name: '寶馬'
      }
    })
    const updateName = () => {
      obj.name = 'zs'
    }
    const updateBrandName = () => {
      obj.brand.name = '奔馳'
    }
    // 2. 監(jiān)聽一個reactive數(shù)據(jù)
    watch(obj, ()=>{
      console.log('數(shù)據(jù)改變了')
    })

    watch(()=>obj.brand, ()=>{
      console.log('brand數(shù)據(jù)改變了')
    },{
      // 5. 需要深度監(jiān)聽
      deep: true,
      // 6. 想默認(rèn)觸發(fā)
      immediate: true
    })

    // 3. 監(jiān)聽多個數(shù)據(jù)的變化
    // watch([count, obj], ()=>{
    //   console.log('監(jiān)聽多個數(shù)據(jù)改變了')
    // }) 


    // 4. 此時監(jiān)聽對象中某一個屬性的變化 例如:obj.name 
    // 需要寫成函數(shù)返回該屬性的方式才能監(jiān)聽到
    // watch(()=>obj.name,()=>{
    //   console.log('監(jiān)聽obj.name改變了')
    // })

    return {count, add, obj, updateName, updateBrandName}
  }
}
</script>

11-組合API-ref屬性

獲取DOM或者組件實(shí)例可以使用ref屬性,寫法和vue2.0需要區(qū)分開
獲取單個DOM或者組件

<template>
  <div class="container">
    <!-- vue2.0 獲取單個元素 -->
    <!-- 1. 通過ref屬性綁定該元素 -->
    <!-- 2. 通過this.$refs.box獲取元素 -->
    <!-- <div ref="box">我是box</div> -->
    <!-- vue2.0 獲取v-for遍歷的多個元素 -->
    <!-- 1. 通過ref屬性綁定被遍歷元素 -->
    <!-- 2. 通過this.$refs.li 獲取所有遍歷元素  -->
    <!-- <ul>
      <li v-for="i in 4" :key="i" ref="li">{{i}}</li>
    </ul> -->

    <!-- 單個元素 -->
    <div ref="dom">我是box</div>
    <!-- 被遍歷的元素 -->
    <ul>
      <li v-for="i in 4" :key="i" :ref="setDom">第{{i}}LI</li>
    </ul>
  </div>
</template>
<script>
import { onMounted, ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 獲取單個元素
    // 1.1 先定義一個空的響應(yīng)式數(shù)據(jù)ref定義的
    // 1.2 setup中返回該數(shù)據(jù),你想獲取那個dom元素,在該元素上使用ref屬性綁定該數(shù)據(jù)即可。
    const dom = ref(null)
    onMounted(()=>{
       console.log(dom.value)
    })
  }
}
</script>
<style scoped lang="less"></style>

獲取v-for遍歷的DOM或者組件

    // 2. 獲取v-for遍歷的元素
    // 2.1 定義一個空數(shù)組,接收所有的LI
    // 2.2 定義一個函數(shù),往空數(shù)組push DOM
    const domList = []
    const setDom = (el) => {
      domList.push(el)
    }
    onMounted(()=>{
      console.log(domList)
    })
    return {dom, setDom}

總結(jié):

  • 單個元素:先申明ref響應(yīng)式數(shù)據(jù),返回給模版使用,通過ref綁定數(shù)據(jù)
  • 遍歷的元素:先定義一個空數(shù)組,定一個函數(shù)獲取元素,返回給模版使用,通過ref綁定這個函數(shù)
    有一個邊界問題:組件更新的時候會重復(fù)的設(shè)置dom元素給數(shù)組:

12-組合API-父子通訊

父傳子:

<template>
  <div class="container">
    <h1>父組件</h1>
    <p>{{money}}</p>
    <hr>
    <Son :money="money" />
  </div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  // 父組件的數(shù)據(jù)傳遞給子組件
  setup () {
    const money = ref(100)
    return { money }
  }
}
</script>
<template>
  <div class="container">
    <h1>子組件</h1>
    <p>{{money}}</p>
  </div>
</template>
<script>
import { onMounted } from 'vue'
export default {
  name: 'Son',
  // 子組件接收父組件數(shù)據(jù)使用props即可
  props: {
    money: {
      type: Number,
      default: 0
    }
  },
  setup (props) {
    // 獲取父組件數(shù)據(jù)money
    console.log(props.money)
  }
}
</script>

子傳父:

<template>
  <div class="container">
    <h1>父組件</h1>
    <p>{{money}}</p>
    <hr>
+    <Son :money="money" @change-money="updateMoney" />
  </div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  // 父組件的數(shù)據(jù)傳遞給子組件
  setup () {
    const money = ref(100)
+    const updateMoney = (newMoney) => {
+      money.value = newMoney
+    }
+    return { money , updateMoney}
  }
}
</script>
<template>
  <div class="container">
    <h1>子組件</h1>
    <p>{{money}}</p>
+    <button @click="changeMoney">花50元</button>
  </div>
</template>
<script>
import { onMounted } from 'vue'
export default {
  name: 'Son',
  // 子組件接收父組件數(shù)據(jù)使用props即可
  props: {
    money: {
      type: Number,
      default: 0
    }
  },
  // props 父組件數(shù)據(jù)
  // emit 觸發(fā)自定義事件的函數(shù)
+  setup (props, {emit}) {
    // 獲取父組件數(shù)據(jù)money
    console.log(props.money)
    // 向父組件傳值
+    const changeMoney = () => {
      // 消費(fèi)50元
      // 通知父組件,money需要變成50
+      emit('change-money', 50)
+    }
+    return {changeMoney}
  }
}
</script>

擴(kuò)展:

  • 在vue2.x的時候 .sync 除去v-model實(shí)現(xiàn)雙向數(shù)據(jù)綁定的另一種方式
<!-- <Son :money='money' @update:money="fn"  /> -->
<Son :money.sync='money'  />
  • 在vue3.0的時候,使用 v-model:money="money" 即可
    <!-- <Son :money="money" @update:money="updateMoney" /> -->
    <Son v-model:money="money" />

總結(jié):

  • 父傳子:在setup種使用props數(shù)據(jù) setup(props){ // props就是父組件數(shù)據(jù) }
  • 子傳父:觸發(fā)自定義事件的時候emit來自 setup(props,{emit}){ // emit 就是觸發(fā)事件函數(shù) }
  • 在vue3.0中 v-model 和 .sync 已經(jīng)合并成 v-model 指令

13-組合API-依賴注入

使用場景:有一個父組件,里頭有子組件,有孫組件,有很多后代組件,共享父組件數(shù)據(jù)。
演示代碼:

<template>
  <div class="container">
    <h1>父組件 {{money}} <button @click="money=1000">發(fā)錢</button></h1>
    <hr>
    <Son />
  </div>
</template>
<script>
import { provide, ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  setup () {
    const money = ref(100)
    const changeMoney = (saleMoney) => {
      console.log('changeMoney',saleMoney)
      money.value = money.value - saleMoney
    }
    // 將數(shù)據(jù)提供給后代組件 provide
    provide('money', money)
    // 將函數(shù)提供給后代組件 provide
    provide('changeMoney', changeMoney)

    return { money }
  }
}
</script>
<style scoped lang="less"></style>
<template>
  <div class="container">
    <h2>子組件 {{money}}</h2>
    <hr>
    <GrandSon />
  </div>
</template>
<script>
import { inject } from 'vue'
import GrandSon from './GrandSon.vue'
export default {
  name: 'Son',
  components: {
    GrandSon
  },
  setup () {
    // 接收祖先組件提供的數(shù)據(jù)
    const money = inject('money')
    return { money }
  }
}
</script>
<style scoped lang="less"></style>
<template>
  <div class="container">
    <h3>孫組件 {{money}} <button @click="fn">消費(fèi)20</button></h3>
  </div>
</template>
<script>
import { inject } from 'vue'
export default {
  name: 'GrandSon',
  setup () {
    const money = inject('money')
    // 孫組件,消費(fèi)50,通知父組件App.vue組件,進(jìn)行修改
    // 不能自己修改數(shù)據(jù),遵循單選數(shù)據(jù)流原則,大白話:數(shù)據(jù)誰定義誰修改
    const changeMoney = inject('changeMoney')
    const fn = () => {
      changeMoney(20)
    }
    return {money, fn}
  }
}
</script>
<style scoped lang="less"></style>

總結(jié):

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

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

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