Vue3.0 的深入学习

本贴最后更新于 1412 天前,其中的信息可能已经渤澥桑田

Vue3.0 来了

2020 年 09 月 18 日在万众期待中 Vue3.0 正式版发布于 git 上并被命名为‘One Piece’。这也许在这个并不平凡的一年里不算一件大事,但是对于我们广大的前端开发者开讲是一个轰动的事情。Vue3.0 历经了 2 年多的开发工作,终于带着全新的特性在万众期待中来了。

Vue3.0 学习

Vue3.0 正式发布之后笔者也和大多数热爱前端的开发者一样迅速的上手了对 Vue3.0 的采坑之旅(全英文当真难啃啊)。

上手准备

安装 Vite

vite 是伴随这 Vue3.0 诞生的单文件组件的非打包开发服务器用来进行 3.0 的编译

安装 vite 命令

//yarn安装
yarn global add create-vite-app
//npm 全局安装
npm i -g create-vite-app

创建项目

完整命令:create-vite-app 'xxx项目名'
缩写命令:cva 'xxx项目名'

Vue3.0 中的新特性

Vue3.0 虽然进行很大程度的重构但是还是保留了 Vue 的友好行。Vue3.0 可以完美兼容 2.0,就是说开发者可以用 2.0 的写法继续在 3.0 中进行使用并且完美运行。但是并不建议这么做,因为 3.0 中一些写的语法使用起来是真的特别方便使用。

Vue3.0 的设计思想

Vue3.0 设计模式与 2.0 不同采用的是多模块架构思想使得项目中引用 Vue 框架时耦合降低,不必要强依赖于 Vue。比如在项目中使用 Vue3.0 时可以按需引用使用的功能模块不再整个引用 Vue 整个的框架,项目打包时只会打包相应的功能模块应用体积会大大降低。若需要进行其他平台的适配、基于 Vue3.0 进行个性化的难度也大大降低,只需要重写相应的更新模块即可。

创建应用程序方式改变

Veu3.0 中应用程序实例创建的方式也进行了更改,通过使用为动态函数而不是静态函数进行创建应用程序实例。这样做好处为:

  1. 创建程序实例时可以实现程序实例之间的相互独立,互不干扰。
  2. 可以通过摇树算法 treeshaking 进行打包优化把未使用的模块不进行打包操作减小程序体积。
  3. 拥抱微前端

setup()函数

写法如下:

setup(porps, context) {
    const formModel = ref({
      name: "zhangsan",
      password: "666",
    });
//需要暴露出去的值再此return出去
    return {
      formModel,
    };
  },

此方法可以接收两个参数 props 和 context

props 中传递的是一个响应式的值可以通过 toRefs 进行结构拿到从上层传递下来的值,但是不能直接进行结构会导致 props 中的属性失去响应式。

context 中传递的是上下文在这里可以找到 attrs 和 slots。

此方法是 Vue3.0 新特性中特别重要的一个特性它的诞生解决了 2.0 中许多遗留的问题。

  1. setup()的出现使得代码的可维护性、复用性、可读性都大大提升了
  2. setup()的出现可以替代掉 mixin()函数,解决掉了 mixin()可能存在命名重复的隐患
  3. 消除 this,拥抱 ts
  4. setup 中的数据优先级高与 data()中 Vue3.0 中获取某个值首先会从 setupDate 中进行查找。
  5. 更小的性能消耗,在 2.0 中 data 中所用的定义的数据都会进行响应式处理不考虑使用者是否需要使用相应式导致性能消耗较高。在 3.0 中是通过 ref()和 reactive()进行按需响应式处理。不仅把选择权交给了开发者,还大大降低了性能消耗。

v-model 的改变

总所周知在 Vue2.0 中 v-model 只能进行动态绑定 value 值的变化,但是在 Vue3.0 中 v-mode 可以同时绑定多个不同值的写法为 v-model:xxx='变量名',子组件需要更改绑定的值时需要使用 setup 函数中的第二个参数进行触发更改方法,写法为 context.emit('update:xxx', false)

示例:

<zuiMengDialog v-model:visible="x" :closeOnClickOverlay="false" :save="f1" :cancel="f2"  >
        <template v-slot:content>
            <strong>hi word</strong>
            <div>你好啊世界</div>
        </template>
        <template v-slot:title>
           这个是一个弹出框
        </template>
         <template v-slot:footer>
         <zuiMengButton theme="button" level="main" @click="toggle">确认</zuiMengButton>
         <zuiMengButton @click="toggle" >取消</zuiMengButton>
        </template>
    </zuiMengDialog>
