Sencha Ext JS 7.8+入门实战:网格、图表与表单开发指南

发布时间:2026/10/25 0:20:03
Sencha Ext JS 7.8+入门实战:网格、图表与表单开发指南

简介:Sencha Ext JS 7.8+入门指南是一份面向具备JavaScript基础、希望构建复杂企业级前端应用的开发者的PDF教程,重点解决从零上手框架时面临的安装配置、项目搭建与组件创建等常见问题。内容从框架特性与优势讲起,覆盖许可证获取、Sencha Command安装、npm环境管理、IDE选型、模板选择及项目文件结构等完整流程,并通过数据网格、柱状图、表单捕获等典型示例,帮助读者快速掌握Ext JS的UI组件体系和MVC架构。资源为单个PDF文档,大小约2.65MB,目前已有193人学习下载。文档按照从工具链到项目实战的次序组织,配套可运行的代码片段演示视图与组件的创建过程,同时列出官方文档、视频教程与社区支持等延伸资源。读者可以结合书中示例,在实际项目中逐步完成调试和优化,在掌握核心组件用法的同时积累企业级界面的搭建经验,形成清晰的学习路径。

1. 从一次选型谈起:Ext JS 7.8+ 到底解决什么问题

几年前我接手一个企业级数据管理项目,前端要求表格、图表、表单混排,还要支持树形导航、多标签页和复杂的权限视图。当时团队里有主张用轻量框架的,也有主张自己封装组件的,结果折腾到中期才发现,光是搞定一个可排序、可过滤、可列显隐的表格,再让它和表单联动,就已经吃掉了一半工期。后来切到 Sencha Ext JS,情况才真正好转。它不是给页面做点缀的UI库,而是一整套面向大型应用的框架:超过140个开箱即用的组件,网格、图表、表单、布局全部自带,数据绑定和 MVC/MVVM 架构也内置在体系里。这套《Sencha Ext JS 7.8+入门指南》的价值在于,它不跟你谈抽象概念,直接带着从许可证获取、环境安装、项目生成,一路做到底层组件落地。对写惯了类 React 或类 Vue 思路的人来说,阅读它会有明显的不适感,但恰恰是这种“不适感”会帮你建立起对复杂前端工程的新认知。

2. 搭建环境:Sencha Command 安装与项目生成

2.1 两个 Toolkit 怎么选:Classic 还是 Modern

环境搭建之前,先要弄明白 Ext JS 里的一个基础选择:Toolkit。Ext JS 7.8+ 提供两套渲染内核,共享同一个数据层和组件模型,但侧重点完全不同。文档里写得很清楚:Modern Toolkit 用 CSS 渲染视图,优先为手机和平板优化;Classic Toolkit 主要靠 JavaScript 渲染视图,面向桌面浏览器上的复杂业务系统。

不要被“Modern 一定优于 Classic”这种惯性带偏。对代码的掌控程度、对表格性能的追求、对现有主题的依赖,才是真正的决策因素。Classic 的最大优势在于组件密度高,网格的列锁定、分组表头、编辑器在桌面端表现非常成熟;Modern 的布局方式更接近 Web 标准,触控交互友好,但某些复杂组件的支持程度需要逐项对照文档确认。

这本入门指南明确把主场景放在 Classic Toolkit 上,理由是它面向绝大多数企业后台的真实使用环境。我的建议是:如果项目有大量表格和表单混合交互,选 Classic;如果是现场演示大屏、移动端优先,再考虑 Modern。两者的类名略有差异,但逻辑结构一致,从一套切到另一套的迁移成本比你想的低。

2.2 安装 Sencha Command:一次只踩一半的坑

Sencha Command 是后续所有操作的命令行入口,创建项目、编译代码、压缩资源、启动测试服务都靠它。安装过程本身不复杂,文档里有几个值得注意的细节:

  1. 从官网下载安装包,Windows 下微软 Defender 大概率会弹警告,选“仍要运行”。
  2. 安装器组件选择界面不要勾 Compass 扩展,那是旧版本 Ext JS 用的,7.8+ 不再依赖它。
  3. 装完以后需要确认命令行可用,直接在 PowerShell 里敲一条命令验证:
sencha which

正常输出会显示 Sencha Command 的安装路径和版本信息。常见的一个坑是环境变量没自动写入,导致sencha命令找不到。解决方法是把安装目录手动加入系统 PATH,再重开一个终端窗口。

如果你不想走独立的安装包路径,也可以选择 npm 方式管理环境。文档给出了两条官方说明链接,分别对应 ZIP 安装和 npm 安装。就我的经验,npm 方式更适合团队统一版本、自动拉取依赖的场景,但生成项目后依然需要 Sencha Command 参与构建,两者并不互斥。

