Vue3.0尝鲜

前言


4月21日,尤大大在B站分享了Vue.js3.0 Beta的最新进展,从2019年6月份的第一次发布的Vue3.0 Function-based API RFC,再到2019年10月份的Vue.js3.0 pre-alpha,经历一次次的更新,如果你还没有对3.0的更新内容有所了解,那么如何升职加薪,迎娶白富美,走上人生巅峰。

对于有理想有追求的小伙伴不要慌,认真读完这篇文章,相信你对于Vue3.0不再迷茫。废话不多说,手摸手带你开始学习!

设计目标


作为Vue这种全球热门的前端框架,每一次大版本的更新都会对开发人员带来巨大的影响,如果没有足够的“好处”,很难让已经习惯了Ctrl CV的同学们去接受。那么Vue3.0带来了哪些改变呢?

亮点

  • 更快
    1. 基于 Proxy 的变动侦测,性能整体优于 getter / setter
    2. Virtual DOM 重构
    3. 编译模板的优化
    4. 更高效的组件初始化
    5. update性能提高1.3~2倍
    6. SSR速度提高了2~3倍
  • 更小:Tree shaking support,可以将未使用模块“剪辑”,仅打包需要的,并不像2.x提供了所有的API,
  • Composition API可以实现更灵活且无副作用的复用代码。在2.x实现逻辑复用最常用的是 Mixins,然而存在两个问题:数据来源不清晰、命名空间容易冲突。那Composition API好在哪,后面会以实际代码介绍
  • 加强TypeScript支持:Vue3.0的源码基本都是由TS编写,Vue 3 + TypeScript 插件正在开发,有类型检查,自动补全等功能。所以还是推荐大家在项目开发过程中使用ts

关于正式版

Vue的发布版本:Alpha - Beta - RC - 正式

所以还会经历RC版本才会有正式版可用,2020年估计不太可能会出了,不过也不用着急,正式版相比于beta主要是开发配套基础功能,比如脚手架、vue-router、以及生态插件等;

不过对于我们开发者来讲,最关心的还是它的语法,这块变动还是比较大的。下面来跟着项目看一下到底有哪些改动。

吐槽

“一堆东西丢在setup里,我还不如直接用react”

“代码结构还没有2.x版本清晰,更不好维护了”

“不就是抄react么”

个人认为吐槽Vue3.0的原因主要有两点

  • 习惯了基于选项的开发方式,但是代码只是看上去更整洁。

    1. 一个复杂的组件往往需要同时处理多个不同的逻辑任务,每个逻辑任务所涉及的代码在选项 API 下是被分散在多个选项之中的。举例来说,从服务端抓取一份数据,可能需要用到 props, data(), mounted 和 watch。极端情况下,如果我们把一个应用中所有的逻辑任务都放在一个组件里,这个组件必然会变得庞大而难以维护,因为每个逻辑任务的代码都被选项切成了多个碎片分散在各处。
    2. 对比之下,基于函数的 API 让我们可以把每个逻辑任务的代码都整理到一个对应的函数中。当我们发现一个组件变得过大时,我们会将它切分成多个更小的组件;同样地,如果一个组件的 setup() 函数变得很复杂,我们可以将它切分成多个更小的函数。而如果是基于选项,则无法做到这样的切分,因为用 mixin 只会让事情变得更糟糕。
  • 关于抄袭react。首先如果从原理的角度对于关于react hook和vue hook会发现其实还是有很大区别。
    当然不得不承认Composition-Api灵感来源于react hooks,但是也谈不上抄不抄的,开发都提倡开源精神,优秀的框架也不是一开始就能赢得所有人的青睐,也是一次一次版本的迭代中逐渐更好的。使用框架的目的不就是为了广发开发人员么,何必非得搞门派之争。

基于 Composition Api 学习Vue3.0


创建项目

  1. npm install -g @vue/cli //升级cli
  2. vue create vue3.0-test // 创建项目,其实这里还是用的vue2.x
  3. npm i @vue/composition-api -S
  4. // mian.js
    import Vue from 'vue'
    import VueCompositionApi from '@vue/composition-api'
    Vue.use(VueCompositionApi)
    

2.x的API可正常使用

Composition-API

setup()函数

  1. 执行时间

    setup() 是 Vue3.0 中引入的一个新的组件选项,这是一个组件的入口,处于2.0的beforeCreate 和 Created 之间,setup 返回的是一个对象,里面的所有被返回的属性值,都会被合并到 Vue2.0 的 render 渲染函数里面,支持返回 JSX 代码片段。

  2. 接收 props 数据

props: {
    msg: String
},
setup (props) {
    console.log(props.msg)
    const data = reactive({
      showList: 1
    })
    console.log(data.showList) // 1
    return {
        showList
    }
}

在 setup() 里的方法不能通过 this 来访问实例上的数据,而是通过直接读取 data 来访问。 这里传进来的 props 对象是响应式的 —— 它可以被当作数据源去观测,当后续 props 发生变动时它也会被框架内部同步更新。但对于用户代码来说,它是不可修改的(会导致警告)。

  1. context

    setup 函数的第二个形参是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 vue 2.x 中需要通过 this 才能访问到,在 vue 3.x 中,它们的访问方式如下:

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

ref

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

<template>
    <div>{{showRef}}</div>
    <div>{{name}}</div>
