Vue 3.0 前瞻,体验 Vue Function API - 蓝蓝设计_UI设计公司

帝皇彩票官网

追求卓越一诺千金

蓝蓝设计,2011年成立,主创清华团队,专注软件和互联网ui设计开发。擅长企业信息化管理、监控、大数据软件UIUE咨询和设计开发服务。立足UI,好好学习,天天进步!


Vue 3.0 前瞻,体验 Vue Function API

2019-9-1 释然 前端及开发文章及欣赏


概述
Vue 2.x 及以前的高阶组件的组织形式或多或少都会面临一些问题,特别是在需要处理重复逻辑的项目中,一旦开发者组织项目结构组织得不好,组件代码极有可能被人诟病为“胶水代码”。而在 Vue 2.x 及之前的版本,解决此类问题的办法大致是下面的方案:

mixin
函数式组件
slots
笔者维护的项目也需要处理大量复用逻辑,在这之前,笔者一直尝试使用mixin的方式来实现组件的复用。有些问题也一直会对开发者和维护者造成困惑,如一个组件同时mixin多个组件,很难分清对应的属性或方法写在哪个mixin里。其次,mixin的命名空间冲突也可能造成问题。难以保证不同的mixin不用到同一个属性名。为此,官方团队提出函数式写法的意见征求稿,也就是RFC:Function-based component API。使用函数式的写法,可以做到更灵活地复用组件,开发者在组织高阶组件时,不必在组件组织上考虑复用,可以更好地把精力集中在功能本身的开发上。

注:本文只是笔者使用vue-function-api提前体验 Vue Function API ,而这个 API 只是 Vue 3.0 的 RFC,而并非与最终 Vue 3.x API 一致。发布后可能有不一致的地方。

在 Vue 2.x 中使用
要想提前在Vue 2.x中体验 Vue Function API ,需要引入vue-function-api,基本引入方式如下:

import Vue from 'vue';
import { plugin as VueFunctionApiPlugin } from 'vue-function-api';
 
Vue.use(VueFunctionApiPlugin);
基本组件示例
先来看一个基本的例子:

<template>
    <div>
        <span>count is {{ count }}</span>
        <span>plusOne is {{ plusOne }}</span>
        <button @click="increment">count++</button>
    </div>
</template>
 
<script>
import Vue from 'vue';
import { value, computed, watch, onMounted } from 'vue-function-api';
 
export default {
    setup(props, context) {
        // reactive state
        const count = value(0);
        // computed state
        const plusOne = computed(() => count.value + 1);
        // method
        const increment = () => {
            count.value++;
        };
        // watch
        watch(
            () => count.value * 2,
            val => {
                console.log(`count * 2 is ${val}`);
            }
        );
        // lifecycle
        onMounted(() => {
            console.log(`mounted`);
        });
        // expose bindings on render context
        return {
            count,
            plusOne,
            increment,
        };
    },
};
</script>
详解
setup
setup函数是Vue Function API 构建的函数式写法的主逻辑,当组件被创建时,就会被调用,函数接受两个参数,分别是父级组件传入的props和当前组件的上下文context。看下面这个例子,可以知道在context中可以获取到下列属性值

const MyComponent = {
    props: {
        name: String
    },
    setup(props, context) {
        console.log(props.name);
        // context.attrs
        // context.slots
        // context.refs
        // context.emit
        // context.parent
        // context.root
    }
}
value & state
value函数创建一个包装对象,它包含一个响应式属性value:



那么为何要使用value呢,因为在JavaScript中,基本类型并没有引用,为了保证属性是响应式的,只能借助包装对象来实现,这样做的好处是组件状态会以引用的方式保存下来,从而可以被在setup中调用的不同的模块的函数以参数的形式传递,既能复用逻辑,又能方便地实现响应式。

直接获取包装对象的值必须使用.value,但是,如果包装对象作为另一个响应式对象的属性,Vue内部会通过proxy来自动展开包装对象。同时,在模板渲染的上下文中,也会被自动展开。

import { state, value } from 'vue-function-api';
const MyComponent = {
    setup() {
        const count = value(0);
        const obj = state({
            count,
        });
        console.log(obj.count) // 作为另一个响应式对象的属性,会被自动展开
 
        obj.count++ // 作为另一个响应式对象的属性,会被自动展开
        count.value++ // 直接获取响应式对象,必须使用.value
 
        return {
            count,
        };
    },
    template: `<button @click="count++">{{ count }}</button>`,
};
如果某一个状态不需要在不同函数中被响应式修改,可以通过state创建响应式对象,这个state创建的响应式对象并不是包装对象,不需要使用.value来取值。

watch & computed
watch和computed的基本概念与 Vue 2.x 的watch和computed一致,watch可以用于追踪状态变化来执行一些后续操作,computed用于计算属性,用于依赖属性发生变化进行重新计算。

computed返回一个只读的包装对象,和普通包装对象一样可以被setup函数返回,这样就可以在模板上下文中使用computed属性。可以接受两个参数,第一个参数返回当前的计算属性值,当传递第二个参数时,computed是可写的。

import { value, computed } from 'vue-function-api';
 
const count = value(0);
const countPlusOne = computed(() => count.value + 1);
 
console.log(countPlusOne.value); // 1
 
count.value++;
console.log(countPlusOne.value); // 2
 
// 可写的计算属性值
const writableComputed = computed(
    // read
    () => count.value + 1,
    // write
    val => {
        count.value = val - 1;
    },
);
watch第一个参数和computed类似,返回被监听的包装对象属性值,不过另外需要传递两个参数:第二个参数是回调函数,当数据源发生变化时触发回调函数,第三个参数是options。其默认行为与 Vue 2.x 有所不同:

lazy:是否会在组件创建时就调用一次回调函数,与 Vue 2.x 相反,lazy默认是false,默认会在组件创建时调用一次。
deep:与 Vue 2.x 的 deep 一致
flush:有三个可选值,分别为 'post'(在渲染后,即nextTick后才调用回调函数),'pre'(在渲染前,即nextTick前调用回调函数),'sync'(同步触发)。默认值为'post'。
// double 是一个计算包装对象
const double = computed(() => count.value * 2);
 
watch(double, value => {
    console.log('double the count is: ', value);
}); // -> double the count is: 0
 
count.value++; // -> double the count is: 2
当watch多个被包装对象属性时,参数均可以通过数组的方式进行传递,同时,与 Vue 2.x 的vm.$watch一样,watch返回取消监听的函数:

const stop = watch(
    [valueA, () => valueB.value],
    ([a, b], [prevA, prevB]) => {
        console.log(`a is: ${a}`);
        console.log(`b is: ${b}`);
    }
);
 
stop();
注意:在RFC:Function-based component API初稿中,有提到effect-cleanup,是用于清理一些特殊情况的副作用的,目前已经在提案中被取消了。

生命周期
所有现有的生命周期都有对应的钩子函数,通过onXXX的形式创建,但有一点不同的是,destoryed钩子函数需要使用unmounted代替:

import { onMounted, onUpdated, onUnmounted } from 'vue-function-api';
 
const MyComponent = {
    setup() {
        onMounted(() => {
            console.log('mounted!');
        });
        onUpdated(() => {
            console.log('updated!');
        });
        // destroyed 调整为 unmounted
        onUnmounted(() => {
            console.log('unmounted!');
        });
    },
};
一些思考
上面的详解部分,主要抽取的是 Vue Function API 的常见部分,并非RFC:Function-based component API的全部,例如其中的依赖注入,TypeScript类型推导等优势,在这里,由于篇幅有限,想要了解更多的朋友,可以点开RFC:Function-based component API查看。个人也在Function-based component API讨论区看到了更多地一些意见:

由于底层设计,在setup取不到组件实例this的问题,这个问题在笔者尝试体验时也遇到了,期待正式发布的 Vue 3.x 能够改进这个问题。

对于基本类型的值必须使用包装对象的问题:在 RFC 讨论区,为了同时保证TypeScript类型推导、复用性和保留Vue的数据监听,包装属性必须使用.value来取值是讨论最激烈的

关于包装对象value和state方法命名不清晰可能导致开发者误导等问题,已经在Amendment proposal to Function-based Component API这个提议中展开了讨论:

setup() {
    const state = reactive({
        count: 0,
    });
 
    const double = computed(() => state.count * 2);
 
    function increment() {
        state.count++;
    }
 
    return {
        ...toBindings(state), // retains reactivity on mutations made to `state`
        double,
        increment,
    };
}
 

