Vue3.x 新特性体验

发表于 2020-06-05 | 最后更新于 2021-12-20 | 开发

前言

去年国庆的时候就有了vue3.0的消息,最近3.0beta也发布了,看来vue3.0版本真是不远了。

芜桐也是从官方仓库扒下来了3.0的源码,尝试剖析一下。

仓库地址放在这里,有兴趣就扒下来看看吧。

vue-next仓库

核心的东西都在pakeages目录下,compiler 是编译相关的东西,runtime 是运行时相关的东西,然后最后有一个 vue的目录,就是整合了这些东西的目录,在pakeage.json下可以看到很多脚本,我们运行一下 npm run dev 就可以在vue目录下生成一个开发环境的dist目录,里边躺着一个vue.global.js的文件,这个就是3.0的vue打包后的文件了,你甚至可以开始拿这个进行一些自己的测试小项目的开发。  

体验新特性准备

  1. 使用vue-cli初始化一个vue2.x的项目

  2. 安装 composition-api

    npm i @vue/composition-api  -S
    

      # OR

      yarn add @vue/composition-api
    
  3. 在入口文件main.js 加入以下代码

      import VueCompositionApi from '@vue/composition-api'
      Vue.use(VueCompositionApi)
    
  4. 可以愉快地玩耍了

1.setup

setup 有着启动的意思。 setup 函数是新版本中提供的新属性。它为我们使用 Composition API 的新特性提供了统一的入口。

setup 函数会在 beforeCreate 之后、created 之前执行。

它可以有两个参数,props和context。props可以拿到父组件传递过来的数据,前提是要先定义接收的props,这和2.x是相同的,setup不再具有this了,所以,以前的许多在this上的属性,现在可以通过context获取

setup(props, context) {
  context.attrs
  context.slots
  context.parent
  context.root
  context.emit
  context.refs
}

2.ref

按照顺序应该先说reactive,但是芜桐感觉把ref写在reactive前面很有必要

vue3.x已经用proxy重写了响应式,所以新的响应式api就是ref和reactive

2.x存在数组更新检测和对象属性更新检测的问题,而且递归的算法十分的消耗内存,3.x已经从根本上解决了之前2.x的响应问题,

ref() 函数用来根据给定的值创建一个响应式的数据对象,ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性

  1. 例子
    <template>
      <div>
        <h1>vue3.0特性</h1>
        <hr />
        <div>foo ref : {{foo}}</div>
      </div>
    </template>
    
    import {reactive,toRefs,onMounted,computed,ref,isRef,watch} from "@vue/composition-api";
    export default {
      setup(props, ctx) {
        let foo = ref("bar");
        //返回值
        return {
          foo
        };
      }
    };
    

可以看到,在template上使用数据是不需要.value的,因为vue已经处理过了,但是这里还是有一个小坑,只要是在setup函数里,没有出去这个范围,取ref生成的值,必须使用.value,否则你只能拿到一个对象,也许以后vue会对这个地方进行改动(期待~)

3.reactive

reactive 函数,用来创建响应式的数据对象,其实就是创建了一堆的ref。

它接受一个普通的数据对象,然后返回一个响应式的数据对象,我通常把它命名为state。但是这个reactive也有一点小坑,就是模板想要使用的数据,必须在setup中return出去,因为要把很多的属性return出去,包括一些处理方法,计算属性,所以我们通常return state的时候更希望把它用扩展运算符解开,...state,这样就会产生一个问题,解开之后,这些状态就变成了静态的值,就不再是响应式的了,算是个小坑吧,后面的toRefs API 就是用来解决这个问题的。

  1. 例子
    <template>
      <div>
        <h1>vue3.0特性</h1>
        <hr />
        <div>name : {{name}}</div>
      </div>
    </template>
    
    import {reactive,toRefs,onMounted,computed,ref,isRef,watch} from "@vue/composition-api";
    export default {
      setup(props, ctx) {
         // 状态
         let state = reactive({
           name: "wutong"
         });
        //返回值
        return {
          ...toRefs(state)
        };
      }
    };
    

4.toRefs

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据。

在上面介绍reactive的时候已经提到,我们展开state的时候会有一些小问题,导致state解构之后变成了非响应数据,toRefs API 就可以让state中的每个属性重新被ref包一次,变为响应数据,nice~~

例子就看上边的吧。

5.isRef

由于ref的存在,ref把数据包装成了一个对象,而且vue对这个数据进行了处理,某些时候我们实际上需要.value才能拿到真正的数据,所以,isRef() 用来判断某个值是否为 ref() 创建出来的对象;应用场景:当需要展开某个可能为 ref() 创建出来的值的时候,例如:

const bar = isRef(foo) ? foo.value : foo

6.computed

2.x就有的API,3.x重写了。

computed() 用来创建计算属性,computed() 函数的返回值是一个 ref 的实例。

看来3.x的响应式跟这个ref是分不开的,proxy牛逼。

  1. 创建只读的计算属性
    const count = ref(1)
    // 根据 count 的值,创建一个响应式的计算属性 plusOne
    // 它会根据依赖的 ref 自动计算并返回一个新的 ref
    const plusOne = computed(() => count.value + 1)
    
  2. 创建可读可写的计算属性
    // 创建一个 ref 响应式数据
    const count = ref(1)
    // 创建一个 computed 计算属性
    const plusOne = computed({
      // 取值函数
      get: () => count.value + 1,
      // 赋值函数
      set: val => {
        count.value = val - 1
      }
    })
    

7.watch

也是2.x就有了。

watch() 函数用来监视某些数据项的变化,从而触发某些特定的副作用操作。

  1. 简单例子

    // 定义数据源
    const state = reactive({ count: 0 })
    // 监视 state.count 这个数据节点的变化
    watch(
      () => state.count,//ref 类型直接写就可以
      (count, prevCount) => {
        /* ... */
      }
    )
    

    第一个参数如果是ref,直接写就可以。也可以指定第三个参数 {lazy:true} 来阻止第一次的监视

  2. 监听多个

    const state = reactive({ count: 0, name: 'zs' })
    watch(
      [() => state.count, () => state.name], // Object.values(toRefs(state)),
      ([count, name], [prevCount, prevName]) => {
      //dosomething
      },
      {
        lazy: true // 在 watch 被创建的时候,不执行回调函数中的代码
      }
    )
    
  3. 清除监视 在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。 如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可。

例子:

// 创建监视,并得到 停止函数
const stop = watch(() => {
  /* ... */
})
// 调用停止函数,清除对应的监视
stop()
  1. 清除无效异步任务 有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个 cleanup registrator function 来执行清除的工作。这个清除函数会在如下情况下被调用:

  2. watch 被重复执行了  

  3. watch 被强制 stop 了 例子:(此示例来自liulongbin)

    <input type="text" v-model="keywords" />
    
    // 定义响应式数据 keywords
    const keywords = ref('')
    // 异步任务:打印用户输入的关键词
    const asyncPrint = val => {
      // 延时 1 秒后打印
      return setTimeout(() => {
        console.log(val)
      }, 1000)
    }
    // 定义 watch 监听
    watch(
      keywords,
      (keywords, prevKeywords, onCleanup) => {
        // 执行异步任务,并得到关闭异步任务的 timerId
        const timerId = asyncPrint(keywords)
        // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
        onCleanup(() => clearTimeout(timerId))
      },
      // watch 刚被创建的时候不执行
      { lazy: true }
    )
    // 把 template 中需要的数据 return 出去
    return {
      keywords
    }
    

8.生命周期

新版的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用。

按需导入有灵魂(hiahiahia~~)

例子:(一看就废~)

import { onMounted, onUpdated, onUnmounted } from '@vue/composition-api'
const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}

9. provide & inject

这难道不就是react中的........

写过react的都不用多bb

provide() 和 inject() 可以实现嵌套组件之间的数据传递。这两个函数只能在 setup() 函数中使用。父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。 例子太过冗长~由于篇幅原因,例子暂时省略,有空看看能补上吧

10.template refs

2.x的时候可以使用refs引用dom元素。 3.x它又回来了~~ 强大的ref~ 例子:(例子摘自liulongbin)

<template>
  <div>
    <h3 ref="h3Ref">TemplateRefOne</h3>
  </div>
</template>
<script>
import { ref, onMounted } from '@vue/composition-api'
 
export default {
  setup() {
    // 创建一个 DOM 引用
    const h3Ref = ref(null)
 
    // 在 DOM 首次加载完毕之后,才能获取到元素的引用
    onMounted(() => {
      // 为 dom 元素设置字体颜色
      // h3Ref.value 是原生DOM对象
      h3Ref.value.style.color = 'red'
    })
 
    // 把创建的引用 return 出去
    return {
      h3Ref
    }
  }
}
</script>

ref能引用也,页面元素,同样也能引用组件,创建一个组件的引用,甚至可以使用组件的方法,非常的amazing 啊,例子就不搞了,自己去实验一哈~

11.createComponent

  提供了类型推断,方便在结合 TypeScript 书写代码   以上就是本文所有内容了,感谢观看,vue3.0有可能存在重大更新,请留意更新时间和本文发布时间.