</template>
// 注意每个用到的api都要引入
import { ref } from '@vue/composition-api'
export default {
  name: 'Home',
  setup () {
    const showRef = ref('test')
    //要访问 ref()创建出来的响应式数据对象的值,必须通过 .value 属性才可以
    console.log(showRef.value) // test
    console.log(name.value) // error 
    return {
      showRef,
      name: ref('zs')
    }
  }
}

reactive

reactive 它主要是处理你的对象让它经过 Proxy 的加工变为一个响应式的对象,类似于 Vue2.0 版本的 data 属性,需要注意的是加工后的对象跟原对象是不相等的,并且加工后的对象属于深度克隆的对象。

<template>
    <div>{{showList}}--{{showData2}}</div>
</template>
// 注意每个用到的api都要引入
import { toRefs, reactive } from '@vue/composition-api'
export default {
  name: 'Home',
  setup () {
    const data = reactive({
      showData1: 1,
      showData2: 2
      })
    })
    return {
      ...toRefs(data)
    }
  }
}

ref和reactive都能创建响应式数据,那有什么区别呢?
ref()单独地为某个数据提供响应式能力;而 reactive()给一整个对象赋予响应式能力。

但是在具体的用法上,通过 reactive() 包装的对象会有一个坑。如果想要保持对象内容的响应式能力,在 return 的时候必须把整个 reactive() 对象返回出去,同时在引用的时候也必须对整个对象进行引用而无法解构,否则这个对象内容的响应式能力将会丢失。

“对象的特性”是赋予给整个“对象”的,它里面的内容如果也想要拥有这部分特性,只能和这个对象捆绑在一块,而不能单独拎出来。

如果无法使用解构取出 reactive() 对象的值,每次都需要通过 . 操作符访问它里面的属性会是非常麻烦的,所以官方提供了 toRefs() 函数来为我们填好这个坑。只要使用 toRefs() 把 reactive() 对象包装一下,就能够通过解构单独使用它里面的内容了,而此时的内容也依然维持着响应式的特性。

对于我个人来说,会更倾向于使用 reactive() 搭配 toRefs() 来使用,因为经过 ref() 封装的数据必须通过 .value 才能访问到里面的值,写法上要注意的地方相对更多一些。

computed

  1. 创建只读计算属性
import { toRefs, reactive, computed } from '@vue/composition-api'

export default {
  name: 'Home',
  setup () {
    const data = reactive({
      showList: 1,
      onShowList: computed(() => {
        return data.showList + 1
      })
    })
    console.log(data.onShowList) // 2
    data.onShowList++ // error
    return {
      ...toRefs(data),
    }
  }
}
  1. 创建可读可写的计算属性
setup () {
    const data = reactive({
      showList: 1,
      onShowList: computed({
        // read
        get: () => data.showList + 1,
        // write
        set: val => {
          data.showList = val - 2
        }
      })
    })
    触发set函数
    data.onShowList = 10
    console.log(data.onShowList) // 10-2+1=9
    return {
      ...toRefs(data)
    }
  }

watchEffect

watchEffect与2.x watch类似,但是不需要分离监视的数据源和副作用回调。

import { toRefs, reactive, watchEffect } from '@vue/composition-api'

export default {
  name: 'Home',
  setup () {
    const data = reactive({
      showList: 1,
      watchShowList: 0,
    })
    watchEffect(() => {
      data.watchShowList = data.showListAdd
    })
    return {
      ...toRefs(data),
    }
  }
}
</script>

function

没有了methods选项,那事件处理该怎么调用方法?

setup () {
    const data = reactive({
      showList: 1,
      showListAdd: 0,
    })
    function getShowList () {
      data.showListAdd = data.showList++
    }
    function getMethods () {
      getShowList()
    }
    return {
      ...toRefs(data),
      getShowList,
      getMethods
    }
  }

LifeCycle Hooks

import { onMounted, onUpdated, onUnmounted } from "@vue/composition-api";
export default {
  setup() {
  const data = reactive({
      showList: 1
    })
    onMounted(() => {
      console.log('mounted!')
    });
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
    return {
      ...toRefs(data)
    };
  }
};

vue 2.x 的生命周期函数与新版 Composition API 之间的映射关系:
(生命周期函数需要按需引入)

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

逻辑复用

来举栗子感受一下:跟踪鼠标的位置

import { ref, onMounted, onUnmounted } from 'vue'

export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}
import { useMousePosition } from './mouse'

export default {
  setup() {
    const { x, y } = useMousePosition()
    // other logic...
    return { x, y }
  }
}

Vue2.x的逻辑复用方式:

  • 渲染上下文中公开的属性的来源不清楚。例如,当使用多个mixin读取组件的模板时,可能很难确定从哪个mixin注入了特定的属性。

  • 命名空间冲突。Mixins可能会在属性和方法名称上发生冲突,而HOC可能会在预期的prop名称上发生冲突。

  • 性能。HOC和无渲染组件需要额外的有状态组件实例,这会降低性能。

相比之下,使用Composition API:

  • 暴露给模板的属性具有明确的来源,因为它们是从合成函数返回的值。

  • 合成函数返回的值可以任意命名,因此不会发生名称空间冲突。

  • 没有创建仅用于逻辑重用的不必要的组件实例。

有兴趣详细了解的小伙伴可以移步vue-composition-api

总结


总的来说,基于api的开发方式更灵活了,更上“档次”了,有木有~,期待正式版早点发布

最后,文章也只是我个人在学习过程中的一点心得,欢迎大家一起交流经验。如果文章对你有帮助的话,麻烦各位小伙伴动动小手点个赞吧 ~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值