27、Ember应用开发:路由与模型详解

Ember应用开发:路由与模型详解

1. npm和Bower安装

npm install bower install 命令末尾使用 --save-dev --save 选项,可将库名和版本的键值对添加到各自工具的JSON文件中。对于Bower,该JSON文件是 bower.json ;对于npm,则是 package.json

例如,在 bower.json 中添加的键值对如下:

{
  "name": "tracker",
  "dependencies": {
    "ember": "~2.4.3"
  }
}

bower.json 文件列出了 ember.js 及其最低版本号。列出的库和资源不会保存到开发项目仓库或版本控制系统中,只有 bower.json 文件会被保存。开发人员检出代码后,可运行 bower install npm install 来创建本地开发环境。

挑战任务

  • 青铜挑战:限制导入 :修改 ember-cli-build.js ,仅导入 collapse.js transition.js 文件。这样做会使 vendor.js 文件变小,同时 NavBar 组件仍可正常工作。操作步骤如下:
    1. 找到 dist/assets/vendor.js 文件,记录其代码行数或文件大小。
    2. 修改 ember-cli-build.js 文件。
    3. 比较修改后 vendor.js 的新文件大小。
  • 白银挑战:添加Font Awesome :Font Awesome是一个用于在项目中添加常用图标的UI库,图标可像字体一样缩放。使用Ember CLI插件添加Font Awesome,并在 app/templates/application.hbs 中添加一个图标。可查看该插件的GitHub仓库获取更多信息。
  • 黄金挑战:自定义NavBar :Bootstrap使用SCSS编写,大量使用了变量和函数。在项目中使用SCSS版本时,可控制库编译样式规则的方式,甚至可创建Bootstrap主题来修改默认变量。通过在 app/stylesheets/app.scss 中添加或修改变量,更改 NavBar 的背景颜色、边框半径和内边距值。

2. 路由基础

路由就像交通警察,当用户访问特定URL时,路由会将用户引导至构成该页面的数据。与之前项目中为表单提交和按钮点击创建事件监听器类似,路由可看作是监听当前URL变化的事件监听器。

每个网站都使用某种形式的路由。例如,访问 www.bignerdranch.com/we-teach/ 时,服务器会将 /we-teach/ 路由映射到服务器上名为 we-teach 的文件夹中的HTML文件。其他服务器可能采用不同方式,如运行一个输出HTML的函数,而不是检索静态HTML文件。

Ember应用也能实现类似功能,且无需向服务器请求HTML。当应用需要切换到不同屏幕时,会使用新的路由名称更新URL。路由器是主应用对象的子对象,具有用于监听和处理URL变化的事件监听器。它使用新路由在路由表中查找,并找到对应的 Ember.Route 对象,然后调用该路由对象的一系列方法,开始获取下一个屏幕所需数据的过程,这个回调过程称为路由生命周期钩子。

2.1 Ember应用路由创建

创建路由是Ember开发的基础。Ember的命名约定假设你将创建与路由名称匹配的关联控制器和模板。例如,创建名为 sightings 的路由时,路由器会将 /sightings 请求映射到 SightingsRoute ,进而设置 SightingsController ,并最终渲染 app/templates/sightings.hbs 模板。

以下是Tracker应用所需的路由列表:
| 路由名称 | 路由路径 | 路由数据 |
| ---- | ---- | ---- |
| index | /index | 无数据 - 重定向到sightings |
| sightings | /sightings | 目击列表 |
| cryptids | /cryptids | 神秘生物列表 |
| witnesses | /witnesses | 目击者列表 |
| sighting | /sighting | 单个目击详情 |
| cryptid | /cryptid | 单个神秘生物详情 |
| witness | /witness | 单个目击者详情 |
| sightings index | /sightings/index | 目击列表着陆页 |
| sightings new | /sightings/new | 创建新目击的表单 |
| sighting index | /sighting/:sighting_id/index | 单个目击着陆页 |
| sighting edit | /sighting/:sighting_id/edit | 编辑单个目击的表单 |

使用Ember CLI的 ember generate (简称 ember g )工具创建这些路由,操作步骤如下:
1. 打开终端,导航到 tracker 目录。
2. 逐行运行以下命令:

ember g route index
ember g route sightings
ember g route sightings/index
ember g route sightings/new
ember g route sighting
ember g route sighting/index
ember g route sighting/edit
ember g route cryptids
ember g route cryptid
ember g route witnesses
ember g route witness

2.2 路由文件和模板分析

运行上述命令后,会在 routes/ templates/ 目录下生成新文件。

打开 app/routes/index.js ,该模块导入Ember并导出一个 Ember.Route

import Ember from 'ember';
export default Ember.Route.extend({
});

.extend 方法创建 Ember.Route 的新子类,并接受一个JavaScript对象作为参数。使用ES6模块语法,可为每个路由创建单独的模块。Ember CLI会自动找到新创建的 Ember.Route 模块并导入到应用中。

打开 app/templates/index.hbs ,该模板用于 IndexRoute ,其中包含一行代码 {{outlet}} ,此助手允许模板在路由之间嵌套内容。在 {{outlet}} 上方添加一个HTML <h1> 元素:

<h1>Index Route</h1>
{{outlet}}

启动应用:
1. 运行 ember server 启动应用。
2. 在项目开发过程中保持服务器运行。
3. 若需要与Ember CLI交互(如生成更多模块),可打开第二个终端窗口。
4. 在Chrome浏览器中导航到 http://localhost:4200 ,应用应显示 app/templates/application.hbs 中的 NavBar 元素和 app/templates/index.hbs 中的 <h1> 元素。

2.3 路由注册

router.js 文件中注册路由,将URL与特定页面关联起来。每个路由可配置一些选项,甚至可创建嵌套路由。

以下是 router.js 中注册路由的代码:

import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
  location: config.locationType
});
Router.map(function() {
  this.route('sightings', function() {
    this.route('new');
  });
  this.route('sighting', function() {
    this.route('edit');
  });
  this.route('cryptids');
  this.route('cryptid');
  this.route('witnesses');
  this.route('witness');
});
export default Router;

Router.map 方法接受一个回调函数,在回调函数中使用 this.route 方法注册路由。Ember会将这些嵌套回调转换为路由层次结构,顶层是 ApplicationRoute

当访问嵌套路由的URL时,Ember会使用父模板的HTML,并在父模板中查找 {{outlet}} 助手,将子模板的HTML放置在该位置。

2.4 嵌套路由

嵌套路由可将相关路由组合在一个基础URL下,有助于结构化视图中的数据。可将父路由视为代表名词,子路由视为代表动词或形容词。

例如:

this.route('sightings', function() {
  this.route('new');
});

sightings 是代表目击事件的父路由(名词), new 是代表创建目击事件操作的嵌套路由(动词)。

以下是编辑模板文件并导航到应用不同页面的操作步骤:
1. 打开 app/templates/sightings.hbs ,在现有 {{outlet}} 助手上方添加一个 <h1> 元素:

<h1>Sightings</h1>
{{outlet}}
  1. 编辑 app/templates/sightings/index.hbs ,添加一个 <h1> 元素并删除 {{outlet}} 助手:
<h1>Index Route</h1>
  1. 保存文件,在浏览器中访问 http://localhost:4200/sightings/ 查看结果。
  2. 编辑 app/templates/sightings/new.hbs ,删除 {{outlet}} 并添加一个 <h1> 元素:
<h1>New Route</h1>
  1. 更改浏览器URL为 http://localhost:4200/sightings/new 查看结果。

2.5 Ember Inspector查看路由

使用Ember Inspector可轻松查看应用的所有路由。点击Ember Inspector中的“Routes”菜单项,可看到众多路由,其中包括许多以 loading error 结尾的自动生成路由,这些路由用于处理路由加载数据的生命周期状态。

2.6 分配模型

为每个路由分配数据,可使用路由的 model 回调函数。每个 Ember.Route 都有一个 model 方法,用于将模型(即支持模板的数据)分配给控制器,该方法返回一个Promise。

app/routes/sightings.js model 回调中添加一些模拟数据:

import Ember from 'ember';
export default Ember.Route.extend({
  model(){
    return [
      {
        id: 1,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      },
      {
        id: 2,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      },
      {
        id: 3,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      },
      {
        id: 4,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      },
      {
        id: 5,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      },
      {
        id: 6,
        location: 'Asilomar',
        sightedAt: new Date('2016-03-07')
      }
    ];
  }
});

model 钩子的语法如下:

model() {
  // 代码逻辑
}

这是以下代码的ES6简写形式:

model: function() {
  // 代码逻辑
}

编辑 app/templates/sightings/index.hbs ,显示模型数据:

<h1>Index Route</h1>
<div class="panel panel-default">
  <ul class="list-group">
    {{#each model as |sighting|}}
      <li class="list-group-item">
        {{sighting.location}} - {{sighting.sightedAt}}
      </li>
    {{/each}}
  </ul>
</div>

在浏览器中访问 http://localhost:4200/sightings ,应用应显示目击列表。

2.7 beforeModel钩子

beforeModel 函数可用于在检索数据之前检查应用状态,也可用于重定向无法访问特定页面的用户,如进行用户身份验证检查。

app/routes/index.js 中添加 beforeModel 回调:

import Ember from 'ember';
export default Ember.Route.extend({
  beforeModel(){
    this.transitionTo('sightings');
  }
});

当访问 http://localhost:4200/ 时,URL会自动重定向到 http://localhost:4200/sightings ,并显示 app/templates/sightings/index.hbs 中的目击列表。

2.8 setupController和afterModel钩子

  • setupController 钩子用于在控制器上设置属性,可使用 this._super 运行设置控制器模型属性的默认行为,同时设置其他活动控制器属性:
setupController(controller, model) {
  this._super(controller, model);
  // this.controllerFor('[other controller]').set("[property name]", [value]);
}
  • afterModel 钩子在 model 钩子(Promise)解析后运行。在某些特殊情况下,由于Promise已解析, model 钩子可能不会被调用,此时 afterModel 会在 setupController 之前被调用,可用于在将模型数据传递给控制器之前测试其完整性。

综上所述,通过上述步骤,应用已具备一些基本路由,包括着陆页、目击列表和添加新目击的路由。创建了路由模板,并向 sightings 路由添加了模型数据,还将 index 路由重定向到 sightings 索引页,为后续开发奠定了良好基础。

3. 路由生命周期总结

路由生命周期是 Ember 应用中非常重要的一部分,它涉及到多个钩子函数的调用顺序,下面通过一个流程图来展示整个路由周期:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    A([开始]):::startend --> B(触发 URL 变化):::process
    B --> C(beforeModel):::process
    C --> D{是否满足条件}:::process
    D -->|是| E(model):::process
    D -->|否| K(重定向或其他处理):::process
    E --> F(afterModel):::process
    F --> G(setupController):::process
    G --> H(渲染模板):::process
    H --> I([结束]):::startend
    K --> I

从这个流程图可以清晰地看到,当 URL 发生变化时,路由对象会依次调用 beforeModel model afterModel setupController 等函数。其中, beforeModel 可用于检查应用状态和重定向用户; model 用于获取渲染模板所需的数据; afterModel 可在数据获取后进行一些额外处理; setupController 则将模型数据设置到控制器上。

4. 路由相关代码优化建议

4.1 代码复用

在 Ember 应用中,许多路由可能会有一些共同的逻辑。例如,多个路由可能都需要进行用户身份验证检查,这时可以将这些共同的逻辑提取到一个基类中。

以下是一个示例:

// app/routes/base-route.js
import Ember from 'ember';

export default Ember.Route.extend({
  beforeModel() {
    // 检查用户身份验证
    if (!this.get('session.isAuthenticated')) {
      this.transitionTo('login');
    }
    this._super(...arguments);
  }
});

// app/routes/some-route.js
import BaseRoute from './base-route';

export default BaseRoute.extend({
  // 其他特定逻辑
});

通过这种方式,多个路由可以继承 BaseRoute ,避免代码重复。

4.2 异步数据处理

在获取数据时,由于 model 方法返回的是一个 Promise,可能会遇到异步操作的问题。为了提高用户体验,可以在页面加载时显示加载状态。

以下是一个示例:

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve([
          {
            id: 1,
            name: 'Item 1'
          },
          {
            id: 2,
            name: 'Item 2'
          }
        ]);
      }, 2000);
    });
  },
  setupController(controller, model) {
    this._super(controller, model);
    controller.set('isLoading', false);
  },
  activate() {
    this.controllerFor(this.routeName).set('isLoading', true);
  }
});

