# EasyWebApp
**Repository Path**: Tech_Query/EasyWebApp
## Basic Information
- **Project Name**: EasyWebApp
- **Description**: 声明式 MVVM Web 引擎,基于 HTML 5、CSS 3、ECMAScript 5、AMD 规范、jQuery API 构建
- **Primary Language**: JavaScript
- **License**: Not specified
- **Default Branch**: MVVM
- **Homepage**: http://tech_query.oschina.io/github
- **GVP Project**: No
## Statistics
- **Stars**: 137
- **Forks**: 38
- **Created**: 2015-07-23
- **Last Updated**: 2023-08-06
## Categories & Tags
**Categories**: webui
**Tags**: None
## README
# 声明式 MVVM 引擎 —— EasyWebApp v4
基于 AMD 规范加载器、jQuery v3.2+ 构建,兼容 IE 9+、ECMAScript 5+、HTML 5+
[](https://github.com/TechQuery/GitHub/)
[](https://gitter.im/EasyWebApp-js/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## 【原生态模板】
**EWA 模板语法**完全沿用各种 **Web 前端原生技术**的标准语义 ——
- **UI 结构**:HTML 5+ 标准标签、`data-*` 自定义属性
- **数据绑定**:HTML 标签文本、属性中书写 **ECMAScript 6 模板字符串**(形如 `${view.propX}`)
- **事件回调**:像绑定数据一样去绑定函数
- **资源加载**:可带参数的 HTTP URL,并自动处理相对路径
MVVM 引擎只需扫描 DOM 树,即可 **自动加载 HTML、JSON** 来构建 VM 树,开发者专注于数据整理、事件回调即可,无需任何本地编译、打包环境,享受低学习成本的 **原生 HTML、JavaScript 开发体验**。
## 【易拆分视图】
**ViewModel** 是对 View (HTML/DOM) 基于 **“数据驱动”模型**的面向对象封装 ——
1. View 的所有 HTML 结构要放在唯一的容器元素中,方便 **VM 挂载**
2. 同一时刻 **View 容器**只能挂载一个 VM 对象
3. VM 在渲染时会检测 **JavaScript 数据类型**,自动匹配 HTML 模板中对应的 HTML 标签属性、DOM 对象属性
4. DOM 标准事件、VM 自定义事件的回调函数,也视同数据的一部分绑定到 View,无需专门的 API
5. View 容器本身的 HTML 标签属于上级 View 的结构,但挂载于其上的 VM 会监听其 `data-*=""` 自定义属性的数据变更
6. 若不存在指向 解挂的 VM 或摘下的容器元素的其它引用,整个视图将被 **自动垃圾回收**
### 内置视图对象
[【API 文档】](View.html)
以上视图的构造函数 均可从 `$.fn.iWebApp` 命名空间访问到,并可无需 `WebApp()` 实例初始化即可单独使用。
[【jQuery 快捷方法】](external-_jQuery.fn_.html#.view)
- 构建视图:`$('selector of view').view(Class_Name, iScope)`
- 查找视图:`$(':view', Root_DOM).view()`
## 【声明式 AJAX】
如下格式的合法 HTTP URL 会被 EWA 引擎理解 ——
```
path/to/template.html?key1=value1&keyN=valueN&data=/path/to/json
```
`path/to/template.html`、`/path/to/json` 会被并行加载,`{key1: value1, keyN: valueN}` 会成为 `template.html` 对应 VM 初始数据的一部分。
### (〇)加载页面
```HTML
新窗口打开外部页面(跨域超链接 默认在新网页中打开)
纯静态 SPA 页
无初始数据的模板
用一份数据初始化一个模板
用一份数据初始化一个模板,并传入一些参数
```
**SPA 页面**总是在初始化 **WebApp 实例**的元素中被加载。
### (一)提交表单
```HTML
```
- `method` 属性支持 [RESTful API 规范](http://www.ruanyifeng.com/blog/2014/05/restful_api.html)中的常用 HTTP 动词
- `enctype` 属性支持 [MIME-Type 标准](http://www.iana.org/assignments/media-types/media-types.xhtml)中的常用类型(如 `application/json`)
- **数据字段校验**:完全依赖 HTML 5 Form API
### (二)引用组件
```HTML
```
**EWA 组件**支持 [Web Components 标准草案](//webcomponents.org)的 Slot 机制、自定义属性,但引用时无需自定义标签。
### (三)请求接口
```HTML
点个赞
列个表
```
## 【纯前端路由】
利用 HTML 5 History API 对“纯 Hash URL”的支持,EWA 引擎直接把 **SPA 页面 URL** 置于 `#!` (Hash Bang,Google 提出的纯前端路由规则)之后,即使无后端渲染支持,用户也可随意【F5】或【Ctrl + C/V】。
### (〇)请求拦截
```JavaScript
(new EWA()).on({
type: 'request',
src: 'api.test.com'
}, function (iEvent, iAJAX) {
// 基于 iQuery 扩展的实用方法,处理 jQuery AJAX 选项对象
iAJAX.option.url = $.extendURL(iAJAX.option.url, {
token: self.sessionStorage.token
});
});
```
### (一)响应处理
```JavaScript
(new EWA()).on({
type: 'template',
href: /\.md$/i
}, function (iEvent, iData) {
// MarkDown 转 HTML
return marked( iData );
}).on({
type: 'data',
src: 'api.test.com'
}, function (iEvent, iData) {
if (iData.code || (iEvent.method != 'GET'))
self.alert( iData.message );
else
return iData.data;
});
```
### (二)加载收尾
```JavaScript
(new EWA()).on({
type: 'ready',
href: 'path/to/template.html'
}, function (iEvent) {
// 一个组件/页面的 DOM Ready
// 即除 img、iframe、audio、video 等多媒体资源的 UI 渲染完成
});
```
### (三)实用方法
- 获取 **页面路由原文**:`WebApp.prototype.getRoute()`
- 为防止某些 URL 解析库欠考虑,EWA 路由做了一次 Base64 编码
- 修改 **路由初始数据**:`WebApp.prototype.setURLData(key, value)`
- 调用时可只传一个数据对象
- 每次调用都生成一条新浏览历史
- **页面浏览历史** 导航:`WebApp.prototype.loadPage( iStep )`
- 不传参时刷新当前页
- 传参时,参数与 `window.history.go()` 的用法一致
- 页面切换后会重新加载,并解决本方法返回的 Promise 对象
## 【异步式组件】
### (〇)HTML 模板
**EWA 组件**代码本身是完全合法的 HTML 片段,可直接被 AJAX 加载并在 DOM 树中实例化 ——
```HTML
现在是 ${(new Date()).toLocaleString()}
```
### (一)JavaScript 模块
无论 EWA 引擎本身,还是 EWA 组件的 JS 代码,均遵循 [AMD 规范](https://github.com/amdjs/amdjs-api/wiki/AMD),其必要格式如下 ——
```JavaScript
require([
'jquery', 'Module_1', 'Module_N', 'EasyWebApp'
], function ($, Module_1, Module_N, EWA) {
// 获取已初始化的 WebApp 单例
var iWebApp = new EWA();
EWA.component(function (iData) {
// iData 是与组件 HTML 并行加载的 JSON
// 此处的 this 是自动创建好的 VM 对象
var VM = this;
iData.handler_1 = function () {
// 回调函数也可以作为数据的一部分,用同样的模板语法绑定到 HTML 上
// 此处的 this 还是 VM 对象
this.xxx = 'yyy';
};
// 若你改变了传入的数据对象的引用,就必须 return 新的对象
iData = $.extend({ }, iData);
return iData;
});
});
```
EWA 引擎会自动用它返回的数据对象来更新 VM。
### (二)官方组件
https://boot-web.tk/
## 【声明式事件】
**声明式事件绑定**的统一形式:``
- `element`:HTML 标签名
- `Event`:HTML / EWA 事件名
- `callback`:事件回调在数据对象中的键名,该函数运行时 `this` 指向 `element` 在 HTML 结构上所处视图的 VM 对象
### 内置事件
| 事件名 | 来源对象 | 监听方式 | 触发原因 | 触发阶段 | 回调数据 |
|:--------:|:---------:|----------|:-----------:|:-------:|-------------------------------------------|
| request | InnerLink | JS、HTML | AJAX 请求 | 之前 | jQuery AJAX 选项、jqXHR 对象 |
| template | HTMLView | JS、HTML | 组件模板 | 之后 | 组件结构代码(如 HTML、MarkDown) |
| data | InnerLink | JS、HTML | 数据加载 | 之后 | JSON 对象 |
| ready | View | JS、HTML | 组件渲染 | 之后 | 视图对象 |
| route | WebApp | JS | 路由切换 | 之后 | 当前页**匹配的超链接**元素集合(jQuery 对象) |
| prefetch | WebApp | JS | 组件渲染 | 之后 | 当前组件**引用的超链接** URL 数组 |
| attach | View | JS | 组件挂载 | 之后 | |
| detach | View | JS | 组件卸载 | 之后 | |
| update | View | JS、HTML | 自定义属性更新 | 之后 | 更新的键值对 |
### 事件接口
- 多条件观察者 `Observer($_Box)`
- 查找实例:`.instanceOf(iDOM, Check_Parent)`
- 注册回调:`.prototype.on(iEvent, iCallback)`
- 回调函数 `this` 指向注册时的对象
- 回调函数 第一参数为事件对象,第二参数为事件触发时的附加数据
- 当前回调函数的返回值 将作为下个执行回调的第二参数
- 注销回调:`.prototype.off(iEvent, iCallback)`
- 一次监听:`.prototype.one(iEvent, iCallback)`
- 省略回调 将返回 Promise 对象
- 触发事件:`.prototype.emit(iEvent, iData)`
- 实现类:`View`、`InnerLink`、`WebApp`
上述 `iEvent` 参数可仅为一个事件名字符串,也可用一个对象描述更多的条件(对象属性值可为字符串、正则表达式、DOM 对象等)。