如何快速上手vue3,學會這幾個API吧!

2022-03-18 22:00:39
如何快速上手vue3?本篇文章給大家分享幾個API,學會這幾個API,vue3可以直接上手,其他的在慢慢去了解,希望對大家有所幫助!

vue2開發過專案的,想直接上手vue3很快,幾個API熟悉了就夠了,其它的特性在使用vue3的過程慢慢去了解。 任何在熟悉了使用之後再去研究一些API的原理,慢慢就能把vue3掌握。

而且vue3的使用結合ts,開發過程中ts的比重沒有那麼大,之前分享的ts那些基礎會了,完全就夠用來開發了。【相關推薦:】

全域性 API 和應用 API

vue3的一個新概念,返回一個提供應用上下文的應用範例,應用範例掛載的整個元件樹共用同一個上下文:

const app = createApp(App);
app.use(store).use(router).mount("#app");

vue2:

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");

然後以前用Vue.的那些API都變成用這個應用範例app.:

vue2vue3
Vue.componentapp.component
app.configapp.config
app.directiveapp.directive
app.mixinapp.mixin
app.useapp.use

其它API像nextTick、h等都是直接從vue結構出來使用:

import { createApp, h, nextTick } from 'vue'

composition API

tips

  • vue3中不再使用this
  • vue3元件不需要根標籤,但是會有警告Extraneous non-props attributes
  • 推薦使用單檔案元件,後面的實現程式碼都是單檔案元件方式

setup

這個可太重要了,vue3把這個函數當作了入口點。接收兩個引數props和context。 函數會在beforeCreate、created之前執行,可以說取代了beforeCreate、created成為新的生命週期。 新增的composition API都寫在setup函數中。

props就是vue2的props,context則提供attrs、slots、emit等。 通過return一個物件,把響應式資料暴露給template,相當於vue2的data,不同的是函數也是這樣暴露給template:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h1 @click="test">{{ isRef }}</h1>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props) {
    console.log(props.msg);
    let isRef = ref("is ref");
    const test = () => {
      console.log(isRef.value);
    };
    return {
      isRef,
      test,
    };
  },
});
</script>

單檔案元件

vue3還提供了單檔案元件(推薦),將setup加到script上,裡面的程式碼就會被編譯成setup函數。 比較重要的幾個點:

頂層的繫結會被暴露給模板

宣告的頂層的繫結 (包括變數,函數宣告,以及 import 引入的內容) 都能在模板中直接使用 響應式資料、元件等也可以直接使用:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h1 @click="test">{{ isRef }}</h1>
  </div>
<MyComponent />
</template>

<script lang="ts" setup>
import MyComponent from './MyComponent.vue'
import { ref } from "vue";
const msg = "msg";
const isRef = ref("");
function test() {
  console.log(isRef.value);
}
</script>

使用單檔案元件,一些屬性的使用也要改變,當然也有其它替代的API:

屬性對應
props和emitsdefineProps和defineEmits
ref或者$parentdefineExpose
slots和attrsuseSlots和useAttrs()

ref

接受一個內部值並返回一個響應式且可變的 ref 物件,在setup函數內部存取ref函數需要加.value, 如果要加型別,要用泛型,不加則會型別推論,也可以不給初始值,則是any,且是undefined:

const ref1 = ref(1);
const ref2 = ref<number>(2);
const ref3 = ref1.value;//不是響應式,相當於變數
const ref4 = ref();//refs.value是undefined
ref2.value = 5;//賦值 讀取都要.value

一般來說,ref只會賦值基礎資料型別和陣列,也可以泛型聯合型別 如果將物件分配為ref值,則它將被reactive函數處理為深層的響應式物件:

//不建議,內部也是reactive處理
const ref1 = ref({
  a: 10,
});

//不確定型別
const ref3 = ref<string | number>();
ref3.value = 1;
ref3.value = "";

//陣列物件,ts型別宣告,用泛型
type Obj1 = { c: string };
type Obj2 = {
  b: string;
  c: Obj1[];
};
const ref2 = ref<Obj2[]>([
  {
    b: "",
    c: [{ c: "" }],
  },
]);

reactive

用來宣告響應式的物件,型別通過泛型新增:

type Obj = {
  a: number;
  b: string;
};
let obj = reactive<Obj>({
  a: 10,
  b: "",
});
let state = reactive({
  a: 10,
  b: "",
});

reactive將會解包所有深層的 refs,同時維持ref的響應性, 將ref分配給reactive的property時,ref也會被自動解包。 簡單說就是ref的值和reactive的值響應式了

