正如它的名字,模块用于amaplejs单页应用的页面分割,所有的跳转更新和代码编写都是以模块为单位的。

定义一个模块

一个模块由<module>标签对包含,内部分为template模板、JavaScript和css三部分,像这样:

<module>
<template>
<!-- 编写模块的html模板 -->
<span>hello amaplejs,page index</span>
<a href="/about?from=index">direct to page about</a>
</template> <script> // 编写模块JavaScript代码
// 在一个模块的JavaScript中,am为自动注入对象,你不需引入它,即使是在webpack环境下
// 这里通常使用new am.Module()创建一个amaple模块对象,它将会在构造函数中处理模板内容和模板样式等一系列的初始化工作
new am.Module ();
</script> <style scoped>
/* 编写模块样式 */
span { font-size: 20px; }
</style>
</module>

设置模块局部样式

大多数情况下我们可能更希望让一个模块中的css样式只作用当前的html模板,这当然我们也有所考虑,只需在<style>中添加scoped属性就会自动限制它的作用范围了,就是如此简单。但如果<style>不带scoped属性时将不会只作用到当前的html模板内。

模块生命周期

当我们在编写一个模块的JavaScript时通常使用am.Module类创建一个amaple模块对象,它将会在构造函数中进行一系列的初始化工作,而一个模块的生命周期函数将会在初始化的不同阶段,或模块数据改变时自动触发,它们共分为5个阶段,具体如下:

  • init:模块初始化时触发,它将要求你在定义时返回<template>模板中所使用的状态数据

关于状态数据的内容将在“模板指令与状态数据(state)”中讲解

  • mounted:解析模板并挂载状态数据时触发,你可以在这里处理一些模块的后续操作,如为此模块请求网络数据并更新到模板中等。
  • queryUpdated:模块更新时部分不需被替换的模块,检测到GET或POST参数变化(增加、移除或修改参数)时触发,如所有页面的header部分总是不变,此时它将不会被替换。
  • paramUpdated:模块更新时部分不需被替换的模块,检测到param参数变化(增加、移除或修改参数)时触发,只有设置了路由通配符的模块才有param参数。

关于路由通配符的内容我们将会在《路由配置》中详细讲解

  • unmount:在更新模块时被替换的模块将会触发。

你可以这样设置一个模块的生命周期函数:

<template>...</template>
<script>
new am.Module ( {
init : function () { // 返回一个包含状态数据的对象,这些状态数据作用是解析挂载模板
return {};
},
mounted : function () {},
queryUpdated : function () {},
paramUpdated : function () {},
unmount : function () {}
} );
</script>

【注意】生命周期函数内的this指向am.Module对象,如果它们使用ES6的箭头函数(Arrow function)会导致函数内this指针指向不正确而出错。

模块跳转与表单提交

和普通页面一样,模块跳转与表单提交都会改变url,在host与port都相同的情况下都会触发模块更新,否则将进行页面刷新的普通跳转。模块匹配及更新看如下图解:

# 模块跳转

在普通页面的跳转中,我们使用<a>标签设置href属性进行跳转,在amaplejs中也是如此,但不同的是,所有dom元素上设置了href属性后,点击都可以触发模块更新,并且当href后带有get参数时将会解析成键值对保存在am.Module对象内:

<a href="/about?from=index">direct to page about</a>

点击此按钮跳转将会匹配到/module/about.html模块,并且about模块对象内的get参数值为{from: "index"}

// about模块内可使用this.get对象的属性值
new am.Module ( {
init : function () {
return {
from: this.get.from
};
}
} );

# 表单提交

amaplejs的表单提交与普通页面的表单提交写法也相同,在<form>内编写表单元素并设置action属性,它指定表单提交路的路径:

<template>
<form action="account/register">
<input type="text" name="username" value="jim" />
<input type="password" name="pwd" value="jim001" />
<input type="text" value="10" />
</form>
...
</template>

提交表单到account/register后,服务端需返回一个路径字符串,来告诉框架表单提交后跳转的路径,它可能像这样:

<?php
// 表单数据的处理
// ... echo "/submit/success";
?>

表单提交后将会使用服务端返回的/submit/success路径进行模块的更新跳转,这样就完成了一个表单提交,此时/submit/success路径所匹配的模块中会自动生成post参数对象{ username: "jim" }(表单元素设置了name属性,且type不为password的值才会在this.post中创建):

