vue 3 從零開始到掌握

vue3從零開始一篇文章帶你學習

升級vue CLI

  1. 使用命令
## 查看@vue/cli版本,確保@vue/cli版本在4.5.0以上
vue --version
## 安裝或者升級你的@vue/cli
npm install -g @vue/cli
## 創建
vue create vue_test
## 啟動
cd vue_test
npm run serve

nvm管理node版本,區分老舊項目

node版本升級,從卸載到使用nvm管理node版本并配置vue環境(學習趟雷版)

創建vue項目

vite創建vue項目傳送門

組合式函數

利用vue組合式API來封裝和復用有狀態邏輯的函數
1.結合官網 使用鼠標跟蹤器來實現組合式API,創建mouse.js 文件,將鼠標跟蹤功能給提前出來

// mouse.js
import {ref,onMounted,onUnmounted
} from 'vue'// 封裝組合式函數
export function useMouse() {// 管理狀態const x = ref(0)const y = ref(0)// 組合函數更改狀態function updated(event) {x.value = event.pageXy.value = event.pageY}// 在生命周期商注冊和卸載鼠標事件onMounted(() => {window.addEventListener('mousemove', updated)})onUnmounted(() => {window.removeEventListener('mousemove', updated)})//  將管理的狀態值暴漏出去return {x,y}
}
  1. 在頁面中使用
<template><div class="container">坐標: {{ mouse.x }} , {{ mouse.y }}</div>
</template>
<!-- setup 語法糖 -->
<script setup>
import {useMouse
} from '@/utils/mouse'
import { reactive } from 'vue';
const mouse = reactive(useMouse())
// 或者使用此解構出來
// const {
//  x,
//  y
// } = useMouse()
</script>
  1. 將添加和清除DOM事件的邏輯也給抽離出來,并在mouse里面使用
  • event.js 文件

import {onMounted,onUnmounted
} from 'vue'// 封裝組合式函數
export function useEventListener(target, event, callback) {// 在生命周期商注冊和卸載鼠標事件// 也可以用字符串形式的 CSS 選擇器來尋找目標 DOM 元素onMounted(() => {target.addEventListener(event, callback)})onUnmounted(() => {target.removeEventListener(event, callback)})
}
  • mouse.js 引入使用

import {ref,
} from 'vue'
import { useEventListener } from './event'
// 封裝組合式函數
export function useMouse() {// 管理狀態const x = ref(0)const y = ref(0)// 組合函數更改狀態function updated(event) {x.value = event.pageXy.value = event.pageY}useEventListener(window, 'mousemove', updated)//  將管理的狀態值暴漏出去return {x,y}
}

vite項目

:vue3兼容vue2語法,vue2的語法可以在vue3項目內使用,vue3不能讀取vue語法內的數據

API

setup組合式 API 的入口
  • setup在生命周期執行時間高于beforeCreate, created,在vue2語法內可以直接this調用setup里面的聲明變量

  • setup里面的this不是vue實例,而是undefined ,在vue3中弱化了this的使用

  • 直接聲明變量:let const var 的值非響應式數據,修改的話,頁面也不會變化的

  • 返回值,可以是值,對象,函數,它會在組件被解析前被調用

  • setup 函數的參數
    setup 函數在組件創建created()之前執行,setup函數是一個函數,它也可以有返回值,類似于Vue 2中的data()和methods()的組合?,接收兩個參數:props 和 context

    • props: 是響應式的,當傳入顯端prop 時他會進行更新,包含父組件傳遞給子組件的所有數據
    • context: 是一個普通的js對象,上下文對象,暴漏setup中可用的值
選項式 API 和組合式 API

官方文檔傳送門

組合式 API

組合式 API 通常會與 <script setup> 搭配使用 vue3.0進行響應式數據操作會使用 ref、reactive、toRef、toRefs四個核心 API 函數

  • ref 通常將基本數據類型(string,Number,Boolean…)轉為響應式,也可以將引用類型轉為響應式,但引用類型內的數據修改就不會及時通知頁面發生變化

  • 配置自動添加value在vscode設置
    在這里插入圖片描述

    <template><div>{{ mease }}<div @click="bntFrom">點擊</div></div>
    </template><script lang="ts" setup>
    import {ref
    } from 'vue'let mease = ref('張三')引用類型let obj = ref({name: "真實的"})function bntFrom(){mease.value = '李四'obj.value.name = '虛假的'console.log('點擊了');}
    </script>
    

在這里插入圖片描述

  • reactive 只能定義引用類型(obj,arry),相比較ref可以改變深層次的屬性響應式 ,ref也可以改變引用對象,但深層的數據還是建議使用reactive

    <template><div><h1>ref定義屬性</h1>{{ mease }}<div @click="bntFrom">點擊</div><h1>reactive定義</h1><p v-for="item in arry" :key="item.name">{{ item.age }}</p></div>
    </template><script lang="ts" setup>
    import {ref,reactive
    } from 'vue'let mease = ref('張三')let obj = ref({name: "真實的"})let arry = reactive([{name: '王二',age: 10}])function bntFrom(){mease.value = '李四'obj.value.name = '虛假的'arry[0].age = 30console.log('點擊了');}
    </script>
    

    在這里插入圖片描述

    • 將reactive定義的響應式重新定義一個對象,就會變成一個非響應式對象,可以使用Object.assign(obj,{name: ‘ssf’})
  • toRefs 將 reactive 定義的對象屬性給解構賦值轉換為響應式引用,保持對象的響應性,一般reactive 創建的對象,直接解構賦值后會失去響應式,所以就需要torefs將其給解構賦值成響應式

  • 如下面代碼,將對象解構賦值直接使用,也可以通過對象使用,只要值改變都會發生改變

<script setup lang="ts">
import { reactive,toRefs
} from 'vue'const parms = reactive({name: '掌聲',count: 0
})
const {count
} = toRefs(parms)
</script><template><h1>{{ msg }}</h1><div class="card"><p>parms.count: {{ parms.count }}</p><button type="button" @click="count++">count is {{ count }}</button></div>
</template>

在這里插入圖片描述

計算屬性computed

  1. 基于緩存: 計算屬性是基于它所依賴的響應式進行緩存的,只有當響應式依賴發生變化時,計算屬性才會重新計算
  2. 聲明式:計算屬性根據返回值來定義,代碼更清晰,更利于理解
  3. 自動更新:當依賴數據發生變化時,計算屬性返回的值也會更新
<script setup lang="ts">
import { reactive, computed } from "vue";// 響應式數據
const state = reactive({name: "手機",price: 2000,
});
// 計算屬性
const productName = computed(() => {return `優惠 ${state.name}`;
});const formattedPrice = computed(() => {return `${state.price.toFixed(2)}`;
});// 方法
const increasePrice = () => {state.price += 100;
};
</script><template><div><p>商品名稱:{{ productName }}</p><p>商品價格:{{ formattedPrice }}</p><button @click="increasePrice">增加價格</button></div>
</template>

在這里插入圖片描述
2. 計算屬性默認是只讀的,如果你想要更改計算屬性的值時,你需要使用getter 和 setter 來創建

  • getter 進行計算,將結果緩存起來,當參與計算的響應數據發生變化時,會觸發更新機制,再次調用getter來重新計算屬性的值
  • setter 函數會接收一個函數值,即要修改的值
