# Vue3的改進及特點
1.性能的提升:打包大小減少 41%,初次渲染快 55%,更新快 133%,內存使用減少 54%。
2.新推出的Composition API 使組件更易維護,減少無用數據綁定頁面更流暢。
4.更好TypeScript支持,可以在創建命令里直接配置,頁面集成暢通無阻。
5.Teleport(瞬移組件)、Suspense(解決異步加載組件問題)和全局 API 的修改和優化。
6.Vue3兼容大部分Vue2的特性,用Vue2代碼開發Vue3都可以。
# 安裝
vue --version # 查看版本
注意:如果以前安裝過,需要檢查一下版本,因為只有最新版本(V4.5.4 以上版本)才有創建 Vue3 的選項。
npm install -g @vue/cli
使用 vue-cli 命令行創建項目
vue create vue3-1 // 根據提示自己選擇配置
啟動命令
yarn serve 或 npm run serve
打包命令
yarn build 或 npm run build
# 新語法 setup(),ref(),reactive()
// 注:setup是為了優化性能讓程序按需引入全局統一
1.用法一
<template>
? <div class="home">
? ? <div>名字:{{ name }}</div>
? ? <ul>
? ? ? <li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li>
? ? </ul>
? </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
// 注:defineComponent 在TypeScript下,給予了組件正確的參數類型推斷
export default defineComponent({
? name: "Home",
? components: {},
? props:['msg'],
? setup(props,context) {
? ? // 注:setup函數是處于生命周期函數 beforeCreate 和 Created 兩個鉤子函數之間的函數 也就說在 setup 函數中是無法使用 data 和 methods 中的數據和方法,而methods等可以使用setup中return出去的數據。
? ? /*
? ? 一.函數的第一個參數是 props 用于接收 props.msg
? ? ? 這個props是一個響應式的Proxy對象,不可以解構,解構后會失去響應,如果要用解構的方式,要用toRefs
? ? ? let { msg } = toRefs(props) //但是解析成ref了要用msg.value,所以直接用props.msg更簡單
? ? 二.context對象在setup()中暴露三個屬性 attrs 、slots 和?emit 因為在setup函數中還沒有創建Vue實例,是無法使用vm.$attrs、vm.$slots和vm.$emit的,所以這三個屬性充當了這樣的作用,使用方法相同。
? ? 注意:
? ? ? context.attrs和vm.$attrts包含的是在實例vm.props中沒有被聲明識別的attribute(class和style除外)。所以setup()中參數props中暴露的變量,就不會在context.attrs中暴露。
? ? ? context.slots和vm.$slots只能訪問具名插槽,沒有命名的插槽或者v-slot:default的是沒有暴露的。
? ? ? context的attrs和slots是有狀態的,當組件更新時也會實時更新,所以也不要解構。但與props不同的是,它們不是響應式的,在setup()中的使用應保持只讀的狀態,如果要改變可以在onUpdated的周期函數中進行。
? ? ? context.emit和vm.$emit可以觸發實例上的監聽事件。
? ? */
? ? const list = ref(["深圳", "北京", "上海"]);
? ? const name = ref("");
? ? //注:用ref是為了轉換成引用類型,讓全局引用保持一致,而之前原始類型是不行的,所以要name.value的方示賦值
? ? const show = (index: string) => {
? ? ? ? name.value = index;
? ? };
? ? // 注:不return出去的數據,模板是無法使用的。
? ? return {
? ? ? ? list,
? ? ? ? name,
? ? ? ? show
? ? };
? },
});
</script>
2.用法二 reactive() 優化
<template>
? <div class="home">
? ? <div>名字:{{ data.name }}</div>
? ? <ul>
? ? ? <li v-for="item in data.list" :key="item" @click="data.show(item)">{{ item }}</li>
? ? </ul>
? </div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
? name: "Home",
? components: {},
? setup() {
? ? const data = reactive({
? ? ? list: ["深圳", "北京", "上海"],
? ? ? name: "",
? ? ? show: (index: string) => {
? ? ? ? data.name = index;
? ? ? },
? ? });
? ? return {
? ? ? data
? ? };
? },
});
</script>
2.用法三 toRefs() 優化
<template>
? <div class="home">
? ? <div>名字:{{ name }}</div>
? ? <ul>
? ? ? <li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li>
? ? </ul>
? </div>
</template>
<script lang="ts">
import { defineComponent,reactive,toRefs } from "vue";
export default defineComponent({
? name: "Home",
? components: {},
? setup() {
? ? const data = reactive({
? ? ? list: ["深圳", "北京", "上海"],
? ? ? name: "",
? ? ? show: (index: string) => {
? ? ? ? data.name = index;
? ? ? },
? ? });
? ? const refData = toRefs(data);
? ? //不能直接解析 ...data 必須用 toRefs()
? ? return {
? ? ? ...refData
? ? };
? },
});
</script>
# Vue3 生命周期函數用法, 需要引入 (注:vue2 生命周期函數不影響)
<script lang="ts">
import { defineComponent,ref,reactive,toRefs,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured,
onRenderTracked,
onRenderTriggered
} from "vue";
export default defineComponent({
? name: "Home",
? components: {},
? setup() {
? ? //setup() 開始創建組件之前,在beforeCreate和created之前執行。
? ? const data = reactive({
? ? ? list: ["深圳", "北京", "上海"]
? ? });
? ? onBeforeMount(()=>{
? ? ? //組件掛載到節點上之前執行的函數。
? ? })
? ? onMounted(()=>{
? ? ? //組件掛載完成后執行的函數。
? ? })
? ? onBeforeUpdate(()=>{
? ? ? //組件更新之前執行的函數
? ? })
? ? onUpdated(()=>{
? ? ? //組件更新完成之后執行的函數。
? ? })
? ? onBeforeUnmount(()=>{
? ? ? //組件卸載之前執行的函數。
? ? })
? ? onUnmounted(()=>{
? ? ? //組件卸載完成后執行的函數。
? ? })
? ? onActivated(()=>{
? ? ? //被包含在<keep-alive>中的組件,會多出兩個生命周期鉤子函數。被激活時執行。
? ? })
? ? onDeactivated(()=>{
? ? ? //比如從 A 組件,切換到 B 組件,A 組件消失時執行。
? ? })
? ? onErrorCaptured(()=>{
? ? ? //當捕獲一個來自子孫組件的異常時激活鉤子函數。
? ? })
? ? //< 調試用生命函數
? ? onRenderTracked((event)=>{
? ? ? //跟蹤所有狀態觸發
? ? ? console.log(event);
? ? });
? ? onRenderTriggered((event) => {
? ? ? //跟蹤當前狀態觸發
? ? ? console.log(event);
? ? ? //key 那邊變量發生了變化
? ? ? //newValue 更新后變量的值
? ? ? //oldValue 更新前變量的值
? ? ? //target 目前頁面中的響應變量和函數
? ? });
? ? // 調試用生命函數 />
? ? const refData = toRefs(data);
? ? return {
? ? ? ...refData
? ? };
? },
? mounted(){
? ? console.log("vue2 生命周期");
? }
});
</script>
# Vue3 watch用法
<script lang="ts">
import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
? name: "Home",
? components: {},
? setup() {
? ? const text = ref("測試單個值");
? ? const data = reactive({
? ? ? list: ["深圳", "北京", "上海"],
? ? ? name: "",
? ? ? show: (index: string) => {
? ? ? ? data.name = index;
? ? ? },
? ? });
? ? //watch(text, 單個用法,watch([text,()=>data.name], 多個用法,注:()=>data.name 為了兼容vue2
? ? watch([text,()=>data.name], (newValue, oldValue) => {
? ? ? console.log(`new--->${newValue}`);
? ? ? console.log(`old--->${oldValue}`);
? ? });
? ? const refData = toRefs(data);
? ? return {
? ? ? ...refData,
? ? };
? },
});
</script>
# Vue3 模塊化重用功能 (優化 mixins)
1.新建useTime.ts文件
import { ref } from "vue";
const time = ref("00:00:00");
const getTime = () => {
? ? const now = new Date();
? ? const h= now.getHours() < 10 ? "0" + now.getHours() : now.getHours();
? ? const m = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes();
? ? const s= now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds();
? ? time.value = h + ":" + m + ":" + s;
? ? setTimeout(getTime, 1000);
};
export { time, getTime }
2.引入
<template>
? <div class="home">
? ? <div>時間:{{time}} <button @click="startTime">開始</button></div>
? </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { time, getTime } from './useTime';
export default defineComponent({
? name: "Home",
? components: {},
? setup() {
? ? const startTime = () => {
? ? ? getTime();
? ? };
? ? return {
? ? ? startTime,
? ? ? time
? ? };
? },
});
</script>
# teleport 獨立掛載組件(解決樣式等沖突問題不掛載到app下)
1. index.html 頁面新加插入點(會掛載到 #headTitie DOM下)
<div id="headTitie"></div>
<div id="app"></div>
2. 在components目錄下新建 headTitle.vue
<template>
? <teleport to="#headTitie">
? ? <div class="head">
? ? ? <h1>{{ title }}</h1>
? ? </div>
? </teleport>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
? name: "headTitie",
? setup() {
? ? const title = ref("Vue3 新特性示例");
? ? return {
? ? ? title,
? ? };
? },
});
</script>
3. 在 App.vue 加
<template>
? <headTitle />
? <router-view />
</template>
<script lang="ts">
import headTitle from "./components/headTitle.vue";
export default {
? name: "App",
? components: {
? ? headTitle,
? },
};
</script>
# Suspense 異步請求組件
1. 新建Demo.vue
<template>
? <div class="Demo">
? ? <div>名字:{{ name }}</div>
? </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
? name: "Demo",
? components: {},
? setup() {
? ? return new Promise((resolve, reject) => {
? ? ? setTimeout(() => {
? ? ? ? return resolve({ name: "我是 Suspense 異步請求組件" });
? ? ? }, 2100);
? ? });
? },
});
</script>
2. 使用引入 home.vue
<template>
? <div class="home">
? ? <Suspense>
? ? ? <template #default>
? ? ? ? <Demo />
? ? ? </template>
? ? ? <template #fallback>
? ? ? ? <p>加載中...</p>
? ? ? </template>
? ? </Suspense>
? </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import Demo from "./Demo.vue";
export default defineComponent({
? name: "Home",
? components: {Demo}
});
</script>