vue3.0进阶基础学习

212 阅读10分钟

摘自尚硅谷的张天禺讲师的视频做的笔记

1. 变化

1.速度的提升

  • 打包大小减少41%
  • 初次渲染快55%,更新渲染快13%
  • 内存减少了54%
  • ...

2.源码升级

  • 使用proxy代替defineProperty 实现响应式
  • 重新虚拟DOM实现和Tree-shaking

3.拥抱typescript

4.新特性

  1. Composition API (组合API)
  • setup配置
  • ref与reactive
  • watch 与 watchEffect
  • provide 与inject
  • ....
  1. 新的内置组件

    • Fragement
    • Teleport
    • Suspense
  2. 其他改变

  • 新的生命周期
  • data选项必须声明是一个函数
  • 移除keycode支持作为v-on的修饰符,如: v-on.enter , enter已经不适用了
  • .....

2. 安装

这里注意node版本要大于18,我安装了nvm

1. 使用vite安装

vite -新一代前端构建工具

// 创建工程
npm init vite-app <project-name>
// 进入工程目录
cd <prject-name>
// 安装依赖
npm install 
// 运行
npm run dev

2. 使用vue-cli 创建

// 查看@vue/cli版本,必须在4.5.0以上的版本
vue -V 或者 vue --version 

// 安装或者升级版本
npm install -g @vue/cli 
// 创建
vue create vue_test
// 启动
cd vue_test
npm run serve

3. vue3 新特性讲解

1.main.js讲解

// 引入的不在是vue的构造函数,引入的是一个名为createApp的工厂函数,不需要在new了
import { createApp } from 'vue'
import App from './App.vue'
// 创建应用实例对象-app (类似于之前的vue2中的vm,但app比vm更’轻‘)
const app = createApp(App)
// 挂载
app.mount('#app')
  1. 页面 vue3的页面可以没有根标签

1. 拉开序幕的setup(函数)

  1. setup 是所有Composition API的表演的舞台

  2. 组件中用到的:数据、方法等,均要配置在setup中

  3. setup函数有2种返回值

    1. 若返回是是一个对象,则对象中的属性、方法在模板中可已直接使用(重点)
    2. 若返回的是渲染函数,则可以自定义渲染内容(了解)
需要先引入h
import {h} from 'vue'
setup(){
  return () => h('h1', 'sss')
}

2. ref 函数 响应式

  • 作用: 定义一个响应式的数据

  • 语法: const xxx = refs(initValue);

    • 创建一个包含响应式数据的引用对象(reference对象)RefImpl
    • js中操作数据: xxx.value
    • 模板中读取数据,不需要.value 直接: <div>{{xxx}}</div>
  • 备注:

    • 接收的数据可以是:基本类型、也可以是对象类型
    • 基本类型: 响应式依然是依靠Objetc.defineProperty 的get 与set 方法
    • 对应类型: 内部求助了vue3的新函数。reactive函数 具体是es6的Proxy实现

3. reactive 函数

  • 作用:定义一个对像类型的响应式数据
  • 语法:const 代理对象= reactive(源对象), 接收一个对象或者数组,返回一个 Proxy对象
  • reactive定义的响应式数据是深层次的
  • 内部基于Es6的Proxy实现,通过其代理对象操作源对象内部数据

4. Vue的响应式原理

vue2.0

  • 实现原理

    • 对象类型: 通过Object.defineProperty()对属性读取,修过拦截
    • 数组类型: 通过重写更新数组类实现拦截(对数组的变更方法进行了包裹)
Object.defineProperty(data, 'count', {
    get(){},
    set(){}
})
  • 存在问题:

    • 新增属性、删除属性,界面不会更新
    • 直接通过下标修过数组,界面不会自动更新

vue3.0

  • 实现原理

    • 通过Proxy(代理): 拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等
    • 通过Reflect(反射): 对被代理属性进行操作
let person = {
    name: '张三'
    age: 19,
}
// 捕获修改,响应式
cosnt p = new Proxy(person, {
    // 有人读取p的某个属性调用
    get(target,propName){
      console.log('有人读取了p身上的某个属性',target, propName)
      // return target[propName]
      return Reflect.get(target, propName)
     },
     // 有人修改p的某个属性,或者给p追加某个属性时调用
     set(target,propName, newValue){
       console.log('有人修改了p身上的某个属性',target, propName,我要去更新界面了)
       //target[propName] = newValue    
        Reflect.set(target,propName,value)
     },
      // 有人删除p的某个属性时调用
     deleteProperty(target, propName){
       console.log('有人删除了p身上的某个属性',target, propName,我要去更新界面了)
       //return  delete target[propName]
        return Reflect.deleteProperty(target, propName)
     }
})