<script setup lang="ts">
import { reactive, computed } from "vue";// 響應式數據
const state = reactive({name: "手機",price: 2000,
});
// 計算屬性
const productName = computed(() => {return `優惠 ${state.name}`;
});const formattedPrice = computed(() => {return `${state.price.toFixed(2)}`;
});const formattedNamePrice = computed({// 當依賴發生變化時再次調用,返回新的值get(){return state.name + '-' + state.price.toFixed();},// 接收用戶傳值并進行修改set(newValue){const [name,price] = newValue.split('-')state.name = namestate.price = Number(price)}});// 方法
const increasePrice = () => {state.price += 100;formattedNamePrice.value = '蘋果手機-5600'
};
</script><template><div><p>商品名稱:{{ productName }}</p><p>商品價格:{{ formattedPrice }}</p><button @click="increasePrice">增加價格</button><p>商品名稱、價格:{{ formattedNamePrice }}</p></div>
</template>

在這里插入圖片描述

監聽屬性

  1. 非緩存:監聽屬性不會緩存其值,每次依賴發生變化時都會執行回調函數,但他有新值和舊值
  2. 靈活:可以監聽一個或者多個數據源,當執行復雜邏輯時,可以進行異步操作
監聽ref聲明值
  1. watch 監聽值發生變化,監聽ref聲明的值
<script setup lang="ts">
import { ref, watch } from "vue";let couent = ref(0);// 監聽
watch(couent, (newValue,oldValue) => {console.log(newValue,oldValue,'值發生變化了');});// 方法
const changeCouent = () => {couent.value +=1 
};
</script><template><div><p>商品價格:{{ couent }}</p><button @click="changeCouent">增加價格</button></div>
</template><style scoped></style>

在這里插入圖片描述
2. 監聽聲明的對象類型,當改變單個值時未曾發生變化,需要使用 deep:tree, 深度監聽屬性,立即監聽的話需要使用immediate: true,

  • 單個監聽
watch(() => person.value.price,(newValue, oldValue) => {console.log(newValue, oldValue, "價格發生變化了");}
);
  • 多個監聽
watch(() => [person.value.name,person.value.price],(newValue, oldValue) => {console.log(newValue, oldValue, "價格發生變化了");}
);
  • 深度監聽,立即執行和監聽整個對象
<script setup lang="ts">
import { ref, watch } from "vue";let person = ref({name: "lisd",price: 100,});watch(person,(newValue, oldValue) => {console.log(newValue, oldValue, "值發生變化了");},{deep: true,immediate: true,}
);
// 方法
const changeName = () => {person.value.name = '理想';
};// 方法
const changePrice = () => {person.value.price += 1;
};// 方法
const changeObj = () => {person.value = {name: "寶馬",price: 345,};
};
</script><template><div><p>品牌:{{ person.name }}</p><p>價格:{{ person.price }}</p><button @click="changeName">增加價格</button><button @click="changePrice">增加價格</button><button @click="changeObj">修改整個品牌</button></div>
</template><style scoped></style>

在這里插入圖片描述

監聽 reactive
  1. 當改變單個值時也能監聽到變化,reactive聲明的對象類型會隱士的開啟 deep:tree 深度監聽屬性,還無法關閉,立即監聽的話需要使用immediate: true,
  • 單個監聽
watch(() => person.price,(newValue, oldValue) => {console.log(newValue, oldValue, "價格發生變化了");}
);
  • 多個監聽
watch(() => [person.name,person.price],(newValue, oldValue) => {console.log(newValue, oldValue, "價格發生變化了");}
);
  • 監聽整個對象可以不用函數寫法,可以直接監聽整個對象
<script setup lang="ts">
import { reactive, watch } from "vue";let person = reactive({name: "lisd",price: 100,
});watch(person,(newValue, oldValue) => {console.log(newValue, oldValue, "值發生變化了");},{immediate: true,}
);
// 方法
const changeName = () => {person.name = "理想";
};// 方法
const changePrice = () => {person.price += 1;
};// 方法
const changeObj = () => {Object.assign(person, {name: "寶馬",price: 345,});
};
</script><template><div><p>品牌:{{ person.name }}</p><p>價格:{{ person.price }}</p><button @click="changeName">增加價格</button><button @click="changePrice">增加價格</button><button @click="changeObj">修改整個品牌</button></div>
</template><style scoped></style>

在這里插入圖片描述
**注意:**監聽時,單個字段時,使用函數監聽值的變化,監聽對象或數組時,可以直接監聽,不用寫函數來監聽,但最好還是寫函數來監聽,不管是單個值還是對象

watchEffect監聽全局
*  無論是哪個字段發生變化,都會觸發,而watch需要寫具體監聽某個值

計算屬性和監聽屬性兩者使用場景

  1. 計算屬性:當你基于組件的響應數據,生成一個新的可緩存值時,可以是使用計算屬性
  2. 監聽屬性:當你數據發生變化,并且想要根據這個變化,進行一些復雜邏輯和異步操作時,可以使用監聽屬性

Class 與 Style 綁定 和vue的寫法沒什么區別

<template>//對象式綁定<div :class="{ active: isActive, 'text-danger'}">內容</div>//數組式綁定<div :class="['conter',{ active: isActive, 'text-danger'}]">內容</div>//使用對象語法動態綁定行內樣式<div :class="{ color: red, fontSize: fontSize}">內容</div>//使用數組語法動態綁定行內樣式<div :class="[basStyle]">內容</div>
</template><script>
export default {data() {return {isActive: true,fontSize: '12px',basStyle: {width: '20px'height: '20px'background: 'red'}}}
}
</script>

TS中:接口,泛型,自定義類型

vue 官方文檔

  1. 接口:interface,用于定義對象是否符合特定的結構,可以用來定義,props,methods 或者 data的類型
  • 對象的結構,屬性名稱,類型

  • 定義的對象如果不符合結構,亦或者缺少屬性,或者類型不匹配,ts就會報錯,當然你也可以添加使其變成一個可選屬性,比如以下錯誤
    在這里插入圖片描述

  • 類型聲明和組合式聲明

    	//類型let test: string | number = 1;組合式APIlet refTest = ref<string | number>("");refTest.value = "123";let reactiveTest = reactive<object>({id: 1,name: "張三",});console.log(reactiveTest);
    
  • 符合定義對象結構寫法

    // 定義一個接口,用于限制posen對象的具體屬性
    <template><div>姓名:{{ posen.name }}-{{ posen.num }}</div>
    </template><script setup lang="ts">
    import { reactive} from "vue";let posen = reactive<PersonInter>({id: 1,name: "張三",
    });
    export interface PersonInter {id: string | number;name: string;num?: number;
    }
    </script><style></style>
    
  1. 自定義類型 type
    自定義類型允許你為現有的類型創建一個名稱,可以組合多個類型,亦或者在類型別名內添加額外的屬性
  • 基本類型的別名

    type PersonType = {id: string | number;name: string;num?: number;
    };
    let person: PersonType = {id: 1,name: "張三",
    };
    
  • 聯合類型

    type Age = string | number;
    type ID = string | number;
    type PersonType = {id: ID;name: string;num?: Age;
    };let refTest = ref<Age>("");
    refTest.value = "123";type combinationType = PersonType & { age: Age };
    let combination: combinationType = {id: 1,name: "張三",age: 23,
    };
    console.log(combination); // {id: 1, name: '張三', age: 23}
    
  1. 泛型
  • 泛型,允許我們編寫可重用的代碼,適用于多種類型,在定義函數,接口,類的時候
  • 通過泛型,可以處理任意類型的輸入,并確保輸入和輸出類型一致
// 定義泛型
function generic<T>(arg: T): T {return arg;
}// 使用泛型
let age = generic<number>(23);   // 明確指定類型
let strText = generic("hello");    // 類型推斷

ref獲取在dom中的使用

vue2的ref和vue3的寫法不一樣,可以直接聲明取值,而不需要使用this.$refs.

  1. 組件直接使用,父組件獲取子組件的內容
  • 子組件

    <template><div>我是子組件</div>
    </template><script setup lang="ts">
    import { ref, defineExpose } from "vue";
    let re = ref('我是上三');
    let con = ref(3);
    defineExpose({re,con
    });
    </script><style></style>
    
  • 父組件

    
    <script setup lang="ts">
    import ChildComponent from "./aItem.vue";
    import { ref } from "vue";// 明確 refDom 的類型
    interface ChildComponentInstance {re: string;
    }const refDom = ref<ChildComponentInstance | null>(null);
    /*** changeRef函數用于檢查和操作一個名為refDom的引用對象* 此函數旨在演示如何在Vue組件中處理和訪問refs引用的元素或組件*/const changeRef = () => {// 檢查refDom引用是否已初始化 if (refDom.value) {// 如果refDom已初始化,打印其值和特定屬性console.log(refDom.value, refDom.value.re); // Proxy(Object) {re: RefImpl, con: RefImpl, __v_skip: true}[[Handler]]: Object[[Target]]: Proxy(Object)[[IsRevoked]]: false '我是上三'} else {// 如果refDom未初始化,打印提示信息console.log('refDom is not initialized');}
    };
    </script><template><ChildComponent ref="refDom" /><el-button @click="changeRef">點擊觸發</el-button>
    </template><style scoped></style>
  1. 在 v-for中使用ref時,這個時候返回的 ref 就是一個包含綁定所有元素的數組或者對象了
<template><div :ref="setItemRef" v-for="item in posenList" :key="item.id">{{ item.name }}</div>
</template>
<script setup lang="ts">
import { ref,ComponentPublicInstance } from "vue";let itemRefs = ref<HTMLDivElement[]>([]);
// 設置每個元素的 ref
const setItemRef = (el: Element | ComponentPublicInstance | null) => {if (el instanceof HTMLDivElement) {itemsRef.value.push(el); // 只添加有效的 HTMLDivElement}
};
console.log("itemRefs 綁定", itemRefs.value);
</script>

組件

官方文檔
動態組件:當多個組件在一個區域來回切換時,可以使用

<!-- currentTab 改變時組件也改變 -->
<component :is="activeComponentName" />
<component :is="tabs[currentTab]"></component>
組件通信
  1. 父組件給子組件傳值
  • Props 是一種特別的 atterbutes 你可以用defineProps函數來定義組件期望接收的props,每個props都可以指定類型、默認值、是否必需等屬性,其中包含了組件傳遞的所有 props

  • defineProps 是一個僅 <script setup> 中可用的編譯宏命令,所以不需要顯示的導入

  • props官方文檔

    • 父組件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { reactive } from "vue";let chPerson = reactive({ id: 1, name: "張三" });
      </script><template><ChildComponent :propsVal="chPerson" car="寶馬" ref="refDom" />
      </template><style scoped></style>
    • 子組件

      <template><div><p>子組件數據: {{ person.name }}</p><p>父組件傳遞數據: 姓名:{{ props.propsVal.name }}-汽車:{{ car }}</p></div>
      </template><script setup lang="ts">
      import {reactive,defineProps } from "vue";let person = reactive({name: "q2",
      });const props = defineProps({propsVal: {type: Object,default: () => {return {name: "q2",};},},car: {type: String,default: "奔馳",},
      });// 解構 props
      // const { propsVal, car } = defineProps({
      //   propsVal: {
      //     type: Object,
      //     default: () => ({
      //       name: "q2",
      //     }),
      //   },
      //   car: {
      //     type: String,
      //     default: "奔馳",
      //   },
      // });
      console.log(props);// 父組件傳遞數據</script><style></style>

    注意:如果沒有在 <script setup> 下,那么 props 就必須以 props選項的方式聲明,props 對象會作為 setup()函數的第一個參數被傳入

    <template><div><p>父組件傳遞數據: 姓名:{{ propsVal.name }}-汽車:{{ car }}</p></div>
    </template><script>
    export default {name: "childItem",props: {propsVal: {type: Object,default: () => {return {name: "q2",};},},car: {type: String,default: "奔馳",},},setup(props, context) {console.log(props, props.propsVal.name);// console.log(context.emit);// console.log(context.slots);}
    }
    </script><style></style>
  1. 子組件給父組件傳值 defineEmits
  • defineEmits方法返回函數并觸發,可以使子組件的值傳遞到父組件中

  • defineEmits 僅可用于<script setup> 中,可以不需要導入直接使用,它返回一個等同于 $emitemit 函數,可以在組件內拋出事件

    • 子組件

      <template><div><el-button @click="changeName">給父組件傳遞數據</el-button></div>
      </template><script setup lang="ts">
      import { reactive,defineEmits } from "vue";let person = reactive({name: "q2",
      });let emit = defineEmits(["childEvent"]);
      const changeName = () => {emit("childEvent", person);
      };
      </script><style></style>
    • 父組件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";interface ChildEventData {id?: number;name?: string;[key: string]: any; // 允許擴展其他字段
      }const handleChildEvent = (val: ChildEventData | {})=> {console.log(val,'子組件傳遞的值');
      }
      </script><template><ChildComponent @child-event="handleChildEvent" />
      </template><style scoped></style>
  • 如果你沒有在使用 <script setup>,你可以從 setup()函數的第二個參數的emit,拋出事件的

    export default {emits: ['enlarge-text'],setup(props, ctx) {ctx.emit('enlarge-text')}
    }
    
  1. $ref + defineExpose(obj)
  • defineExpos可以用來顯式暴露組件內部的屬性或方法,使得父組件可以通過 ref 訪問子組件的內容

  • 由于子組件的內容不會自動暴露給父組件,所以需要defineExpose 選擇性地暴露內部內容,從而避免不必要的屬性泄漏,同時提供更好的封裝性

  • defineExpose 是專為 <script setup> 設計的,不能用于普通的 <script>setup() 函數中

  • 不建議直接暴露整個組件內部狀態,應該只暴露需要的內容,從而保持組件封裝性

    • 父組件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref } from "vue";const refDom = ref();const changeRef = () => {// 檢查refDom引用是否已初始化if (refDom.value) {// 如果refDom已初始化,打印其值和特定屬性console.log(refDom.value.messe, "父組件hi", refDom.value);const { btnChild } = refDom.value;btnChild('父組件傳遞哦')} else {// 如果refDom未初始化,打印提示信息console.log("refDom is not initialized");}
      };
      </script><template><ChildComponent ref="refDom" /><el-button @click="changeRef">調用子組件方法</el-button>
      </template><style scoped></style>
    • 子組件

      <template><div></div>
      </template><script setup lang="ts">
      import { ref, reactive, defineExpose } from "vue";let person = reactive({name: "q2",
      });
      let messe = ref("子組件消息");
      const btnChild = (value: string) => {console.log("子組件按鈕", value);
      };defineExpose({person,messe,btnChild,text: "我是子組件的text",
      });
      </script><style></style>
  1. 兄弟組件之間通信 mitt
    安裝 mitt

npm install --save mitt

  • 你可以封裝成一個xx.ts使用

    import mitt from "mitt";
    export default mitt();
  • 組件內使用

    // 兄弟組件1
    <template><div><el-button @click="btnChild">子組件按鈕</el-button></div>
    </template><script setup lang="ts">
    import { ref } from "vue";
    import emittler from "@/utils/emitter";let messe = ref("你好啊");
    const btnChild = (value: string) => {console.log("子組件按鈕", value);emittler.emit("myEvent", messe.value);
    };
    </script><style></style>
    // 兄弟組件2
    <template><div></div>
    </template><script setup lang="ts">
    import emittler from "@/utils/emitter";
    emittler.on('myEvent', (message) => {console.log(message) // 輸出: "你好啊"
    })</script><style></style>
  1. useAttrs + a t t r s 如果需要在子組件接受很多 p r o p s , 但你又沒在 p r o p s 中定義,那么其他傳遞的值就會放在 ‘ attrs 如果需要在子組件接受很多props, 但你又沒在 props中定義,那么其他傳遞的值就會放在 ` attrs如果需要在子組件接受很多props,但你又沒在props中定義,那么其他傳遞的值就會放在atters`中
    在這里插入圖片描述
  • 父組件傳值

    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    import { reactive } from "vue";let chPerson = reactive({ id: 1, name: "張三" });</script><template><ChildComponent :propsVal="chPerson" :test="234" car="寶馬" ref="refDom" />
    </template><style scoped></style>
  • 子組件接收,你可以直接 $atters去取值,也可以聲明變量去接值

    <template><div><p>父組件傳遞數據: 姓名:{{ props.propsVal.name }}-汽車:{{ $attrs.car }}</p></div>
    </template><script setup lang="ts">
    import { defineProps,useAttrs } from "vue";const attrs = useAttrs();
    console.log(attrs.car); // 寶馬const props = defineProps({propsVal: {type: Object,default: () => {return {name: "q2",};},},
    });
    </script><style></style>
  1. 雙向綁定 v-model + defineModel 官方文檔
  • 父子組件數據雙向綁定 ,v-model 在組件上實現雙向綁定

  • 多個v-model 可以接收一個參數,我們可以通過將這個參數當成字符串給defineModel來接收對應的值

  • 如果聲明之后,那么如果你不通過字符串來獲取,那你將獲取不到值

    • 父組件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref,reactive } from "vue";
      let titleString = ref("收到反饋及時");
      let chPerson = reactive({ id: 1, name: "張三" });</script><template><p>{{ chPerson }}</p><p>{{ titleString }}</p><ChildComponent v-model="chPerson" v-model:title="titleString" car="寶馬" />
      </template><style scoped></style>
    • 子組件

      <template><div><el-button type="primary" @click="brnHa">點擊修改</el-button></div>
      </template><script setup lang="ts">
      import { defineModel } from "vue";
      interface Person {name: string;id: number;
      }const chPerson = defineModel<Person>({default: () => {return {name: "寶馬",id: 1,};},
      });
      const stringTitle = defineModel('title',{type: String,default: '中NSA公司',
      });
      console.log(chPerson.value,stringTitle.value); // 寶馬
      const brnHa = () => {stringTitle.value = '中發的時間分厘卡'chPerson.value = {name: "奔馳",id: 2,};console.log(chPerson);
      };
      </script><style></style>
  • v-model 可以綁定一些內置修飾符,如.trim,.number,.lazy 等,當然我們也可以自己定義一個修飾符, 通過 解構defineModel的返回值,我們可以在子組件訪問時給定義修飾符,基于修飾符可以選擇性的調節值的讀取和寫入,我們給 defineModel傳入get,set兩個選擇,根據判斷修飾符來實現我們的代碼邏輯,這里使用了set

    • 父組件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref } from "vue";let character = ref("dhasdh");</script><template><p>{{ character }}</p><ChildComponent  v-model:character.capitalLetters="character" />
      </template><style scoped></style>
    • 子組件

      <template><div><input type="text" v-model="character"></div>
      </template><script setup lang="ts">
      import { defineModel } from "vue";
      const [character,modifiers] = defineModel('character',{set: (val:string) => {// 含有capitalLetters修飾符if(modifiers.capitalLetters){return val.charAt(0).toUpperCase() + val.slice(1)}console.log(val,character,modifiers);return val}
      });</script><style></style>
  1. provide / Inject(提供/注入)
  • 在父組件中定義值和事件 provide(提供數據)

    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    import { reactive,provide } from "vue";let chPerson = reactive({ id: 1, name: "張三" });
    provide('sae',chPerson)const refDom =  ()=>{console.log('234')
    }
    provide('abnt', refDom)</script><template><p>{{ chPerson }}</p><ChildComponent :propsVal="chPerson" :test="234" car="寶馬" />
    </template><style scoped></style>
  • 子組件或孫子組件 中使用 inject (獲取數據)

    <template><div><el-button type="primary" @click="brnHa">點擊修改</el-button></div>
    </template><script setup lang="ts">
    import { inject } from "vue";const car = inject("sae", { name: "未知品牌" }); // 提供默認值避免 undefined
    const abnt = inject("abnt", () => {}); // 提供空函數作為默認值console.log(car); // 寶馬
    const brnHa = () =>{car.name = "奔馳";abnt()console.log(car);
    }</script><style></style>
  • 注意:如果在其中一個組件修改,那么所有組件都會同步修改后的數據的

  1. pinia vue官方推薦的狀態集中管理工具可以看下面的 vuex => pinia

vuex => pinia的使用

官方文檔

  1. Pinia 提供了更簡潔直接的 API,并提供了組合式風格的 API,最重要的是,在使用 TypeScript 時它提供了更完善的類型推導

    • state (狀態)

      • 應用的數據來源,是一個響應式對象
    • getters (計算屬性)

      • 類似于技術屬性,getters可以根據state的值來派生出新的值
      • getters是有緩存的,只有所依賴的數據發生改變的時候才會重新計算
    • actions (動作)

      • actions中定義事件函數,來改變state中的值
  2. Pinia 的使用 選項式API

    • 先下載 pinia

      npm install pinia
      # 或者
      yarn add pinia
      
    • 在 mina.ts里引用

      import { createApp } from 'vue'import App from './App.vue'import {  createPinia } from 'pinia'
      let store = createPinia()
      const app = createApp(App);
      app
      .use(store)
      .mount('#app')
    • src/store 目錄下創建一個 例如 useStore.ts 文件

      import { defineStore } from "pinia";export const useertStore = defineStore("main", {state: () => {return {// all your data herecount: 0,};},getters: {doubleCount: (state) => state.count * 2,},actions: {// all your methods hereincrement() {this.count++;},},
      });
      
    • 在組件里面使用

      
      // 父組件
      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";import { useertStore } from "@/store/useStore"
      const store = useertStore();
      </script><template><p>{{ store.count }}</p><ChildComponent />
      </template><style scoped></style>// 子組件
      <template><div><el-button type="primary" @click="btnAdd">點擊添加{{store.count}}</el-button><p>{{ store.doubleCount }}</p></div>
      </template><script setup lang="ts">
      import { useertStore } from "@/store/useStore"
      const store = useertStore();const btnAdd = () => {store.increment();console.log(store.count)
      }
      </script><style></style>
  3. pinia 的使用 組合式API 在組件內用法和選項式API一樣

    import { defineStore } from "pinia";
    import { ref, computed } from "vue";
    export const useertStore = defineStore("main", () => {let count = ref(0);let doubleCount = computed(() => count.value * 2);const increment = () => {count.value++;};return { doubleCount, count, increment };
    });

插槽使用slot

  • 默認插槽:用于在子組件模板中定義一個位置,父組件可以在該位置插入自己的內容。

  • 具名插槽:允許你在子組件模板中定義多個插槽位置,每個位置可以有自己的名字。在父組件中,你可以指定內容應該插入到哪個具名插槽。你可以 v-slot:header也可以簡寫#header

  • 條件插槽:我們可以通過 $slotsv-if來實現

  • 作用域插槽:允許子組件數據傳遞給父組件,以便父組件可以自定義如何渲染這些數據,你可以這樣寫 v-slot:name="slotProps"也可以這樣簡寫#name="slotProps"

  • 動態插槽:允許插槽的名稱是動態的,以滿足更多的業務需求

    • 子組件聲明插槽
    <template><div><slot></slot><div class="container"><header><slot name="header"></slot></header><main><slot name="main" maintext="主要內容頭部"></slot><div class="list"><div class="item" v-for="item in list"><slot name="item" :item="item">默認信息</slot></div></div></main><footer><template v-if="$slots.footer"><slot name="footer">我是底部</slot></template></footer></div></div>
    </template><script setup lang="ts">
    import { reactive } from 'vue';let list = reactive([1, 2, 3]);
    </script><style></style>
    • 父組件使用
    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    </script><template><ChildComponent>發生的<template #header><div>我是具名插槽</div></template><!-- 作用域插槽 --><template #main="props"><div>{{ props.maintext }}</div></template><!-- 動態插槽 --><template #item="items"><div>{{items.item}}</div></template></ChildComponent>
    </template><style scoped></style>

生命周期

  1. Vue2 的生命周期鉤子代碼更新到 Vue3 官方文檔
  • setup 是vue 3 新增的鉤子函數,位于組件創建實例之前,適用于進行異步數據獲取,狀態管理,邏輯代碼封裝
    • beforeCreate -> 使用 setup(): 實例創建前
    • created -> 使用 setup():實例創建完畢,可以用于訪問和修改數據,但還未掛載到dom元素上
  • beforeMount -> onBeforeMount: 掛載前,可以用于修改dom解構
    mounted -> onMounted:掛載完畢,也就是組件渲染你完成,可以進行dom的操作和事件調用和監聽
    beforeUpdate -> onBeforeUpdate: 組件將要更新到dom樹之前,可以在vue更新dom之前訪問dom狀態
    updated -> onUpdated: 組件更新到dom樹之后,用于執行依賴dom的更新操作
    beforeDestroy -> onBeforeUnmount:銷毀前->卸載前, 用于清理資源
    destroyed -> onUnmounted:銷毀完畢->卸載完畢,用于清理資源
    errorCaptured -> onErrorCaptured:捕獲錯誤,在錯誤發生時調用
  1. 組件緩存 <KeepAlive>
  • <KeepAlive>是一個內置組件,它可以在多個人組件動態切換時緩存被移除的組件實例

    <!-- 非活躍的組件將會被緩存! -->
    <KeepAlive><component :is="activeComponent" />
    </KeepAlive>
    
    • 我們可以通過 include來制定是否需要緩存,當名稱匹配時組件才會被緩存

      <!-- 以英文逗號分隔的字符串 -->
      <KeepAlive include="a,b"><component :is="view" />
      </KeepAlive><!-- 正則表達式 (需使用 `v-bind`) -->
      <KeepAlive :include="/a|b/"><component :is="view" />
      </KeepAlive><!-- 數組 (需使用 `v-bind`) -->
      <KeepAlive :include="['a', 'b']"><component :is="view" />
      </KeepAlive>
      
    • exclude來排除不緩存的組件,當匹配時不緩存,用法和 include一樣

    • max 可以設置緩存組件的最大值,當緩存組件數量達到最大數值時,那么在新組件創建之前,已緩存組件中很久未曾訪問的組件就會被銷毀掉

  • 生命周期

    • onActivated 在組件掛載時也會調用(組件從緩存中被激活時觸發)
    • onDeactivated 在組件卸載時也會調 (組件切換到其他頁面時觸發)
  1. 父子組件生命周期的先后順序,vue2 和 vue3 變化不大,只是`beforeCreate ,created 被 setup() 取代
  • 加載渲染依次順序:

    父組件:beforeCreate => 父組件: created => 父組件:beforeMount(onBeforeMount)=> 子組件:beforeCreate => 子組件:created => 子組件:beforeMount(onBeforeMount) =>子組件:mounted(onMounted) => 父組件:`mounted(onMounted)

  • 更新過程中依次順序:

    父組件:beforeUpdate(onBeforeUpdate) => 子組件: beforeUpdate(onBeforeUpdate) => 子組件:updated(onUpdated)=> 父組件:updated(onUpdated)

  • 銷毀過程中依次順序:

    父組件:父組件:beforeDestroy(onBeforeUnmount) => 子組件: beforeDestroy(onBeforeUnmount) => 子組件:destroyed(onUnmounted)=> 父組件:destroyed(onUnmounted)

hooks

  1. vue hooks 是一個遵循特點規則的函數,命名以use起始,依托于vue 的組合式API構建,將組件邏輯拆分為獨立,可復用的小塊
  • useCount.ts 一個簡單的計算屬性

    import { ref } from "vue";
    export const useCount = () => {const count = ref(0);const increment = () => {count.value++;};return { count, increment };
    };
    <template><div><div> 計算結果:{{ count }}</div><el-button type="primary" @click="increment">添加</el-button></div>
    </template><script setup lang="ts">
    import { useCount } from "@/hooks/useCount";
    const { count, increment } = useCount();
    </script><style></style>
  • 多個嵌套使用

    import { ref } from "vue";
    export const useRide = () => {const rideNum = ref(2);const ride = (num: number) => {return rideNum.value * num;};return { rideNum, ride };
    };
    <template><div><div>計算結果:{{ count }}</div><el-button type="primary" @click="increment">添加</el-button><p>{{ result }}</p></div>
    </template><script setup lang="ts">
    import { useCount } from "@/hooks/useCount";
    import { useRide } from "@/hooks/useRide";
    import { ref, watch } from "vue";
    const { count, increment } = useCount();
    const {  ride } = useRide();
    let result = ref(0)
    watch(() => count.value,(newValue, oldValue) => {console.log(newValue, oldValue);result.value = ride(newValue);}
    );
    </script><style></style>

自定義指令

傳送門

其他API

  • shallowRef : 創建一個響應式數據,但只對頂層屬性進行響應式處理,只跟蹤引用值變化,不關心值內部屬性變化
  • shallowReactive:創建一個淺層響應式對象,但只對對象頂層屬性進行響應式處理,對象內部屬性變化不會做任何響應
  • readonly:創建一個對象,對象的所以屬性包括嵌套屬性都只能讀,不能修改
  • shallowReadonly::和readonly相似,創建一個對象,對象的頂層屬性只能讀,不能修改,但嵌套屬性是可以更改的

其他組件

  • teleport:是一種能夠將我們組件的HTML結構一定到指定位置的技術
<teleport to="body">html內容
</teleport>
  • Suspense

安裝路由

Vue Router 是 Vue.js 的官方路由。它與 Vue.js 核心深度集成,讓用 Vue.js 構建單頁應用變得輕而易舉

  1. 安裝路由

    npm install vue-router@4
    # 或者
    yarn add vue-router@4
    
  2. 在src/router/index.ts創建路由實例

    import {createRouter,createWebHistory,type RouteRecordRaw,
    } from "vue-router";
    export const Layout = () => import("@/layout/index.vue");
    // 靜態路由
    export const constantRoutes: RouteRecordRaw[] = [{path: "/",component: Layout,meta: { hidden: true },children: [{path: "/",component: () => import("@/views/index.vue"),},{path: "/aItem",component: () => import("@/views/aItem.vue"),},],},
    ];
    const router = createRouter({history: createWebHistory(), routes: constantRoutes,});export default router;
  3. 在main.ts內掛載使用

    import router from './router'const app = createApp(App);
    app
    .use(router)
    .mount('#app')
    
  4. 路由工作模式

  • history模式(HTML5 模式):createWebHistory

    • 優點:不含有#,顯得更優雅

    • 缺點:項目上線,需要服務端配合處理路徑問題,如果沒有適當的服務器配置,用戶在瀏覽器中直接訪問 https://example.com/user/id,就會得到一個 404 錯誤

      const router = createRouter({history: createWebHistory(),routes: constantRoutes,});
      
  • hash模式

    • 優點:不需要服務端進行特殊的處理

    • 缺點:url上會出現一個#,在seo中影響不好

      const router = createRouter({history: createWebHashHistory(), //history: createWebHashHistory(),routes: constantRoutes,
      });
      
  1. RouterLink 和 RouterView
  • RouterLink : 創建導航鏈接

  • RouterView : 渲染組件,也就是當前路由顯示匹配的組件

     <RouterLink to="/">Go to Home</RouterLink><RouterLink to="/aItem">Go About</RouterLink><RouterView />
    
  • 攜帶參數跳轉

    
    <RouterLink to="/aItem?id:3">跳轉</RouterLink>
    // 或
    <router-link :to="{ path: '/aItem', params: { id: '12' } }">跳轉
    </router-link>
    
  1. 嵌套路由
  • <router-view>嵌套一個 <router-view>,如果渲染到這個嵌套的 router-view 中,我們需要在路由中配置 children
  • children 配置只是另一個路由數組,就像 routes 本身一樣。因此,你可以根據自己的需要,不斷地嵌套視圖
    
    import {createRouter,createWebHashHistory,type RouteRecordRaw,
    } from "vue-router";
    export const Layout = () => import("@/layout/index.vue");
    // 靜態路由
    export const constantRoutes: RouteRecordRaw[] = [{path: "/",component: Layout,meta: { hidden: true },children: [{path: "/",component: () => import("@/views/index.vue"),},{path: "/aItem/:id",name: "aItem",component: () => import("@/views/aItem.vue"),},],},
    ];
    const router = createRouter({history: createWebHashHistory(),routes: constantRoutes,
    });export default router;
  1. 命名路由
  • 當創建路由時我們可以給路由一個name
const routes = [{path: '/user/:username',name: 'profile', component: User}
]
  1. 動態路由
  • 通過路徑傳遞參數

    <script setup lang="ts">
    </script><template>頁面二<router-link :to="{ name: 'aItem', params: { id: '12' } }">跳轉aItem</router-link>
    </template><style scoped></style>
    
  • 需要路由配置支持動態路徑參數

    import { createRouter, createWebHistory } from 'vue-router';const routes = [{path: '/aItem/:id',name: 'aItem',component: () => import('@/views/aItem.vue'),},
    ];const router = createRouter({history: createWebHistory(),routes,
    });export default router;
    
  1. 編程時導航
    我們可以通過useRouter來訪問路由
  • 導航到不同位置

    • 使用router.push方法,會給 history 棧添加一個新的記錄,當用戶點擊返回,即返回上一頁時就會回到之前的URL,此方法相當于點擊<router-link :to="...">
      <script setup lang="ts">
      import { useRouter } from 'vue-router'
      const router = useRouter()
      const goAitem = () => {router.push({name: 'aItem',params: {id: '12'}})
      }
      </script><template>頁面二<router-link :to="{ name: 'aItem', params: { id: '12' } }">跳轉aItem</router-link><el-button type="primary" @click="goAitem">跳轉aItem</el-button>
      </template><style scoped></style>
      
  • 替換當前位置

    • router.replace,它不會向history添加新的記錄,會直接替換當前條目,亦或者router.push 中添加一個 replace: true

      <script setup lang="ts">
      import { useRouter } from 'vue-router'
      const router = useRouter()
      const goAitem = () => {router.replace({name: 'aItem',params: {id: '12'}})// 亦或者 添加 replace: true,// router.push({//   name: 'aItem',//   replace: true,//   params: {//     id: '12'//   }// })
      }
      </script><template>頁面二<router-link :to="{ name: 'aItem', replace: true, params: { id: '12' } }">跳轉aItem</router-link><el-button type="primary" @click="goAitem">跳轉aItem</el-button>
      </template><style scoped></style>
  1. 路由組件傳參
  • 傳遞參數

    • params:是URL的一部分,通常用于傳遞靜態數據,若使用 to的對象寫法時,必須使用 name配置項,在路由里面配置,也就是動態路由
    • query :參數也會附加在URL后面,用于傳遞敏感數據
    <script setup lang="ts">
    import { useRouter } from 'vue-router'
    const router = useRouter()
    const goAitem = () => {// paramsrouter.push({name: 'aItem',params: {id: '12'}})// queryrouter.push({name: 'aItem',query: {id: '12'}})
    }
    </script><template>頁面二<router-link :to="{ name: 'aItem', replace: true, params: { id: '12' } }">跳轉aItem</router-link><router-link :to="{ name: 'aItem', replace: true, query: { id: '12' } }">跳轉aItem</router-link><el-button type="primary" @click="goAitem">跳轉aItem</el-button>
    </template><style scoped></style>
  • 接收參數

    • 通過 useRoute來獲取 query 參數,useRoute 返回的是響應式的路由對象,其中 query包含了所以查詢參數
      <script setup lang="ts">
      import { useRoute } from 'vue-router';
      const route = useRoute(); 
      console.log(route,' `query`');
      </script>
      
  • 路由 props 配置

    • 當 props 設置為 true 時,route.params 將被設置為組件的 props

      const routes = [{ path: '/aItem/:id',name: 'aItem',component: User, props: true }
      ]
      
    • 函數模式下無論是動態路由參數還是查詢參數,params, query 都可以方便地作為 props 傳遞到組件中

      • 配置
      const routes = [{path: '/aItem',component: SearchUser,props: route => ({ query: route.query.q })}
      ]
      
      • 組件接收
      <script setup>
      defineProps({id: {type: String,default: "奔馳",}
      })
      </script>
      

部署服務器

傳送門

vue創建項目使用element-plus

  • 下載引用element-plus
# 選擇一個你喜歡的包管理器# NPM
npm install element-plus --save# Yarn
yarn add element-plus# pnpm
pnpm install element-plus
  • 在main.js內引用
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'// 引入 Element-Plus 依賴
import ElementPlus from 'element-plus'
// 引入全局 cSS 樣式
import 'element-plus/dist/index.css'createApp(App)
.use(store)
.use(router)
.use(ElementPlus)
.mount('#app')
  • 頁面中使用,這里實現element-plus中英文切換 結合 vuex切換中英文
<template><div class='about'><el-select @change="handleClear" v-model="selectValue" placeholder="選擇語言" style="width: 240px"><el-option v-for="item in langOptions" :key="item.value" :label="item.label" :value="item.value"><span style="float: left">{{ item.label }}</span><span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">{{ item.value }}</span></el-option></el-select></div>
</template><script>
import { useStore } from "vuex";
import {reactive, ref 
} from 'vue'
export default {name: 'about',setup() {const selectValue = ref('')const langOptions = reactive([{value: 'en',label: 'English'},{value: 'zhCn',label: '中文'}])const store = useStore()const handleClear = (value) => {store.dispatch('provider/updateLanguage', value)}return {selectValue,langOptions,handleClear}}
};
</script>
<style lang='less' scoped></style>
  • 全局實現中英文
<template><el-config-provider :locale="locale"><el-table mb-1 :data="[]" /><router-view /></el-config-provider>
</template><script>
import {computed,
} from 'vue'
import { useStore } from "vuex";export default {name: 'App',setup() {const store = useStore()const locale = computed(() => store.state.provider.language);// 返回數據return {locale,}}
}
</script>
  • vuex 配置與使用
import { createStore } from 'vuex'
import provider  from './modules/provider'
export default createStore({state: {},mutations: {},actions: {},modules: {provider }
})
  • modules的創建provider文件
// 導入 Element Plus 中英文語言包
import zhCn from "element-plus/es/locale/lang/zh-cn";
import en from "element-plus/es/locale/lang/en";
const user = {namespaced: true,state: {language: zhCn,},mutations: {setLanguage(state, language) {if (language == "en") {state.language = en;} else {state.language = zhCn;}}},actions: {/*** 根據語言標識讀取對應的語言包*/updateLanguage({ commit }, language) {commit('setLanguage', language)}},getters: {language(state) {return state.language}}
}
export default user
  • 最終實現效果
    在這里插入圖片描述
  • 啟動項目報錯
  • 預轉換錯誤:未找到預處理器依賴項“sas-embedded”。你安裝了嗎?嘗試npm install-D sass-embedded
    在這里插入圖片描述
  • 按照提示安裝重新啟動就好

按需引入

  • 按需導入,下載兩款插件 unplugin-vue-componentsunplugin-auto-import這兩款插件

npm install -D unplugin-vue-components unplugin-auto-import

  • 在vite.config.ts 中配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({plugins: [vue(),AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],
})
  • 重啟項目接下來直接引用就行了

組件名稱的單個配置

  • vue3 會根據組件文件名稱自動推導出name屬性,item.vue name 名為item ,但當我們起個文件夾名稱,但文件夾內容的文件是index.vue時就無法推導出文件內名稱了
    在這里插入圖片描述

    • 這在我們調試和定位問題時就不太方便了,當然我們也可以單獨添加一個scrit 來去寫name名稱,但這種方法有點過于繁瑣

      ```
      <script lang="ts">export default {name: 'pramas'}
      </script>
      ```
      
    • 社區推出了 unplugin-vue-define-options 來簡化該操作

      npm i unplugin-vue-define-options -D

      // vite.config.ts
      import DefineOptions from 'unplugin-vue-define-options/vite'
      import Vue from '@vitejs/plugin-vue'export default defineConfig({plugins: [Vue(), DefineOptions()],
      })
      
    • 頁面中使用

      <script setup lang="ts">
      defineOptions({name: "pramas"  
      })
      <script>
      
    • 三方插件在 <script lang="ts" name="pramas"> 上添加name

    <script setup name="pramas">let a = '慮是否'
    </script>
    
    • 下載此插件可以動態修改vue文件名稱

    npm i vite-plugin-vue-setup-extend -D

  • 在 vite.config.ts內引用

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import vueDefineNmae from 'vite-plugin-vue-setup-extend'
    export default defineConfig({plugins: [vue(),vueDefineNmae()],
    })
    
  • 代碼運行就可以看見我們自定義的name名稱了

在這里插入圖片描述

遇到問題

1. 淘寶鏡像過期,更改淘寶鏡像

在這里插入圖片描述

  • 在這里插入代碼片

1.查看當前npm鏡像
npm config get
2.配置新的鏡像
npm config set registry https://registry.npmmirror.com
3.再次查看鏡像配置情況
npm config get

2.npm run dev` 無法啟動項目,顯示vite不是內部或外部命令,這是系統啟動vite項目,但找不到vite,意味著你未曾安裝vite,你可以執行以下命令全局安裝,再執行之前命令

npm install -g vite

  • node 和 npm 版本不兼容,官網需要18.3或更高版本的
    在這里插入圖片描述

3.vscode打開vite創建項目引入組件報錯解決

在這里插入圖片描述

  • 在src下創建此文件xxx.d.ts在你的 src 目錄中,填入以下內容,幫助 TypeScript 理解 .vue 文件

    	declare module "*.vue" {import { defineComponent } from "vue";const Component: ReturnType<typeof defineComponent>;export default Component;}
    
  • 如果未曾解決,將.vue 改為vue并重新打開項目

  • 如果還未解決可以看看官網的方法
    在這里插入圖片描述

4.vue3+vite3+ts使用@alias路徑別名爆紅報錯解決

在這里插入圖片描述

  1. vite.config.ts 中配置以下內容
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";
// https://vite.dev/config/
export default defineConfig({plugins: [vue()],resolve: {alias: {"@": path.resolve(__dirname, "./src"),},},
})
  1. 如果顯示找不到 path 按照以下命令安裝

npm install --save-dev @types/node

  1. tsconfig.app.json 中配置以下內容,然后重啟項目
{"compilerOptions": {"baseUrl": ".","paths": {"@/*": ["src/*"],}},"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75944.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75944.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75944.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mysql專題篇章

一、事務的四大特性&#xff1f; 1、原子性&#xff1a;是指事務包含的所有操作要么全部成功&#xff0c;要么全部失敗回滾。 2、一致性&#xff1a;是指一個事務執行之前和執行之后都必須處于一致性狀態。比如a與b賬戶共有100塊&#xff0c;兩人之間轉賬之后無論成功還是失敗…

CAD插件實現:自動遞增編號(前綴、后綴、位數等)——CADc#實現

cad中大量輸入一定格式的遞增編號時&#xff0c;可用插件實現&#xff0c;效果如下&#xff1a; ①本插件可指定數字位數、起始號碼、加前綴、后綴、文字顏色等&#xff08;字體樣式和文字所在圖層為cad當前圖層和當前字體樣式&#xff09;。 ②插件采用Jig方式&#xff0c;即…

k8s1.24升級1.28

0、簡介 這里只用3臺服務器來做一個簡單的集群&#xff0c;當前版本是1.24.17目標升級到1.28.17 地址主機名192.168.160.40kuber-master-1192.168.160.41kuber-master-2192.168.160.42kuber-node-1 因為1.24已經更換過了容器運行時&#xff0c;所以之后的升級相對就會簡單&am…

4.3-2 jenkins

一.登錄jenkins 二.修改密碼 三.配置節點 新建節點 編輯節點名稱 編輯節點配置 激活節點 將jar下載到指定的路徑 再到dos命令下的路徑 E:\az\wx 執行 配置節點成功 四. 安全設置中&#xff0c;勾選代理 五.新建項目 編輯項目名稱 編輯項目執行的 路徑&#xff1a;C:\Users\Ad…

js對象與數組的互轉

js對象與數組的互轉 文章目錄 js對象與數組的互轉一、數組轉對象1.使用forEach,for in,es6展開運算符,assign2. 使用 Object.fromEntries()3. 將數組轉為鍵值對對象4. 使用 reduce()4. 數組元素為對象時提取屬性 二、對象轉數組1. 提取鍵/值/鍵值對2. 轉換為特定結構的數組 三、…

HTTPS在信息傳輸時使用的混合加密機制,以及共享、公開密鑰加密的介紹。

HTTPS在信息傳輸時使用的混合加密機制&#xff0c;其中包括了共享密鑰加密和公開密鑰加密&#xff0c;我們先來介紹一下這兩種加密方式。 共享密鑰加密&#xff08;對稱密鑰&#xff09; 對稱加密是指加密和解密使用的是同一個密鑰。就像家里的門鎖&#xff0c;鑰匙只有一把&…

Oracle 23ai Vector Search 系列之4 VECTOR數據類型和基本操作

文章目錄 Oracle 23ai Vector Search 系列之4 VECTOR數據類型和基本操作VECTOR 數據類型基本語法Vector 維度限制和向量大小向量存儲格式&#xff08;DENSE vs SPARSE&#xff09;1. DENSE存儲2. SPARSE存儲3. 內部存儲與空間計算 Oracle VECTOR數據類型的聲明格式VECTOR基本操…

機器學習——ROC曲線、PR曲線

一、ROC曲線簡介 1.1 ROC曲線的構成 1.橫軸&#xff08;假正率&#xff0c;FPR&#xff09;&#xff1a; 表示負樣本被錯誤分類為正的比例&#xff08;越小越好&#xff09; 2.縱軸&#xff08;真正率&#xff0c;TPR&#xff0c;即召回率&#xff09;&#xff1a; 表示正樣…

IntelliJ IDEA下開發FPGA——FPGA開發體驗提升__上

前言 由于Quartus寫代碼比較費勁&#xff0c;雖然新版已經有了代碼補全&#xff0c;但體驗上還有所欠缺。于是使用VS Code開發&#xff0c;效果如下所示&#xff0c;代碼樣式和基本的代碼補全已經可以滿足開發&#xff0c;其余工作則交由Quartus完成 但VS Code的自帶的git功能&…

昂貴的DOM操作:一次DOM導致的性能問題排查記錄

公司來了一個前端實習生&#xff0c;踏實&#xff0c;勤快&#xff0c;很快得到老大的認可&#xff0c;分配給她一個需求&#xff0c;大概如下&#xff1a;構建一個公司產品的評論展示頁面&#xff0c;頁面可以滾動加載新的內容&#xff0c;同時如果已經加載的內容發生變化&…

前端服務配置詳解:從入門到實戰

前端服務配置詳解&#xff1a;從入門到實戰 一、環境配置文件&#xff08;.env&#xff09; 1.1 基礎結構 在項目根目錄創建 .env 文件&#xff1a; # 開發環境 VUE_APP_API_BASE_URL http://localhost:3000/api VUE_APP_VERSION 1.0.0# 生產環境&#xff08;.env.produc…

【學習筆記】計算機網絡(七)—— 網絡安全

第7章 網絡安全 文章目錄 第7章 網絡安全7.1 網絡安全問題概述7.1.1 計算機網絡面臨的安全性威脅7.1.2 安全的計算機網絡7.1.3 數據加密模型 7.2 兩類密碼體制7.2.1 對稱密鑰密碼體制7.2.2 公鑰密碼體制 7.3 鑒別7.3.1 報文鑒別7.3.2 實體鑒別 7.4 密鑰分配7.4.1 對稱密鑰的分配…

我用Cursor + DeepSeek + Claude-3.7-Sonnet + DevBox,10分鐘開發了一個系統

大家好&#xff0c;我是袁庭新。Cursor最近可謂是火的一塌糊涂&#xff0c;于是我深度體驗了一波。我用的環境是Cursor Claude-3.7-Sonnet DevBox&#xff0c;整個過程我一行代碼都沒有寫&#xff0c;10分鐘幫我開發了一個系統&#xff0c;且前后端聯調一把通過。驚出一身冷汗…

SpringBoot企業級開發之【用戶模塊-登錄】

開發之前我們先看一下接口文檔的要求&#xff1a; 開發思路&#xff1a; 開發實操&#xff1a; 因為我們之前開發注冊的時候&#xff0c;就有了一些相關的操作&#xff0c;所以在這里我們只需要定義登錄的controller即可&#xff1a; //用戶登錄PostMapping("/login"…

mysql 8.0.27-docker

安裝 可以略過本步 https://dev.mysql.com/downloads/https://dev.mysql.com/downloads/ 鏡像查詢與安裝 先查詢&#xff1a; docker search mysql 明顯會報錯 Error response from daemon: Get "https://index.docker.io/v1/search?qmysql&n25": dial tcp…

Pgvector的安裝

Pgvector的安裝 向量化數據的存儲&#xff0c;可以為 PostgreSQL 安裝 vector 擴展來存儲向量化數據 注意&#xff1a;在安裝vector擴展之前&#xff0c;請先安裝Postgres數據庫 vector 擴展的步驟 1、下載vs_BuildTools 下載地址&#xff1a; https://visualstudio.microso…

Python高階函數-sorted(深度解析從原理到實戰)

一、sorted()函數概述 sorted()是Python內置的高階函數&#xff0c;用于對可迭代對象進行排序操作。與列表的sort()方法不同&#xff0c;sorted()會返回一個新的已排序列表&#xff0c;而不改變原數據。 基本語法 sorted(iterable, *, keyNone, reverseFalse)二、核心參數詳…

ArcGIS Pro/GeoScene Pro AI 助手 2.1

引言 面對ArcGIS Pro/GeoScene Pro復雜的操作界面和腳本開發需求&#xff0c;你是否還在為功能定位、代碼調試和效率優化而煩惱&#xff1f;今天&#xff0c;推出自制的Pro AI助手2.0版本&#xff0c;七大核心功能將革新你的GIS工作方式&#xff01;無論是界面操作指引、一鍵生…

如何將本地更改的README文件同步到自己的GitHub項目倉庫

如何將本地更改的 README 文件同步到 GitHub 倉庫 在你 git clone 下來的工程目錄下&#xff1a; 先使用 robocopy YOUR\SOURCE\CODE\DIR YOUR\GIT\CLONE\DIR /E /XD .git /DCOPY:T 將你的更改Copy到你git下來的工程中&#xff08;上面的命令會自動處理&#xff0c;例如只會C…

PostIn V1.0.8版本發布,IDEA 插件支持一鍵掃描上報,讓接口定義不再繁瑣

PostIn是一款國產開源免費的接口管理工具&#xff0c;包含項目管理、接口調試、接口文檔設計、接口數據MOCK等模塊&#xff0c;支持常見的HTTP協議、websocket協議等&#xff0c;支持免登陸本地接口調試&#xff0c;同時可以對項目進行靈活的成員權限、消息通知管理等。本周Pos…