vue3.0学习笔记

完善中,后面会不间断更新。

一、Vue 3.0六大亮点

1、performance:性能比vue2.x快1.2-2倍。
2、Treeshaking support:按需编译,体积比vue2.x更小。
3、Composition API(类似react Hooks)。
4.Better TypeScript support:更好的TS支持。
5、Custom Renderer API:暴露了自定义API。
6、Fragment,Teleport(Protal),Supense:更先进的组件。

二、Vue3.0是如何变化更快的?

difff算法优化:

Vue2中的虚拟dom是进行全量的对比。
Vue3新增了静态标记(PatchFlag)。
在与上次虚拟节点进行对比的时候,只对比带有patch flag的节点
并且可以通过flag的信息得知当前节点对比的具体内容。

三、 静态提升

1、vue2中无论与元素是否参与更新,每次都会重新创建,然后在渲染。
3、vue3中对于不参与更新的元素,会做静态提升你如果,只会被创建一次,在渲染时直接复用即可。

四、事件监听缓存

默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化,但因为时同一个函数,所以没追踪到变化,会直接缓存起来复用。

五、vue3.0如何在组合API(Composition API)中定义方法定义变量

1、setup函数是 Composition API(组合API/注入API)的入口。
2、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间来执行的。

注:在setup方法中定义的变量或者方法必须return才能在视图中使用,ref用来监听简单类型的数据变化,不能监听复杂类型的数据变化

demo-1

在setup方法中定义个变量count并在视图中渲染这个变量,点击按钮count加一,视图随之更新。
在这里插入图片描述

demo-2-1

声名一个变量,并将数据渲染到页面,点击那条数据,就删除掉页面对应的数据。
注:如果数据时一个复杂类型那么就需要使用reactive来监听数据的变化。
在这里插入图片描述

demo-2-2,解决vue2.x数据业务逻辑分散的问题

同demo-2-1的功能换一种写法。
在这里插入图片描述
这样写解决了vue2.x数据业务逻辑分散的问题,现在的数据和逻辑就整合在一起就不分散了。

demo3,将删除功能和新增功能进行封装使用。

看了下面这种写法后觉得确实很强,将页面的业务逻辑都放在了一块,逻辑不在分散,下面只是初步封装,当然你的项目有很多业务逻辑的话,你也可以拿出来单独进行封装。

<template>
    <div>
      <form>
        <input type="text" v-model="state2.obj.id">
        <input type="text" v-model="state2.obj.name">
        <input type="text" v-model="state2.obj.age">
        <input type="submit" @click="addObj">
      </form>
      <ul>
        <li v-for="(v,i) in state.obj" :key="i" @click="remObj(i)">
          {{v.name}}---{{v.age}}
        </li>
      </ul>
    </div>
</template>
<script>
import {reactive} from "vue"
export default {
  name: 'App',
  setup(){
    let {state,remObj}=useRemoveObj()
    let {state2,addObj}=useAddObj(state)
    return {state,remObj,state2,addObj}
  }
}
// 删除数据
  function useRemoveObj(){
    let state=reactive({
      obj:[
        {id:1,name:"guo-1",age:10},
        {id:2,name:"guo-2",age:20},
        {id:3,name:"guo-3",age:30},
      ]
    })
    function remObj(i){
      state.obj=state.obj.filter((item,index)=>index!==i)
    }
    return {state,remObj}
  }
  // 新增数据
  function useAddObj(state){
    let state2=reactive({
      obj:{
        id:"",
        name:"",
        age:""
      }
    })
    function addObj(e){
      e.preventDefault()
      const stu = Object.assign({},state2.obj)
      state.obj.push(stu)
      state2.obj.id=""
      state2.obj.name=""
      state2.obj.age=""
    }
    return {state2,addObj}
  }
</script>

六、setup梳理及注意点

1、setup:执行时机
  • setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数。
  • beforeCreate:表示组件刚刚被创建出来,组件的data和methods还没有初始化好
  • Created:表示组件刚刚被创建出来,组件的data和methods已经初始化好了
2、setup注意点
  • 由于执行setup函数的时候,还没有执行Created生命周期方法,所以setup函数中,无法使用data和methods
  • 由于不能在setup函数中使用data和methods,所以vue为了避免我们错误使用,他将setup函数中的this修改成了undefined。
  • setup函数只能是同步的,不能是异步的。

七、什么是reactive

1、reactive是Vue3中提供的实现响应式数据的方法。
2、在Vue2中响应式数据是通过defineProperty实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的。

reactive注意点

1、reactive参数必须是对象(json/arr)
2、如果给reactive传递了其他对象例如日期对象

  • 默认情况下修改对象,界面不会更新。
  • 如果想更新,可以通过重新赋值的方式。
例1:传递一个Number类型
<template>
    <div>
      {{state}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive} from "vue"
export default {
  name: 'App',
  setup(){
    // 创建一个响应式数据
    // 本质:就是将传入的数据包装成一个Proxy对象
    let state=reactive(123)
    function myFn(){
      state=666
      console.log("state",state)//666
    }
    return {state,myFn}
  }
}
</script>

通过以上代码可以发现,reactive如果传递的是一个简单类型的数据,数据是可以在页面中渲染出来的,但是当我们修改数据之后,页面不会更新,这个时候就需要使用ref,如下案例。

<template>
    <div>
      {{state}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref} from "vue"
export default {
  name: 'App',
  setup(){
    let state=ref(123)
    function myFn(){
      state.value=666
      console.log("state",state)//666
    }
    return {state,myFn}
  }
}
</script>

如上可以发现,如果是一个简单数据类型,那么我们使用ref,那么就可以监听到数据的变化,如果json或者arr就用reactive,就可以监听到数据变化。

例2——如果给reactive传的是一个日期对象,数据修改页面不会刷新问题。
<template>
    <div>
      {{state.time}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref} from "vue"
export default {
  name: 'App',
  setup(){
    let state=reactive({
      time:new Date()
    })
    function myFn(){
      state.time.setDate(state.time.getDate()+1)
      console.log("state",state)
    }
    return {state,myFn}
  }
}
</script>

如上代码,我们给reactive传递了一个日期对象,但当我们修改日期对象的时候可以发现,数据已经修改了,但是页面不会刷新。 上面也总结过,如果reactive传递了json和arr以外的其他对象,修改数据视图是不会更新的,想要更新可以通过重新赋值的方式来实现

例3——如下,如果reactive传递的是如期对象

如下代码可以发现,直接修改日期对象视图是不会刷新的。

<template>
    <div>
      {{state.time}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref} from "vue"
export default {
  name: 'App',
  setup(){
    let state=reactive({
      time:new Date()
    })
    function myFn(){

      state.time.setDate(state.time.getDate()+1)
      console.log("state",state.time)
    }
    return {state,myFn}
  }
}
</script>

按照如下方法重新赋值之后,可以发现视图会随着数据的变化而更新。

<template>
    <div>
      {{state.time}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref} from "vue"
export default {
  name: 'App',
  setup(){
    let state=reactive({
      time:new Date()
    })
    function myFn(){
      const newTime=new Date(state.time.getTime())
      newTime.setDate(state.time.getDate()+1)
      state.time=newTime
      console.log("state",state.time)
    }
    return {state,myFn}
  }
}
</script>

八、什么是ref

  • ref和reactive一样,也是用来实现响应式数据的方法。
  • 由于reactive必须传递一个对象所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了ref方法,实现对简单类型数据的监听。
ref本质
  • ref底层的本质其实还reactive系统会自动根据我们给ref传入的值将它转换成ref(xx)—>reactive({value:xx})
ref注意点
  • 在Vue中使用ref的值不用通过value获取
  • 在JS中使用ref必须通过value获取

ref和reactive的区别

  • 如果在template里使用的是ref类型的数据,那么Vue会自动帮我们添加。value。
  • 如果在template里使用reactive类型的数据,那么Vue不会帮我们添加.value
Vue如何决定是否需要添加.value的
  • Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不添加。
Vue如何判断当前数据是否为ref类型
  • 通过当前数据的__v_isRef属性来判断的,有这个私有属性,并且值为true,那么就代表是ref类型的数据

在这里插入图片描述

