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带来了哪些改变呢?
Vue的发布版本:Alpha - Beta - RC - 正式
所以还会经历RC版本才会有正式版可用,2020年估计不太可能会出了,不过也不用着急,正式版相比于beta主要是开发配套基础功能,比如脚手架、vue-router、以及生态插件等;
不过对于我们开发者来讲,最关心的还是它的语法,这块变动还是比较大的。下面来跟着项目看一下到底有哪些改动。
"一堆东西丢在setup里,我还不如直接用react"
"代码结构还没有2.x版本清晰,更不好维护了"
"不就是抄react么"
个人认为吐槽Vue3.0的原因主要有两点
习惯了基于选项的开发方式,但是代码只是看上去更整洁。
关于抄袭react。首先如果从原理的角度对于关于react hook和vue hook会发现其实还是有很大区别。 当然不得不承认Composition-Api灵感来源于react hooks,但是也谈不上抄不抄的,开发都提倡开源精神,优秀的框架也不是一开始就能赢得所有人的青睐,也是一次一次版本的迭代中逐渐更好的。使用框架的目的不就是为了广发开发人员么,何必非得搞门派之争。
npm install -g @vue/cli //升级cli
vue create vue3.0-test // 创建项目,其实这里还是用的vue2.x
npm i @vue/composition-api -S
// mian.js import Vue from 'vue' import VueCompositionApi from '@vue/composition-api' Vue.use(VueCompositionApi) 复制代码
2.x的API可正常使用
执行时间
setup() 是 Vue3.0 中引入的一个新的组件选项,这是一个组件的入口,处于2.0的beforeCreate 和 Created 之间,setup 返回的是一个对象,里面的所有被返回的属性值,都会被合并到 Vue2.0 的 render 渲染函数里面,支持返回 JSX 代码片段。
接收 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 发生变动时它也会被框架内部同步更新。但对于用户代码来说,它是不可修改的(会导致警告)。
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() 函数调用的返回值是一个对象,这个对象上只包含一个 .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 它主要是处理你的对象让它经过 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 才能访问到里面的值,写法上要注意的地方相对更多一些。
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), } } } 复制代码
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与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> 复制代码
没有了methods选项,那事件处理该怎么调用方法?
setup () { const data = reactive({ showList: 1, showListAdd: 0, }) function getShowList () { data.showListAdd = data.showList++ } function getMethods () { getShowList() } return { ...toRefs(data), getShowList, getMethods } } 复制代码
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 之间的映射关系: (生命周期函数需要按需引入)
来举栗子感受一下:跟踪鼠标的位置
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的开发方式更灵活了,更上“档次”了,有木有~,期待正式版早点发布
最后,文章也只是我个人在学习过程中的一点心得,欢迎大家一起交流经验。如果文章对你有帮助的话,麻烦各位小伙伴动动小手点个赞吧 ~