Vue3.0 | vue3的新特性

Vue3的變化

官網(wǎng)地址: https://v3.cn.vuejs.org/guide/migration/introduction.html

一盐股、對比vue2的變化

1.優(yōu)點

  • vue3支持vue2的大多數(shù)特性,實現(xiàn)對vue2的兼容
  • vue3對比vue2具有明顯的性能提升
    • 打包大小減少41%
    • 初次渲染快55%载慈,更新快133%
    • 內(nèi)存使用減少54%
  • 更好的支持TypeScript
  • 使用Proxy代替defineProperty實現(xiàn)響應(yīng)式數(shù)據(jù)

2.性能提升的原因

  • 靜態(tài)標(biāo)記
    • vue2從根節(jié)點開始對虛擬dom進行全量對比(每個節(jié)點不論寫死的還是動態(tài)的都會一層一層比較)
    • vue3新增了靜態(tài)標(biāo)記 與上次虛擬dom對比的時候,只對比帶有 patchFlags 的節(jié)點谆构。跳過一些靜態(tài)節(jié)點對比(下圖編譯結(jié)果中-1跟1就屬于靜態(tài)標(biāo)記)
      patchFlags配置文件地址 https://github.com/vuejs/core/blob/main/packages/shared/src/patchFlags.ts
    <div id='app'>
      <div>helloWorld</div>
      <div>{{msg}}</div>
    </div>
    
    <!--編譯后-->
    <script>
      const _hoisted_1 = { id: "app" }
      const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "helloWorld", -1 /* HOISTED */)
    
      export function render(...) {
        return (
          _openBlock(),
          _createBlock('div', _hoisted_1, [
            _hoisted_2,
            _createElementVNode('div',null,_toDisplayString(_ctx.msg),1 /* TEXT */),
          ])
        )
      }
    </script>
    
  • hoistStatic 靜態(tài)提升
    • vue2里每當(dāng)觸發(fā)更新的時候谅河,不管元素是否參與更新,每次都會重新創(chuàng)建
    • vue3為了避免每次渲染的時候都要重新創(chuàng)建這些對象叶骨,會把不參與更新的元素保存起來茫多,只創(chuàng)建一次,每次復(fù)用忽刽。比如上面_hoisted_1天揖,_hoisted_2被提升到渲染函數(shù)render之外夺欲,
  • cacheHandlers 事件緩存
    • vue2里綁定事件都要重新生成新的function去更新
    • vue3會自動生成一個內(nèi)聯(lián)函數(shù),同時生成一個靜態(tài)節(jié)點今膊。onclick時會讀取緩存些阅,如果緩存沒有的話,就把傳入的事件存到緩存里
    <div @click="handleClick">點擊</div>
      
    <!--編譯后-->
    <script>
      export function render(...) {
        return (_openBlock()._createElementVNode('div',{onClick: _ctx.todo}, '點擊'))
        )
      }
    </script>
    <script>
      export function render(...) {
        return (_openBlock()._createElementVNode('div',{
          onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.todo(...args)))
        },'點擊'))
      }
    </script>
    