new am.Module ( {
init : function () {
return {
this.username : this.post.username
};
}
// ...
} );

为了实现多个模块间的跳转,你可以继续创建一个module/about.html模块文件,内容与index.html几乎相同,像这样:

<module>
<template>
<span>hello amaplejs,page about</span>
<a href="/index?from=about">direct to page index</a>
</template> <script>
new am.Module ();
</script> <style scoped>
span { font-size: 20px; }
</style>
</module>

嵌套模块

当一个页面需设置二级菜单时,我们可能希望在一个模块中嵌套一个模块节点,而这在amaplejs中也是支持的,你只需在一个模块的<template>模板内继续使用:module属性定义模块节点,如:

<template>
<span>hello amaplejs,page index</span>
<a href="/about?from=index">direct to page about</a>
<div :module></div>
</template>

这样就嵌套了一个不具名的模块节点,你可能会问怎么可以定义两个不具名的模块节点呢,这是因为amaplejs可支持你设置无限层级的嵌套模块,就像html的元素嵌套一样,并且不同层级模块节点的命名空间(namespace)是相互独立的,但相同层级只能有一个不具名的模块节点,且不能出现名称相同的模块节点(其实不具名的模块节点的名称默认是“default”)。

【对模块层级的理解】模块层级与dom元素层级无关,需以模块为单位节点作为层级参考标准,不同模块内定义的嵌套模块为不同层级,而同一个模块内定义的嵌套模块不论位置如何都属于同一层级。

# 配置子路由

定义嵌套模块节点后,我们也需为它配置路由映射,指定嵌套模块节点在不同url下应该匹配哪些模块:

am.startRouter( {
routes : function ( router ) { // 使用route函数的第三个参数设置嵌套模块节点的路由映射
router.module ().route ( "/", "module/index", function ( childRouter ) { // 此回调函数将接收一个childRouter参数
// 不同层的命名空间是独立的,所以调用router.module函数指定的是嵌套层的模块节点
childRouter.module ().route ( "subpage", "module/index/sub_page" );
// 它将首先匹配“/”,然后继续匹配嵌套模块“subpage”
} )
.route ( "/about", "module/about" );
},
// ...
} );

父子模块之间的通信

嵌套模块节点与上一层模块节点的关系为父子模块,它们经常需要进行通信,如父模块下发数据到子模块中,或父模块访问子模块的数据,此时你可以这样做:

// 在子模块中通过parent属性来获取父模块对象
new am.Module ( {
init : function () { // this.parent为父模块对象,当一个模块为最顶层模块时,它的parent属性将为null
return {
btnText : this.parent.state.text
};
}
// ...
} );

在父模块中不能直接访问到子模块对象,但可通过子模块主动传值的方式让父模块获取到子模块的数据,就像我们的代码作用域,外层作用域不能直接获取内层作用域的值,但可通过内层作用域为外层作用域的变量赋值来访问。在子模块中通过parent属性来获取父模块对象,然后调用父模块中定义的模块函数并传入相应的值即可主动传值到父模块了。

模块函数将在《模板指令与状态数据》中讲解

继续学习下一节:【AmapleJS教程】3. 模板指令与状态数据(state)
也可回顾上一节:【AmapleJS教程】1. 启动路由

