vue2和vue3的区别(超详细)

目录

Vue3.0

与2.0的区别

基本代码体

ref、reactive

ref、toref

torefs

inputref

CustomRef

shallowReactive、shallowRef

响应式原理proxy和reflect

setup

生命周期

hooks

readonly、shallowReadonly

toRaw与markRaw

provide、inject

响应式判断

Vue3.0

vue3中支持vue2的大多数特性。比如内置指令、自定义指令、watch监听器、computed、methods、路由、组件、vuex...

创建方式:

①脚手架创建。vue create 项目名 ,只要vue版本高于4就ok,

②提高vite创建。vue init vite-app 项目名,npm run sev运行

与2.0的区别

  1. 性能提升、打包大小减少了40%,第一次渲染快了55%左右,更新提高了大概130%、内存减少了50%左右

  2. 可以使用vite构建项目,也可以用脚手架创建

  3. 响应式原理

    使用proxy代理配合reflect反射,不用再和vue2一样每一个属性都写一个set和get

  4. 重写虚拟DOM的实现和Tree-Shaking(摇树)

  5. 新增Composition(组合)API

    • 不在使用mixin封装函数,而是使用hook封装

    • Vue的响应式特性、Vue的生命周期

  6. 可以使用ts语法

  7. setup

    ref和reactive(写在setup里的数据不是响应式的,需要他们来协助,vue2中data的数据都是响应式的) computed和watch 新的生命周期函数 provide.与inject

  8. Vue3中Template支持多个根标签(会默认包裹一个Fragment标签,但是不会渲染到页面),Vue2不支持【根标签】

  9. 新增组件:Teleport瞬移组件、Suspense异步加载组件、loading界面

  10. 全局api的修改,以前都是Vue.xxx,现在修改为app.xxx

  11. 模板语法的细微变化

基本代码体

//1、
<template>//可以没有根div
   <h3>m1:{{m1}}</h3>
   <button @click="update">更新数据</button>
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName2">
</template>
​
//2、
<script lang="ts">
//这里可以使用ts的代码
//defineComponent函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent, reactive, ref,computed,watch} from "vue";
//引入子组件
// import AsyncComponent from './components/AsyncComponent.vue';
​
//暴露出去一个定义好的组件
export default defineComponent({
  //当前组件名称
  name: "App",
  //props:{}
  setup() {
    const m1 =ref('abc')
    
    const update=()=>{//方法
      m1.value+='-'      
      console.log(m1);
    }
    
    const fullName2=computed({//计算属性
        get(){return },
        set(val:string){}
    })
//3.0版本生命周期在setup中定义,以回调函数的形式返回,2.0版本的在外面定义
    onBeforMount(()=>{//生命周期
       console.log("3.0中的onBeforeMount");
    })
      
    return {//导出
    m1,update,fullName2
    }
},
    data:{}//不建议同时使用
    components:{ //AsyncComponent},
    computed:{},                           
    methods:{},
    watch:{},
});
</script>
​
<style lang="scss">//样式
#app {
  margin-top: 60px;
}
</style>

ref、reactive

ref是一个函数

作用:定义一个响应式的数据,返回一个ref对象,对象中有一个value属性,如果需要对数据进行操作,需要使用该Ref对象调用value属性的方式进行数据的操作

setup(){//setup中定义的数据不是响应式,需要通过ref函数来变为响应式
    //ref
    let count =ref(20) //数据变为响应式,不仅仅是数值变、页面也变
    function add(){
        count.value++
    }
    //reactive
    let obj={
      name:'小米',
      wife:{
        name:'小红',
        cars:['奔驰','宝马','奥迪']
      }
    }
      //把数据变成响应式的数据
  //返回的Proxy的代理对象,被代理的目标对象就obj对象
     const user =reactive(obj)//
      function updateUser(){
        user.name+="=="
        user.wife.cars[0]+="--"
      }
    return{
        count,
        add,
        user,
        updateUser
          }
}
​
​
 `ref和reactived的区别`
/*ref和reactive都是vue3的composition API中的2个重要的响应式API
​
  ref用来处理基本数据类型,reactive用来处理对象(递归深度响应式的)
  如果ref处理对象/数组,内部会自动将对象/数组转换为reactive的代理对象,但是实际操作还是和基本数据类型操作一样,需要通过.value.xxx
  
  ref内部:通过value属性添加getter/setter来实现对数据的劫持
  reactive内部:通过使用Proxy来实现对对象内部所有属性数据的劫持,并且通过Reflect操作对象内部数据(内部的value是proxy对象)
  
  ref的数据操作:在js中需要.value,在模板中不需要(内部解析模板的时候会自动添加.value)
  */

ref、toref

都是把reactive包裹的对象内的属性变为ref响应式的

区别:toref使得操作对象的属性,{{对象}}和对象内{{属性}}都变为响应式,而ref仅让{{属性}}变为响应式,对象不发生变化

{{state}} //{age:5, money:100}
{{age}}
{{money}}
const state=reactive({
          age:5,
          money:100
        })
