先来说说当下市场开发使用的问题,目前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.value
const tow = person.age
console.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.pageX
point.y = event.pageY
console.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——名为:myRef
function myRef(value,times){
let time
return 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 = newValue
trigger() //通知Vue去重新解析模板(必须要有)
},times)
},
}
})
}
let keyWord = myRef('HelloWorld',1000) //使用自定义的ref
return {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 {}
}
}