3.響應(yīng)式數(shù)據(jù)的變化

  • vue2的響應(yīng)式(實現(xiàn)地址:https://github.com/jiaomengyuan
    • 核心
      • Object.defineProperty 來劫持各個屬性的setter斑唬、getter市埋,在數(shù)據(jù)變動時發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)
      • 對象:遞歸調(diào)用defineProperty對對象已有屬性值的讀取和修改進行攔截
      • 數(shù)組:重寫數(shù)組更新數(shù)組一系列更新元素的方法來實現(xiàn)元素修改的劫持
    • 問題
      • 不能監(jiān)聽到對象屬性的添加和刪除赖钞,需要Vue.set()來添加和刪除腰素。
      • 不能通過下標(biāo)替換元素或更新length
  • vue3的響應(yīng)式(實現(xiàn)地址:https://github.com/jiaomengyuan
    • 核心
      • 通過Proxy(代理): 攔截對data任意屬性的任意(13種)操作, 包括屬性值的讀寫, 屬性的添加, 屬性的刪除等
      • 通過 Reflect(反射): 動態(tài)對被代理對象的相應(yīng)屬性進行特定的操作

二、 新增特性

1.Composition (組合) API

  • Option API:vue2創(chuàng)建組件時雪营,會把數(shù)據(jù)放到data弓千,計算屬性放到computed,事件處理放到methods献起,監(jiān)聽改變放到watch洋访;共同處理頁面邏輯
    • 組件功能越來越多,邏輯功能點分散谴餐,不易閱讀(新增或修改一個需求姻政,就需要分別在data,methods等...進行修改岂嗓,功能多時汁展,滾動條來回滾動 )
    • 可以通過Mixins重用邏輯代碼,但是數(shù)據(jù)來源模糊還會有Mixins命名沖突的問題
  • Composition API:將零散的data厌殉,methods代碼重新組合食绿,一個功能的代碼放一塊兒,并且可以單出拆分出函數(shù)
    • 兼容Option API公罕,還可繼續(xù)使用
    • 利于代碼重用器紧,沒有對this的使用,減少了this指向不明的情況
    • 幾乎是函數(shù)楼眷,編輯器可以幫我們進行類型檢查和建議

2. setup

  • setup函數(shù)是一個新的option铲汪,在初始化時執(zhí)行一次,可以理解為使用Composition API 的入口點罐柳。
  • 這個函數(shù)的返回一個對象掌腰,對象里的屬性和方法,可以直接在模版中使用
  <template>
    <div>{{msg}}</div>
  </template>

  <script>
  export default {
    setup () {
      const msg = 'hello World'
      return {
        msg
      }
    }
  }
  </script>

注意:

  • 在beforeCreate之前創(chuàng)建张吉,因此齿梁,這個函數(shù)中沒有this。因此不能訪問data芦拿,methods等士飒。但methods中可以訪問setup提供的屬性和方法
  • return中返回的屬性跟data合并查邢,返回的方法跟methods里的方法合并;如有重名酵幕,setup優(yōu)先
  • setup不能是一個async函數(shù)扰藕,使用async后返回值不是return的對象,而是promise芳撒。非要使用邓深,需要使用<suspense>包裹組件
  • setup接收兩個參數(shù)setup(props, context) || setup(props, {attrs, slots, emit}) 不能解構(gòu)props,否則會失去響應(yīng)式

1) ref

  • 定義一個基本數(shù)據(jù)類型的響應(yīng)式引用
  <template>
    <div>{{num}}</div>
    <button @click="addNum">添加</button>
  </template>

  <script>
  import { ref } from 'vue'
  export default {
    setup () {
      const num = ref(1)
      function addNum () {
        num.value = num.value + 1
      }
      return {
        num,
        addNum
      }
    }
  }
  </script>

注意:

  • 在setup中使用ref定義的響應(yīng)式引用需要.value(內(nèi)部通過給value屬性添加getter笔刹、setter實現(xiàn)對數(shù)據(jù)的劫持)芥备,在模版中不需要(解析模板時會自動添加.value)
  • ref常用來處理基本數(shù)據(jù)類型,如果用ref定義引用數(shù)據(jù)類型, 內(nèi)部會自動將對象舌菜,數(shù)組轉(zhuǎn)換為reactive的代理對象

2) reactive

  • 定義多個數(shù)據(jù)的響應(yīng)式引用
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup () {
    const obj = reactive({ name: "張三", age: 25 });
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
    };
    return {
      obj,
      updateObj,
    };
  }
}
</script>

3) toRef(obj,key) 萌壳、 toRefs(obj)

  • reactive定義的響應(yīng)式數(shù)據(jù),解構(gòu)后進行使用日月,數(shù)據(jù)就不是響應(yīng)式的,使用toReftoRefs將解決這個問題
  • toReftoRefs都是復(fù)制reactive里的屬性然后轉(zhuǎn)成ref袱瓮。因為是淺拷貝,所以修改復(fù)制出來的值時原來reactive里的數(shù)據(jù)也會跟著更新
  • toRef是復(fù)制reactive里的單個key轉(zhuǎn)成ref爱咬,toRefs復(fù)制reactive里的所有key轉(zhuǎn)成ref
  • 也可以用來將props接收的數(shù)據(jù)進行解構(gòu)響應(yīng)式
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ name }}</div>
  <div>{{ age }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { reactive, toRefs, toRef } from "vue";
export default {
  props: {
    msg: String,
  },
  setup (props) {
    const obj = reactive({ name: "張三", age: 25 });
    // const name = toRef(obj, "name");
    // const { msg } = toRefs(props)
    const { name, age } = toRefs(obj);
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
    };
    return {
      obj,
      name,
      age,
      updateObj,
    };
  }
}
</script>

