最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

Vue 3.0 前瞻Vue Function API新特性体验

来源:懂视网 责编:小采 时间:2020-11-27 21:52:34
文档

Vue 3.0 前瞻Vue Function API新特性体验

Vue 3.0 前瞻Vue Function API新特性体验:最近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API。笔者出于学习的目的,提前在
推荐度:
导读Vue 3.0 前瞻Vue Function API新特性体验:最近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API。笔者出于学习的目的,提前在

最近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API。笔者出于学习的目的,提前在项目中尝试了vue-function-api。

笔者计划写两篇文章,本文为笔者计划的第一篇,主要为笔者在体验 Vue Function API 的学习心得。第二篇计划写阅读vue-function-api的核心部分代码原理,包括setup、observable、lifecycle。

本文阅读时间约为15~20分钟。

概述

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的核心部分代码原理,包括setup、observable、lifecycle等,从内部探索 Vue Function API 可能带给我们的改变。

    当然,目前 Vue Function API 还处在讨论阶段,Vue 3.0 还处在开发阶段,还是期待下半年 Vue 3.0 的初版问世吧,希望能给我们带来更多的惊喜。

    声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文档

    Vue 3.0 前瞻Vue Function API新特性体验

    Vue 3.0 前瞻Vue Function API新特性体验:最近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API。笔者出于学习的目的,提前在
    推荐度:
    标签: VUE API 体验
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top