前言
????????
????????最近有個小朋友想了解Vue前端技術,但他只懂一些HTML基礎,讓我用最簡單的方式講解。于是就有了這篇面向初學者的博文。
????????老手請繞行,本文專為新手準備。如果發現用詞不當的地方歡迎留言指正,覺得對新手有幫助的話請收藏點贊。
基礎
-
什么是Vue
它是一個前端框架、目前最新版本是Vue3、本質上就是在單個頁面上進行組件渲染。
-
為什么是Vue
原因很多,但最重要的是生態好,學、用的人多;(參照來源各招聘平臺對于前端的要求)
-
Vue 版本選擇
作為技術,當然選擇新版本了,但相對舊版本有一些變化,新版本同時支持組合式API與選項式API,目前官方推薦采用組合式API(用到什么就引用什么速度更快)
項目\版本 | VUE2 | VUE3 |
---|---|---|
構建工具 | Vue CLI | Vite |
狀態管理 | VueX | Pinia |
IDE 增強插件 | Vetur | vue-official |
靜態生成 | VuePress | VitePress |
-
Vue 引擎選TS還是JS
JavaScript (JS):簡單、廣泛。TypeScript(TS):類型安全、更好的可維護性。同樣都是要花精力學習的話還是推薦TS,盡管你學習了JS,還是建議你慢慢接受TS。
-
Vue Router
VUE Router 官言路由,用于跳轉用;之前用MVC是用后端Controller層控制路由跳轉,現在由前端控制。
-
Vue Panner
基于Vue3.0 狀態管理工具
-
VitePress
靜態網站成生器:VUE本質上就是在單個頁面上組裝拆解,所以它不利于SEO優化(換言之搜索引擎的網站爬不到),現在有了它就解決了這個問題。
-
開發工具選擇
這個因人而異、可以是VSCode(免費)、也可以是WebStorm(收費),以下是常見的工具對比。
類型/常用工具 | VSCode | HBuilder | WebStrom | Trae | cursor |
---|---|---|---|---|---|
費用 | 免費 | 免費 | 收費 | 免費 | 免費 |
插件生態評分(個人) | 95 | 60 | 95 | 80 | 80 |
智能化(個人) | 80 | 60 | 70 | 90 | 90 |
-
準備
在開發的電腦上必有安裝好Node,推薦使用新版本。
-
創建
npm init vue@3
npm init vue@3
Need to install the following packages:
create-vue@3.16.4
Ok to proceed? (y) y //是否要創建版本為3.16.4 的VUE┌ Vue.js - The Progressive JavaScript Framework
│
◆ 請輸入項目名稱:
│ demo█
-
選擇
Need to install the following packages:
create-vue@3.16.4
Ok to proceed? (y) y
┌ Vue.js - The Progressive JavaScript Framework
│
◇ 請輸入項目名稱:
│ demo
│
◆ 請選擇要包含的功能: (↑/↓ 切換,空格選擇,a 全選,回車確認)
│ ? TypeScript
│ ? JSX 支持
│ ? Router(單頁面應用開發)
│ ? Pinia(狀態管理)
│ ? Vitest(單元測試)
│ ? 端到端測試
│ ? ESLint(錯誤預防)
│ ? Prettier(代碼格式化)
-
目錄
node_modules/ 存放NPM下載的依賴包 package.json 項目依賴配置文件 index.html VUE項目的入口文件 vite.config.ts vue的構建、打包配置文件 env.d.ts TypeScri環境變量,全局類型聲明 public/ 靜態資源目錄 src/源碼目錄app.vue 應該主組件main.ts 應該入口文件,創建Vue實例與掛載assets/ 靜態資源文件components/通常存放復用組件目錄 main.css 全局樣式文件 base.css 基礎樣式文件
項目運行流程
1.main.ts里聲明一個VUE項目中的唯一一個實例
import './assets/main.css'import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
2.index.html 引用main.ts的實例,去掛載主組合app.vue
<!DOCTYPE html>
<html lang=""><head><meta charset="UTF-8"><link rel="icon" href="/favicon.ico"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vite App</title></head><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body>
</html>
3.app.vue 總父組件去掛載各種子組件
<!DOCTYPE html>
<html lang=""><head><meta charset="UTF-8"><link rel="icon" href="/favicon.ico"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vite App</title></head><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body>
</html>
VUE模板
app.vue是主組件,以下相關功能演示都在這個組件中測試。
一個完整Vue文件必須包含script與模板template,且忽略前后順序(前后位置不影響)
<script setup lang="ts">//數據業務處理層
</script><template>//數據呈現
</template>
數據綁定渲染示例
<script setup lang="ts">const msg = 'Hello World'
</script><template><h1>{{ msg }}</h1>
</template>
組合式API
選項式API網上教程太多了不再說明,我這里只對ts下的組合式API進行示例。
<script setup lang="ts">
// setup 相關于 選項式中的default....那一套,setup 函數 ;lang="ts" 告訴編譯器,這個文件是ts文件//推薦使用ref()來定義響應式數據import {computed, onMounted, onUnmounted, ref, watch} from "vue";const msg=ref('這是一個消息');let msg2="這也是一個消息"//定義方法function changeMsg(){msg.value="這是修改后的消息";msg2="這也是修改后的消息";}//監聽msg數據watch(msg, (newVal) => {alert(newVal);})watch(() => msg2, (newVal) => {alert(newVal);})// 計算屬性const Isok = computed(() => {return msg.value === "這是一個消息" ? "這是一個消息" : "";});// 生命組件周期..........onMounted(() => {alert("組件已掛載");});onUnmounted(() => {alert("組件已卸載");});</script><template><div><h1>{{msg}}</h1><h1>{{msg2}}</h1><button @click="changeMsg">修改消息</button></div>
</template>
VUE的基本操作
-
渲染?
? ? ? ? 條件渲染
????????
<script setup lang="ts">const code='A';const isShow=true;
</script><template><div ><!-- 條件渲染:if ---><h1 v-if="code === 'A'">A</h1><h1 v-else-if="code === 'B'">B</h1><h1 v-else>C</h1><!-- 條件渲染顯示控制 ---><h1 v-show="isShow">isShow</h1></div></template>
? ? ? ? 列表渲染
? 可以將List和對象渲染出來,原理是:For循環方式處理
<script setup lang="ts">const list = [{id: 1,name: 'Todo 1',completed: false},{id: 2,name: 'Todo 2',completed: true},{id: 3,name: 'Todo 3',completed: false}];const obj={name:'test',age:18,sex:'男',height:1.8};
</script><template><div ><li v-for="(item,index) in list" :key="item.id">{{index}} - {{ item.name }}</li><li v-for="(item,index) in obj" :key="index">{{ item }}</li></div></template>
-
事件
? ? ? ? 無參
<script setup lang="ts">const showMessageWithParam = (message: string) => {alert(message);
};</script><template><div ><button @click="showMessageWithParam('TEST!')">點擊我</button></div></template>
? ? ? ? 有參? ?
<script setup lang="ts">
const msg="";
const showMessageWithParam = (message: string) => {alert(message);
};
</script>
<template><div ><input v-model="msg" placeholder="請輸入內容" /><button @click="showMessageWithParam(msg)">傳遞輸入內容</button></div>
</template>
? ? ? ? Event處理? ??
<script setup lang="ts">
const list = ["1", "2", "3"];
const showMessageWithParam = (e: Event, message: string) => {(e.target as HTMLButtonElement).innerHTML = "已點擊";alert(message);
};
</script>
<template><div ><li v-for="item in list" :key="item" @click="showMessageWithParam($event,item)">{{ item }}</li></div>
</template>
? ? ? ? 事件修飾
對事件進行修飾以達到阻止事件或事件冒泡等問題
<script setup lang="ts">
const message = "hello world";
const showMessageWithParam = (e: Event, message: string) => {(e.target as HTMLButtonElement).innerHTML = "已點擊";alert(message);
};
</script>
<template><div ><a href="https://www.baidu.com" @click.prevent="showMessageWithParam($event,message)">測試一下</a></div>
</template>
-
屬性
? ? ? ? class
在TS中定義一個變量去指向一個樣式類,再模板中用這個定義的變量去渲染就實現了屬性綁定;綁定方式以『:』+屬性="定義變量的名"形式實現,如下所示。
<script setup lang="ts">
const classObj = 'test';
const demoid=11;
const msg = 'Hello World!';
</script><template><div :class="classObj" :id="demoid.toString()"> <h1>{{ msg }}</h1></div></template><style scoped>
.test {color: red;font-size: 50px;
}
</style>
以上可以把一組綁定直接封裝一個對象進行綁定
<script setup lang="ts">
const msg = 'Hello World!';
const obj={class:'test',id:'abc'
};
</script><template><!-- 綁定對象 ---><div v-bind="obj"><h1>{{ msg }}</h1></div></template><style scoped>
.test {color: red;font-size: 50px;
}
</style>
以數組形式綁定
<script setup lang="ts">
const classObj = 'test';
const demoid=11;
const msg = 'Hello World!';
const t1="test2";
const t2="test3";
</script><template><!--- 以對象形式綁定 --><div :class="classObj" :id="demoid.toString()"><h1>{{ msg }}</h1></div><!--- 以數組形式綁定 --><div :class="[t1,t2]"><h1>{{ msg }}</h1></div></template><style scoped>
.test {color: red;font-size: 50px;
}
.test2{color: blue;
}
.test3{font-size: 30px;
}
</style>
? ? ? ? style
以:style='類'進行綁定,也可以通過:style='[類1、類2]'以數組進行綁定
<script setup lang="ts">
const msg = 'Hello World!';
const obj={color :'red',fontSize :'30px'
};
const ob2={backgroundColor :'yellow'
};
</script><template><!--- 以對象形式綁定 --><div :style="obj"><h1>{{ msg }}</h1></div><div :style="[obj,ob2]"><h1>{{ msg }}</h1></div>
</template>
-
數組
方法 | 解釋 |
---|---|
push() | 向數組中添加數據 |
pop() | 刪除數組中的數據 |
shift() | 刪除第一個 |
unshift() | 向數組中添加數據并放在第一位 |
splice() | splice(開始刪除的位置, 數量(可選擇), 刪除的元素1(可選擇), ..., 刪除的元素N(可選擇)) |
sort() | 排序(升) |
reverse() | 反轉數組 |
concat() | 以替換數組形式添加數據 |
filter() |
<script setup lang="ts">
import { ref } from "vue";const newItem = ref("");
const list = ref(["a", "b", "c"]); // 使用 ref 包裹 list 以保證響應性
const addItem = () => {if (newItem.value.trim()) {list.value.push(newItem.value); // 向數組中添加數據newItem.value = "";}
};
const addItem2 = () => {if (newItem.value.trim()) {list.value = list.value.concat([newItem.value]);//以替換的方式添加到數組中newItem.value = "";}
};
</script><template><div><div><input v-model="newItem" placeholder="輸入新項" /><button @click="addItem">添加</button><button @click="addItem2">添加2</button></div><ul><li v-for="item in list" :key="item">{{ item }}</li></ul></div>
</template>
-
偵聽
通過watch定義要監聽的對象實現更多實用的功能
<script setup lang="ts">
import { ref,watch } from 'vue';
const msg = ref('Hello World!');
function changeMsg() {msg.value = 'Hello TONGYI Lingma!';
}watch(() => msg.value,(newVal, oldVal) => {alert(`msg 從 ${oldVal} 變為 ${newVal}`);}
);</script><template><!--- 以對象形式綁定 --><div ><h1>{{ msg }}</h1> <button @click="changeMsg">change</button></div></template>
-
計算屬性
為了解決復雜的計算,讓代碼具備一定的可維護性
<script setup lang="ts">
import { computed, ref } from "vue";const newItem = ref("");
// 計算屬性
const Isok = computed(() => {return newItem.value === "ok" ? "ok" : "";
});</script><template><div><div><input v-model="newItem" placeholder="輸入新項" /><!--- 計算屬性 ---><p v-text="Isok"></p></div></div>
</template>
-
表單
表單:v-mode="數據"可以對數據進行雙向綁定
<script setup lang="ts">
import { ref } from 'vue';
const msg = ref('Hello World!');
function changeMsg() {msg.value = 'Hello TONGYI Lingma!';
}
</script><template><!--- 以對象形式綁定 --><div ><h1>{{ msg }}</h1></div><div><form><input type="text" v-model="msg"></input></form></div>
</template>
-
DOM操作
盡管不提倡直接操作DOM,也有時我們需要對DOM進行操作;通過refs的方式就能實現;
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const msg = ref('Hello World!');
const testRef = ref<HTMLElement | null>(null);const sendMessage = () => {if (testRef.value) {testRef.value.innerHTML = '修改了!';}
};
</script><template><div ref="testRef"><h1>{{ msg }}</h1><button @click="sendMessage">操作</button></div>
</template>
組件
-
生命周期
組件類似于當年我在做CS程序的時候,窗口有創建事件、顯示事件、關閉事件等,在相應的事件里寫相應的代碼;VUE也是同理,生命周期如下圖所示。根據這些生命周期去處理相應周期的代碼去實現相應的邏輯功能。
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import {ref, onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUnmount, onBeforeUpdate} from 'vue';const msg = ref('199991111');
//組件創建前
onBeforeMount(() => {console.log('組件創建前');
})
// 組件創建后示例
onMounted(() => {console.log('組件創建后示例');
})
//組件掛載前
onBeforeMount(() => {console.log('組件掛載前');
})
//組件掛載后
onMounted(() => {console.log('組件掛載后');
})
//組件更新前
onUpdated(() => {console.log('組件更新前');
})
//組件更新后
onBeforeUpdate(() => {console.log('組件更新后');
})
//組件卸載前
onBeforeUnmount(() => {console.log('組件卸載前');
})
//組件卸載后
onUnmounted(() => {console.log('組件卸載后');
})</script>
<template></template>
-
構成部分
????????前面講過VUE 本質上就是在一個HTML不斷的組拆,而APP.VUE文件是一個入口文件,所以每一個vue文件視作一個組件。
????????組件包含必要的<template>負責數據呈現 、<script> 業務邏輯處理(如無邏輯與數據可以不包含)、樣式<style>部分(如無樣式或默認總樣式可以不包含。
<script setup lang="ts"></script><template></template><style scoped></style>
-
引用
定義的組件:test.vue
<script setup lang="ts">const msg='測試一下';
</script>
<template><div><h1>{{msg}}</h1></div>
</template>
調用組件:app.vue
<script setup lang="ts">import Test from '@/com/test.vue'
</script><template><Test/>
</template>
-
嵌套
下面是一個關系圖:
人(app.vue)-頭(head.vue)-中部(mind.vue)-左手(leftHand.vue)-胸(chest.vue)-右手(rightHand.vue)-下部(boot.vue)-左腿(leftLeg)-右腿(rightLeg)
接下來我將用代碼進行實現
head.vue:頭部代碼
<template><div><h1>------頭部-------</h1></div>
</template>
leftHand.vue:左手
<template><div><h1>左手</h1></div>
</template>
chest.vue:胸
<template><div><h1>這是胸部</h1></div>
</template>
rightHand.vue:右手
<template><div><h1>右手</h1></div>
</template>
mind.vue:中部
<script setup lang="ts">
import LeftHand from "@/com/leftHand.vue";
import Chest from "@/com/chest.vue";
import RightHand from "@/com/rightHand.vue";
</script><template><div><h1>-----中部-------</h1><LeftHand /><Chest /><RightHand /></div>
</template>
leftLeg.vue:左腿
<template><div><h1>左腿</h1></div>
</template>
rightLeg.vue:右腿
<template><div><h1>右腿</h1></div>
</template>
boot.vue:下部
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
</script>
<template><h1>---下部------</h1><LeftLeg /><RightLeg />
</template>
app.vue:整體
<script setup lang="ts">import Head from "@/com/head.vue";import Mind from "@/com/mind.vue";import Boot from "@/com/boot.vue";
</script><template><div><Head/><Mind/><Boot/></div></template>
-
全局注冊
????????以上是做了個示例,如果你想不引用,實現處理調用那就要配置相應組為全局組件,那需要在main.ts里聲明組件。以下是把test.vue注冊為全局,然后用app.vue直接調用。
? ? ? ? ps:非必要不提供注冊全局組件
main.ts
import './assets/main.css'import { createApp } from 'vue'
import App from './App.vue'
import Test from "@/com/test.vue";createApp(App).mount('#app')
app.vue
<script setup lang="ts">import Head from "@/com/head.vue";import Mind from "@/com/mind.vue";import Boot from "@/com/boot.vue";import Test from "@/com/test.vue";
</script><template><div><test/><Head/><Mind/><Boot/></div></template>
-
傳值
父傳子
方案1:props 逐級傳值
在父組件上調用子組件以:傳遞名="數據"的形式進行傳值;在子組件上的props上用傳遞名接收,且接收到的數據無法被修改。
父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import {ref} from "vue";// 自定義事件函數
const handleCustomEvent = (data :any) => {msgFormLeft.value = data;
};
const msgFormLeft=ref('空');
const test2="test2";
const test3={name : "test3",age : 18,
}
</script><template><!--- 只用test傳數據 test2傳綁定數據 test3傳對象---><LeftLeg test="msg" :test2="test2" :test3="test3"/><a>{{msgFormLeft}}</a><RightLeg />
</template>
子組件
<script setup lang="ts">
import { defineEmits, ref, watch } from 'vue';// 定義 props 接收父組件傳遞的 PMSG 參數
const props = defineProps({test: {type: String,required: true},test2: {type: String},test3: {type: Object,default: () => ({ name: '', age: 0 })}
});// 將 PMSG 參數賦值給 msg
const msg = ref(props.test);</script>
<template><div><h1>左腿</h1><a>{{ msg }}</a><a>{{ test2 }}</a><a>{{ test3.name }}</a><a>{{ test3.age }}</a></div>
</template>
方案2:依賴注入
上面的方案雖然可以解決父傳子,但很多項目是組件間層層嵌套。想像一下如果通過上述方式傳值不僅代碼量變大,而且可維護性也差。那么通過依賴注入的方式就可以實現父組件直傳到基于父組件下的任何一個組件中。
首先provide在父組件中定義,子組件中用inject接收就可以了。
app.vue
<script setup lang="ts">import Boot from "@/com/boot.vue";import {provide} from "vue";
const myValue = "Hello from provide/inject";
const obj={name: 'John',age: 30
};
provide('myKey', myValue);
provide('myObj', obj);
</script><template><div><Boot/></div></template>
子子組件rightLeg.vue
<script setup lang="ts">
import {inject} from "vue";
const msg = inject<string>("myKey");
const obj = inject<{ name: string, age: number}>("myObj");
</script>
<template><div><h1>右腿</h1><hr><h1> {{ inject<string>("myKey") }} </h1><hr><h1>{{ msg }}</h1><hr><h1>{{ obj?.name }}</h1><hr><h1>{{ obj?.age }}</h1></div>
</template>
子傳父
? ? ? ? 方案1:自定義事件傳遞
-
子組件里定義一個方法
-
在子組排方法里以emit('標識')的方式命名一個標識
-
父組件里在調用子組件那里用@標識名綁定個這標識并在后面對應父組件對應的事件
? ? ? ? 無參
? ? ? ? 子組件
????????
<script setup lang="ts">import { defineEmits } from 'vue';const emit = defineEmits(['fTest']);const test = () => {emit('fTest');}
</script>
<template><div><h1>左腿</h1><button @click="test">回傳調用父組件</button></div>
</template>
? ? ? ? 父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";// 自定義事件函數
const handleCustomEvent = () => {alert("來自子數據的調用")
};
</script><template><LeftLeg @fTest="handleCustomEvent"/><RightLeg />
</template>
? ? ? ? 有參????????
????????子組件
<script setup lang="ts">import { defineEmits } from 'vue';const emit = defineEmits(['fTest']);const test = () => {emit('fTest', '左腿');}
</script>
<template><div><h1>左腿</h1><button @click="test">回傳調用父組件</button></div>
</template>
? ? ? ? 父組件
????????
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";// 自定義事件函數
const handleCustomEvent = (data :any) => {alert(data)
};
</script><template><LeftLeg @fTest="handleCustomEvent"/><RightLeg />
</template>
? ? ? ? 時時傳
????????子組件
<script setup lang="ts">
import { defineEmits, ref, watch } from 'vue';const emit = defineEmits(['fTest']);
const msg = ref('');const test = () => {emit('fTest', msg.value);
};watch(msg, (newVal) => {// 當msg值發生變化時執行test方法test();
});
</script>
<template><div><h1>左腿</h1><input type="text" v-model="msg" @change="test"></div>
</template>
? ? ? ? 父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import {ref} from "vue";// 自定義事件函數
const handleCustomEvent = (data :any) => {msgFormLeft.value = data;
};
const msgFormLeft=ref('空');
</script><template><LeftLeg @fTest="handleCustomEvent"/><a>{{msgFormLeft}}</a><RightLeg />
</template>
? ? ? ? 方案2:利用props傳事件實現
父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import {ref} from "vue";
// 自定義事件函數
const handleCustomEvent = (data :any) => {msgFormLeft.value = data;
};
const msgFormLeft=ref('空');
</script><template><!--- 只用test傳數據 test2傳綁定數據 test3傳對象---><LeftLeg test="msg" :handleCustomEvent="handleCustomEvent"/><a>{{msgFormLeft}}</a><RightLeg />
</template>
子組件
<script setup lang="ts">
import { ref, watch } from 'vue';
const props = defineProps({handleCustomEvent: {type: Function,required: true}
});
const msg = ref('');watch(() => msg.value, (newValue, oldValue) => {props.handleCustomEvent(newValue)
});
// 如果需要在子組件中直接調用父組件函數,可以通過以下方式:
// 例如:props.handleCustomEvent('aaaaaaaaa');
</script>
<template><div><h1>左腿</h1><input type="text" v-model="msg"></div>
</template>
-
插槽
以<slot>為標簽顯示,把帶有引用的子組件內容塊顯示
直接插
父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import { ref } from "vue";const msgFormLeft = ref('空1111111');</script><template><LeftLeg><!-- 插槽A用于傳遞父組件的固定內容 --><div><h2>這是傳過來的內容</h2></div></LeftLeg><RightLeg />
</template>
子組件
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import { ref, watch } from 'vue';const msg = ref('');</script>
<template><div><h1>左腿</h1><slot ></slot><hr/></div>
</template>
插進去帶數
?父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import { ref } from "vue";const msgFormLeft = ref('空1111111');</script><template><LeftLeg><!-- 插槽A用于傳遞父組件的固定內容 --><div><a>{{ msgFormLeft }}</a></div></LeftLeg><RightLeg />
</template>
子組件
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import { ref, watch } from 'vue';const msg = ref('');</script>
<template><div><h1>左腿</h1><slot ></slot><hr/></div>
</template>
一次插入多個,分別顯示
?父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import { ref } from "vue";const msgFormLeft = ref('空1111111');</script><template><LeftLeg><!-- 插槽A用于傳遞父組件的固定內容 --><template #A><div><h2>這是傳過來的內容</h2></div></template><!-- 插槽B用于傳遞父組件的數據內容 --><template #B><div><a>{{ msgFormLeft }}</a></div></template></LeftLeg><RightLeg />
</template>
子組件
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import { ref, watch } from 'vue';const msg = ref('');</script>
<template><div><h1>左腿</h1><slot name="A"></slot><hr/><slot name="B"></slot><hr/></div>
</template>
插進去的數據混合父數據與子數據
子組件
<script setup lang="ts" xmlns="http://www.w3.org/1999/html">
import { ref, watch } from 'vue';const msg = ref('199991111');</script>
<template><div><h1>左腿</h1><slot name="A"></slot><hr/><slot name="B"></slot><hr/><slot name="C" :msg="msg"></slot><hr/></div>
</template>
?父組件
<script setup lang="ts">
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";
import { ref } from "vue";const msgFormLeft = ref('空1111111');</script><template><LeftLeg><!-- 插槽A用于傳遞父組件的固定內容 --><template #A><div><h2>這是傳過來的內容</h2></div></template><!-- 插槽B用于傳遞父組件的數據內容 --><template #B><div><a>{{ msgFormLeft }}</a></div></template><!-- 插槽C用于接收來自子組件的消息 --><template #C="{ msg }"><div><p>來自子組件的消息: {{ msg || '暫無消息' }}</p></div></template></LeftLeg><RightLeg />
</template>
-
動態組件
我們大多數情況下不可能把所有組件都呈現在父組件中,而是希望動態實現它。
這時我們只需要通過<component :is="組件名或者動態數據中的組件名">就能實現;
基本動態組件
<script setup lang="ts">
import { ref } from "vue";
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";// 定義當前激活的組件名稱,類型為組件對象的鍵,初始值為 "LeftLeg"
const tabComponent = ref<keyof typeof components>("LeftLeg");// 組件對象,包含 LeftLeg 和 RightLeg 兩個組件,并通過 as const 確保其鍵值不可變
const components = {LeftLeg,RightLeg
} as const;// 切換組件函數,接收當前組件名稱作為參數,切換為另一個組件
const switchComponent = (componentName: "LeftLeg" | "RightLeg") => {// 如果當前是 LeftLeg,則切換為 RightLeg,反之亦然tabComponent.value = componentName === "LeftLeg" ? "RightLeg" : "LeftLeg";
};</script><template><!-- 使用動態組件并確保正確綁定 --><component :is="components[tabComponent]" /><button @click="switchComponent(tabComponent)">切換</button></template>
動態組件駐留
以上組件每次加載都會完成一次初始化會讓對應頁面的數據還原,此時在外部增加keep-alive組件包含一下就可以了
父組件
父組件
<script setup lang="ts">
import { ref } from "vue";
import LeftLeg from "@/com/leftLeg.vue";
import RightLeg from "@/com/rightLeg.vue";// 定義當前激活的組件名稱,類型為組件對象的鍵,初始值為 "LeftLeg"
const tabComponent = ref<keyof typeof components>("LeftLeg");// 組件對象,包含 LeftLeg 和 RightLeg 兩個組件,并通過 as const 確保其鍵值不可變
const components = {LeftLeg,RightLeg
} as const;// 切換組件函數,接收當前組件名稱作為參數,切換為另一個組件
const switchComponent = (componentName: "LeftLeg" | "RightLeg") => {// 如果當前是 LeftLeg,則切換為 RightLeg,反之亦然tabComponent.value = componentName === "LeftLeg" ? "RightLeg" : "LeftLeg";
};</script><template><!-- 使用動態組件并確保正確綁定 --><keep-alive><component :is="components[tabComponent]" /></keep-alive><button @click="switchComponent(tabComponent)">切換</button></template>
子組件
<script setup lang="ts">
import { ref } from "vue";const tttt = ref("左腿");function updateMsg(newMsg: string) {tttt.value = newMsg;
}
</script><template><h1>{{ tttt }}</h1><button @click="updateMsg('1111111')">更新</button>
</template>
動態組件懶加載
動態組件時會一次性把組件所有組件進行渲染,而用到什么就渲染什么其實才科學。
<script setup lang="ts">
import {defineAsyncComponent, ref} from "vue";
// 異步組件
const LeftLeg = defineAsyncComponent(() => import("@/com/leftLeg.vue"));
const RightLeg = defineAsyncComponent(() => import("@/com/rightLeg.vue"));// 定義當前激活的組件名稱,類型為組件對象的鍵,初始值為 "LeftLeg"
const tabComponent = ref<keyof typeof components>("LeftLeg");// 組件對象,包含 LeftLeg 和 RightLeg 兩個組件,并通過 as const 確保其鍵值不可變
const components = {LeftLeg,RightLeg
} as const;// 切換組件函數,接收當前組件名稱作為參數,切換為另一個組件
const switchComponent = (componentName: "LeftLeg" | "RightLeg") => {// 如果當前是 LeftLeg,則切換為 RightLeg,反之亦然tabComponent.value = componentName === "LeftLeg" ? "RightLeg" : "LeftLeg";
};</script><template><!-- 使用動態組件并確保正確綁定 --><keep-alive><component :is="components[tabComponent]" /></keep-alive><button @click="switchComponent(tabComponent)">切換</button></template>
指令
所謂指令是指操作DOM元素的命令,VUE有內置的v-if、v-show就是指令;
自定義指令
-
以變量形式以對象方式出現,具有固定的生命周期
-
變量名命名以駝峰方式進行命令。
-
使用則駝峰以'-'拆分且全部小寫。
示例
<script setup lang="ts">
import LeftHand from "@/com/leftHand.vue";
import Chest from "@/com/chest.vue";
import RightHand from "@/com/rightHand.vue";
const vTest={create:()=>{console.log("創建")},mounted:()=>{console.log("掛載")},unmounted:()=>{console.log("卸載")},beforeUpdate:()=>{console.log("更新前")},updated:()=>{console.log("更新")},update:() => {console.log("更新")},beforeUnmount:()=>{console.log("卸載前")}
}
</script><template><div><!-- 測試 --><h1 v-test>-----中部-------</h1>
<!-- <LeftHand />-->
<!-- <Chest />-->
<!-- <RightHand />--></div>
</template>
相應的周期函數都包含el對象,利用el對象可以實現具體的功能。
<script setup lang="ts">
import LeftHand from "@/com/leftHand.vue";
import Chest from "@/com/chest.vue";
import RightHand from "@/com/rightHand.vue";
const vTest = {created() {console.log("創建");},mounted(element, binding, vnode) {element.innerHTML=element.innerHTML+"眾納原創";},unmounted() {console.log("卸載");},beforeUpdate() {console.log("更新前");},updated() {console.log("更新");},beforeUnmount() {console.log("卸載前");}
};
</script><template><div><!-- 測試 --><h1 v-test>-----中部-------</h1>
<!-- <LeftHand />-->
<!-- <Chest />-->
<!-- <RightHand />--></div>
</template>
以上定義的指令都是局部指令,如果想整體項目都要用到需要到main.ts里進行定義
import './assets/main.css'import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.directive('focus', {mounted(element) {element.style.color='red';element.style.fontSize='90px';}
})
app.mount('#app')
使用時則以v-"定義的名稱"進行綁定使用
```typescript
import './assets/main.css'import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.directive('focus', {mounted(element) {element.style.color='red';element.style.fontSize='90px';}
})
app.mount('#app')
```> 使用時則以v-"定義的名稱"進行綁定使用```html
<script setup lang="ts">
import LeftHand from "@/com/leftHand.vue";
import Chest from "@/com/chest.vue";
import RightHand from "@/com/rightHand.vue";
const vTest = {created() {console.log("創建");},mounted(element, binding, vnode) {element.innerHTML=element.innerHTML+"眾納原創";},unmounted() {console.log("卸載");},beforeUpdate() {console.log("更新前");},updated() {console.log("更新");},beforeUnmount() {console.log("卸載前");}
};
</script><template><div><!-- 測試 --><h1 v-test v-focus>-----中部-------</h1>
<!-- <LeftHand />-->
<!-- <Chest />-->
<!-- <RightHand />--></div>
</template>
```
本篇完成 ,下篇會涉及路由、狀態管理、Axios通信、Element UI等擴展知識,請關注博主,第一時間獲取高品質的技術文章。