2.3 用一条命令生成项目

装好 Sencha Command 后,真正生成项目只需要一条命令。不过命令的参数结构要先搞清楚,否则容易把目录生成到你不想去的地方:

sencha generate app classic -ext MyApp ./

这条命令的参数拆开看是这样:

  • classic是 Toolkit 类型,声明生成使用 Classic Toolkit 的应用。
  • -ext表示使用 Ext JS 框架的正式版本来生成项目(对比有些场景会用-ext的试玩版)。
  • MyApp是应用名称,必须符合变量命名规则,不能用中文、不能用数字开头。
  • ./是相对路径,代表当前文件夹。

执行后 Sencha Command 会拉取框架依赖,生成一整套模板工程。整个过程快则几十秒,慢则几分钟,取决于网络和本机性能。命令执行完毕后,建议先跑一次构建验证环境,构建命令如下:

sencha app build development

development是构建环境参数,最常用;还有testing和production,分别对应测试验证和部署发布。这里的成功标准是终端不抛红色错误,且输出里出现构建完成信息。需要留意的是,生成完项目以后不要直接手动粘贴源代码进去就跑,先让模板原型构建通过一次,能排除很多环境层面的干扰。

3. 深入模板工程:目录结构、配置参数与构建

3.1 模板项目里到底有哪些文件夹

项目生成后,第一件事是认目录。很多框架的模板工程会刻意“藏”很多东西,但 Ext JS 的模板结构算是开诚布公的。一个刚生成的项目目录大致是这样的:

MyApp/ ├── app/ │ ├── Application.js │ ├── controller/ │ ├── model/ │ ├── store/ │ └── view/ ├── classic/ │ ├── src/ │ └── sass/ ├── modern/ ├── resources/ ├── workspace.json └── app.json

app/下直接对应 MVC 的各个部分,Application.js 是应用入口;classic/和modern/是分工具包的源码目录,各自有独立的src和sass;resources/存放全局静态资源;app.json是全局配置文件,应用ID、主题、依赖的SDK版本、启动页面等都在这定义。

典型的误用是:不管什么代码都往app/view/里塞。实际上,store 应该跟数据模型放一起,controller 单独管理事件逻辑,自定义工具函数可以考虑独立目录。目录结构越清晰,后边做构建时依赖分析越顺畅。

3.2 app.json 里的关键配置项

app.json是构建和运行的基础,初学者最容易在这里翻车。文件格式是标准 JSON,注释以//出现,和普通 JSON 文件稍有差异。几个关键配置字段:

配置项作用常见调整
name应用名称对应生成时传入的 ApplicationName
toolkit默认工具包可设置为 classic 或 modern
theme主题名称如 theme-triton、theme-material 等
requires依赖列表需要引入的 Ext JS 组件或第三方库
sdkSDK 路径指向本地 Ext JS 框架目录

比较常用的是调整requires来加入图表、Excel 导出等功能模块。比如模板工程默认不带图表功能,后面做柱状图视图时,就需要在requires里加入图表相关的包,这一点后面会详细展开。

还有一个容易忽略的配置:loader和loaderPath,它们决定了开发模式下按需加载的机制。如果配置不对,容易出现运行时突然报找不到某个类,而构建阶段完全不报错的情况。

3.3 构建环境的差异与运用

Sencha Command 的构建命令支持三种环境参数,它们的差异不只是压缩程度:

环境特点适用场景
development不压缩、保留可读性、方便调试日常开发、断点跟踪
testing模拟生产行为但保留部分调试信息测试环境、自动化验证
production全量压缩、删除调试代码、优化资源正式部署

我从实际经验里得到的教训是:日常开发不要用production构建去验证代码逻辑,压缩后的报错信息缺失严重,排错成本极高。反过来,上线前至少要用production构建一次完整流程,才能暴露开发模式下被隐藏的依赖问题。

构建完成以后,输出目录在build/{environment}/{appName}/下,可以直接用静态服务器托管。如果你用的是 Sencha 自带的测试服务器,就不需要额外配置 Web 服务器,直接执行sencha app watch会在本地启动一个开发服务器,文件改动后自动编译刷新,调试体验不错。

4. 视图与组件实战:Grid、图表、表单的三次落地

4.1 MVC 架构和视图创建方式

Ext JS 的 MVC 模式和常见的后端 MVC 不太一样。View 负责界面布局和组件嵌套,Controller 监听组件事件并调用 Model/Store 的数据方法,Model 定义字段和校验规则,Store 则负责数据加载和排序过滤。这个模型中,View 之间通过xtype和reference互相引用,Controller 用control或listen统一管理事件绑定。