//子组件中更改父组件绑定值方法
<script lang="ts" setup='props,context' >
import { SetupContext } from 'vue';
import {zuiMengButton} from './index'
    export const close = () => {
           //visible为需要更改的值名字
            context.emit('update:visible', false)
        }

</script>

ref()和 reactive()

这两个方法都是 Vue3.0 中暴露出的进行响应式绑定的函数。但是两者不同点在于:

ref(null)一般处理是基础数据类型生成的响应式对象获取值或者更改值需要使用 value 才能获取到对应的值,不仅如此 Vue3.0 取消了 this.$refs 进行组件的绑定,而是通过 ref()绑定组件实例进行相应操作。

reactive(null)一般处理的是复杂数据类型,生成的响应式对象获取值则可以直接使用和修改。

setup(props, context) {
   let count=ref(1) 
   console.log(count.value)//1
   count.value=2
   console.log(count.value)//2
   let state = reactive({
      stateSon: 1,
      stateDaughter: 2
   })
     console.log(state .stateSon)//1
     state.stateSon=2
     console.log(state .stateSon)//2
   
   return {
       count,
       state
   }
}

template 模板部分

Vue3.0 中由于通过 Fragment()函数巧妙进行切片处理,在 template 中可以进行多个根节点的书写。在 2.0 中是无法进行实现,无疑是一个重大的突破。

watchEffect()函数

此函数是一个监听函数,在 props 中数据第一次初始化和更改时进行触发,类似于 React 中的 useEffect()函数。需要留意的是这个方法在初始化时会在 onMounted 周期之前调用一次,但是此时 dom 节点未挂载,若进行操作 dom 会报错。

此方法传递两个参数

第一个参数为回调函数(effect()副作用函数),effect()中还能接收一个参数 onInvalidate()清除副作用函数,主要作用就是在组件销毁是进行 effect()的清除操作。这个思想和 react 中很相似,当你使用监听之后需要在组件销毁是进行清除监听。

第二个参数为 options 他的作用是一个指针调度器用来控制 watchEffect 的触发事件是在数据的更改之前还是在数据的更改之后 flush 有两个值'post'(数据更新之后调用)和'pre'(数据更新之前调用)

setup(props, context) {
  watchEffect(() => {
              //回调函数主体
            }, {
               //options值
                flush: 'post'
            })
        })
}

watch() 监听器

watch 作用和之前的 2.0 中并没有两样但是书写方式却进行也改变

在 setup 函数中 watch 监听单个的写法

setup(props, context) {
   let count=ref(1)
   let state = reactive({
      stateSon: 1
   })
    //监听reactive类型
   watch(
   () => state.stateSon, //需要被监听的数据
   (newVlaue, oldVlaue) => { //参数1为新值, 参数2为旧值
       console.log(newVlaue) //数据变化后执行的回调函数
   }, 
   {lazy: false}//首次创建是否监听
)
   
   //监听ref类型
     watch(count1,(newVlaue, oldVlaue) => {//参数1为新值, 参数2为旧值
       console.log(newVlaue) //数据变化后执行的回调函数
     })
   
   return {
       count,
       state
   }
}

监听多个数据写法

setup(props, context) {
   let count=ref(1)
   let countTwo=ref(2)

   let state = reactive({
      stateSon: 1,
      stateDaughter: 2
   })
    //监听reactive类型
   watch(
[ () => state.stateSon, () => state.stateDaughter]//需要被监听的数据
  , 
   ([sonNewVlaue,aughterNewVlaue],[sonOldVlaue,aughterOldVlaue]) => { //参数1为新值, 参数2为旧值
       console.log('回调函数触发了') //数据变化后执行的回调函数
   }, 
   {lazy: false}//首次创建是否监听
)
   
   //监听ref类型
     watch([count,countTwo],([countNewVlaue,countTwoNewVlaue],[countOldVlaue,countTwoOldVlaue]) => {//参数1为新值, 参数2为旧值
       console.log('回调函数触发了') //数据变化后执行的回调函数
     })
   
   return {
       count,
       countTwo,
       state
   }
}

生命周期变化

在 Vue3.0 中生命周期也是按照 2.0 写法进行调用,在基础上又进行了优化可以按需进行导入在 setup()函数中进行调用

但是需要注意的是 created()和 beforeCreated()生命周期无法在 setup()函数中进行使用他们执行之间在 setup()函数之前

import { onBeforeMount, onMounted, updated } from '@vue/composition-api'
setup () {
   onBeforeMount(() => {
     console.log('onBeforeMount生命周期被调用了')
   })
   onMounted(() => {
     console.log('onMounted生命周期被调用了')
   })
   updated(() => {
     console.log('updated生命周期被调用了')
   }) 
}

响应式数据实现的改变

Vue2.0 是通过 Object.defineProperty 进行数据劫持给每个数据加上 set 和 get 方法同时在数据的 get()方法触发时进行更新函数依赖的收集,在数据的 set()触发时循环遍历调用收集起来的更新函数进行视图的更新来实现数据响应式
Vue3.0 中通过实现 Proxy(代理)和 Reflect(映射)来实现数据响应式,完美解决 Vue2.0 响应式一些弊端:如对象或者数组新增属性无法实现响应式需要手动进行添加、数组实现响应式需要额外进行处理、响应式数据过多性能问题(因为 Vue3.0 中实现响应式时进行了懒执行优化不管数据有多大只要用户不访问,就不会进行响应式处理)

computed()计算属性的变化

vue2.0 中计算属性是只读的不能进行更改,但是在 3.0 中 computed 可以进行值的修改

setup () {
      const count = ref(1)
      const computedDate= computed(() => count.value + 1) //不允许修改返回count的值+1
  
      const computedDateTwo= computed({
          get:() =>count.value + 666,
          set: (value) => count.value = value 
      })  
      // addCount2.value = 123   //赋值方法
  
     return {
         count,
         computedDate,
         computedDateTwo
     }
  }

Vue3.0 中移除的部分属性

this.$refs.xxx Vue3.0 移除了通过此方法进行组件实例的绑定但是新的写法就是通过 ref(null)进行绑定写法如下

<template>
  <demo> 常规使用 </demo>
  <div>
    <zuiMengForm :model="formModel" :rules="rules" ref="ceShiForm">
      <zuiMengFormItem lable="密码" @click="clickCeShi" prop="password">
        {{ formModel.password }}
      </zuiMengFormItem>
    </zuiMengForm>
  </div>
</template>

setup(){
const ceShiForm = ref(null);
const clickCeShi = () => {
      ceShiForm.value.validate();
    };

}

this.$emit

this.$on 这两个属性方法在 Vue 中被移除了 Vue3.0 中认为这个不是 vue 应该做的事情就直接移除了,想要在继续使用这两个方法需要进行第三方差劲进行使用

Vue3.0 中的优化策略

静态节点提升

vue3.0 进行编译时会将节点进行分类存储如果需要编译的是静态节点,就将节点存储到编译函数外,后续更新时直接使用,不需要再进行编译更新,大大节省了编译时间

补丁标记和动态属性记录

Vue3.0 中进行模板编译时遇到节点中需要动态编译元素,会根据元素类别不同,通过位运算进行标记。标记之后 diff 算法渲染时根据标记的不同进行不同的处理,减少部分不必要操作,大大提高的渲染性能。

缓存事件处理程序

编译器在编译回调函数时,会优先从缓存中进行查找,若找到则进行使用,查找不到则进行生成新的会的回调函数,同时存放到缓存中。方便下次更新时使用,避免每次重新创建大大减少了性能损耗

区块 block

对于一个父元素存在很多个子元素,编译器进行出处理时会将此父元素下所用的动态子元素查找出来,同时存放到以此父元素创建的 block 对象的动态子元素数组中。后面进行更新时 diff 算法会直接比较动态子元素数组中元素变化进行更新大大提升更新效率。

总结

Vue 在 2.0 的基础上进行了翻天覆地式的改变不仅将 2.0 中存在的缺点进行了优化,还完全向下兼容对之前版本的 Vue 的开发者特别友好,再加上 Vue3.0 中新的优化策略对资源的节约和性能的提升使 Vue3.0 更加强大更值得去使用开发项目,虽然美中不足的兼容性暂时未进行优化(Vue3.0 基本上已经放弃了 IE)但是不妨碍 Vue3.0 雄起,期待 Vue3.0 在以后大放异彩。

附:自己学习 Vue3.0 中的练手项目 zuimengUI

此次分享到此结束欢迎大家来相互学习交流

  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    265 引用 • 666 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...