4) shallowRef尺借、triggerRef 與 shallowReactive

  • refreactive 是把數(shù)據(jù)變?yōu)轫憫?yīng)式,無論層級多深精拟,始終都會對數(shù)據(jù)進行深度監(jiān)聽燎斩。shallowRefshallowReactive可以解決這個
  • shallowRef 只處理了.value的響應(yīng)式(只監(jiān)聽.value的變化)。因此如果需要修改應(yīng)該xxx.value={} 或者使用 shallowRef 將數(shù)據(jù)強制更新到頁面
  • 注:shallowReactive 的數(shù)據(jù)使用shallowRef 強制更新無效
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { shallowRef, triggerRef } from "vue";
export default {
  setup() {
    const obj = shallowRef({ name: "張三", age: 25, c: { d: 2 } });
    const updateObj = () => {
      // 這些都不會修改成功,obj對象會變蜂绎,不會更新到模版
      // obj.value.name = "王五";
      // obj.value.age = 21;
      // obj.value.c.d = 4;
      // 調(diào)用此方法栅表,上方數(shù)據(jù)會更新到頁面
      // triggerRef(obj);

      // 如要修改,要用此方法
      obj.value={ name: "王五", age: 21, c: { d: 4 } }
    };
    return {
      obj,
      updateObj,
    };
  },
};
</script>
  • shallowReactive 只處理對象最外層屬性的響應(yīng)式(淺響應(yīng)式)荡碾,默認(rèn)情況下只能監(jiān)聽第一次的數(shù)據(jù)谨读。如要修改則需要先改第一層的數(shù)據(jù)局装,然后再去更改其他層的數(shù)據(jù)
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { shallowReactive } from "vue";
export default {
  setup() {
    const obj = shallowReactive({ name: "張三", age: 25, c: { d: 2 } });
    const updateObj = () => {
      // 這樣不會監(jiān)聽到修改,obj會變坛吁,不會更新到模版
      // obj.c.d = 4;
      // 如要修改需要用下面方法,先改第一層
      obj.name = "王五";
      obj.age = 21;
      obj.c.d = 4;
    };

    return {
      obj,
      updateObj,
    };
  },
};
</script>

5) readonly 與 shallowReadonly

  • readonly 是讓一個響應(yīng)式數(shù)據(jù)只讀铐尚,深層只讀
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { reactive, readonly } from "vue";
export default {
  setup() {
    let obj = reactive({ name: "張三", age: 25, c: { d: 2 } });
    obj = readonly(obj);
    //這里不會修改成功(還是proxy對象拨脉,但obj不會修改成功)
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
      obj.c.d = 4;
      console.log(obj)// 這里obj還等于之前定義的值
    };

    return {
      obj,
      updateObj,
    };
  },
};
</script>
  • shallowReadonly 是讓一個響應(yīng)式數(shù)據(jù)只讀,淺層只讀(響應(yīng)式對象的最外層只讀宣增,但再深一層的屬性可以被修改)
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { shallowReadonly, reactive } from "vue";
export default {
  setup() {
    let obj = reactive({ name: "張三", age: 25, c: { d: 2 } });
    obj = shallowReadonly(obj);
    const updateObj = () => {
      //這個不會修改
      obj.name = "王五";
      //這個不會修改
      obj.age = 21;
      //這個會被修改
      obj.c.d = 4;
    };

    return {
      obj,
      updateObj,
    };
  },
};
</script>