5.reactive 与ref

  • 从定义数据的角度对比:

    • ref用来定义:基本数据类型
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型,它内部会通过reactive转为代理对象
  • 从原理角度对比:

    • ref通过Object.defineProperty()getset实现响应式(数据劫持)
    • reactive通过Proxy来实现响应式,并通过Reflect操作源对象内部的数据
  • 从使用角度对比:

    • ref定义数据: 操作数据需要.value,读取数据模板中直接读取不需要.value
    • reactive定义约读取数据,均不需要.value

6. setup的两个注意点

  • setup的执行时机

    beforeCreate之前执行一次,且此时this是undefined

  • setup的参数

    • props: 值为对象, 包含: 组件外部传递过来,且在组件内部声明接收了的属性

    • context: 上下文对象

      • attrs: 值为对象,包含: 组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
      • slots: 收到的插槽内容,相当于 this.$slots
      • emit: 分发自定义事件的函数,相当于this.$emit

7. computed属性

  • 与vue2.0中computerd配置功能一致
import { computed } from 'vue'
// 计算属性-么有考虑计算属性可能被修改的情况
  person.fullName = computed(() => {
    return person.name + '---' + person.job.type
  })
  // 计算属性-完整写法
  person.fullName = computed({
    get() {
      return person.name + '-' + person.job.type
    },
    set(value) {
      console.log('newValue', value)
      const nameArr = value.split('-');
      person.name = nameArr[0]
      person.job.type = nameArr[1]
    }
  })

8. watch 属性

  • 与vue2.种的配置功能一致

  • 两个‘坑’

    • 监听reactive定义的一个响应式数据的全部属性时:无法正确获取oldvalue、强制开启了深度监听无效
    • 监听的是raactive定义的某个属性也是对象时,deep有效
 //第一种情况: 监听ref所定义的一个响应式数据(监听的数据,回调函数,配置的参数)
    watch(sum, (newvalue, oldvalue) => {
      console.log('sum变了, newvalue,oldvalue', newvalue, oldvalue)
    }, { immediate: true })
    // 第二种情况:监听ref所定义的多个响应式数据
    watch([sum, msg], (newvalue, oldvalue) => {
      console.log('数据变了, newvalue,oldvalue', newvalue, oldvalue) //[],[]
    })
    // 情况三:监视reactive定义的一个响应式数据的全部属性,
    //1注意:此处无法正确获取oldvalue 2.注意:强制开启了深度监听无效
    watch(person, (newvalue, oldvalue) => {
      console.log('数据变了, newvalue,oldvalue', newvalue, oldvalue)
    })
    // 情况四:监听reactive定义的一个响应式数据的某个属性
    watch(() => person.name, (newvalue, oldvalue) => {
      console.log('数据变了, newvalue,oldvalue', newvalue, oldvalue)
    })
    // 情况五: 监听reactive定义的一个响应式数据的某些属性
    watch(() => [person.name, person.job], (newvalue, oldvalue) => {
      console.log('数据变了', newvalue, oldvalue)
    }, { deep: true })
    // 特殊情况,监听的是raactive定义的某个属性也是对象,deep有效
    watch(person.job, (newvalue, oldvalue) => {
      console.log('多层对象的数据, newvalue,oldvalue', newvalue, oldvalue)
    }, { deep: true })

9.watchEffect函数

  • watch的套路是:既要指明监听的属性,又要指明监听的回调

  • watchEffect的套路是: 不用指明监听哪个属性,监视的回调中用到哪个属性,就监视哪个属性

  • watchEffect有点像computed:

    • 但computed注重的是计算算出来的值(回调函数返回的值),所以必须要写返回值
    • 而wacthEffect更注重的是过程(回到函数的函数体),所以不用返回值
 watchEffect(() => {
      const x1 = person.name
      console.log('watchEffect所指定的回调执行了')
    })

10.vue3.的生命周期

  • 命名变了

    • beforeDestory ⇒ beforeUnmount
    • destoryed ⇒ unmounted
  • 也提供了Composition API形式的生命周期钩子

    • beforeCreate ⇒ setup()
    • created ⇒ setup()
    • beforeMount ⇒ onBeforeMount()
    • mounted⇒ onMounted
    • beforeUpdate ⇒ onBeforeUpdate
    • updated ⇒ onUpdated
    • beforeUnmount ⇒ onBeforeUnmount
    • unmounted ⇒ onUnmounted

11. 自定义hook函数

  • 什么是hook? -本质是一个函数,把setup中使用的Composition API进行了封装
  • 类似于vue2.中的mixin
  • 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂

toRef与toRefs

  • 作用: 创建一个ref对象,其value值指向另一个对象中的某个属性
  • 语法:const name = toRef('person', name) 参数(对象,value)
  • 应用:要将响应式对像中的某个属性单独提供给外部使用
  • 扩展:toRefstoRef功能一致,但可以批量创建多个ref对象,语法:toRef(person)
  • 与ref()区别,toRef是引用的,会影响原数据,ref()是复制,不会影响原数据

12. 其他Composition API

1. shallowReactive Vs shallowRef

  • shallowReactive 只考虑对象类型的第一层数据的响应式

  • shallowRef 支处理基本类型的响应式,不进行对象的响应式处理

  • 什么时候使用?

    • 如果是一个对象,结构比较深,但变化时只是外层属性变化 =⇒ shallowReactive
    • 如果一个对象数据,后续功能不会修改对象中的属性,而是生成新的属性累替换=⇒ shallowRef

2. readonly vs shallowReadonly

  • readonly 让一个响应式数据变成只读的(深只读)
  • shallowReadonly 让一个响应式数据变成只读(浅只读)
  • 应用场景: 不希望数据被修改时

3. toRaw 与 markRaw

  • toRaw

    • 作用:将一个由reactive 生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象。对这个普通对象的所有操作,不会引起页面更新
  • markRaw

    • 作用:标记一个对象,使其永远不会在成为响应式对象

    • 应用场景:

      1. 有些值不应被设置成响应式的,例如复杂的第三方类库axios等
      2. 当选人具有不可变数据源的大列表是,跳过响应式转换可以提高性能

4. customRef

  • 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制
自定义一个ref,延迟一段时间显示更改的数据
function myMref(value,delay){
  let timer
  return customRef((track,trigger) => {
    return {
       get(){
          console.log('有人读取了这个数据value')
           track() // 3通知vlaue追踪数据的变化(提前和get商量一下)
          return value
       }
       set(newValue){
           console.log('有人吧myRef的数据改了')
           clearTimeout(timer)
           timer= setTimeout(() => {
             value = newValue // 1数据改掉
             trigger() // 2通知value重新解析模板
           },delay) 
       }
    }
  })
}
let keyword = myRef('hello', 500)

5. provide(提供)与inject(注入)

  • 作用: 实现祖孙组件间通信

  • 套路:父组件有有个provide选项来提供数据,后代组件有个inject选项来开始使用这些数据

  • 具体写法:

      1.祖组件中:
    
setup(){
  ....
  let car = reactive({name:'奔驰'price:'10元'})
  provide('car', car)
   ...
}
 2. 孙组件中:
setup(props,context){
  ....
   const car = inject('car')
   return {car}
}

6. 响应式数据的判断

  • isRef: 检查一个值是否为一个ref对象
  • isReactive: 检查一个值是否是一个Reactive的响应式代理
  • isReadonly: 检查一个对象是否有readonly创建的只读代理
  • isProxy:检查一个对象是否由reactive或者readonly方法创建的代理

4. 新的组件

1. Fragment组件

  • 在vue2中: 组件必须有一个根标签
  • 在vue3中:组件可以么有根标签,内部会将多个标签包含在一个Fragment的虚拟元素中
  • 减少内存占用,减少标签层级

2.Teleport(传送)

  • 是一种能够将我们的组件html结构移动到指定位置的技术
<teleport to="body">
     <div v-if="isShow">
           <h3>我是一个弹框</h3>
     </div>
</teleport>

3. Suspense(异步组件)

等待异步组件时渲染的一些额外内容,让用户有更好的体验,类似于骨甲屏之类的

  • 使用步骤

    • 异步引入组件
import child from '../child' // 静态引入
import { defineAsyncComonent } from 'vue'
const child =  defineAsyncComonent(() => import('./child)) // 异步引
  • 使用Suspense包裹组件,并配置好default 与fallback插槽
<Suspense>
  // 正常要显示的
  <template v-slot:default><child /></template>
  // 网速不行的时候
  <template v-slot:fallback><h1>加载中。。。</h1></template>
</Suspense>

5. 其他改变

1. 全局API的转移

  • vue2.0有很多全局API和配置

例如: 注册全局组件、组册全局指令等

  • vue3.0对Api做出来调整

    • 将全局的Api,即Vue.xxx调整到应用实例app上
2.x全局api3.x实例api
Vue.config.xxxapp.config.xxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vur.prototypeapp.config.globalProperties

2. 其他改变

  • data始终被声明为一个函数
  • 过度类名的更改: