阅读 892

VUE 3.0 API 尝鲜体验

一、体验方式

  • 可以下载@vue/composition-api包,在main.js中引入,就可以在vue2的项目中同时使用vue3的api啦。
  • GitHub地址:github.com/vuejs/compo…
import VueCompositionApi from '@vue/composition-api';
Vue.use(VueCompositionApi);
复制代码
  • 但是这个里面的响应式不是用Proxy实现的,是用Object.defineProperty实现的,如果想体验proxy的,可以下载vue-next源码,仓库地址:github.com/vuejs/vue-n…
  • clone下来,install 依赖后,build一下,然后run dev 就可以在/packages/vue/dist目录里找到编译好的vue,引入html后可以直接使用,下面是demo,感受一下新的api.
  • demo.html
<script src="../../dist/vue.global.js"></script>
<!--  模版 -->
<script type="text/x-template" id="demo-template">
    <h1 ref="refYear">year:{{year}}</h1>
    <h1>list:{{list}}</h1>
    <h1>age:{{age}}</h1>
    <h1>father:{{fatherAge}}</h1>
    <h1>momAge:{{momAge}}</h1>
    <h1>w1:{{}}</h1>
    <button @click="changeYear">改变year</button>
</script>
<script>
    const { reactive, computed, toRefs, onMounted, ref, watch } = Vue
    const subApp = {
        name: 'subApp',
        template: '#demo-template',
        props: {
            model: Object
        },
        setup(props) {
            let info = reactive({
                year: 2018,
                weather: 'sunny',
                list: [13, 16, 73]
            })
            let age = ref(23);
            let fatherAge = computed(() => age.value + 30)
            let momAge = ref(10);
            let w1 = ref(1);
            let w2 = ref(2)
            // watch 一个ref
            watch(fatherAge, (old, newVal) => {
                momAge.value = old - 3
            })
            // watch一个响应式对象
            watch(() => info.year, (year, preYear) => {
                console.log('year:' + year);
                console.log('preYear:' + preYear);
            })
            // watch多个数据源
            watch([w1, w2], ([w1, w2], [preW1, preW2]) => {
                console.log('w1:' + w1);
            })
            function changeYear() {
                info.year++;
                info.list[1]++;
                age.value++;
                w1.value++;
            }
            onMounted(() => {
                console.log(this);
            })
            return {
                ...toRefs(info),
                age,
                fatherAge,
                momAge,
                changeYear
            }
        }
    }
</script>
<!-- 父组件 -->
<div id="app">
    <sub-app :model="model"></sub-app>
</div>
<script>
    const App = {
        components: {
            subApp,
        },
        data: {
            model: {
                year: 2019,
                weather: 'sunny'
            }
        }
    }
    Vue.createApp().mount(App, '#app')
</script>


<style>
    body {
        font-size: 20px;
        color: #000;
    }

    #app {
        font-size: 20px;
        color: #000;
    }
</style>
复制代码

二、都有哪些新API,都怎么用

1.第一个也是特别重要的一个,setup()函数

打印结果

  • setup函数是vue3中的新选项,为新的api,也叫composition API提供了统一的入口
  • setup和data很像,都会返回一个对象暴露给模版,要注意,与vue2不同的是,所有方法,对象,computed的返回值都需要在setup中返回,才能被模版访问到。
  • 这个方法接收两个参数,props和context,并且顺序固定,顺序固定的原因是因为官方认为props的使用场景更多一些,附议!一个都认识,是接收父组件的值,第二个是上下文,用它替代this,也就是说在setup()函数中,可以访问this,但是不需要再访问this,打印的this长这样,访问到的是vue直接暴露出来的所有api!

  • setup函数的调用时机和data基本一致,都是在created前,完整的生命周期长这样,也就是说在vue3里,我们可以不用写created了。

  • beforeCreate -> use setup()
  • created -> use setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted

2.响应式API - reactive & ref

  • 在vue3里,我们想要使数据响应式,只需把数据放到reactive函数里, 如果是基本数据类型,需要用ref进行包装。