6) toRaw 玫膀、 markRaw

  • toRaw 將一個響應(yīng)式對象(由 reactive定義的響應(yīng)式)轉(zhuǎn)換為普通對象
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { toRaw, reactive } from "vue";
export default {
  setup() {
    let obj = reactive({ name: "張三", age: 25, c: { d: 2 } });
    obj = toRaw(obj);// 使用此方法,obj則變?yōu)槠胀▽ο螅ǚ莗roxy)
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
      obj.c.d = 4;
    };

    return {
      obj,
      updateObj,
    };
  },
};
</script>
  • markRaw 標(biāo)記一個對象爹脾,使其不能成為一個響應(yīng)式對象
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <div>{{ obj.c.d }}</div>
  <button @click="updateObj">修改</button>
</template>

<script>
import { markRaw, reactive } from "vue";
export default {
  setup() {
    let data = { name: "張三", age: 25, c: { d: 2 } };
    // 在此處標(biāo)記后帖旨,之后使用reactive則不生效(不會成為一個proxy對象)箕昭,markRaw放到reactive之后則無效
    data = markRaw(data);
    let obj = reactive(data);
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
      obj.c.d = 4;
    };

    return {
      obj,
      updateObj,
    };
  },
};
</script>

7) isRef 、unref 解阅、 isReactive 落竹、isProxy 、isReadonly

  • isRef 檢查值是否為一個 ref 對象货抄。 unrefval = isRef(val) ? val.value : val 的語法糖
const obj = ref({ name: "張三", age: 25, c: { d: 2 } });
console.log(isRef(obj))
console.log(unref(obj))
  • isReactive 檢查值是否為一個 reactive 對象
const obj = reactive({ name: "張三", age: 25, c: { d: 2 } });
console.log(isReactive(obj))
  • isProxy 檢查對象是否是由 reactive 或 readonly 創(chuàng)建的 proxy
const obj = reactive({ name: "張三", age: 25, c: { d: 2 } });
console.log(isProxy(obj))
const obj = readonly({ name: "張三", age: 25, c: { d: 2 } });
console.log(isProxy(obj))
  • isReadonly 檢查對象是否是由 readonly 創(chuàng)建的只讀代理
const obj = readonly({ name: "張三", age: 25, c: { d: 2 } });
console.log(isReadonly(obj))

8) customRef

  • 創(chuàng)建一個自定義的ref述召,接受一個函數(shù)作為參數(shù),這個函數(shù)接受兩個參數(shù)track(通知vue需要追蹤后續(xù)內(nèi)容的變化)trigger(通知vue重新解析模版)
<template>
  <input type="text" v-model="inpValue">
  <div>{{inpValue}}</div>
</template>
<script>
import {customRef} from "vue";
export default {
  setup() {
    // 自定義一個 myRef
    function myRef(value) {
      return customRef((track, trigger) => {
        return {
          get() {
            track() // 追蹤后續(xù)數(shù)據(jù)的變化
            return value
          },
          set(newValue) {
            value = newValue
            trigger() // 重新解析模板
          }
        }
      })
    }
    let inpValue = myRef('hello')
    return {
      inpValue,
    }
  }
}
</script>

9) computed蟹地、watch积暖、watchEffect

  • 在setup中也有單獨的computedwatch用法基本相同
  • watchEffect是監(jiān)視所有回調(diào)中使用的數(shù)據(jù),因為每次初始化的時候都會執(zhí)行一次回掉自動獲取依賴怪与,并不用手動傳需要監(jiān)聽的對象夺刑。注:無法獲取到oldValue
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <input type="text" v-model="userNews"/>
  <input type="text" v-model="userNews2"/>
  <input type="text" v-model="userNews3"/>
  <input type="text" v-model="userNews4"/>
  <button @click="updateObj">修改</button>
</template>

<script>
import { ref, reactive, computed, watch, watchEffect } from "vue";
export default {
  props: {
    msg: String,
  },
  setup (props) {
    const obj = reactive({ name: "張三", age: 25 });
    const updateObj = () => {
      obj.name = "王五";
      obj.age = 21;
    };
    // computed只有g(shù)et
    const userNews = computed(() => {
      return obj.name + "," + obj.age;
    })
    // computed同時有g(shù)et和set
    const userNews2 = computed({
      get() {
        return obj.name + "," + obj.age;
      },
      set(value) {
        const nameAge = value.split(",");
        obj.name = nameAge[0];
        obj.age = nameAge[1];
      },
    });
    // watch
    const userNews3 = ref("");
    watch(
      obj,
      (newValue, oldValue) => {
        userNews3.value = newValue.name + "," + newValue.age;
      },
      {
        immediate: true,
        deep: true,
      }
    );
    // watchEffect
    const userNews4 = ref("");
    watchEffect(() => {
      userNews4.value = obj.name + "," + obj.age;
    });
    return {
      obj,
      userNews,
      userNews2,
      userNews3,
      userNews4,
      updateObj,
    };
  }
}
</script>

注意:
當(dāng)使用 watch 選項偵聽數(shù)組時分别,只有在數(shù)組被替換時才會觸發(fā)回調(diào)性誉。換句話說,在數(shù)組被改變時偵聽回調(diào)將不再被觸發(fā)茎杂。要想在數(shù)組被改變時觸發(fā)偵聽回調(diào)错览,必須指定 deep 選項。

10) 生命周期

  • setup中也有新的生命周期onBeforeMount -> onMounted -> onBeforeUpdate -> onUpdated -> onBeforeUnmount -> onUnmounted -> onErrorCaptured跟options api混用時onBeforeMount在beforeMount之前煌往,onMounted在mounted之前倾哺。。刽脖。之后都是
  • vue中父子順序 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted在setup中聲明周期也適用

11) provide 羞海、 inject

  • provideinject提供依賴注入 曲管,實現(xiàn)祖孫級組件通信
<template>
  <h1>父組件</h1>
  <one />
</template>
<script>
import { provide, ref } from 'vue'
import one from './one.vue'
export default {
  components: {
    one
  },
  setup() {
    const msg = ref('red')
    provide('msg', msg)
    return {
      msg
    }
  }
}
</script>

<!--子組件(one)-->
<template>
  <div>子組件</div>
  <two />
</template>
<script>
import two from './two.vue'
export default {
  components: {
    two
  },
}
</script>

<!--孫子組件(two)-->
<script>
import { inject } from 'vue'
export default {
  setup() {
    const msg = inject('msg')
    return {
      msg
    }
  }
}
</script>

12) $refs

  • 在vue2中使用 this.$refs.XXX 獲取却邓,vue3中setup函數(shù)沒有this,所以也有單獨的獲取ref的方法
<template>
  <input type="text" ref="inputRef" value="這是input的文本"/>
</template>

<script>
import { onMounted, ref } from "vue";
export default {
  setup() {
    const inputRef = ref(null);/// 本質(zhì)是reactive({value:null})
    onMounted(() => {
      console.log(inputRef.value.value);
    });
    console.log(inputRef.value);// null dom還沒形成
    return {
      inputRef,
    };
  },
};
</script>

13) 自定義hook函數(shù)

  • 與mixin類似院水,抽離公共代碼
import { reactive } from "vue";
export default function hookTest() {
  const obj = reactive({ name: "張三", age: 25 });
  return { obj };
}
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
</template>

<script>
import hookTest from './hookTest'
export default {
  setup (props) {
    const {obj} = hookTest()
    return { obj };
  }
}
</script>

3. 其他新特性

1) 全局API

  • 在vue2中的main.js中有以下代碼腊徙。如果使用全局api則是 Vue.directive、Vue.component檬某、Vue.config撬腾、Vue.mixin、Vue.prototype等恢恼,都是掛載在Vue原型上
import Vue from 'vue'
import App from './App.vue'

const app = new Vue(App)
app.$mount()
  • vue3提供的是實例api民傻。通過createApp創(chuàng)建vue實例
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
全局API.png

2) v-if 與 v-for 的優(yōu)先級對比

  • 在vue2中:當(dāng)v-if和v-for同時使用時,v-for的優(yōu)先級高于v-if(因此我們通常需要計算屬性先對數(shù)據(jù)進行加工處理,以達到性能優(yōu)化的目的)
  • 在vue3中:當(dāng)v-if和v-for同時使用時漓踢,v-if的優(yōu)先級高于v-for

3) v-for 中的 Ref 數(shù)組

  • vue2中牵署,在 v-for 語句中使用ref屬性,會生成refs數(shù)組插入$refs屬性中
<div v-for="item in 5" :key="item" :ref="item">
  {{ item }}
</div>
this.$refs將會是個數(shù)組
  • vue3中喧半,在v-for中使用ref屬性碟刺,將不會自動在$refs里創(chuàng)建數(shù)組,而是將ref綁定到一個函數(shù)中薯酝,在函數(shù)中可以處理ref
<template>
  <div v-for="item in 5" :key="item" :ref="setItemRef">
    {{ item }}
  </div>
</template>

<script>
import { onMounted } from "vue";
export default {
  setup() {
    const refArray = [];
    const setItemRef = (e) => {
      refArray.push(e);
    };
    onMounted(() => {
      console.log(refArray);
    });
    return {
      setItemRef,
    };
  },
};
</script>

4) v-bind合并行為

  • vue2如果一個標(biāo)簽同時定義了動態(tài)屬性和一個相同的單獨的屬性半沽,那么這個單獨的屬性總是會覆蓋動態(tài)屬性。說明單獨屬性優(yōu)先級高于動態(tài)屬性
 <div id="red" :id="'blue'"></div>
 <!--這里div只會綁上id等于red-->
  • vue3將會吧動態(tài)屬性跟單獨屬性合并
<div id="red" :id="'blue'"></div>
<!--結(jié)果為-->
<div id='red blue'></div>

5) v-model

  • vue2中吴菠,在組件上使用v-model默認(rèn)prop與event為valueinput者填。如要修改,通過子組件的model選項中的prop值和event值來指定屬性名和事件名做葵。
<child v-model="pageTitle" />
<!--等同于-->
<child :value="pageTitle" @input="pageTitle = $event" />
export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    title: {
      type: String,
      default: 'Default title'
    }
  },
  methods:{
    handleClick(val) {
      this.$emit('change', val)
    }
  }
}
  • 除了使用上面方法對某一個prop進行 ‘雙向綁定’ 還可以通過這種方式 v-bind.sync
  • vue3中v-bind 的 .sync 修飾符已移除占哟。:title.sync就要替換為v-model:title
<child :title="pageTitle" @update:title="pageTitle = $event" />
<!--等同于-->
<child :title.sync="pageTitle" />
this.$emit('update:title', newValue)
  • vue3默認(rèn)prop與event為modelValueupdate:modelValue。如要修改酿矢,直接通過v-model后面參數(shù)v-model:title來指定屬性名榨乎,并且支持綁定多個v-model
<child v-model="pageTitle" />
<!--等同于-->
<child :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
  • 如果需要修改model的名稱瘫筐,我們可以為v-model傳遞一個參數(shù)蜜暑,作為子組件內(nèi)model選項的代替
<child v-model:title="pageTitle" />
<!--等同于-->
<child :title="pageTitle" @update:title="pageTitle = $event" />

6) $attrs

  • vue2中使用 v-bind='$attrs' 進行將父組件不被認(rèn)為props特性綁定的屬性傳入子組件(不包含class以及style),配合interitAttrs一起使用策肝,如果為true則將所有attribute添加到子組件的跟標(biāo)元素上肛捍。但如果為false時,因為class以及style不屬于$attrs之众,所以仍會添加到組件的跟元素上
  • vue3中$attrs包含所有傳遞給子組件的attribute拙毫,包含class以及style

7) emits選項

  • vue2中子組件觸發(fā)父親組件的方法this.$emit(方法名)
  • vue3提供了一個類似props的emits選項,emits選項可以配置校驗emit事件棺禾,為null的時候不校驗缀蹄。校驗時會把參數(shù)攜帶過去,當(dāng)校驗不通過膘婶,控制臺會發(fā)出一個警告缺前,但emit事件還會繼續(xù)執(zhí)行
  • 官方建議我們在組件中所有的emit事件都能在組件的emits選項中聲明
<template>
  <one @open="open" />