九、如何判断数据是ref还是reactive类型的数据。

  • 判断数据类型为ref使用isRef方法,返回true则为真,反之为假。
  • 判断数据类型为reactive使用isReactive方法,返回true则为真,反之为假。
Demo
<template>
    <div>
      {{state.time}}
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref,isRef,isReactive} from "vue"
export default {
  name: 'App',
  setup(){
    let state=reactive({ 
      time:new Date()
    })
    let age=ref(10)
    function myFn(){
      state.time.setDate(state.time.getDate()+1)
      console.log("state",isRef(state),isReactive(state))//state false true
      console.log("age",isRef(age),isReactive(age))//age true false
    }
    return {state,myFn}
  }
}
</script>

十、递归监听

递归监听就是不管你数据嵌套多少层它都能够监听到。

  • 默认情况下无论是ref还是reactive都是递归监听
递归监听存在的问题
  • 不论是使用ref还reactive,他们内部都会做一个递归,将每层数据都包装成proxy对象,既然他要将每层数据都通过递归包装成proxy对象,那么内部是要做很多事的,所以他比较消耗能的。
Demo
<template>
    <div>
      <p>{{state.a}}</p>
      <p>{{state.gf.b}}</p>
      <p>{{state.gf.f.c}}</p>
      <p>{{state.gf.f.s.d}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,ref,isRef,isReactive} from "vue"
export default {
  name: 'App',
  setup(){
    let state=reactive({ 
      a:"a",
      gf:{
        b:"b",
        f:{
          c:"c",
          s:{
            d:"d"
          }
        }
      }
    })
    function myFn(){
      console.log(state)
      console.log(state.gf)
      console.log(state.gf.f)
      // 由打印结果可以看出来不论是使用ref还reactive,他们内部都会做一个递归,将每层数据都包装成proxy对象,既然他要将每层数据都通过递归包装成proxy对象,那么内部是要做很多事的,所以他比较消耗能的。
      state.a=1
      state.gf.b=2
      state.gf.f.c=3
      state.gf.f.s.d=4

      // state.value.a=1
      // state.value.gf.b=2
      // state.value.gf.f.c=3
      // state.value.gf.f.s.d=4

    }
    return {state,myFn}
  }
}
</script>

十一、非递归监听

  • 非递归监听只能监听第一层
  • shallowReactiveshallowRef分别类似于reactiveref,但是 shallowReactiveshallowRef是非递归监听,reactiveref是递归监听。

十二、toRaw

ref/reactive数据类型特点

每次修改都会被追踪到,都会更新视图,但是这样其实非常消耗性能,所以如果我们有一些操作不需要追踪,不需要视图刷新,那么这个时候我们就可以通过toRaw方法拿到它的原始数据,对原始数据进行修改这样就不会被追踪,这样视图就不会刷新,这样就第一程度上防止了性能的消耗

十三、markRaw

  • markRaw:让某一条数据永远都不被追踪。
<template>
    <div>
      <p>{{state}}</p>

      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {reactive,markRaw} from "vue"
export default {
  name: 'App',
  setup(){
    let obj={name:"guo123",age:18}
    obj=markRaw(obj)
    let state=reactive(obj)

    function myFn(){
      state.name="guodong"
      // 再次可以发现打印结果变了,但是视图没有刷新,
      //markRaw的作用就是让这是数据不要被追踪
      console.log(obj.name)//guodong

    }
    return {state,myFn}
  }
}
</script>

十四、toRef和ref的区别

  • ref是值的复制,修改响应式数据不会影响原始数据,数据变化视图更新。
  • toRef是值的引用,修改响应式数据会影响原始数据,数据变化视图不更新。
toRef应用场景

如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后视图不需要更新,那么就可以使用toRef。

例——ref

由下面案例打印结果可以看出来,用ref将某一个对象中的属性变成响应式的数据,我们修改响应式数据是不会影响到原始数据的,页面随数据的变化而更新。

<template>
    <div>
      <p>{{state}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {ref} from "vue"
export default {
  name: 'App',
  setup(){
    let obj={name:"guo123"}
    let state=ref(obj)
    console.log("state",state)
    function myFn(){
      state.value.name="guodong"
      console.log("obj",obj.name)//guodong
      console.log("state",state.value.name)//guodong
    }
    return {state,myFn}
  }
}
</script>
例二——toRef

如果利用toRef将某一个对象中的属性变成响应式的数据,我们修改响应式的数据是会影响到原始数据,但是响应式数据是通过toRef创建的,那么修改了数据并不会触发视图更新

<template>
    <div>
      <p>{{state}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {toRef} from "vue"
export default {
  name: 'App',
  setup(){
    let obj={name:"guo123"}
    // let state=ref(obj)
    let state=toRef(obj,'name')
    console.log("state",state.value)
    function myFn(){
      state.value="guodong"
      // 打印结果可以看出来,数据变了但视图没有更新。
      console.log("obj",obj.name)//guodong
      console.log("state",state.value)//guodong
    }
    return {state,myFn}
  }
}
</script>

十五、toRefs

如果我们由多个数据的修改不希望被追踪到,需要将没一个值传递给toRef,显然这样做比较麻烦,这个时候就需要使用toRefs,可以直接传递一个对象给他。

Demo
<template>
    <div>
      <p>{{state}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {toRefs} from "vue"
export default {
  name: 'App',
  setup(){
    let obj={name:"guo123",age:18}
    let state=toRefs(obj)
    function myFn(){
      state.name.value="guodong"
      state.age.value=20
      console.log(state)
    }
    return {state,myFn}
  }
}
</script>

十六、customRef——自定义ref方法

customRef方法接收一个回调函数,在回调函数中必须返回一个对象,在这个对象里面还由get和set这两个方法。

Demo
<template>
    <div>
      <p>{{age}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>
import {customRef} from "vue"
function myRef(value){
  return customRef((track, trigger)=>{
    return {
      get(){
        console.log("get",value)
        track();//track方法作用是告诉Vue这个数据是需要追踪变化的
        return value
      },
      set(newValue){
        console.log("set",newValue)
        value=newValue
        trigger();//告诉Vue触发界面更新
      }
    }
  })
}
export default {
  name: 'App',
  setup(){
    let age=myRef(18)
    function myFn(){
      age.value+=1
    }
    return {age,myFn}
  }
}
</script>

十七、customRef中请求异步数据

setup函数只能是同步的,所以异步请求放在setup函数中是不行的,这个时候就需要用到customRef。

<template>
    <div>
      <ul>
        <li v-for="(v,i) in state" :key="i">{{v.name}}</li>
      </ul>
    </div>
</template>
<script>
import {customRef,ref} from "vue"
function myRef(value){
  return customRef((track, trigger)=>{
    //异步数据请求
    fetch(value)
      .then((res)=>{
        return res.json()
      })
      .then((data)=>{
        console.log("data",data)
        value=data;
        trigger();
      })
      .catch((err)=>{
        console.log("err",err)
      })
    return {
      get(){
        console.log("get",value)
        track();//track方法作用是告诉Vue这个数据是需要追踪变化的
        return value
      },
      set(newValue){
        console.log("set",newValue)
        value=newValue
        trigger();//告诉Vue触发界面更新
      }
    }
  })
}
export default {
  name: 'App',
  setup(){
    let state=myRef("../public/data.json")
      return {state}
  }
  
}
</script>

十八、Vue3中如何获取元素节点并在父组件中调用子组件方法

父组件
<template>
  <div ref="box">
    我是div
    <Child ref="testRef" />
    <div @click="handleTest">调用子组件方法</div>
  </div>
</template>

<script setup lang="ts">
  import { onMounted, ref } from 'vue';
  import Child from './component/Child.vue';
  let box = ref<HTMLElement>();
  //此处是获取子组件dom  变量名testRef必须与Child组件上绑定的ref的值相同,不然获不到子组件的dom
  let testRef = ref<InstanceType<typeof Child>>();
  const handleTest = async () => {
    testRef.value?.initData();
  };
  onMounted(() => {
    console.log('testRef', testRef.value);
    console.log('onMounted', box.value);
  });
</script>

子组件
在 script-setup 模式下,如果要调用子组件的数据,需要先在子组件显式的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose API 来完成。
<template>
  <div>
    <h1>这是一个子组件</h1>
  </div>
</template>

<script setup lang="ts">
  // 第一步:子组件中声明方法
  const initData = async () => {
    console.log('父组件中调用了该方法');
  };
  // 第二步 重要 :使用 defineExpose 声明父组件要调用的方法
  defineExpose({
    initData,
  });
</script>

十九、readonly

  • readonly:用于创建一个只读数据,并且是递归只读
    通过下面案例可以发现,当点击按钮后修改数据是无效的。
<template>
    <div>
      <p>{{state.name}}</p>
      <p>{{state.attr.age}}</p>
      <p>{{state.attr.height}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>

import {readonly} from "vue"
export default {
  name: 'App',
  setup(){
    let state=readonly({name:"guo",attr:{age:18,height:1.88}})
    function myFn(){
      state.name="张三"
      state.attr.age=18
      state.attr.height=1.80
      console.log(state)
    }
    return {state,myFn}
  }
}
</script>

二十、shallowReadonly

  • 用于创建一个只读数据,但是不是递归读的
<template>
    <div>
      <p>{{state.name}}</p>
      <p>{{state.attr.age}}</p>
      <p>{{state.attr.height}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>

import {readonly,shallowReadonly} from "vue"
export default {
  name: 'App',
  setup(){
    let state=shallowReadonly({name:"guo",attr:{age:18,height:1.88}})
    function myFn(){
      state.name="张三"
      state.attr.age=666
      state.attr.height=111
      console.log(state)
    }
    return {state,myFn}
  }
}
</script>

二十一、isReadonly

  • 检查对象是否是由readonlyshallowReadonly创建的只读数据,返回ture为真。
<template>
    <div>
      <p>{{state.name}}</p>
      <p>{{state.attr.age}}</p>
      <p>{{state.attr.height}}</p>
      <button @click="myFn">按钮</button>
    </div>
</template>
<script>

import {readonly,shallowReadonly,isReadonly} from "vue"
export default {
  name: 'App',
  setup(){
    let state=shallowReadonly({name:"guo",attr:{age:18,height:1.88}})
    function myFn(){
      console.log(isReadonly(state))
    }
    return {state,myFn}
  }
}
</script>

二十二、子组件中调用父组件方法

父组件
<template>
  <div ref="box"
    >我是div
    <Child ref="testRef" @handleSon="handleSon" />
  </div>
</template>

<script setup lang="ts">
  import Child from './component/Child.vue';
  const handleSon = () => {
    console.log('这是子组件调用了父级的方法');
  };
</script>

子组件
<template>
  <div>
    <h1>这是一个子组件</h1>
    <div @click="handleFather">调用父组件方法</div>
  </div>
</template>

<script setup lang="ts">
  import { defineEmits } from 'vue';
  const emit = defineEmits(['handleSon']);
  const handleFather = () => {
    emit('handleSon');
  };
</script>

二十三、插槽使用

父组件
<template>
  <div ref="box">
    我是父亲
    <!-- 子组件 -->
    <Child ref="testRef">
      <!-- 匿名插槽 -->
      <p>这是匿名插槽的内容</p>
      <!-- 匿名插槽 -->

      <!-- 具名插槽 -->
      <template #msg>
        <p>这是具名插槽</p>
      </template>
      <!-- 具名插槽 -->
    </Child>
    <!-- 子组件 -->
  </div>
</template>

<script setup lang="ts">
  import Child from './component/Child.vue';
</script>
子组件
<template>
  <div class="Child">
    <h1>我是儿子</h1>

    <!-- 匿名插槽 -->
    <slot></slot>
    <!-- 匿名插槽 -->

    <!-- 具名插槽 -->
    <slot name="msg"></slot>
    <!-- 具名插槽 -->
  </div>
</template>

<script setup lang="ts"></script>

二十三、顶级 await 的支持

在 script-setup 模式下,不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup 。
<script setup lang="ts">
const res = await fetch(`https://example.com/api/foo`)
const json = await res.json()
console.log(json)
</script>

它转换成标准组件的写法就是:

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  async setup() {
    const res = await fetch(`https://example.com/api/foo`)
    const json = await res.json()
    console.log(json)

    return {
      json,
    }
  },
})
</script>

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值