const count = ref(1)
const obj = reactive({ count })

// ref 會被解包
console.log(obj.count === count.value) // true

// 它會更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 它也會更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

//跟上面一樣效果
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true

toRef

為源響應式物件上的某個property新建立一個ref,也就是reactive建立的才可以, 會保持對其源 property 的響應式連線:

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

toRefs

將響應式物件轉換為普通物件,其中結果物件的每個property都是指向原始物件相應property的ref:

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的型別:
{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已經「連結」起來了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

toRef和toRefs在對一些響應式物件結構展開會很有用,可以有用的時候看看。

watch

watch接收兩個引數,第一個引數可以是有return的函數,也可以是一個ref, 第二個引數則跟vue2相同的函數,並且watch可以很多個:

//一般ref不用這種方式
let count = ref(0);
watch(
  () => count.value,
  (val, old) => {
    console.log(old, val);
  }
);

//單個ref推薦
watch(count, (val, old) => {
  console.log(old, val);
});

//監聽reactive物件
let state = reactive({ count: 0 });
//函數引數必須具體到某個值,如果是() => state無效,或者加上{ deep: true }
watch(
  () => state.count,
  (val, old) => {
    console.log(old, val);
  }
);

//加上{ deep: true }
watch(
  () => state,
  (val, old) => {
    console.log(old, val);
  },
  { deep: true }
);

//監聽整個物件,新舊的值都相同,或者用lodash.cloneDeep進行深拷貝
//state改成() => _.cloneDeep(state)
watch(state, (val, old) => {
  console.log(old.count, val.count);
});

也可以同時監聽多個,兩個引數分別用陣列,個人還是建議單個:

const state = reactive({ count: 1 });
const count = ref(0);
// 監聽一個陣列
watch(
  [() => state.count, count],
  ([newState, newCount], [oldState, oldCount]) => {
    console.log("new:", newState, newCount);
    console.log("old:", oldState, oldCount);
  }
);

官網一直提到惰性,其實就是是否加immediate: true,加了初始化就會執行。

watchEffect

它"立即執行"傳入的一個函數,同時響應式追蹤其依賴,並在其依賴變更時重新執行該函數:

const state = reactive({ count: 1 });
const count = ref(0);

watchEffect(() => {
  if (state.count > 3) {
    count.value++;
  }
});
watchEffect(() => console.log(count.value));

至於watch和watchEffect共用停止偵聽,清除副作用 (相應地 onInvalidate 會作為回撥的第三個引數傳入)、 副作用重新整理時機和偵聽器偵錯行為後面要仔細研究一下。

computed

從vue2開始,就很多人分不清什麼時候用computed什麼時候用watch,computed主要是用來宣告 有兩個及以上的依賴的資料,也就是說一個變數是根據多個資料進行判斷的,用computed,單個的用watch。 至於語法的不同就不多說了,vue3的computed常用語法是一個帶有return的函數,也可以同時存在多個:

let count = ref(0);
let page = ref(0);
let pg = computed(() => {
  return count.value + page.value;
});

要注意,computed宣告的變數(pg)是不能直接去修改的(唯讀),跟vue2一樣也有get、set函數(可讀可寫)。

defineProps、defineEmits

在單檔案元件中必須使用defineProps和defineEmits API來宣告props和emits,可以算是語法糖。 父元件還是跟原來一樣傳值,子元件接收:

//父元件
<template>
  <div class="home">
    <input v-model="msg" />
    <HelloWorld :msg="msg" @change="change" />
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import HelloWorld from "@/components/HelloWorld.vue";
let msg = ref("is parent");
const change = (val: string) => {
  msg.value = val;
};
</script>

//子元件
<template>
  <div>{{ msg }}</div>
  <button @click="change">emit</button>
</template>

<script lang="ts" setup>
import { defineProps, defineEmits } from "vue";
const props = defineProps({
  msg: String,
});
console.log(props.msg);

const emit = defineEmits(["change"]);
const change = () => {
  emit("change", "is son");
};
</script>

//設定預設值
defineProps({
  msg: {
    type: Number,
    default: 100,
  },
});

template可以直接使用msg,要呼叫的話就得props.msg。

withDefaults

defineProps只能限制型別,沒有提供預設值(這邊應該是限定用ts的介面才沒有預設值)。為了解決這個問題,提供了 withDefaults 編譯器宏:

type Porps = {
  msg: string;
};
const props = withDefaults(defineProps<Porps>(), {
  msg: "default",
});

withDefaults接收兩個引數,第一個引數是defineProps加props欄位的泛型,第二個欄位是預設值,也可以不設定。

也可以監聽props:

watch(
  () => props.msg,
  (val) => {
    console.log(val);
  }
);

this.$refs

vue2很多情況還是需要用到這個api,vue3中比較特別, 為了獲得對模板內元素或元件範例的參照,我們可以像往常一樣宣告ref, 在渲染上下文中暴露root,並通過ref="root",將其繫結到 div 作為其 ref。 在虛擬DOM修補程式演演算法中,如果 VNode 的 ref 鍵對應於渲染上下文中的 ref, 則VNode的相應元素或元件範例將被分配給該ref的值。 這是在虛擬 DOM 掛載/打修補程式過程中執行的,因此模板參照只會在初始渲染之後獲得賦值。

<button ref="testRef">testRef</button>

let testRef = ref(null);
onMounted(() => {
  // DOM 元素將在初始渲染後分配給 ref
  console.log(testRef.value); // <button>testRef</button>
});

nextTick

跟vue2一樣的使用:

nextTick(() => {
  console.log(testRef.value);
});

也可以跟官網提供的一樣用async的函數await:

let testRef = ref(null);
const nextTickFn = async () => {
  await nextTick();
  console.log(testRef.value);
};
nextTickFn();

defineExpose

vue2有時候會用this.$refs呼叫子元件的函數或者變數, 單檔案元件是預設關閉的,所以單檔案元件要用defineExpose編譯器宏暴露出去:

//父元件
<template>
  <div class="home">
    <HelloWorld ref="sonRef" />
  </div>
</template>

<script lang="ts" setup>
import { nextTick, ref } from "vue";
import HelloWorld from "@/components/HelloWorld.vue";
let sonRef = ref();
nextTick(() => {
  sonRef.value.sonFn();
  console.log(sonRef.value.sonRef);
});
</script>

//子元件
let sonRef = ref("is son");
const sonFn = () => {
  console.log("is son fn");
};
defineExpose({ sonFn, sonRef });

vue2的this.$parent在單檔案元件中,自己是實現了,但是官方沒有直接給出程式碼:

//父元件
const parentRef = ref("is parent ref");
const parentFn = () => {
  console.log("is parent fn");
};
defineExpose({
  parentRef,
  parentFn,
});

//子元件
let parent = getCurrentInstance();
console.log(parent?.parent?.exposed?.parentRef.value);
parent?.parent?.exposed?.parentFn();

新增元件

teleport

官網介紹了很多,這個元件最大的作用就是可以讓元件脫離固定的元件位置,可以掛載在邏輯上最優的位置, 其它使用都跟元件一樣,只有位置改變:

<teleport to="#teleportDiv">
  <HelloWorld />
</teleport>

<teleport to="body">
  <HelloWorld />
</teleport>

掛載的元素會從上向下解析,第一個to的元素,標籤、class、id等。一般使用也不會隨便,都會用id元素或者body。

Suspense

Suspense是一個試驗性的新特性,官方也說生產環境請勿使用。 主要是允許將元件非同步處理等待過程提升到元件樹中處理。

單檔案元件裡面頂層await裡面提到: async setup() 必須與 Suspense 組合使用,Suspense 目前還是處於實驗階段的特性。 我們打算在將來的某個釋出版本中開發完成並提供檔案。

所以這邊就不給出不是單檔案元件的程式碼實現了。

生命週期

生命週期還是跟原來的一樣,只是前面都加上了on,destroy都變成了unmount。 setup會在beforeCreate、created之前執行,取代了beforeCreate、created。

  • beforeCreate --> beforeCreate
  • created --> setup
  • beforeMount --> onBeforeMount
  • mounted --> onMounted
  • beforeUpdate --> onBeforeUpdate
  • updated --> onUpdated
  • beforeDestroy --> onBeforeUnmount
  • destroyed --> onUnmount

使用:

onMounted(() => {
  console.log("mounted");
});

試了一下,居然可以寫多個。

vue3新增或者刪除了哪些API,其實沒必要一下子全部去記住,當一些API使用的時候如果發現不生效了, 再去官網查一下,當然,基礎的這些API就得記下來。

還是那句話,先學會基礎,上手開發,然後慢慢去了解學習不常用的,甚至是原始碼。

(學習視訊分享:、)

以上就是如何快速上手vue3,學會這幾個API吧!的詳細內容,更多請關注TW511.COM其它相關文章!