//把响应式数据state对象中的某个属性age变成ref对象(对象改变)
    const age=toRef(state,'age')
//把响应式数据state对象中的某个属性age变成ref对象(对象属性改变)
    const money=ref(state.money)
    //按钮更新
     const update=()=>{
      //state里的age和自定义age都+8
      state.age+=3
      age.value+=5
      //仅自定义money+10
      money.value+=10//state中money不变

torefs

包裹一个对象,把对象内部的属性变为响应式的。把响应式对象变为普通对象,但是对象内属性变为ref响应式

reactive会将一个对象变成响应式{{obj}},但是如果页面中的内容是{{具体某一个属性}},则无法实现页面响应式。

和toref区别:对象内的全部属性都变为ref响应式,可以解构{xxx,xxx}或...state出来

<template>
  <h2>name:{{name}}</h2>
  <h2>age:{{age}}</h2>
</template>
​
<script lang="ts">
​
export default defineComponent({
    name: "App",
    setup() {
      const state = reactive({
          name:"fanfan",
          age:19
      })
      //将对象内的属性变为ref响应式对象
      let {name,age}=torefs(state)
      
      setInterval(() => {
         name.value += "=";//对ref对象内value进行操作
         }, 1000);
      return {
          name,age
        }
    }
})
</script>
  

inputref

获取表单元素

例如:实现页面的输入框初始就有焦点

<template>
  <!-- ref="inputRef" -->
    <input type="text" ref="inputRef">
  </template>
  <script lang="ts">
  import { defineComponent, onMounted ,ref} from 'vue';
  export default defineComponent({
    name:'App',
    setup(){
      //保持定义的名字和input的ref中定义的名字是一样的,可以识别
      const inputRef=ref<HTMLElement|null>(null)
      
      onMounted(()=>{
        inputRef.value&&inputRef.value.focus()//获取焦点
      })
        
      return {
        inputRef
      }
    }
  })
  </script>

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 {
          //1、  
          get(){
//通知Vue追踪value的变化(必须要有,并且必须要在return之前)
            track() 
            return value
          },
          //2、  
          set(newValue){
            clearTimeout(time)//清除多个定时器,解决重影
            time = setTimeout(()=>{
              value = newValue
              trigger() //通知Vue去重新解析模板(必须要有)
            },times)
          },
        }
      })
    }
    let keyWord = myRef('HelloWorld',1000) //使用自定义的ref
​
    return {keyWord}
  }
}
</script>
​

shallowReactive、shallowRef

对象的浅劫持

注意:

页面中有除了目标变化以外的任何一个可以刷新页面的操作,都会影响浅度劫持,将其变成深度劫持,发生牵连影响。

也就是说,浅劫持命令只在没有其他刷新操作的页面起作用

<template>
  <h3>m2:{{m2}}</h3>
  <h3>m4:{{m4}}</h3>
</template>
setup(){
const m2 =shallowReactive({
    name:'tom',
    age:10,
    car:{
      name:'奔驰',
      color:'black'
    }
  })
  const m4 =shallowRef({
    name:'tom',
    age:10,
    car:{
      name:'奔驰',
      color:'black'
    }
  })
  
   m2.name+='='//变化
    // m2.car.name+='='//不变化,但是和上面的一起,就也会变化
   m4.value.car.name+='='//不变化
}

响应式原理proxy和reflect

// vue3中响应式原理使用Proxy进行代理,使用window内置对象Reflex反射,它提供了一些可拦截js操作的方法
// Proxy可以拦截对象中任意的属性变化,当然包括读写get,添加/修改set,删除deleteProperty等
​
语法:
new proxy(target,handler){
    //target:对象
    //handler:处理器对象,用来监视数据,以及堆数据的操作
}
​
示例:
data={
    name:"fanfan",
    age:28,
    msg:{
        home:"江西"
    }
}
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)
}}
​
console.log(p.name);//'fanfan'
p.name='hw'//修改
p.tall=160//添加
delete p.age//删除
p.msg.mom="郭先英"//深度监听
console.log(data);
//{name:"hw",msg:{home:"江西",mom:"郭先英"}}

好处

整个对象进行代理proxy,内部的所有属性都可以被劫持到变化,可以实现深度监听(对象内地对象属性)。简单方便,包含到的使用场景多。

setup

setup

细节:setup在beforeCreate.之前执行的,并且只执行一次

推断出:setup在执行的时候,当前组件还没有被创建出来,

意味着:组件实例对象this根本就不能用,this是undefined,所以不能通过this再去调用data/computed/methods/props巾的相关内容了,其实所有的组合API相关的回调函数中都不可以

setup中的返回值是一个对象,内部的属性和方法和data函数中的return对象的属性是给html模板使用的

混合使用

setup中的对象内部的属性和data函数中的return对象的属性会合并组件对象的属性

setup中的对象中的方法和methods对象中的方法会合并为组件对象的方法

注意

在vue3中尽量不要混合使用,因为methods可以访问setup提供的属性和方法,但是setup中方法不能访问methods(setup比beforecreate先执行,this无法访问加载的方法)