引入reactive API 和 binding API,其中reactive API 类似于 state API , binding API 类似于 value API。
之前使用的方法名state在 Vue 2.x 中可能被用作组件状态对象,导致变量命名空间的冲突问题,团队认为将state API 更名为 reactive 更为优雅。开发者能够写出const state = ... ,然后通过state.xxxx这种方式来获取组件状态,这样也相对而言自然一些。
value方法用于封装基本类型时,确实会出现不够优雅的.value的情况,开发者可能会在直接对包装对象取值时忘记使用.value,修正方案提出的 reactive API,其含义是创建响应式对象,初始化状态state就使用reactive创建,可保留每项属性的getter和setter,这么做既满足类型推导,也可以保留响应式引用,从而可在不同模块中共享状态值的引用。
但reactive可能导致下面的问题,需要引入binding API。 解决,如使用reactive创建的响应式对象,对其使用拓展运算符...时,则会丢失对象的getter和setter,提供toBindings方法能够保留状态的响应式。
当然,目前 Vue Function API 还处在讨论阶段,Vue 3.0 还处在开发阶段,还是期待下半年 Vue 3.0 的初版问世吧,希望能给我们带来更多的惊喜。

标签: 体验 API VUE 3.0 前瞻 Function « 交互控件科普系列! Sheet 的常见样式和设计注意事项总结 | 如何在Google Play商店中使你的应用获得推荐»


蓝蓝 http://www.bjhbys.com

  1. 2019年10月(53)
  2. 2019年9月(48)
  3. 2019年8月(63)
  4. 2019年7月(59)
  5. 2019年6月(59)
  6. 2019年5月(31)
  7. 2019年4月(37)
  8. 2019年3月(43)
  9. 2019年2月(26)
  10. 2019年1月(45)
  11. 2018年12月(41)
  12. 2018年11月(40)
  13. 2018年10月(29)
  14. 2018年9月(40)
  15. 帝皇彩票官网2018年8月(87)
  16. 2018年7月(107)
  17. 2018年6月(86)
  18. 2018年5月(110)
  19. 2018年4月(40)
  20. 2018年3月(35)
  21. 2017年8月(35)
  22. 2017年7月(45)
  23. 2017年6月(7)
  24. 2017年5月(27)
  25. 2017年4月(51)
  26. 2017年3月(70)
  27. 2017年2月(65)
  28. 2017年1月(69)
  29. 帝皇彩票官网2016年12月(55)
  30. 帝皇彩票官网2016年11月(111)
  31. 2016年10月(92)
  32. 帝皇彩票官网2016年9月(53)
  33. 2016年8月(9)
  34. 2016年7月(4)
  35. 2016年6月(9)
  36. 2016年3月(19)
  37. 帝皇彩票官网2016年2月(26)
  38. 2016年1月(30)
  39. 2015年12月(33)
  40. 2015年11月(35)
  41. 2015年10月(46)
  42. 2015年9月(43)
  43. 2015年8月(40)
  44. 2015年7月(33)
  45. 2015年6月(46)
  46. 帝皇彩票官网2015年5月(58)
  47. 2015年4月(70)
  48. 2015年3月(55)
  49. 2015年2月(17)
  50. 2015年1月(33)
  51. 2014年12月(21)
  52. 2014年11月(84)
  53. 2014年10月(94)
  54. 2014年9月(6)
  55. 2014年8月(1)
  56. 2014年7月(13)
  57. 帝皇彩票官网2014年6月(66)
  58. 2014年5月(99)
  59. 2014年4月(88)
  60. 2014年3月(102)
  61. 2014年2月(68)
  62. 2014年1月(83)
  63. 2013年12月(106)
  64. 2013年11月(112)
  65. 2013年10月(61)
  66. 2013年9月(20)
  67. 2013年7月(13)
  68. 2013年6月(27)
  69. 2013年5月(48)
  70. 2013年4月(39)
  71. 2013年3月(8)
  72. 帝皇彩票官网2013年2月(20)
  73. 帝皇彩票官网2013年1月(31)
  74. 2012年12月(33)
  75. 帝皇彩票官网2012年11月(31)
  76. 2012年10月(23)
  77. 2012年9月(8)
  78. 2012年7月(14)
  79. 2012年6月(15)
  80. 2012年5月(31)
  81. 2012年4月(24)
  82. 2012年2月(4)
  83. 2012年1月(8)
  84. 2011年12月(35)
  85. 2011年11月(32)
  86. 2011年10月(13)
  87. 2011年8月(1)
  88. 2011年6月(1)
订阅Rss
江西快3 上海11选5 海南4+1走势图 状元彩票官网 迪士尼彩乐园 吉利彩票投注 新疆喜乐彩走势图 帝皇彩票 博发彩票开户 乐彩网