在模板中可以根据 isLoading 状态显示加载提示:

{{#if isLoading}}
  <p>Loading...</p>
{{else}}
  <!-- 显示数据 -->
  <ul>
    {{#each model as |item|}}
      <li>{{item.name}}</li>
    {{/each}}
  </ul>
{{/if}}

5. 常见问题及解决方案

5.1 路由跳转失败

  • 问题描述 :在调用 transitionTo replaceWith 方法时,路由跳转失败。
  • 可能原因
  • 路由名称拼写错误。
  • 路由未正确注册。
  • 路由参数传递错误。
  • 解决方案
  • 检查路由名称是否正确,可通过 Ember Inspector 查看路由列表。
  • 确保在 router.js 中正确注册了路由。
  • 检查传递的路由参数是否符合路由定义。

5.2 模型数据未正确加载

  • 问题描述 :在模板中无法显示模型数据。
  • 可能原因
  • model 方法返回的数据格式不正确。
  • 数据获取过程中出现错误。
  • 控制器未正确接收模型数据。
  • 解决方案
  • 检查 model 方法返回的数据格式是否符合模板的使用要求。
  • model 方法中添加错误处理逻辑,例如:
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return new Promise((resolve, reject) => {
      // 模拟数据获取
      setTimeout(() => {
        const error = false; // 模拟错误情况
        if (error) {
          reject(new Error('Data fetch failed'));
        } else {
          resolve([
            {
              id: 1,
              name: 'Item 1'
            }
          ]);
        }
      }, 1000);
    }).catch((error) => {
      console.error(error);
      return [];
    });
  }
});
  • 确保在 setupController 方法中正确设置了模型数据。

6. 总结与展望

通过前面的介绍,我们了解了 Ember 应用中路由和模型的相关知识,包括路由的创建、嵌套、生命周期钩子的使用,以及如何为路由分配模型数据等。这些知识为构建复杂的 Ember 应用奠定了基础。

在未来的开发中,可以进一步探索以下方面:
- 更复杂的路由配置 :例如动态路由、路由守卫等,以实现更灵活的页面导航和权限控制。
- 与后端服务的集成 :使用 Ember Data 等工具与后端 API 进行交互,实现数据的持久化和实时更新。
- 性能优化 :通过缓存、懒加载等技术提高应用的响应速度和性能。

总之,Ember 提供了强大的路由和模型机制,合理运用这些技术可以开发出高效、稳定的 Web 应用。

内容概要:本文档围绕六自由度机械臂的ANN人工神经网络设计展开,涵盖正向逆向运动学求解、正向动力学控制,并采用拉格朗日-欧拉法推导逆向动力学方程,所有内容均通过Matlab代码实现。同时结合RRT路径规划B样条优化技术,提升机械臂运动轨迹的合理性平滑性。文中还涉及多种先进算法仿真技术的应用,如状态估计中的UKF、AUKF、EKF等滤波方法,以及PINN、INN、CNN-LSTM等神经网络模型在工程问题中的建模求解,展示了Matlab在机器人控制、智能算法系统仿真中的强大能力。; 适合人群:具备一定Ma六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)tlab编程基础,从事机器人控制、自动化、智能制造、人工智能等相关领域的科研人员及研究生;熟悉运动学、动力学建模或对神经网络在控制系统中应用感兴趣的工程技术人员。; 使用场景及目标:①实现六自由度机械臂的精确运动学动力学建模;②利用人工神经网络解决传统解析方法难以处理的非线性控制问题;③结合路径规划轨迹优化提升机械臂作业效率;④掌握基于Matlab的状态估计、数据融合智能算法仿真方法; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点理解运动学建模神经网络控制的设计流程,关注算法实现细节仿真结果分析,同时参考文中提及的多种优化估计方法拓展研究思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值