并且在vue3中,我们需要引入我们用到的api,这是vue3对代码的压缩优化,没有使用的api的相关代码最终打包时会移除掉。另外,基于函数API所写的代码可以更好的压缩,所有的函数名和setup函数体内部的变量名都可以压缩。但是对象和class的属性与方法名不可以。

  • 那么ref和reactive到底应该用哪个呢?官方给了解释,习惯第一种写法的需要用ref,习惯第二种写法的则只需要直接用reactive。

  • 还有一个问题, 就是当我们想要对响应式对象进行解构的时候,直接解构或者扩展会使响应式对象引用丢失,就丧失了响应式效果。官方提供了一个方法可以解决,toRefs,经过toRefs包装后,依然是响应式

  • 这里看看toRefs如何做到的,通过遍历对象,将属性都转换成ref,解构出来还是ref,从而实现了解构后依然响应。
    export function toRefs(object) {
        const ret = {}
        for (const key in object) {
            ret[key] = toProxyRef(object, key)
        }
        return ret
    }

    function toProxyRef(object, key) {
        const v = {
            _isRef: true,
            get value() {
                return object[key]
            },
            set value(newVal) {
                object[key] = newVal
            }
        }
        return v;
    }
复制代码

3.computed & watch

  • computed的变化不大,效果也和vue2一样,只是写法上有些变化,从options变成了基于函数的方式。

从ppt上截的图,看着好大😱

  • watch的变化比较大,watch也实现了观察数据变化而执行相关逻辑 watch() 接收的第一个参数被称作 “数据源”,它可以是:
  1. 一个返回任意值的函数
  2. 一个包装对象
  3. 一个包含上述两种数据源的数组
  4. watch() 的回调会在创建时就执行一次。类似 2.x watcher 的 immediate: true 选项
  5. watch()的回调会在dom更新后调用,可以配置flus属性进行改变,也支持lazy等配置项
 let age = ref(23);
 let fatherAge = computed(() => age.value + 30)
 let momAge = ref(10);
watch(fatherAge, (old, newVal) => {
     momAge.value = old - 3
})
复制代码

还可以同时监测多个数据源,只要其中有一个数据发生变化,便触发回调

watch(([w1, w2]), ([w1, w2], [preW1, preW2]) => {
   console.log("TCL: setup -> w1", w1)
   console.log("TCL: setup -> w2", w2)
   console.log("TCL: setup -> preW1", preW1)
   console.log("TCL: setup -> preW2", preW2)
})
复制代码

4.其他

当我们想获取虚拟节点时,vue3里也需要用ref获取,像这样,获取到的值就是子组件了,等价于vue2的this.$refs.perRoot

<template>
    <test-ref ref="perRoot"></test-ref>
</template>
setup(){
  const perRoot = ref(null);
  onMounted(() => {
    console.log(perRoot.value);
  });
  return {
      refRoot
  }
}
复制代码

5. vue3特性总结

  • 合成(Composition)API 是 Vue3的特色之处,这是一种全新的逻辑复用和代码组织方法。
  • API特色方面

我们目前使用的是options API,这种基于选项的方式,并不是有效的js代码,需要vue编译成可用的代码,我们也无法使用ts的类型检查功能,vue3的基于函数的API,注意,不是函数式,是基于函数的API,天生对TS友好。

  • 代码复用方面

composition API,可以灵活的组合逻辑,达到模块业务分离成独立函数,其中同时兼具vue的各类响应式效果,比minxins要好太多。

  • 压缩能力

基于函数的API可以进行单独引入,并且没有使用的API不会打包,从而达到更小的体积。

  • 实施方案

使用新的API感觉需要一些规范,因为他不像vue2中的options,一一列出,比较清晰,而vue3更容易写出面条代码。

官方提供两种升级方案,

  1. 兼容版本:同时支持新的API和2.x的所有options;
  2. 标准版本:只支持新的API和部分2.x的options。

项目中同时引入vue2和vue3会导致项目体积变得很大,就像当初angular的升级,vue3的变化可谓不小,这样兼容,得不偿失。 更建议在独立的新项目中才使用vue3。

看源码系列需要了解的几个es6API

  • Proxy()

Vue3使用了es6的api,代替了Object.defineProperty()

const target = {
  a: 1
}
const handers = {
  get(target, key) {
    // 进行取值时会触发
    track() // 依赖收集
  },
  set(target,key) {
    // 进行赋值时会触发
    trigger() // 依赖更新
  },
  ....
}
const observed = new Proxy(target, handers)
复制代码
  • WeakMap()

vue3使用了这种数据类型,是因为WeakMap数据类型支持弱引用,在vue做track的过程中,会收集依赖,并进行存储,用weakmap进行存储,可以避免无法垃圾回收。

  • Reflect()

简单地讲Reflect()可以代替try catch,它的用法和proxy一致

老写法
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // failure
}
新写法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}
复制代码