</template>
<script>
import one from "./one.vue";
export default {
  components: {
    one,
  },
  setup() {
    open = () => {
      console.log(1);
    };
    return {
      open,
    };
  },
};
</script>
<!--子組件-->
<template>
  <div @click="open">點擊</div>
</template>
<script>
export default {
  emits: {
    //open: null,
    open: (value) => {
      if (typeof value === "string") {
        return true;
      } else {
        return false;
      }
    },
  },
  setup(props, { emit }) {
    open = () => {
      emit("open", 11);
    };
    return {
      open,
    };
  },
};
</script>

7) 事件 API(eventBus)

  • 移除$on$off$once 實例方法竣付,組件實例不再實現(xiàn)事件觸發(fā)接口诡延。

8) 函數(shù)式組件

  • 函數(shù)式組件有兩個特性1滞欠、Stateless無狀態(tài):組件自身沒有狀態(tài) 2.Instanceless無實例:組件自身沒有實例古胆,也就是沒有this
  • vue2中,函數(shù)式組件通常用為性能優(yōu)化,它的初始化速度比有狀態(tài)組件快的多
Vue.component('typeButton',typeButton)
const typeButton = {
  functional:true,//標(biāo)記逸绎,無狀態(tài)無實例
  render(h , { props }){
    const { type } = props
    return <div class={ type }>{type}</div>
  }
}
  • vue3中不需要functional定義 接收兩個參數(shù),props和context惹恃。context包含組件的 attrsslotsemit property
import { h } from 'vue'
const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading

9) 異步組件 defineAsyncComponent

const asyncModal = () => import('./Modal.vue')
//帶有選項
const asyncModal = {
  component: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
} 
  • vue3中函數(shù)式組件被定義為純函數(shù)棺牧,因此異步組件需要包裹在defineAsyncComponent
