【Amaple教程】4. 组件

在Amaple单页运用中,一个页面实在存在两种模块化单元,离别是

  1. 模块 am.Module类),它是以web单页运用跳转更新为最小单元所拆分的自力块;
  2. 组件 am.Component类),它的定位是具有特定功用的封装块,就像由一堆代码封装成的具有特定功用的函数一样,一个组件也有自力的视图、状况数据对象、组件行动以及性命周期。经常运用的组件有DialogBubbleNavigatorMenubar等。

在模块中定义并运用一个简朴的组件衍生类

运用am.class类组织器继承am.Component类定义一个组件,而继承am.Component建立的类被称为 组件衍生类 ,你能够如许定义一个组件衍生类:

// 在am.class函数的参数中指定该组件衍生类的类名,它返回指定称号的组件衍生类
// 类名须遵照首字母大写的驼峰式定名范例,如"BubbleDemo",不然将会报错。但吸收变量名没有限定
var BubbleDemo = am.class ( "BubbleDemo" ).extends ( am.Component ) ( {

    // 在init函数返回该组件的状况数据对象
    init : function () {
        return {
            bubbleText: "this is a component bubble"
        };
    },

    // 组件中必需定义render函数,在该函数中指定组件的template模板和款式
    render : function () {
        this.template ( "<span>{{ bubbleText }}</span>" )
        .style ( {
            span: { background: "red", fontSize: 20, padding: "10px 16px" }
            // !注重:当元素挑选器为相符变量定名划定规矩时可不必引号,如上面挑选span元素时。当挑选器不相符变量名划定规矩时需运用引号,如:
            // ".class-name": { fontSize: 15 }
            // "span #id": { margin-top: 24 }
        } );
        // this.template ( templateHTML )函数中传入html字符串来定义该组件的视图
        // this.style ( styleObj )函数为该组件的视图定义款式,这些款式也只作用于组件视图
        // 需注重的是该函数传入一个对象,对象属性名为css挑选器语法,值为css款式对象,款式名也是运用驼峰式示意,款式值为量值时可直接写为数字
    }
} );

在一个模块中运用 组件衍生类 衬着组件视图也是异常简朴的,起首在am.startRouter函数中设置组件加载的baseURL

am.startRouter ( {
    baseURL : {
        // ...

        // 为组件文件设置base途径,一切的组件文件请求途径都将基于“/component”目次,不设置时默许“/”
        component: "/component"
    },
    // ...
} );

然后在须要运用的模块或组件中经由过程import函数引入,并在<template>中经由过程自动以标署名来运用组件

<template>
    <!-- 自定义标署名为该组件衍生类的类名以悉数小写的中划线式范例转换而来,而不是吸收的变量名的转换 -->
    <bubble-demo></bubble-demo>
</template>
<script>

    // 当你将上面的组件衍生类零丁编写在src/component文件里时,你须要运用“import ( componentFilePath )”来引入此组件,
    // 如许在template模板中的<bubble-demo>元素就会剖析为组件模板“<span>this is a component bubble</span>”了。
    // 引入时可省略“.js”文件后缀
    var BubbleDemo = import ( "BubbleDemo" );

    // 固然你也能够直接在模块中编写运用一个组件,像如许:
    // var BubbleDemo = am.class ( "BubbleDemo" ).extends ( am.Component ) ( ... );
    new am.Module ( {
        init : function () { ... }
    } );
</script>

组件性命周期

与模块性命周期阶段数一样,一个组件从建立到卸载也分为5个阶段的性命周期,详细以下:

  • init:组件初始化时触发,它返回组件<template>模板剖析与挂载所运用的状况数据。init函数内可挪用this.propsType函数举行props参数考证。

props相干学问将在本章节的背面部份引见

  • render:衬着组件视图时触发,该性命周期函数内可离别挪用this.template函数定义视图标签的字符串,和this.style函数为组件视图增加款式
  • mounted:剖析并挂载状况数据到组件视图后触发,你能够在此函数中处置惩罚一些视图剖析完成后的操纵,如为此组件请求收集数据并更新到模板等
  • updated:当组件在页面中的位置转变时触发,在组件上运用:for指令时将会衬着多个组件,此时转变:for指令所绑定的状况数组时将能够转变组件的位置
  • unmount:组件卸载时触发,有两种状况将会卸载组件:
    ①. 经由过程:for指令衬着多个组件视图后,挪用绑定的状况数组的变异函数都能够在页面上卸载一个或多个组件;
    ②. 当一个模块或组件被卸载时,该模块或组件中的组件也将会卸载。

组件行动

我们已晓得组件是一个具有特定功用的封装块,所以它会有自身特定的 组件行动 ,如Dialog组件有翻开和封闭行动,轮播图组件有翻页行动等。你能够如许定义 组件行动

var Dialog = am.class ( "Dialog" ).extends ( am.Component ) ( {
    init : function () {
        return { open: false, text: "" };
    },
    render : function () {
        this.template ( [
            '<div :if="open">',
                '<span>{{ text }}</span>',
            '</div>'
        ].join ( "" ) );
    },

    // 增加action成员函数,该函数返回组件行动的函数鸠合对象,该对象被称为组件行动对象
    // action函数的this指针也是指向该组件对象自身
    action : function () {
        var _this = this;
        return {

            // 组件行动函数的this指针不会指向任何值
            // 经由过程state.open来掌握Dialog视图的隐蔽与显现
            open: function ( text ) {
                _this.state.text = text;
                _this.state.open = true;
            },
            close: function () {
                _this.state.open = false;
            }
        };
    }
} );

# 组件行动的两种运用方法

  • [1]在组件的性命周期函数mountedupdateunmount中可经由过程this.action运用组件行动对象;
  • [2]在组件元素上运用:ref指令,挪用module.refs函数猎取组件援用时将返回该组件的组件行动对象。

嵌套组件

组件与组件之间合营运用能够发挥更壮大的组件才能,在一个组件的<template>模板中能够嵌套其他组件,你能够如许写:

// ComponentB组件依靠ComponentA组件
// ComponentA组件的编写与一般组件编写雷同,这里省略
var CompoenntB = am.class ( "CompoenntB" ).extends ( am.Component ) ( {

    // 在组织函数中经由过程this.depComponents来指定该组件的依靠组件数组
    constructor : function () {
    
        // 和ES6的class关键字定义类一样,在组织函数中需起首挪用super()函数,不然将会抛出毛病
        this.__super ();
        this.depComponents = [ ComponentA ];
    },
    init : function () { ... },
    render : function () {
        this.template ( "<component-a></component-a>" );
    }
} );

ComponentAComponentB组件都编写在零丁的文件中时,你须要在模块中同时引入 组件 嵌套组件 ,像如许:

<template>...</template>
<script>

    // 在ComponentB组件中只需经由过程this.depComponents = [ ComponentA ]指定它所依靠的组件即可,然后在运用的模块中统一引入这些组件文件
    // 由于ComponentB组件依靠ComponentA组件,所以需在ComponentB之前引入ComponentA
    // 此时ComponentA组件就能够被ComponentB所猎取到
    var ComponentA = import ( "component/ComponentA" );
    var ComponentB = import ( "component/ComponentB" );

    new am.Module ( ... );
</script>

组件与组件、组件与模块之间的通讯

组件作为一个零丁的封装块,它必需与其他组件或模块举行通讯,你能够在模块平分发数据到差别组件,也能够在组件平分发数据到嵌套组件中。在组件中能够运用props举行数据的通讯,运用subElements举行html模板块分发。

# 运用props通报静态值

<template>
    <!-- 在组件元素上定义任何非指令属性(属性名不为“:”开首),它都会被当作props属性传入组件中 -->
    <dialog text="this is a external message!"></dialog>
</template>
<script>
    am.class ( "Dialog" ).extends ( am.Component ) ( {
        init : function () {

            // 在组件中运用this.props吸收外部传入的数据
            // this.props.text的值为"this is a external message!",即外部传入的字符串
            return { text: this.props.text };
        },
        // ...
    } );

    new am.Module ( ... );
</script>

# 运用props通报动态值

props还支撑运用插值表达式的体式格局通报状况数据,这被称为 动态props 。动态props将建立一个对外部状况数据的代办属性,当在组件内变动了此代办属性时,外部对应的状况数据也将同步更新。以下:

  • 在运用Dialog组件的视图中,将状况属性text传入组件后,组件的this.props.text即为该状况属性的代办属性。
<template>
    <dialog text="{{ text }}"></dialog>
</template>
<script>
    new am.Module ( {
        init : function () {
            text: "this is a external message!"
        },
        // ...
   } );
</script>
  • Dialog组件的代码中,可经由过程this.props.text猎取外部通报的text状况属性。
am.class ( "Dialog" ).extends ( am.Component ) ( {
    init : function () {
        return {

            // 运用text1吸收并运用this.props.text的值
            text1: this.props.text,

            // 假如你愿望更新外部的text属性后,组件视图中挂载了this.props.text数据的处所也同步更新,
            // 你能够在组件中建立一个盘算属性作为this.props.text的代办,以下建立的text2盘算属性:
            computed: {
                var _this = this;
                text2: {
                    get: function () {
                        return _this.props.text;
                    },
                    set: function ( newVal ) {
                        _this.props.text = newVal;
                    }
                }
                // 由于组件内对this.props.text的值更新后,外部的text状况属性也会同步更新,反之也建立
                // 如许在组件视图中挂载text2就即是挂载props.text
                // 此时需注重的是,变动text2的值也将同步变动外部text属性的值
            }
        };
    },
    // ...
} );

# props考证

当你愿望开放你所编写的组件给其他开辟者运用时,你不确定其他开辟者传入的props参数是不是相符组件内的处置惩罚请求,此时你能够为你的组件设置props数据考证,你能够在组件的init函数内挪用this.propsType函数举行考证:

am.class ( "Dialog" ).extends ( am.Component ) ( {
    init : function () {

        // 每项值的考证都能够设置validate、require和default属性
        this.propsType ( {
            text: {
                validate: String,  // 示意当传入text值时它必需为字符串
                require: true,  // 示意text参数为必需传入的参数,默许为false
                default: "Have no message"   // 示意不传入text参数时的默许值,默许值不会介入props考证,不指定default时无默许值
                // validate的值能够有四种范例的参数,离别为:
                // ①. 基础数据组织函数,离别有String、Number、Boolean三种基础数据范例组织函数,Function、Object、Array三种援用范例组织函数,
                //     以及undefined和null,它示意许可传入的数据范例
                // ②. 正则表达式,如/^1\d{10}$/示意只许可传入一个手机号码
                // ③. 函数,它吸收此props参数值,必需返回true或false示意是不是经由过程考证,如:
                // function ( text ) { return text.length > 5 }
                // ④. 数组,数组内是以上三种值的组合,经由过程数组内恣意一项考证都能够经由过程,相当于“||”运算符

            }

            // 当text属性考证只需设置validate属性时,可直接以下缩写:
            // text: String
        } );

        return { text: this.props.text };
    },
    // ...
} );

运用subElements分发html片断

假如你想开辟一个越发通用的Dialog组件,你应当不愿望Dialog的视图规划是牢固稳定的,而是能够依据差别的需求自定义Dialog视图,由于如许才显得越发天真多变,组件的subElements就是用来处理这个题目的,它能够使你从组件外部传入html片断与组件视图夹杂:

<dialog>
    <!-- <span>的内容会被作为html片断传入组件内 -->
    <span>this is external HTML template</span>
</dialog>

然后在组件内经由过程subElements属性猎取外部通报的视图,并插进去到组件视图中的恣意位置。subElement吸收的视图可分为 默许subElements subElements的单数分块 subElements的不定数分块 三种情势。

# 默许subElements

在组件元素中传入html片断时,组件内将会建立一个默许的subElements局部变量,你能够在组件内的模板中经由过程{{ subElements.default }}插进去此html片断:

am.class ( "Dialog" ).extends ( am.Component ) ( {
    init : function () { ... },
    render : function () {

        // {{ subElements.default }}将会输出外部传入的“<span>this is external HTML template</span>”
        this.template ( "<div>{{ subElements.default }}</div>" );
    }
    // ...
} );

Dialog组件将会被衬着成:

<div>
    <span>this is external HTML template</span>
</div>

【注重】分发的html片断也能够运用模板指令与组件,此html片断剖析时挂载的数据泉源是组件外部的状况数据,以下:

<template>
     <dialog>
          <!-- {{ text }}的数据泉源是此模块的状况 -->
          <!-- 它就像JavaScript中传入某个函数内的回调函数,该回调函数可对外部数据接见而不是函数内 -->
          <span>{{ text }}</span>
     </dialog>
</template>
<script>
     new am.Module ( {
          init : function () {
               return { text: "this is external HTML template" };
          },
          // ...
     } );
</script>

# subElements的单数分块

假如你愿望开辟的Dialog分为头部和内容两部份视图,再夹杂到组件模板的差别位置,subElements也许可你如许编写html片断:

<dialog>
    <header>
        <span>this is a title</span>
    </header>
    <content>
        <span>this is external HTML template</span>
    </content>
</dialog>

<header><content>将分发的代码块分为了两部份,你能够在组件视图中离别将它们插进去到差别的位置,只需在组件中离别定义HeaderContent两个 组件子元素

am.class ( "Dialog" ).extends ( am.Component ) ( {
    init : function () { ... },
    render : function () {

        // 指定分块的组件子元素名
        // 组件子元素名也需遵照首字母大写的驼峰式划定规矩,在组件元素内运用时也是悉数小写的中划线式范例
        this.subElements ( "Header", "Content" )
        .template ( [
            "<div>",
                "<div>{{ subElements.Header }}</div>",
                "<div>{{ subElements.Content }}</div>",
            "</div>"
        ].join ( "" ) );
        // <header>、<content>两个子元素只能作为<dialog>子元素运用
        // 组件模板中离别经由过程subElements.Header和subElements.Content输出对应的html分块片断
    }
    // ...
} );

此时Dialog组件将会被衬着成:

<div>
    <div><span>this is a title</span></div>
    <div><span>this is external HTML template</span></div>
</div>

【注重】①. 如没有在
this.subElements函数中定义响应的组件子元素时,Amaple只会将它们作为一般dom元素看待。

②. 除
<header>
<content>外的其他html片断会自动包括在
subElements.default中。

# subElements的不定数分块

subElements的分块分发能够会让你想到许多原生的元素,如<ul><li><select><option><table><tr><td>等,他们都属于包括与被包括的关联,但你会发明实在<ul>中能够定义一个或多个,在subElements中你也能够定义一个 组件子元素 的不定数分块,如Grid组件(网格)可包括不定个数的GridItem

<grid>
    <grid-item>a</grid-item>
    <grid-item>b</grid-item>
    <grid-item>c</grid-item>
</grid>

在组件中如许定义不定数分块的subElements

am.class ( "Grid" ).extends ( am.Component ) ( {
    init : function () { ... },
    render : function () {
        this.subElements ( { elem: "GridItem", multiple: true } )
        .template ( [
            "<ul>",
                "<li :for='item in subElements.GridItem'>{{ item }}</li>",
            "</ul>"
        ].join ( "" ) )
        .style ( ... );
        // 此时局部变量subElements.GridItem为一个包括一切GridItem分块片断的数组,在组件内运用:for指令轮回输出,
        // 也能够运用数组索引如subElements.GridItem [ 0 ]
        
        // 实在上面定义单数分块的Header的全写是{ elem: "Header", multiple: false },但它可缩写为"Header"
    }
    // ...
} );

继承进修下一节:【AmapleJS教程】5. 插件
也可回忆上一节:【AmapleJS教程】3. 模板指令与状况数据(state)

    原文作者:JOU_amjs
    原文地址: https://segmentfault.com/a/1190000012966552
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