關(guān)于Vue3&TypeScript的踩坑匯總
安裝環(huán)境
1.node版本12以上
創(chuàng)建項目
npm init @vitejs/app + 項目名
npm init vite@latest +項目名
1、配置路由
router-index.ts
npm install vue-router@4 --save
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"; const routes: Array<RouteRecordRaw> = [ ?{ ?path: "/", ?name: "Home", ?meta: { ? title: "首頁", ? keepAlive: true ?}, ?component: () => import("../views/Home/index.vue"), ?}, ?{ ?path: "/login", ?name: "Login", ?meta: { ? title: "登錄", ? keepAlive: true ?}, ?component: () => import("../views/Login/index.vue"), ?}, ]; const router = createRouter({ ?history: createWebHashHistory(), ?routes }); export default router;
2、安裝VueX
npm i vuex@next --save
import { createStore } from "vuex"; export default createStore({ state: { listData:{1:10}, num:10 }, mutations: { setData(state,value){ state.listData=value }, addNum(state){ state.num=state.num+10 } }, actions: { setData(context,value){ context.commit('setData',value) }, }, modules: {} });
記得掛載
3、安裝國際化
npm i vue-i18n@next -S npm i js-cookie -S npm i @types/js-cookie -D
4、vite.config.ts常用配置
npm i vite-plugin-compression
5、找不到模塊聲明declare
shime.d.ts
declare moudle ''
6、path模塊找不到
npm install @types/node --save-dev
打包問題
tsconfig.json
"skipLibCheck": true,
Vite打包后顯示跨域
解決辦法:,npm 安裝 http-server 包。
打開dist文件夾,終端輸入
http-server -p 端口號
Vue3.0的新語法糖-script setup
?置e?sS?端S?端置td小。。優(yōu)點果。外面V了出。來。xx可。割。方法屬性屬性變量參數(shù)定:
Options API 約定:
我們需要在 props 里面設(shè)置接收參數(shù)
我們需要在 data 里面設(shè)置變量
我們需要在 computed 里面設(shè)置計算屬性
我們需要在 watch 里面設(shè)置監(jiān)聽屬性
我們需要在 methods 里面設(shè)置事件方法
你會發(fā)現(xiàn) Options APi 都約定了我們該在哪個位置做什么事,這反倒在一定程度上也強(qiáng)制我們進(jìn)行了代碼分割。
現(xiàn)在用 Composition API,不再這么約定了,于是乎,代碼組織非常靈活,我們的控制代碼寫在 setup 里面即可。
setup函數(shù)提供了兩個參數(shù) props和context,重要的是在setup函數(shù)里沒有了this,在 vue3.0 中,訪問他們變成以下形式: this.xxx=》context.xxx
我們沒有了 this 上下文,沒有了 Options API 的強(qiáng)制代碼分離。Composition API 給了我們更加廣闊的天地,那么我們更加需要慎重自約起來。
對于復(fù)雜的邏輯代碼,我們要更加重視起 Composition API 的初心,不要吝嗇使用 Composition API 來分離代碼,用來切割成各種模塊導(dǎo)出。
1、**template標(biāo)簽下面可以有多個節(jié)點了**,終于不用先寫一個DIV了
2、**setup函數(shù)可以代替之前的data,methods,computed,watch,Mounted等對象**,但是props聲明還是在外面
ref與reactive方法的區(qū)別是什么?一個是把值類型添加一層包裝,使其變成響應(yīng)式的引用類型的值。另一個則是引用類型的值變成響應(yīng)式的值。所以**兩者的區(qū)別只是在于是否需要添加一層引用包裝**?其目的都是對數(shù)據(jù)添加響應(yīng)式效果。
##### 思想優(yōu)點
**vue2采用面向?qū)ο缶幊痰乃枷?,vue3則采用函數(shù)式編程的思想。**
原因:充分利用函數(shù)式編程組合大于繼承的優(yōu)勢,采用函數(shù)式編程更利于邏輯功能的復(fù)用,webpack打包時更有利于tree-shaking,更利于代碼的壓縮,更利于返回值類型校驗,壓縮后的文件體積更小。
<script setup lang="ts"> // imported components are also directly usable in template import Foo from "../Foo/index.vue"; import { ref } from "vue"; // write Composition API code just like in a normal setup() // but no need to manually return everything const count = ref(0); const inc = () => { count.value++; }; </script> <template> <div> <Foo /> HomePage組件 </div> </template>
Css中使用JS代碼
Css中使用變量
.Title { font-size: 30px; @media screen and (max-width: 1000px) { font-size: 12px; } color: v-bind(colorRed); }
Css去掉scope
:root { --varColor: red; } .aColor { color: var(--varColor); }
Ref、toRef、toRefs、Reactive總結(jié)
Ref拷貝出來的值不能直接進(jìn)行算數(shù)運算,必須使用.value
let qq = ref<number>(1); qq.value++;
Ref和Reactive,在不影響數(shù)據(jù)源的情況下可以將一般數(shù)據(jù)類型轉(zhuǎn)化成響應(yīng)式數(shù)據(jù)類型。
當(dāng)響應(yīng)式對象里頭數(shù)據(jù)變化的時候原始對象的數(shù)據(jù)也會變化
toRef和toRefs是引用型,是在不影響數(shù)據(jù)源的情況下,將原始數(shù)據(jù)轉(zhuǎn)化為響應(yīng)式數(shù)據(jù)。且不更新UI界面,但是如果頁面中有Reactive等其他響應(yīng)式,則會同時更新UI界面。
defineProps父子傳值
定義子組件props(子組件的data)
defineProps({ msg: String, });
defineEmits
const emit = defineEmits(['click']);
v-model
v-model 指令擴(kuò)展為 modelValue 和 onUpdate:modelValue 在模板編譯過程中,我們必須自己提供這些 props:
export default { props: ['modelValue'], emits: ['update:modelValue'], render() { return h(SomeComponent, { modelValue: this.modelValue, 'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value) }) } }
PropType
假如我有一個todoItem 組件,需要item 信息屬性,需要準(zhǔn)守 TodoItem interface。
1、引入
import { PropType } from 'vue'
2、定義接口
export interface TodoItem { ? text: string ? done: boolean }
3、屬性驗證
props: { ? ? todo: { ? ? ? type: Object as PropType<TodoItem>, ? ? ? default: { ? ? ? ? text: '', ? ? ? ? done: false ? ? ? } ? ? } ? }
toRefs解構(gòu)、Reactive
對于以上代碼,toRefs()將響應(yīng)式的對象 state 變?yōu)槠胀▽ο?stateAsRefs 后,return 時使用 ES6 的擴(kuò)展運算符,在模版中可以直接使用其內(nèi)部屬性,且仍具有響應(yīng)性( 對響應(yīng)式對象 state 使用擴(kuò)展運算符后,其內(nèi)部屬性就失去了響應(yīng)性 )。
對響應(yīng)式對象進(jìn)行 toRefs 后,可以對其進(jìn)行解構(gòu)方便 vue 模版使用,但是不會使其失去響應(yīng)性。
Ref、toRef、toRefs、Reactive
ref()接收一個 js 基本數(shù)據(jù)類型的參數(shù);toRef()接收兩個參數(shù),第一個為對象,第二個為對象中的某個屬性;
ref()創(chuàng)建的數(shù)據(jù)會觸發(fā) vue 模版更新;toRef()創(chuàng)建的響應(yīng)式數(shù)據(jù)并不會觸發(fā) vue 模版更新,所以toRef()的本質(zhì)是引用,與原始數(shù)據(jù)有關(guān)聯(lián)
toRef[data]
toRef針對的是響應(yīng)式,針對的不是普通對象,如果用于非響應(yīng)式,產(chǎn)出的結(jié)果不具有響應(yīng)式
toRef 用于為源響應(yīng)式對象上的屬性新建一個ref,從而保持對其源對象屬性的響應(yīng)式連接。
接收兩個參數(shù):源響應(yīng)式對象和屬性名,返回一個ref數(shù)據(jù)。例如使用父組件傳遞的props數(shù)據(jù)時,要引用props的某個屬性且要保持響應(yīng)式連接時就很有用。
<template> <div> <h1 style="color: royalblue" @click="clickHandler"> {{ names }} </h1> </div> </template> <script lang="ts" setup> import { ref, defineProps, toRef } from "vue"; const props = defineProps({ kindleValue: { type: Object, default: () => { return {}; }, }, }); const clickHandler = () => { names.value++; console.log(names.value, props.kindleValue); }; const names = toRef(props.kindleValue, "name"); </script> <style scoped lang="less"></style>
readonly
接受一個對象 (響應(yīng)式或純對象) 或 ref 并返回原始對象的只讀代理。只讀代理是深層的:任何被訪問的嵌套 property 也是只讀的。
isProxy
檢查對象是否是由 reactive 或 readonly 創(chuàng)建的 proxy
toRaw
作用:將一個由reactive生成的響應(yīng)式對象轉(zhuǎn)為普通對象
使用場景:用于讀取響應(yīng)式對象對應(yīng)的普通對象,對這個普通對象的所有操作,不會引起頁面更新。
const props = defineProps({ navbarList: { type: Object, default: () => { return {}; }, }, activeKey: { type: Number, default: () => { return 0; }, }, isBlur: Boolean, }); const emit = defineEmits(['change']); // 選擇Tab const changeActiveKey = (key: number) => { const { navbarList } = props; const linkUrl = toRaw(navbarList) as Array<linkType>; console.log(key); emit('change', key); router.push(`${linkUrl[key].children[0].linkUrl}`); };
markRaw
作用:標(biāo)記一個對象,使其永遠(yuǎn)不會再成為響應(yīng)式對象。
應(yīng)用場景:
1.有些值不應(yīng)被設(shè)置為響應(yīng)式的,例如復(fù)雜的第三方類庫等。
2.當(dāng)渲染具有不可變數(shù)據(jù)源的大列表時,跳過響應(yīng)式轉(zhuǎn)換可以提高性能。
Computed
返回值是readOnly,屬性是只讀的。
用法一
const show = computed({ get() { return props.value; }, set(val: boolean) { emit("update:modelValue", val); } });
用法二
const itemStyle = computed(() => { return item => { return { background: item?.path === active.value ? useEpThemeStoreHook().epThemeColor : "", color: item.path === active.value ? "#fff" : "", fontSize: item.path === active.value ? "16px" : "14px" }; }; });
const navbarList = computed(() => { return store.state.user.menu; });
defineComponent
VueCli4.5+TypeScript
傳值方式
多層組件傳值:Provide-inject
父子組件傳值:defineProps、defineEmits
VueX狀態(tài)管理
watch和watchEffect的區(qū)別
1、watch 是需要傳入偵聽的數(shù)據(jù)源,而 watchEffect 是自動收集數(shù)據(jù)源作為依賴。
2、watch 可以訪問偵聽狀態(tài)變化前后的值,而 watchEffect 沒有。
3、watch 是屬性改變的時候執(zhí)行,而 watchEffect 是默認(rèn)會執(zhí)行一次,然后屬性改變也會執(zhí)行。
// imported components are also directly usable in template import { ref, reactive, watch, watchEffect } from "vue"; // write Composition API code just like in a normal setup() // but no need to manually return everything const count = ref(0); let a = ref<number>(0); type obj = { name: string; age: number; }; const Person = reactive<Array<obj>>([{ name: "LKK", age: 10 }]); watch( () => Person[0].age, (newVal, oldVal) => { console.log(newVal, oldVal, "ssss"); } ); watchEffect(() => { console.log(Person[0].age); // 即使 testObject.a 沒有發(fā)生變化,也會在組件更新之前輸出一遍 }); const inc = () => { count.value++; }; const clickHandler = function () { Person[0].age += 1; // a.value++; };
監(jiān)聽多個數(shù)據(jù)
watch(() => [state.province, state.country], ([newprovince,newcountry],[oldprovince,oldcountry]) => { console.log(oldprovince,'省份---', newprovince); console.log(newcountry,'cs---', oldcountry); // 判斷是不是省份發(fā)生的變化 if(oldprovince !== newprovince) { const arr = state.allCity.filter((item) => item.province == state.province); state.countries = arr[0].cities; console.log(arr); state.country = ""; } state.detailedAdress = ""; console.log(state.countries); } );
類型“unknown”上不存在屬性“name”
import type { PropType } from 'vue';
父元素獲取子元素的方法
方法一
子元素需要用defineComponent導(dǎo)出寫法
<template> <div> <a-modal title="Title" v-model:visible="visible" :confirm-loading="confirmLoading" @ok="handleOk"> <p>{{ modalText }}</p> </a-modal> </div> </template> <script lang="ts"> import { ref, defineComponent } from 'vue'; export default defineComponent({ setup() { const modalText = ref<string>('Content of the modal'); const visible = ref<boolean>(false); const confirmLoading = ref<boolean>(false); const showModal = () => { visible.value = true; }; const handleOk = () => { modalText.value = 'The modal will be closed after two seconds'; confirmLoading.value = true; setTimeout(() => { visible.value = false; confirmLoading.value = false; }, 2000); }; return { modalText, visible, confirmLoading, showModal, handleOk, }; }, }); </script>
defineComponent無法獲取父組件傳過來的后臺數(shù)據(jù)。
方法二
defineExpose
// 子組件 <script setup> import { defineExpose } from 'vue' const childFun = () => { console.log('我是子組件方法') } // 重點!!這里需要使用defineExpose暴露出去 defineExpose({ childFun }) </script>
// 父組件 <template> <child ref="childRef"></child> </template> <script setup> import { ref } from "vue"; // 引入子組件 import child from "./child.vue"; // 獲取子組件 const childRef = ref(null); const fun = () => { childRef.value.childFun();// 調(diào)用子組件的方法 } </script >
h(標(biāo)簽, {屬性},[可以繼續(xù)嵌套h()])
其實h()函數(shù)和createVNode()函數(shù)都是創(chuàng)建dom節(jié)點,他們的作用是一樣的,但是在VUE3中createVNode()函數(shù)的功能比h()函數(shù)要多且做了性能優(yōu)化,渲染節(jié)點的速度也更快。
import { createApp } from "vue"; //import App from "./App.vue"; import { defineComponent, h, createVNode } from "vue"; import HelloWorld from "./components/HelloWorld.vue"; const img = require('./assets/logo.png'); // eslint-disable-line const App = defineComponent({ render() { return h("div", { id: "app" }, [ h("img", { src: img }), h(HelloWorld, { msg: "HelloWorld" }), createVNode("h1", { class: "hello" }, "HelloWorld") ] ); }, }); createApp(App).mount("#app");
Vue2-Vue3生命周期對比
beforeCreate -> 使用 setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決vue?app.js/vender.js過大優(yōu)化啟動頁
這篇文章主要為大家介紹了解決vue?app.js/vender.js過大優(yōu)化啟動頁過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Vue.js獲取被選擇的option的value和text值方法
今天小編就為大家分享一篇Vue.js獲取被選擇的option的value和text值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08vue3項目vite.config.js配置代理、端口、打包名以及圖片壓縮
這篇文章主要給大家介紹了關(guān)于vue3項目vite.config.js配置代理、端口、打包名以及圖片壓縮的相關(guān)資料,因為3.0版本中vue已經(jīng)內(nèi)置了很多關(guān)于webpack的配置,一般情況下開箱即用,需要修改則可以在vue.config.js文件中完成,需要的朋友可以參考下2023-12-12vue項目中請求數(shù)據(jù)特別多導(dǎo)致頁面卡死的解決
這篇文章主要介紹了vue項目中請求數(shù)據(jù)特別多導(dǎo)致頁面卡死的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09