setup尽量不要是个async函数,因为返回值不再是return的对象了,而是promise对象,模板根本看到return对象中对象中的属性数据了

setup可以接收一些参数`setup(props,{atters,slots,emit})`
    //props参数:是一个对象,里面有父组件向子组件传递的数据,并且是在子组件通过props声明接收的所有属性
​
    /*context参数:是一个对象(可以解构,选择其中的部分对象),里面有
attrs对象(获取当前组件标签上所有的属性对象,且是在props中没有声明接收过的所有属性的对象),
emit方法(分发事件的)在子组件的方法中直接触发父组件定义的方法(类似自定义事件,直接对父组件的内容进操作)emit("方法名","参数"),
slots对象(插槽)*/
​
props和atters、slots都是proxy对象

生命周期

//需要引入生命周期
import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from "vue";
 
export default defineComponent({
    name: "App",
    //VUE2.X中的的生命周期钩子函数
    beforeCreate(){
      console.log('2.0中的beforeCreate');
    },
    created(){
      console.log('2.0中的created');
    },
     //vue3
 setup(){
      console.log("setup执行了");
      //3.0版本没有created,用setup代替,其他都比2.0快,3.0修改了销毁的函数名beforeUnmount/unmounted
     //细节:setup在beforeCreate之前执行的,并且只执行一次
     //3.0版本生命周期在setup中定义,以回调函数的形式返回,2.0版本的在外面定义
     
      onBeforeMount(()=>{
          console.log("3.0中的onBeforeMount");
      })
      onMounted(()=>{
          console.log("3.0中的onMounted");
      })
      onBeforeUpdate(()=>{
          console.log("3.0中的onBeforeUpdate");
      })
      onUpdated(()=>{
          console.log("3.0中的onUpdated");
      })
      onBeforeUnmount(()=>{
          console.log("3.0中的onBeforeUnmount");
      })
      onUnmounted(()=>{
          console.log("3.0中的onUnmounted");
      })
      return {
          ...
      }
    }
  });

hooks

默认导出封装的一些函数,return变量,在组件setup中解构获取变量,以此应用到网络中

比如封装一个点击显示页面鼠标位置方法,通过组合API来实现Vue的响应式特性(ref/reactive)、Vue的生命周期、hooks写法...

 
//hook/useMousePosition.ts
import { onBeforeUnmount,onMounted,ref } from "vue";
//导出为一个函数
export default function(){
    const x =ref(-1)
    const y =ref(-1)
    const clickHander=(event:MouseEvent)=>{
        //Page:鼠标相对文档
      x.value=event.pageX
      y.value=event.pageY
    }
    onMounted(()=>{//鼠标事件监听
      window.addEventListener("click",clickHander)
    })
    //解绑
    onBeforeUnmount(()=>{
      window.removeEventListener("click",clickHander)
    })
    return{
        x,y
    }
}
//App.vue
<template>
  <h2>x:{{ x }} y:{{ y }}</h2>
</template>
​
<script lang="ts">
import useMousePosition from "./hook/useMousePosition";
export default defineComponent({
    name: "App",
    setup() {
      const { x, y } = useMousePosition();
      return {
          x,y
        }
    }
})
</script>  
                               
`❤在vue2中使用mixin`
<script>
import MousePositionMix from '@/mixin/MousePositionMix'
export default {
    mixins: [MousePositionMix],
    //看起来方便,但是如果是读别人的代码,不知道页面的变量来哪里,且不能重写mixin中的变量。如果有好几个mixin,查找页面中的变量比较困难
}
</script>   
                   

readonly、shallowReadonly

readonly深只读和shallowReadonly浅只读

shallowReadonly仍可以对对象内属性是对象,其内的属性可以进行修改,但是,对象内的属性不可以(浅只读)

readonly无论套多少层对象,都是不可修改的

toRaw与markRaw

toRaw:将一个由reactive生成的响应式对象转为普通对象。如果是ref定义的话,是没有效果的(包括ref定义的对象)

但是如果在后续操作中对数据进行了添加的话,添加的数据仍为响应式数据,当然要是将数据进行markRaw操作后就不会变为响应式

 state:{{state}}
​
//把代理对象变成普通对象,页面不会响应
    const testToRaw=()=>{
      const user=toRaw(state)//页面数据不变
      user.name+='='
      console.log("testToRaw",state,user);//log数据还是变化的
      const likes=['吃','喝','玩','乐']//新增属性
      state.likes=markRaw(likes)//属性设置不是响应式
      console.log("testMarkRaw",state);
    }

provide、inject

父-子孙组件传递数据,不需要借助父组件一层一层传值

父组件provide("color",color)

子孙组件const color =inject('color')

响应式判断

isRef: 检查值是否为一个 ref 对象。 isReactive:检查对象是否是由 reactive 创建的响应式代理。 isReadonly: 检查对象是否是由 readonly 创建的只读代理。 isProxy:检查对象是否是由 reactive 或 readonly 创建的 proxy。

  • 18
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值