import { defineAsyncComponent } from 'vue'
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
//帶有選項
const asyncModalWithOptions = defineAsyncComponent({
  loader: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

10) data選項

  • vue2中巫糙,可以通過Object或者function定義data選項
<!-- Object -->
<script>
  const app = new Vue({
    data: {
      num: 1
    }
  })
</script>
<!-- Function -->
<script>
  const app = new Vue({
    data() {
      return {
        num: 2
      }
    }
  })
</script>
  • vue3中,data只接受返回object的function
<script>
  import { createApp } from 'vue'

  createApp({
    data() {
      return {
        num: 1
      }
    }
  }).mount('#app')
</script>
  • 另外于mixin合并操作將被淺層次執(zhí)行(data和mixin中聲明同樣的對象颊乘,vue2會把對象中的合并参淹,vue3將只取mixin中)

三、 新組件

1. setup語法糖

  • setup這個option乏悄,暴露變量必須都return出來浙值,模板中才能使用。vue3也提供了相關(guān)語法糖檩小,不用export 开呐,也不需要寫setup函數(shù)
  • 需要將 setup attribute 添加到 <script> 代碼塊上.變量跟函數(shù)都不需要return,即可在模板中使用
  • 引入組件可以直接使用规求,不需要通過components進行注冊
<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
</template>

<script setup>
import { ref } from "vue";
const obj = ref({ name: "張三", age: 25, c: { d: 2 } });
</script>

2. style

3. Fragment(片斷)

  • 在vue2中組件必須要有一個根標(biāo)簽
  • vue3組件里可以沒有根標(biāo)簽筐付,vue3內(nèi)部會把多個標(biāo)簽放在一個Fragment虛擬元素中

4. Teleport(瞬移)

  • <teleport> 用于移動dom到指定元素
  • 還可配置disabled屬性,禁用teleport的功能阻肿,不會移動到任何位置瓦戚,但他仍會出現(xiàn)在正常位置下(里面的所有元素依然保持正常狀態(tài))
<teleport to="#some-id" />
<teleport to=".some-class" />
<teleport to="[data-teleport]" />
<teleport to="body" disabled=‘true’>
  <div></div>
</teleport>

5. Suspense -- 實驗

  • 組件渲染被渲染之前,可能存在異步操作丛塌,<suspense>幫助我們創(chuàng)建一個平滑的用戶體驗
  • <suspense> 有兩個插槽default伤极、fallback。如果default里的標(biāo)簽(任意深度姨伤,所有的后代組件)顯示不出來哨坪,則展示fallback
  • fallback被觸發(fā)的機制還有上面說的setup是一個async函數(shù)。這樣return的則是promise
<template>
  <suspense>
    <template #default>
      <todo-list />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense>
</template>

<script>
export default {
  components: {
    TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
  }
}
</script>

四乍楚、 廢除屬性

1. $children

  • 在vue2中胡岔,可以使用this.$children訪問當(dāng)前實例的子組件衙传。vue3中已移除,推薦使用$refs

2. 過濾器filter

  • 在vue2中,可以使用過濾器來處理數(shù)據(jù)格式国瓮。vue3中已移除,推薦使用方法調(diào)用或計算屬性

3. $listeners

  • vue2可以使用v-bind='$attrs' 訪問父組件不被認(rèn)為的props特性綁定的屬性傳入子組件走哺,還可以通過在中間組件添加 v-on=‘$listeners’ 使其子組件可以通過emits觸發(fā)其父組件的方法
  • vue3將移除這一方法坏平,因為現(xiàn)在事件監(jiān)聽器是 $attrs 的一部分(相當(dāng)于父組件的@init=‘init’。不用在中間組件添加v-on=‘$listeners’渠概,只需要v-bind='$attrs'茶凳,由$attrs負(fù)責(zé)傳遞方法嫂拴。即可在其子組件訪問)

4. inline-template

  • 當(dāng) inline-template 出現(xiàn)在一個子組件上時,這個組件里面的內(nèi)容作為模版贮喧,而不是將其作為被分發(fā)的內(nèi)容
  • vue3將移除這一屬性筒狠。可以通過默認(rèn)插槽進行實現(xiàn)

5. propsData

  • vue2中使用propsData用于在創(chuàng)建Vue實例的過程中傳入prop
  • vue3移除了這一屬性箱沦。如果需要傳遞辩恼,則需要createApp的第二個參數(shù)
const Comp = Vue.extend({
  props: ['username'],
  template: '<div>{{ username }}</div>'
})

new Comp({
  propsData: {
    username: 'Evan'
  }
})
//vue3中需要這樣
const app = createApp(
  {
    props: ['username'],
    template: '<div>{{ username }}</div>'
  },
  { username: 'Evan' }
)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谓形,隨后出現(xiàn)的幾起案子灶伊,更是在濱河造成了極大的恐慌,老刑警劉巖寒跳,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谁帕,死亡現(xiàn)場離奇詭異,居然都是意外死亡冯袍,警方通過查閱死者的電腦和手機匈挖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來康愤,“玉大人儡循,你說我怎么就攤上這事≌骼洌” “怎么了择膝?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長检激。 經(jīng)常有香客問我肴捉,道長,這世上最難降的妖魔是什么叔收? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任齿穗,我火速辦了婚禮,結(jié)果婚禮上饺律,老公的妹妹穿的比我還像新娘窃页。我一直安慰自己,他們只是感情好复濒,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布脖卖。 她就那樣靜靜地躺著,像睡著了一般巧颈。 火紅的嫁衣襯著肌膚如雪畦木。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天砸泛,我揣著相機與錄音十籍,去河邊找鬼蛆封。 笑死,一個胖子當(dāng)著我的面吹牛妓雾,可吹牛的內(nèi)容都是我干的娶吞。 我是一名探鬼主播垒迂,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼械姻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了机断?” 一聲冷哼從身側(cè)響起楷拳,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吏奸,沒想到半個月后欢揖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡奋蔚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年她混,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泊碑。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡坤按,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出馒过,到底是詐尸還是另有隱情臭脓,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布腹忽,位于F島的核電站来累,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窘奏。R本人自食惡果不足惜嘹锁,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望着裹。 院中可真熱鬧兼耀,春花似錦、人聲如沸求冷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匠题。三九已至拯坟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間韭山,已是汗流浹背郁季。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工冷溃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梦裂。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓似枕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親年柠。 傳聞我的和親對象是個殘疾皇子凿歼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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