【Amaple教程】2. 模块的更多相关文章

  1. Drupal8开发教程:模块开发——创建新页面

    之前我们已经通过<Drupal8开发教程:认识.info.yml文件>对模块的YAML文件有了了解,今天我们来看如何通过模块开发的方式添加一个新的页面. 在 Drupal 7 中,通过模块 ...

  2. Python爬虫教程-09-error 模块

    Python爬虫教程-09-error模块 今天的主角是error,爬取的时候,很容易出现错,所以我们要在代码里做一些,常见错误的处,关于urllib.error URLError URLError ...

  3. 【Amaple教程】4. 组件

    在Amaple单页应用中,一个页面其实存在两种模块化单位,分别是 模块 (am.Module类),它是以web单页应用跳转更新为最小单位所拆分的独立块: 组件 (am.Component类),它的定位 ...

  4. 【Amaple教程】3. 模板指令与状态数据(state)

    一个模块的template模板.JavaScript和css之间的关系其实可以如下图表示: 如果你了解Angular.Vue动态模板,那你将会对Amaple的模板感到很熟悉,在Amaple中,temp ...

  5. 【Amaple教程】5. 插件

    Amaple 拥有非常强大插件功能,这也是它的突出功能之一,Amaple插件一般表现为功能块(函数)或功能块(包含一系列函数和属性的Object对象),它除了支持Amaple规范定义的插件外,还支持所 ...

  6. 【Amaple教程】6. 路由配置

    在 第1节<启动路由> 章节中为了能让单页应用顺利跑起来,我们提前介绍了简单的路由配置方法.我们已了解路由配置的目的是指定不同的url下对应的 模块节点(也叫做模块容器)内应该显示哪个模块 ...

  7. Node.js 教程 04 - 模块系统

    前言: Node.js的模块系统类似于C/C++的文件引用,可以声明对象,也可以定义类 创建对象. 大家这么理解,就简单了. 定义: 为了让Node.js的文件可以相互调用,Node.js提供了一个简 ...

  8. Python基础教程:模块重载的五种方法

    环境准备 新建一个 foo 文件夹,其下包含一个 bar.py 文件 $ tree foo foo └── bar.py 0 directories, 1 file bar.py 的内容非常简单,只写 ...

  9. Odoo 二次开发教程(二)-模块的基础构建

    注:本篇及后续均以8.0为基础. 一. Odoo模块的构成 __init__.py 文件是python包导入所必须的文件,内容可以为空,通常情况下我们用来导入自己写的py文件. __openerp__ ...

随机推荐

  1. [Algo] 26. Kth Smallest Number In Sorted Matrix

    Given a matrix of size N x M. For each row the elements are sorted in ascending order, and for each ...

  2. Traffic Network in Numazu

    Traffic Network in Numazu 题目描述 Chika is elected mayor of Numazu. She needs to manage the traffic in ...

  3. Mock相关知识和简单应用

    一.moco的简单应用 moco地址:https://github.com/dreamhead/moco api文档地址: https://github.com/dreamhead/moco/blob ...

  4. iOS动画效果合集、飞吧企鹅游戏、换肤方案、画板、文字效果等源码

    iOS精选源码 动画知识运用及常见动画效果收集 3D卡片拖拽卡片叠加卡片 iFIERO - FLYING PENGUIN 飞吧企鹅SpriteKit游戏(源码) Swift封装的空数据提醒界面Empt ...

  5. [C#] 动态指定泛型类型

    前言 今天为了程序能写好看一点,一直在纠结怎么指定动态泛型, 但是想想实用性好像不太大,可是把这技术忘掉太可惜XD 还是记录下来,以防忘记 以下程序范例 类 cs 123456789101112131 ...

  6. Qt QString的arg()方法的使用

    1.QString的arg()方法用于填充字符串中的%1,%2...为给定的参数,如 QString m = tr("); // m = "12:60:60: 2.它还有另外一种重 ...

  7. SecureCRT8.1安装破解

    博主本人平和谦逊,热爱学习,读者阅读过程中发现错误的地方,请帮忙指出,感激不尽 一.安装破解 [基本信息] SecureCRT v8.x 注册机,TEAM Z.W.T 出品,MD5 = 44114b9 ...

  8. HOG算法基础

    实现思路步骤: 1.对原图像gamma校正,img=sqrt(img); 2.求图像竖直边缘,水平边缘,边缘强度,边缘斜率. 3.将图像每16*16(取其他也可以)个像素分到一个cell中.对于256 ...

  9. CF-1111C-Creative Snap

    前两天过年,所以两天前的比赛题目现在才来回顾. 这题是一个最平常的递归,加一个剪枝.题目说如果一段距离没有复仇者看守,消耗的能量为A,A一定是正整数.由此可知对于没有复仇者看守的段,不拆一定比拆成两半 ...

  10. ajax 接受后台中文数据出现"?"(疑问号)解决方案

    把后端要返回的数据转成一个JSONObject类型返回,返回String 类型数据使用JSONObject来封装然后返回,绝对不会出现???了, 要是返回的是一个实体类的话,需要在前端或者后端做转换成 ...