视图创建有两种方式:Ext.define和Ext.create。前者定义一个新的类,可被复用和继承;后者直接创建对象实例。两者的选择标准很简单:如果这个视图会在多个地方出现,或者需要被继承扩展,用Ext.define;如果只是临时用一次,懒得起名字,用Ext.create。

文档里的三个练习示例(数据网格、柱状图、捕获表单)恰好覆盖了这三种典型场景,下面逐个拆解。

4.2 实战:数据网格视图

数据网格是企业应用里出现频率最高的组件。第一个练习的目标是在导航菜单里加入一个入口,点击后展示网格视图。顺着代码走一遍会清晰很多:

第一步,先在主视图的导航栏里加一个菜单项。导航定义通常在主视图文件里,通过Ext.define声明,类似这样:

Ext.define('MyApp.view.main.Main', { extend: 'Ext.container.Container', items: [{ xtype: 'mainlist', itemId: 'mainList', listeners: { select: 'onMainListSelect' } }] });

这里的mainlist是导航列表的 xtype,listeners里的select事件交给控制器里的onMainListSelect方法处理。注意事件名和回调方法名是一一对应的,回调函数定义在 Controller 里,不在 View 里写匿名函数,这是 MVC 模式的要求。

第二步,创建网格视图类。网格的声明方式高度声明式,每一列的行为都写在配置项里:

Ext.define('MyApp.view.grid.ExampleGrid', { extend: 'Ext.grid.Panel', xtype: 'examplegrid', title: 'Data Grid', store: { type: 'examplestore' }, columns: [{ text: 'Name', dataIndex: 'name', flex: 1 }, { text: 'Email', dataIndex: 'email', flex: 1 }, { text: 'Score', dataIndex: 'score', width: 100 }] });

列定义里dataIndex对应数据源里的字段名,flex表示列宽按比例分配,width则固定列宽。实际项目里,我会先用固定宽度把列撑开看效果,全部调通后再换成flex做响应式。

第三步,定义数据源。网格本身不关心数据从哪来,只要 store 提供给它的数据符合字段定义:

Ext.define('MyApp.store.ExampleStore', { extend: 'Ext.data.Store', alias: 'store.examplestore', fields: ['name', 'email', 'score'], proxy: { type: 'ajax', url: '/data/examples.json', reader: { type: 'json', rootProperty: 'data' } }, autoLoad: true });

alias是在整个应用里注册的短名称,让 View 里可以通过type: 'examplestore'引用。proxy负责数据源的网络层,autoLoad: true表示在 store 实例化后立即加载数据。初学阶段最常见的错误是忘了给 store 设置alias,导致 View 里引用时找不到对应类。

网格除了展示,还有几个值得默认打开的交互项。列头的排序只需在列配置中加sortable: true,如果要支持列显隐和拖拽调整宽度,需要在Ext.grid.Panel配置项里打开对应功能:

Ext.define('MyApp.view.grid.ExampleGrid', { extend: 'Ext.grid.Panel', xtype: 'examplegrid', enableColumnHide: true, enableColumnMove: true, columnLines: true, // 其他配置... });

enableColumnHide开启列显隐菜单,enableColumnMove允许拖拽列头调整顺序,columnLines控制表格列间的分割线。这些都是企业后台几乎必须开启的选项。网格工具栏里的筛选功能属于另一个大话题,原理是在 store 上挂 filter,网格组件本身不负责筛选逻辑。

4.3 实战:柱状图视图

图表功能的激活路径和网格差别很大。模板工程默认不打包图表组件,需要先在app.json的requires里加入对应依赖,这一步不做,后边引用图表类会直接报“找不到类”的错误。

在app.json的 requires 数组中加入:

"requires": [ "Charts" ]

实际配置时,依赖名字要跟 SDK 版本里的包名严格一致,如果不确定,可以先在项目根目录执行sencha package list查看当前环境里有哪几类包,避免写错包名导致构建失败。

图表类的定义方式和网格高度类似,核心区别在于 axes 和 series 的配置:

Ext.define('MyApp.view.chart.ExampleChart', { extend: 'Ext.chart.CartesianChart', xtype: 'examplechart', width: '100%', height: 400, store: { type: 'chartstore' }, axes: [{ type: 'numeric', position: 'left', title: 'Value', fields: ['value'] }, { type: 'category', position: 'bottom', title: 'Category', fields: ['name'] }], series: [{ type: 'bar', xField: 'name', yField: ['value'], style: { fill: '#4A90D9' } }] });

axes里numeric是数值轴,category是分类轴;series里的type: 'bar'声明这是个柱状图,xField和yField决定横轴和纵轴对应的数据字段。这里有一个新手高频错误:yField的值是数组,如果你写成字符串,图表能渲染但数据不上轴。

图表绑定数据的 store 和普通 store 没有本质区别,只是字段要跟xField、yField匹配。你可以使用静态数据模拟,也可以像之前网格示例那样通过 proxy 从接口拉取。开发阶段我个人习惯先用静态数组把图表的配置跑通,再接真实接口,这样排错范围会明显缩小。

4.4 实战:捕获表单

表单是三个练习里交互逻辑最重的,因为涉及字段值读取、校验、提交三件事。表单视图的写法比网格更接近传统 HTML 表单,只不过封装得更深:

Ext.define('MyApp.view.form.ExampleForm', { extend: 'Ext.form.Panel', xtype: 'exampleform', title: 'Capture Form', bodyPadding: 16, items: [{ xtype: 'textfield', name: 'username', fieldLabel: 'Username', allowBlank: false }, { xtype: 'numberfield', name: 'age', fieldLabel: 'Age', minValue: 0, maxValue: 120 }, { xtype: 'combobox', name: 'department', fieldLabel: 'Department', store: { type: 'departmentstore' }, queryMode: 'local', displayField: 'name', valueField: 'id' }], buttons: [{ text: 'Submit', formBind: true, handler: 'onSubmitForm' }] });

allowBlank: false在 textfield 上声明该字段不能为空;formBind: true时提交按钮会根据表单校验状态自动禁用。这个机制很实用,不用手写校验逻辑就能获得基础的提交前验证。handler: 'onSubmitForm'是事件方法名,定义在对应的 Controller 里。

Controller 侧的表单处理逻辑同样简单:

Ext.define('MyApp.controller.FormController', { extend: 'Ext.app.ViewController', alias: 'controller.formcontroller', onSubmitForm: function () { var form = this.lookupReference('form'); if (form.isValid()) { form.submit({ url: '/api/submitForm', success: function () { Ext.Msg.alert('Success', 'Form submitted'); }, failure: function () { Ext.Msg.alert('Error', 'Submit failed'); } }); } } });

表单数据量较小时,把表单直接读取为普通对象,再用 Ajax 提交也很常见。form.getValues()会把当前表单所有字段的值收集为一个对象。不过需要留意,getValues()拿到的只是表单字段值,不是序列化后的完整请求参数,后端接口字段名对不上时排查起来会很费劲,建议提交前用日志把值打出来确认一遍。

4.5 避坑:组件创建的三个高频问题

第一个坑是“类找不到”。现象是运行时报错:Cannot read properties of undefined (reading 'xxx'),或者直接提示缺少某个类。原因通常有两种:一是Ext.define里声明的xtype没有被自动加载,二是在app.json里漏了依赖声明。解决方法是检查requires配置,确认依赖包名称正确,然后在开发模式下重新构建。

第二个坑是 store 没有autoLoad导致页面空白表格。现象是网格组件渲染正常,但没有数据行。原因不是写错字段,而是 store 实例还没加载完成就开始消费数据。解决方法是给 store 配置autoLoad: true,或者 view 里显式调用store.load()。

第三个坑是表单验证不触发。现象是填了非法值,按钮却仍然可点。原因往往是跳过了formBind: true,或者字段在校验前就被赋了初始值,导致 valid 状态没有更新。解决方法是确认按钮开启formBind,并且在字段change事件里主动调用form.isValid()刷新状态。

这些坑在文档示例里不会直接告诉你,但在把练习项目从模板改造成真实业务组件时,出现的频率几乎百分之百。

5. 避坑指南:许可证、构建与环境配置的五个教训

5.1 许可证激活失败

现象是执行sencha generate app时提示许可证无效或未激活,项目生成到一半中止。原因多为登录信息未同步到本机,或试用的许可证已经过期。解决方式是重新运行sencha license相关命令完成认证,再确认当前使用的 Ext JS 版本和许可证类型匹配。不要尝试直接用破解或绕过机制,这既不符合合规要求,也容易让后续升级寸步难行。

5.2 依赖包版本冲突导致构建失败

现象是构建到一半,终端报一堆版本冲突或者类重复定义的错误。原因是项目里引用的第三方包和 Ext JS 内置模块之间存在同名的类或样式文件。解决方法是先定位是哪个requires引入的包出问题,再调整依赖顺序,必要时把冲突的包升级到与 SDK 匹配的版本。这类问题隐蔽,建议每添加一个依赖后单独构建一次,而不是攒到最后统一构建。

5.3 开发模式下正常、生产模式构建报错

现象是sencha app build development一切正常,切换到sencha app build production就报错。原因是代码里依赖了开发模式独有的资源或者路径,比如开发模式下自动生成的临时文件,生产构建时会被清掉。解决方式是检查是否有硬编码相对路径、依赖了resources目录下随环境变化的文件,统一改成通过app.json或构建变量引用。

5.4 修改主题后样式没有生效

现象是改了 Sass 变量或主题配置,构建后页面样式没变化。原因是主题变更需要触发完整重新构建,增量编译不一定覆盖所有使用该变量的组件。解决方式是执行一次干净的构建:

sencha app clean sencha app build development

clean会清掉旧的构建产物,然后再全量重建,基本能解决大多数样式不更新问题。

5.5 本地开发环境与团队其他成员不一致

现象是同一套代码,在不同人机器上构建结果不一样。原因是 Sencha Command 或 SDK 版本存在差异,npm 依赖也可能被锁定到不同版本。解决方式是在项目根目录维护一份依赖/版本说明文件,明确 SDK 版本和 Command 版本,并在代码仓库里固化构建脚本。团队协作时,不要手动从官网站点下载 SDK 覆盖到本地目录,尽量统一走同一个安装流程。

6. 进阶实践:模板项目改造为真实工程的两个技巧

6.1 用sencha app watch提升开发效率

如果你还在每次改代码后手动执行构建命令,强烈建议切换到sencha app watch模式。它会监听源码目录的文件变更,自动触发增量编译,并在本地启动一个开发服务器。整个过程基本是秒级反馈,改完 store 字段、调整列宽,保存后刷新浏览器就能看到新结果。

使用方式很简单,在项目根目录执行:

sencha app watch

执行后终端会输出本地访问地址,浏览器打开即可预览效果。注意watch模式下日志会有大量输出,不要看到一条警告就紧张,只要页面能正常渲染就没问题。如果改动后页面没有更新,先确认文件保存路径在app/或classic/src/下,放在resources/或项目根目录的文件不一定会触发监听。

搭配浏览器开发者工具一起用,可以省掉很多不确定的排错时间。我自己惯用的流程是:代码保存后,先在终端看构建日志有没有报错;没有报错,再切到浏览器刷新;刷新后页面有问题,直接打开控制台定位到具体的代码文件和行号。这套顺序走顺了,多数问题能在两分钟以内锁定范围。

6.2 用Ext.define把复用组件沉淀下来

模板工程里的代码结构偏向“演示型”,所有视图都平铺在 view 目录下。真实项目里,会有多个页面要用到同一个 Table 组件,或者同一个字段组合在不同表单里反复出现。这时一定要把复用逻辑沉淀成独立的类。

一个简单例子,把带筛选功能的网格封装为一个可复用的组件:

Ext.define('MyApp.view.common.FilterGrid', { extend: 'Ext.grid.Panel', xtype: 'filtergrid', config: { filterable: true }, initComponent: function () { var me = this; if (me.getFilterable()) { me.tbar = [{ xtype: 'textfield', emptyText: 'Filter...', listeners: { change: function (field) { var store = me.getStore(); store.clearFilter(); store.filter(me.getFilterProperty(), field.getValue()); } } }]; } me.callParent(); } });

这段代码的思路是:把筛选输入框塞进网格自带的工具栏,filter时按指定属性过滤当前 store,而 view 的使用方只需要设置filterable: true和filterProperty,就能在任意页面复用一套带筛选功能的网格。等到这个组件被两三个页面同时引用了,你就能真切体会到 Ext JS 这套“声明式继承 + 配置化复用”的设计价值。

还有一个小技巧:自定义组件发布到团队内部时,一定要给它一个规范化的xtype命名,比如统一定义前缀wt-,这样使用方通过xtype引用即可,不必关心完整的类名路径,团队内部协作时彼此都不会误解。

无论是做数据后台、报表系统,还是内部管理系统,Ext JS 7.8+ 的学习曲线都陡峭在“框架自己的思考和范式”上,而不是 JavaScript 语法本身。跟着文档把网格、图表、表单三个示例完整落地一遍,再回头看自己手头项目的需求,思路会清晰很多。那次做完选型切换以后,我把这套“先跑通模板、再改造组件、最后沉淀公共类”的顺序固定成了团队里的标准动作,每次接手新的 Ext JS 项目都强制走一遍,省下的返工时间远远超过初期的学习成本。希望这些踩坑记录能让你少走一段弯路,也希望这套入门资料能成为你构建复杂企业应用的稳定起点。

本文还有配套的精品资源,点击获取