先來說說當下市場開發使用的問題,目前2021年使用vue3開發的企業還是少,基本上都還是以vue2的形式進行開發,vue3的開發模式跟react很像,這時候有人就會想那我學vue3有用么,淦,他喵的,先別激動,冷靜聽我說說,vue3對于現在企業來說都在慢慢的把一些vue2的東西進行升級,這個非常關鍵,因為vue2中可以使用vue3的方法,vue3不能使用vue2,你連vue2都沒有搞定,還拿個錘子去搞vue3,我們先來看看vue3和vue2的一些區別
1.webpack和vite
① vue2使用的是webpack形式去構建項目
webpack是一開始是入口文件,然后分析路由,然后模塊,最后進行打包,然后告訴你,服務器準備好了可以開始干了
②vue3使用vite構建項目
先告訴你服務器準備完成,然后等你發送HTTP請求,然后是入口文件,Dynamic import(動態導入)code split point(代碼分割)
最大的好處和區別就是為了讓項目中一些代碼文件多了以后去保存更新數據時更快能夠看到實際效果,也就是所謂的(熱更新)
2.main.js文件
vue2中我們可以使用pototype(原型)的形式去進行操作,引入的是構造函數
vue3中需要使用結構的形式進行操作,引入的是工廠函數
vue3中app組件中可以沒有根標簽
3.setup函數
setup函數必須要return 返回出去
<script>export default {name: 'appName',setup(){//變量let name = '打工仔'let age = 18//方法function say(){console.log(`我只是一個${name},今年${age}歲`)}//返回一個對象return {name,age,say}}}
</script>
你會發現當前的${name}中name不需要使用this去進行操作,this的作用只不過是取到某個作用域中變量而已,而setup函數提供了當前只在這個作用域中
這時候就很不爽了,那豈不是每次我定義的變量和方法都需要return,vue3中給我們提供了
在script標簽上添加setup 如:<script setup></script>
,相當在編譯運行時放到了setup中注:setup比beforeCreate、created生命周期更早,也就是說在當前直接用this去獲取data中的數據打出來的還是undefined
setup語法中課接收2個參數setup(props,context)
都知vue2中this.$attrs,this.$slots,this.$emit
等同context中attrs,slots,emit
注:當我們只接受一個參數時,頁面會提示警告,但是不影響使用
4.指令與插槽
- vue2中使用slot可以直接使用slot,而vue3中必須使用v-slot的形式
- v-for與v-if在vue2中優先級高的是v-for指令,而且不建議一起使用
- vue3中v-for與v-if,只會把當前v-if當做v-for中的一個判斷語句,不會相互沖突
- vue3中移除keyCode作為v-on的修飾符,當然也不支持config.keyCodes
- vue3中移除v-on.native修飾符
- vue3中移除過濾器filter
5.ref與reactive
ref
把數據變為響應式數據,ref把它們變成了對象,如果我們直接去操作代碼是修改不了的,你會發現當前name和age還是通過get和set修改頁面,這時你需要使用.value,并且ref還是Refimpl
<template><div class="home"><h1>姓名:{{name}}</h1><h1>年齡:{{age}}</h1><button @click="say">修改</button></div>
</template><script>
import {ref} from 'vue'
export default {name: 'Home',setup(){let name = ref('中介')let age = ref(18)console.log(name)console.log(age)//方法function say(){name='波妞'age=18}return {name,age,say}}
}
</script>
這樣的話那我們在頁面上不是得{{name.value}}來做顯示,實際不用這樣的,在我們vue3中會檢測到你的ref是對象,自動會給你加上.value,如果我們自己定義的ref對象,實例會變為refimpl,這個時候用XX.value.XX進行修改
<template><div class="home"><h1>姓名:{{name}}</h1><h1>年齡:{{age}}</h1><h2>職業:{{job.occupation}}</h2><h2>薪資:{{job.salary}}</h2><button @click="say">修改</button></div>
</template><script>
import {ref} from 'vue'
export default {name: 'Home',setup(){let name = ref('中介')let age = ref(18)let job=ref({occupation:'程序員',salary:'10k'})console.log(name)console.log(age)//方法function say(){job.value.salary='12k'}return {name,age,job,say}}
}
</script>
這時我們打印obj.value,他已經不再是refimpl對象,變成了proxy對象,因為vue3底層是proxy對象,基本數據類型都是按Object.defineProperty里面get和set進行數據劫持,vue3已經把reactive封裝進去了,相當于我們在調用ref時,會自動調用reactive
reactive
上面我們說ref里面的對象會調用reactive,把Object轉換為Proxy,現在我們直接通過reactive變成Proxy,它進行了一個深層次的響應式
<template><div class="home"><h1>姓名:{{name}}</h1><h1>年齡:{{age}}</h1><h2>職業:{{job.occupation}}<br>薪資:{{job.salary}}</h2><h3>愛好:{{hobby[0]}},{{hobby[1]}},{{ hobby[2] }}</h3><button @click="say">修改</button></div>
</template><script>
import {ref,reactive} from 'vue'
export default {name: 'Home',setup(){let name = ref('波妞')let age = ref(18)let job=reactive({occupation:'程序員',salary:'10k'})let hobby=reactive(['吃飯','睡覺','打豆豆'])console.log(name)console.log(age)//方法function say(){job.salary='12k'hobby[0]='學習'}return {name,age,job,say,hobby}}
}
</script>
這時你肯定會覺得方法太多了,還不如使用ref提供的.value,是不是感覺爽爽爽,但是有一個問題,如果有一堆數據那不是要一直去.value,點到冒煙,這個時候你可以用模擬vue2中data的形式,就會感覺更香
<template><div class="home"><h1>姓名:{{data.name}}</h1><h1>年齡:{{data.age}}</h1><h2>職業:{{data.job.occupation}}<br>薪資:{{data.job.salary}}</h2><h3>愛好:{{data.hobby[0]}},{{data.hobby[1]}},{{ data.hobby[2] }}</h3><button @click="say">修改</button></div>
</template><script>
import {reactive} from 'vue'
export default {name: 'Home',setup(){let data=reactive({name:'波妞',age:18,job:{occupation:'程序員',salary:'10k'},hobby:['吃飯','睡覺','打豆豆']})//方法function say(){data.job.salary='12k'data.hobby[0]='學習'}return {data,say,}}
}
</script>
ref與reactive區別
- ref定義的是基本數據類型
- ref通過Object.defineProperty()的get和set實現數據劫持
- ref操作數據.value,讀取時不需要.value
- reactive定義對象或數組數據類型
- reactive通過Proxy實現數據劫持
- reactive操作和讀取數據不需要.value
6.vue3的響應式原理
vue2的響應式原理用Object.defineProperty的get和set進行數據劫持,從而實現響應式
- vue2中只有get和set方法去進行屬性的讀取和修改操作,當我們進行新增,刪除時,頁面不會實時更新
- 直接通過下標改數組,頁面也不會實時更新
vue3中響應式原理使用Proxy進行代理,使用window內置對象Reflect反射,學了Es6的語法的就知道我們在使用Proxy進行代理,好比甲方公司給出需要什么技術的前端攻城獅,讓乙方去干招聘、面試等環節
- Proxy可以攔截對象中任意的屬性變化,當然包括讀寫,添加,刪除等
- Reflect對
源對象
屬性進行操作
const p=new Proxy(data, {
// 讀取屬性時調用get (target, propName) {return Reflect.get(target, propName)},
//修改屬性或添加屬性時調用set (target, propName, value) {return Reflect.set(target, propName, value)},
//刪除屬性時調用deleteProperty (target, propName) {return Reflect.deleteProperty(target, propName)}
})
7.computed和watch與watchEffct區別
computed
vue2中computed方法直接去寫上當前方法去進行調用完事
computed:{ //計算屬性_suming(){return parseInt(this.one)+parseInt(this.two)},dataTimeing(){console.log("計算屬性方法");// return "計算屬性方法"+new Date()return "普通方法"+this.time}},
vue3中computed變為組合式Api,那么意味著需要引入,當前如果需要去修改,就需要去終結computed
<template><div class="home">姓:<input type="text" v-model="names.familyName"><br>名:<input type="text" v-model="names.lastName"><br>姓名:{{fullName}}<br></div>
</template><script>
import {reactive,computed} from 'vue'
export default {name: 'Home',setup(){let names=reactive({familyName:'波',lastName:'妞'})fullName=computed(()=>{return names.familyName+'.'+names.lastName})//修改寫法names.fullName=computed({get(){return names.familyName+'.'+names.lastName},set(value){let nameList=value.split('.')names.familyName=nameList[0]names.lastName=nameList[1]}})return {names,fullName}}
}
</script>
watch
vue2中watch通過對象的形式去直接監聽
watch: {userName: {handler(val,res){console.log(val);console.log(res);},immediate:true,deep:true},}
vue3中watch是不是跟computed都是組合APi呢?它就是
<template><div class="home"><h1>當前數字為:{{num}}</h1><button @click="num++">點擊數字加一</button></div>
</template><script>
import {ref,watch} from 'vue'
export default {name: 'Home',setup(){let num=ref('0')//監聽單個watch(num,(newValue,oldValue)=>{console.log(`當前數字增加了,${newValue},${oldValue}`)})//監聽多個響應數據//watch([num,msg],(newValue,oldValue)=>{// console.log('當前改變了',newValue,oldValue)//})return {num}}
}
</script>
為什么newValue與oldValue一樣呢,就很尷尬,都是新的數據,就算你使用ref來定義,還是沒有辦法監聽到oldValue(他喵的,都給你說了ref定義的對象會自動調用reactive),所以在監視reactive定義的響應式數據時,oldValue無法正確獲取,并且你會發現,它是強制開啟深度監視(deep:true),并且無法關閉。
reactive監聽的是響應式數據只是監聽其中一個,我們都知道vue3會監聽reactive或者ref定義,并不能監聽,那需要監聽多個屬性怎么辦呢,可以只能是寫成下面這種
watch([()=>names.age,()=>names.familyName],(newValue,oldValue)=>{console.log('names改變了',newValue,oldValue)
})
如果需要監聽深度屬性怎么辦呢,我們都知道reactive是響應式數據屬性,如果這個屬性是對象,那么我們就可以開啟深度監聽
//第一種
watch(()=> names.job.salary,(newValue,oldValue)=>{console.log('names改變了',newValue,oldValue)
})
//第二種
watch(()=> names.job,(newValue,oldValue)=>{console.log('names改變了',newValue,oldValue)
},{deep:true})
watchEffect
watchEffect是vue3中新增的函數,看字面意思就知道他也有watch,實際上他跟watch功能一樣
優勢
- 默認開啟
immediate:true
- 需要用哪個就監聽哪個
- 值發生改變就調用一次,且不需要返回值
watchEffect(()=>{const one = num.valueconst tow = person.ageconsole.log('watchEffect執行了')
})
8.生命周期
vue2中我們是通過new Vue(),在執行beforeCreate與created接著問你有沒有vm.$mount(el)
vue3中是先準備好所有后再執行
區別:beforeCreate與created并沒有組合式API中,setup就相當于這兩個生命周期函數
setup中
- beforeCreate===>Not needed*
- created=======>Not needed*
- beforeMount ===>onBeforeMount
- mounted=======>onMounted
- beforeUpdate===>onBeforeUpdate
- updated =======>onUpdatedupdated
- beforeUnmount ==>onBeforeUnmount
- unmounted =====>onUnmounted
9.hooks函數
//一般都是建一個hooks文件夾,都寫在里面
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){//鼠標點擊坐標let point = reactive({x:0,y:0})//實現鼠標點擊獲取坐標的方法function savePoint(event){point.x = event.pageXpoint.y = event.pageYconsole.log(event.pageX,event.pageY)}//實現鼠標點擊獲取坐標的方法的生命周期鉤子onMounted(()=>{window.addEventListener('click',savePoint)})onBeforeUnmount(()=>{window.removeEventListener('click',savePoint)})return point
}
//在其他地方調用
import useMousePosition from './hooks/useMousePosition'
let point = useMousePosition()
10.toRef與toRefs
toRef相當于ref類型數據
<template><div class="home"><h1>當前姓名:{{names.name}}</h1><h1>當前年齡:{{names.age}}</h1><h1>當前薪水:{{names.job.salary}}K</h1><button @click="names.name+='!'">點擊加!</button><button @click="names.age++">點擊加一</button><button @click="names.job.salary++">點擊薪水加一</button></div>
</template><script>
import {reactive} from 'vue'
export default {name: 'Home',setup(){let names=reactive({name:'老譚',age:23,job:{salary:10}})return {names}}
}
</script>
這時我們只是操作變量,如果我們需要去操作頁面字符串可以達到響應式嗎?這個時候我們需要把name.XX變為toRef去進行操作name中的數據,
那這時你肯定會去想說句媽賣批,我為何不使用ref去改變呢,ref也能達到響應式數據效果,當前的names里面的數據并不是源數據,而是新定義出的數據,自然操作修改的也不是源數據的names
return {name:names.name,age:names.age,salary:names.job.salary
}return {name:toRef(names,'name'),age:toRef(names,'age'),salary:toRef(names.job,'salary')
}return {name:ref(names.name),age:ref(names.age),salary:ref(names.job.salary),
}
toRefs
toRefs與toRef有什么不同呢,字面意思也能看出來s肯定是更多的意思,(這時你又在猜想,是這樣的)自信一些,特喵的,當前...
是結構了一次,不懂的可以去看看Es6,這就不過多的談
<h1>當前姓名:{{name}}</h1><h1>當前薪水:{{job.salary}}K</h1>
return {...toRefs(names)
}
11.router
vue2中還是使用this. r o u t e r . p u s h 來 進 行 路 由 跳 轉 , 在 v u e 3 中 沒 有 這 些 , 而 是 定 義 了 一 個 v u e ? r o u t e r , 直 接 引 入 u s e R o u t e , u s e R o u t e r , 相 當 于 v u e 2 中 提 供 的 ‘ t h i s . router.push來進行路由跳轉,在vue3中沒有這些,而是定義了一個vue-router,直接引入useRoute,useRouter,相當于vue2中提供的`this. router.push來進行路由跳轉,在vue3中沒有這些,而是定義了一個vue?router,直接引入useRoute,useRouter,相當于vue2中提供的‘this.route,this. r o u t e r ‘ , 別 問 這 ‘ t h i s . router`,別問這`this. router‘,別問這‘this.route,this.$router`有什么區別,這都沒看過我建議你去先看看vue2
import {useRouter,useRoute} from "vue-router";
setup(){const router= useRouter()const route= useRoute()function fn(){this.$route.push('/about')}onMounted(()=>{console.log(route.query.code)})return{fn}
}
12.全局Api的轉移(很重要)
2.x 全局 API( Vue) | 3.x 實例 API(app) |
---|---|
Vue.config.xxxx | app.config.xxxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
vue2中可以通過Vue.prototype去操作原型,在vue3中只能通過app.config.globalProperties,當時玩的時候還以為自己寫錯了,修改的這些你會發現改動的東西挺多
其他APi(了解)
13.shallowReactive與shallowRef
shallowReactive淺層次的響應式,它就是只把第一層的數據變為響應式,深層的數據不會變為響應式,shallowRef如果定義的是基本類型的數據,那么它和ref是一樣的不會有什么改變,但是要是定義的是對象類型的數據,那么它就不會進行響應式,之前我們說過如果ref定義的是對象,那么它會自動調用reactive變為Proxy,但是要是用到的是shallowRef那么就不會調用reactive去進行響應式。
- shallowReactive:只處理對象最外層屬性的響應式(淺響應式)。
- shallowRef:只處理基本數據類型的響應式, 不進行對象的響應式處理
let person = shallowReactive({name:'大理段氏',age:10,job:{salary:20}
})
let x = shallowRef({y:0
})
14.readonly與shallowReadonly
- readonly是接收了一個響應式數據然后重新賦值,返回的數據就不允許修改(深層只讀)
- shallowReadonly卻只是淺層只讀(第一層只讀,其余層可以進行修改)
names=readonly(names)
names=shallowReadonly(names)
15.toRaw與markRaw
toRaw其實就是將一個由reactive生成的響應式對象轉為普通對象。如果是ref定義的話,是沒有效果的(包括ref定義的對象)如果在后續操作中對數據進行了添加的話,添加的數據為響應式數據,當然要是將數據進行markRaw操作后就不會變為響應式,可能大家會說,不就是和readonly一樣嗎?那肯定不一樣咯,readonly是根本沒辦法改,但markRaw是不轉化為響應式,但是數據還會發生改變
<template><div class="home"><h1>當前姓名:{{names.name}}</h1><h1>當前年齡:{{names.age}}</h1><h1>當前薪水:{{names.job.salary}}K</h1><h1 v-if="names.girlFriend">女朋友:{{names.girlFriend}}</h1><button @click="names.name+='!'">點擊加!</button><button @click="addAges">點擊加一</button><button @click="addSalary">點擊薪水加一</button><button @click="add">添加女朋友</button><button @click="addAge">添加女朋友年齡</button></div>
</template><script>
import {reactive,toRaw,markRaw} from 'vue'
export default {name: 'Home',setup(){let names=reactive({name:'老伍',age:23,job:{salary:10}})function addAges(){names.age++console.log(names)}function addSalary(){let fullName=toRaw(names)fullName.job.salary++console.log(fullName)}function add(){let girlFriend={sex:'女',age:40}names.girlFriend=markRaw(girlFriend)}function addAge(){names.girlFriend.age++console.log(names.girlFriend.age)}return {names,add,addAge,addAges,addSalary}}
}
</script>
16.customRef
customRef創建一個自定義的 ref,并對其依賴項跟蹤和更新觸發進行顯式控制
可以使用其特性做防抖節流,原生的方法就不具體寫,說一下概念,后續出一個節流防抖,遞歸,深淺拷貝等
- 防抖:規定時間內觸發同一時間,只執行一次(執行的這一次可以在最開始也可以在最后)
- 節流:在規定一定間隔時間內觸發同一事件,在當前時間內只執行一次,下一個時間內也只執行一次(當前執行的可以在最開始也可以最后)
<template><input type="text" v-model="keyWord"><h3>{{keyWord}}</h3>
</template><script>
import {customRef} from 'vue'
export default {name: 'App',setup() {//自定義一個ref——名為:myReffunction myRef(value,times){let timereturn customRef((track,trigger)=>{return {get(){console.log(`有人從myRef中讀取數據了,我把${value}給他了`)track() //通知Vue追蹤value的變化(必須要有,并且必須要在return之前)return value},set(newValue){console.log(`有人把myRef中數據改為了:${newValue}`)clearTimeout(time)time = setTimeout(()=>{value = newValuetrigger() //通知Vue去重新解析模板(必須要有)},times)},}})}let keyWord = myRef('HelloWorld',1000) //使用自定義的refreturn {keyWord}}
}
</script>
17.provide與inject
都知道組件傳值吧,在vue2中,如果要在后代組件中使用父組件的數據,那么要一層一層的父子組件傳值或者用到vuex,但是現在,無論組件層次結構有多深,父組件都可以作為其所有子組件的依賴提供者。這個特性有兩個部分:父組件有一個 provide 選項來提供數據,子組件有一個 inject 選項來開始使用這些數據
//父
import { provide } from 'vue'
setup(){let fullname = reactive({name:'阿月',salary:'15k'})provide('fullname',fullname) //給自己的后代組件傳遞數據return {...toRefs(fullname)}
}
//后代
import {inject} from 'vue'
setup(){let fullname = inject('fullname')return {fullname}
}
18.響應式判斷
- isRef: 檢查值是否為一個 ref 對象。
- isReactive:檢查對象是否是由 reactive 創建的響應式代理。
- isReadonly: 檢查對象是否是由 readonly 創建的只讀代理。
- isProxy:檢查對象是否是由 reactive 或 readonly 創建的 proxy。
import {ref, reactive,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
export default {name:'App',setup(){let fullName = reactive({name:'小唐',price:'20k'})let num = ref(0)let fullNames = readonly(fullName)console.log(isRef(num))console.log(isReactive(fullName))console.log(isReadonly(fullNames))console.log(isProxy(fullName))console.log(isProxy(fullNames))console.log(isProxy(num))return {}}
}
---------------------
作者:BiKABi
來源:CSDN
原文:https://blog.csdn.net/weixin_43932097/article/details/121512132
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件