美文网首页Vue 从入门到进阶
编写简单的 Vue 组件

编写简单的 Vue 组件

作者: yibuyisheng | 来源:发表于2018-03-09 00:27 被阅读65次

    既然 Vue 是基于组件去构造视图的,那么最重要的事情当然是要掌握组件的知识点了。

    Vue 为了极大简化组件的开发,把组件的书写形式精炼为配置的形式,一个组件使用一个 js 对象来描述,然后利用该 js 描述对象可以生成多个该组件实例。

    打开 vue/types/index.d.ts 文件,可以看到四个与组件相关的类型:

    • Component
    • ComponentOptions
    • FunctionalComponentOptions
    • AsyncComponent

    此处主要关注 AsyncComponentComponentOptionsFunctionalComponentOptions ,分别对应异步组件普通组件函数组件

    ComponentOptions

    vue/types/options.d.ts 中可以看到 ComponentOptions 的声明,重点关注如下属性声明:

    • data

      组件中使用到的数据声明与初始化。可以是一个对象,也可以是一个方法,不过强烈推荐使用方法,原因将在后续文章讲解。

    • props

      组件对外暴露的数据接口,外部可以通过该接口向组件传递数据。

    • methods

      组件实例上的方法。

    • template

      组件模板,默认使用 HTML 配合一些简单的 Vue 语法来书写,也可以借助于编译时插件去支持其它的一些模板语法(比如 pug )。

    • created

      生命周期方法,组件实例化时,在各项数据准备就绪之后,解析 template 之前执行。

    以上是构造普通组件的最基础属性,那么定义一个简单普通组件就可以这样写:

    import Vue, { ComponentOptions } from 'vue';
    
    const MyComponent: ComponentOptions<Vue> = {
        props: {
            name: String,
            age: number
        },
        data() {
            return {
                counter: 0
            };
        },
        created() {
            this.setTimer();
        },
        methods: {
            setTimer() {
                setInterval(() => this.counter++, 1000);    
            }
        },
        template: '<div class="my-component">{{ name }} {{ age > 18 ? 'old' : 'young' }} {{ counter }}</div>'
    };
    
    Vue.component('MyComponent', MyComponent);
    

    在上述组件定义中,有若干知识点:

    • data 方法、 methods 配置中的方法、 created 方法中,注入的 this 都指向当前组件实例 vm

      遵照 Vue 官方文档的惯例,我们使用 vm( ViewModel 缩写) 表示 Vue 组件实例。

    • template 中的 {{ expression }} 是 Vue 给模板添加的语法,用于在当前位置输出字符串,其中表达式转换成字符串的方式与 '' + expression 行为一致,因此,如果表达式值是 undefined ,将会在相应位置输出 undefined 字符串。

    • 目前, template 只能且必须返回一个元素根节点(有例外,但是正常用法是碰不到的)。

    • template 中访问外部传入的数据( props )、 data 中声明的数据时,不需要写 this ,直接访问就行,因为这些数据都会被直接挂载到 vm 实例上去。

    上述组件配置通过 Vue.component 方法注册到 Vue 的全局空间中去,其中,第一个参数指定该组件在全局空间中的名字为 MyComponent ,在需要使用该组件的地方通过名字调用就行了:

    import Vue, { ComponentOptions } from 'vue';
    
    const App: ComponentOptions<Vue> = {
        el: 'app',
        data() {
            return {
                age: 27
            };
        },
        template: '<MyComponent name="yibuyisheng" :age="age"></MyComponent>'
    };
    
    Vue.component('MyComponent', MyComponent);
    

    对于 props 属性,直接通过标签属性(比如 name="yibuyisheng")的形式传递给组件。

    在执行上述示例的时候,可以发现,{{ counter }} 表达式部分的输出会一秒加一。实际上, data 里面事先声明的数据都是响应式的,改变 data 中声明的数据,模板中相应部分就会发生变化。

    FunctionalComponentOptions

    函数式组件的主要特点在于没有本地状态 data ,不会生成组件实例,组件需要的一切信息都从上下文参数中获取。

    函数式组件配置项非常少:

    • name

      主要用于组件递归调用,以及开发的时候能方便找到针对该组件打印的日志。由于在渲染的时候并不会生成组件实例,因此在 Vue devtools 的组件树中是看不到函数式组件的。

    • props

      同普通组件的 props 。

    • inject

      配置从上层组件注入的数据,后续文章会详细介绍。

    • functional

      标记该组件是否为函数式组件。

    • render

      普通组件和函数式组件都会有 render 函数,用于渲染界面。虽然上面在讲解普通组件的时候没有提到 render 函数,但是实际上 template 会被 Vue 的模板编译器转换成 render 函数。

      函数式组件的 render 函数与普通组件的有很大区别:

      • 函数式组件不会生成实例,所以在 render 中没有 this
      • 函数式组件的 render 存在第二个参数 context ,从 context 中可以获取到 props 等信息,具体参考官方文档

    编写一个简单的函数式组件:

    import Vue, { FunctionalComponentOptions } from 'vue';
    
    const MyFunctionalComponent: FunctionalComponentOptions<Record<string, any>, string[]> = {
        props: ['name'],
        functional: true,
        render(createElement, context) {
            return createElement('div', context.data, ['name: ' + context.props.name]);
        }
    };
    Vue.component<string>('MyFunctionalComponent', MyFunctionalComponent);
    

    说明:

    • createElement 方法用于创建一个 VNode 节点,详细内容将会在后续文章介绍,目前只需要知道 createElement('div', ...) 会在 DOM 树中生成一个 div 元素节点。
    • 通过 context.props 访问函数式组件外部传入的 props 数据。

    AsyncComponent

    在开发一些大型( SPA )应用的时候,可能会存在大量前端组件代码,对首屏进入可交互状态造成一定的延迟。

    实际上,渲染页面1的时候,最好不要去加载页面2的组件,直到用户点击进入页面2的时候,才去加载页面2相关的组件。

    为了实现这个功能, Vue 组件支持异步写法:

    import Vue, { AsyncComponent } from 'vue';
    
    const MyAsyncComponent: AsyncComponent = function (resolve) {
        setTimeout(function () {
            resolve({
                template: '<div>async component.</div>'
            });
        }, 1000);
    };
    Vue.component('MyAsyncComponent', MyAsyncComponent);
    

    在一秒后,组件加载完成,相应界面得到渲染。

    预告

    下一篇文章将会深入介绍 Vue 内部的响应式原理。

    相关文章

      网友评论

        本文标题:编写简单的 Vue 组件

        本文链接:https://www.haomeiwen.com/subject/kiiufftx.html