ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Ext JS 7.8 入门实战:环境搭建到 Grid、图表与表单落地

Ext JS 7.8 入门实战:环境搭建到 Grid、图表与表单落地

简介:《Sencha Ext JS 7.8+入门指南》是一份面向具备JavaScript基础、希望构建企业级复杂前端应用开发者的PDF教程,聚焦Ext JS 7.8+的安装、项目创建与组件实践。内容从框架特点与工具包讲起,覆盖试用与商业许可证、Sencha Command安装、npm环境管理、IDE选择及项目文件结构,并延伸到基础模板中的导航编辑、视图和组件使用。读者可借助数据网格、柱状图、表单捕获等示例理解MVC架构下的组件组织与数据可视化实现;文档还提供代码示例、操作步骤以及官方文档、视频教程和社区资源指引,便于结合实际项目调试优化。资源包共1个PDF文件,约2.65MB,结构紧凑便于查阅;目前已有196人学习,适合前端开发者快速上手并系统梳理Ext JS核心概念,也为团队评估企业级前端方案提供入门参考。

1. 从装到跑通:一份 60 页的 Ext JS 7.8 入门手册能省掉哪些试错

做前端开发的人第一次碰到 Sencha Ext JS,十有八九会先问一句:这东西还用 JavaScript 写吗。答案是不光用,而且是用类的方式写,视图靠声明式配置堆出来,跟现在流行的函数组件思路几乎是两个方向。这份《First Steps with Sencha Ext JS 7.8+》入门手册六十来页,从装 Sencha Command、跑 generate 命令,一路写到数据网格、柱状图和录入表单三个能点的视图,中间夹着 MVC 分层怎么落、Ext.define和Ext.create什么时候用哪个。适合有一定 JavaScript 底子、要接手或新建企业级后台的开发者;如果手上的活只是一个活动页或者轻量管理台,扛这套框架反而是负担。

2. 环境与 toolkit 选型:Classic 和 Modern 到底该选哪个

Ext JS 7.8 最容易让人在第一步就卡住的地方,不是安装包,而是选错 toolkit。两个 toolkit 共享同一套核心类系统,但渲染机制和组件成熟度差别不小,一旦在项目跑到一半才发现表格能力不够用,改起来是全量返工。这一章先把选型判据讲清楚,再把安装环节里几个容易被忽略的勾选项说透。

2.1 两个 toolkit 的渲染差异与选型判据

官方的定位是:Modern Toolkit 主要以 CSS 渲染视图,针对平板、手机这类触摸设备做了优化;Classic Toolkit 主要以 JavaScript 构建组件,针对桌面浏览器上的 Web 应用做了优化。这句描述里最容易被误读的是"针对"两个字——两者都能跨平台跑,Classic 不是只能跑桌面,Modern 也不是只能跑手机,真正的分水岭是你项目里最重的那个页面长什么样。

我一般按下面这张表做初判,重点看第三行和第四行:

维度Classic ToolkitModern Toolkit
渲染方式以 JavaScript 构建组件 DOM,桌面端事件模型成熟以 CSS 为主渲染视图,触摸手势与响应式更顺
典型场景桌面后台、密集表格、多窗口、树形权限平板/手机端、卡片式布局、触屏操作
Grid 能力锁定列、分组、行编辑、拖拽列等能力更完整复杂表格需要自己组合扩展,部分特性缺失
组件命名类名与 xtype 与 Modern 存在差异,核心类共享同上,跨 toolkit 迁移不是零成本
学习曲线配置项多,组件文档厚,排查靠文档和源码更接近现代响应式思维,但社区示例相对少

判断逻辑很直接:项目里有没有超过 30 列、需要锁定列或者行内编辑的表格。如果有,直接上 Classic,不要在 Modern 上折腾替代方案。反过来,如果交付目标是移动端优先、页面以卡片和列表为主,Classic 的组件堆出来会显得笨重,包体积也不划算。

这份手册本身也是以 Classic Toolkit 为主线写的,作者在前言里明确说了"部分类名在两个 toolkit 之间有差异,但底层逻辑一致"。这句话的意思是:你在 Classic 里学到的 MVC 分层、Store 数据流、视图声明方式,换到 Modern 依然成立,只是xtype和个别配置项要查一下对应的类。所以入门阶段跟着一条线走通,比两边都浅尝一遍更划算。

还有一个实操层面的细节:生成项目时 toolkit 是作为第一个位置参数传进去的,写死了就定了,后期想换基本等于新建项目。所以sencha generate app classic -ext MyApp ./这条命令敲下去之前,选型应该已经拍板了,而不是边写边试。

2.2 Sencha Command 安装:安装器里哪些组件不用勾

Sencha Command 是整套工具链的入口,创建、构建、压缩、测试全靠它。安装流程本身不复杂,但有三个地方容易出问题。

第一步是拿到安装包。试用版走评估页填信息后下载,下载区里通常还会列出 Architect、Themer、Rapid Ext JS 这些配套工具,入门阶段只需要 Command 一个,其余的先放着,等真需要可视化编辑或者主题定制再回来拿。

第二步是解压并运行安装器。Windows 上大概率会弹 Microsoft Defender 的拦截提示,原因是安装包没有大规模签名分发,点"仍要运行"继续就行,这是常态不是异常。

第三步是安装组件选择界面。这里有一个明确的坑:Compass 扩展不用勾。Compass 是早期版本用来编译 Sass 的扩展,7.8 的构建链已经不需要它,勾上之后反而可能因为找不到 Ruby 环境而在构建阶段报一堆无关错误。第一次装的人看到"扩展"两个字总觉得多勾点没坏处,实际是给自己埋雷。

装完之后必须新开一个终端窗口再验证,因为 PATH 是在安装时写入系统环境变量的,已经开着的 PowerShell 读不到更新后的值:

# 新开一个 PowerShell 窗口执行,别在旧窗口里试 sencha -v # 如果上面报"不是内部或外部命令",先确认 PATH 里有没有 Cmd 的 bin 目录 echo $env:Path # 实在不行就用绝对路径调用,先让流程跑起来再回头修 PATH & "C:\Program Files\Sencha\Cmd\7.x\sencha.exe" -v

这三行命令的逻辑是:先用最轻的方式确认命令可用;不可用时打印 PATH 看安装路径有没有进去;最后用绝对路径兜底,保证不影响后续的项目生成。参数上没有什么需要调的,-v就是打印版本号。如果版本号能出来但sencha generate app报错,那问题就不在安装,而在后面的项目路径或者授权状态。

2.3 npm 管理环境的适用边界

除了装全局 Cmd,7.8 也支持用 npm 来管理环境。这条路适合两类团队:一是构建机是容器化的,不希望镜像里塞一个全局安装器;二是项目要锁版本,希望 Cmd 的版本跟着仓库走而不是跟着开发机走。

常见做法是在项目根目录初始化包管理,把 Cmd 和框架依赖写进devDependencies,然后通过npx调用本地版本,避免和全局版本打架:

# 1. 确认 Node 版本,7.8 的构建链在 12.x 以上比较稳 node -v npm -v # 2. 初始化包管理,生成 package.json npm init -y # 3. 把发布渠道给出的 Cmd 包写进 devDependencies 后安装 npm install # 4. 用 npx 调本地版本,验证是否可用 npx sencha -v

逻辑说明:npx会优先在node_modules/.bin里找可执行文件,找不到才回落到全局,所以只要本地装了,团队里所有人跑的都是同一个版本。参数上唯一要注意的是不要把包名凭记忆手敲,不同发布渠道的包名可能有出入,照着渠道页面上的写法抄,不然会卡在404 Not Found上浪费时间。

需要提醒的是,npm 方式并不改变 Cmd 的行为,它只是换了个安装位置。项目生成、构建、watch 的命令完全一致,所以入门阶段先用全局安装把流程跑通,等团队要上 CI 了再切 npm 也不迟,不用一上来就两条路都折腾。

2.4 试用与商业两条授权路径对生成命令的影响

手册把项目创建拆成了两章:试用授权项目和商业授权项目。这两条路径在"生成项目"这一步的差别,主要落在可用模板的数量上。

试用版走默认模板就够跑通后续所有示例,生成命令很朴素,一个 toolkit 参数加应用名和输出目录就完事。商业版在激活之后,sencha generate app会多出若干预置模板可选,适合快速起一个带侧边导航和仪表盘骨架的项目。

模板清单别靠猜,直接用帮助命令看当前环境里到底注册了哪些:

# 打印 generate app 的全部参数与可用模板,比翻文档快 sencha generate app --help # 指定模板生成(模板名以上面命令的输出为准) sencha generate app -t <模板名> -ext MyApp ./

这里的关键是--help的输出就是当前这台机器上的真实状态,它同时反映了 Cmd 版本、授权状态和已安装的模板包。很多人对着旧文档里的模板名敲命令,报"模板不存在",其实不是命令写错了,是授权方式或者版本对不上。养成先看 help 再敲命令的习惯,能省掉一半的无效搜索。

3. 项目骨架:sencha generate app 一条命令产出的目录怎么读

生成项目这条命令只有一行,但它产出的目录结构决定了后面所有代码往哪放。我见过不少人把视图写在app/view下,把 store 写在classic/src里,最后构建时一部分类加载不到,控制台报Unable to load class,排查半天才发现是路径和命名空间对不上。这一章把生成命令的参数、目录职责和导航改造讲清楚。

3.1 生成命令的参数拆解

手册里给的示例命令是:

# 在项目文件夹内执行,生成一个使用 Classic Toolkit 的应用 sencha generate app classic -ext MyApp ./

拆开看四个部分。classic是 toolkit 类型,决定用哪套组件库,另一可选值是modern。-ext表示使用已安装的 Ext JS SDK 来生成应用,而不是从 workspace 里的本地包解析,入门阶段没有多项目共享的需求,加这个参数最省事。MyApp是应用名,必须符合 JavaScript 标识符规则,不能带空格、不能以数字开头,而且它会同时作为顶层命名空间,后面所有类都长成MyApp.view.xxx这个样子。./是输出目录,相对当前路径。

一个反直觉的点:应用名一旦定下来,改名成本很高。它不只是个标签,会出现在app.json、类名前缀、构建产物目录名里。所以敲命令之前先把名字想好,别用test、demo这种临时名字,后面要么忍着要么重建。

命令执行完之后还不能直接打开页面,必须先构建一次,因为generate只生成源码骨架,编译后的 CSS 和 bootstrap 文件要构建阶段才产出:

# 进入应用目录 cd MyApp # 首次构建,产出开发环境可用的资源和 bootstrap sencha app build development # 开发期用 watch,改完源码刷新浏览器即可,不用反复 build sencha app watch

sencha app build接受三个环境参数:development保留可读源码,带 source map,适合日常开发;testing会做压缩但保留一定的调试信息;production做完整压缩和混淆,产物直接可部署。刚生成完的项目如果跳过 build 直接开浏览器,看到的是一片白,这不是代码写错了,是资源还没生成——这个坑在第 5 章还会再展开。

3.2 目录结构与构建产物位置

生成之后的项目结构大致如下,重点是搞清楚哪些目录是手写的、哪些是构建出来的、哪些绝对不该手改:

路径作用要不要手改
app/跨 toolkit 共享的模型、store、工具类要,业务逻辑放这
app/view/共享视图类(不依赖特定 toolkit 的部分)要
classic/src/view/Classic 专属视图,如主框架、导航要,示例里的视图多在这里
classic/sass/主题变量与自定义样式要,但先改变量别直接覆盖
resources/图片、字体等静态资源要
build/构建产物,按环境分子目录不要,每次构建会覆盖
index.html入口页,引用构建后的 bootstrap一般不动
app.json应用级配置,包依赖、构建选项都在这要,加 charts、filters 等包时改这里
bootstrap.js类加载器引导文件,构建生成不要手改

这张表里最值得记住的是app.json。后面要加图表功能,得在这里的requires里显式声明charts包,否则图表类加载不到。同理,表格的筛选特性也要在这里引入。很多人习惯在代码里Ext.require临时解决,能跑起来但构建时会被摇树逻辑漏掉,还是得回到app.json里补。

build/目录下的产物按环境分:build/development/MyApp/、build/testing/MyApp/、build/production/MyApp/。排查"线上空白但本地正常"这类问题时,第一件事就是去对应的产物目录里看index.html引用的 JS 文件是不是存在、路径是不是对得上,比在源码里瞎猜快得多。

3.3 改导航:在 Main 视图里加菜单项

生成出来的默认模板带一个侧边导航加中央内容区的骨架,三个示例视图要挂进去,改的就是这个骨架。典型写法是在主视图里用一个treelist做导航,内容区放一个tabpanel,两者通过reference和lookup建立联系:

// classic/src/view/main/Main.js Ext.define('MyApp.view.main.Main', { extend: 'Ext.panel.Panel', xtype: 'app-main', controller: 'main', layout: 'border', // 边界布局:西侧导航 + 中央内容 items: [{ region: 'west', xtype: 'treelist', width: 200, reference: 'navigation', // 控制器里用 lookup('navigation') 拿到 store: { root: { expanded: true, children: [ { text: '数据网格', leaf: true, xtype: 'gridview' }, { text: '柱状图', leaf: true, xtype: 'examplechart' }, { text: '录入表单', leaf: true, xtype: 'captureform' } ] } } }, { region: 'center', xtype: 'tabpanel', reference: 'mainCardPanel', items: [] }] });

这段配置的逻辑是:layout: 'border'把面板切成东西南北中五块,这里只用了西和中央两块;treelist的每个叶子节点上挂了一个xtype,作为"点这个节点该打开哪个视图"的元数据;reference是给控制器用的句柄,不用自己维护组件 ID。

配套的控制器负责在节点被选中时往中央区域加标签页:

// classic/src/view/main/MainController.js Ext.define('MyApp.view.main.MainController', { extend: 'Ext.app.ViewController', alias: 'controller.main', onNavigationSelect: function (tree, node) { var xtype = node.get('xtype'); // 从节点上取元数据 if (!xtype) { return; } // 非叶子节点直接忽略 var cardPanel = this.lookup('mainCardPanel'); var existing = cardPanel.down('component[xtype=' + xtype + ']'); if (existing) { // 已打开过就切过去,避免重复创建 cardPanel.setActiveTab(existing); return; } cardPanel.add({ xtype: xtype, closable: true }); } });

参数说明:node.get('xtype')读的是节点自定义字段,treelist允许在节点数据里塞任意键,这里借它做路由。this.lookup('mainCardPanel')依赖前面配置的reference名字,两处必须一致,拼错不会报错,只会返回null,然后下一行就抛Cannot read property 'down' of null,这是很典型的静默失败。

还有一个容易忘的收尾:控制器里的方法要真正被调用,得在视图上绑定事件。treelist的选中事件是selectionchange,在Main.js的listeners里加上{ selectionchange: 'onNavigationSelect' }才算接通。事件名拼错的话页面能渲染、点击没反应、控制台干净,属于最难查的一类问题。

4. 视图与组件:Ext.define 到 Grid、Chart、Form 的三条落地链路

手册的第三章到第五章是三个完整视图:数据网格、柱状图、录入表单。它们分别对应 Ext JS 三条最常见的技术链路——数据展示、数据可视化、数据录入。这一章把三条链路拆开,每条都落到可抄的配置上,同时说清楚为什么这么配。

4.1 Ext.define 与 Ext.create 的分界

这两个方法经常被混着用,但它们干的是完全不同的事。Ext.define是向类系统注册一个类,注册本身不产生实例,也不创建任何 DOM;Ext.create是拿一个已注册的类名去实例化,每次调用返回一个新对象。手册专门用一节讲这个对比,原因是 Ext JS 的类系统是懒加载的,define只是登记,真正加载脚本发生在第一次被引用的时候。

// 声明类:注册到 MyApp 命名空间下,此时还没有任何 DOM Ext.define('MyApp.view.main.GridView', { extend: 'Ext.grid.Panel', xtype: 'gridview', // 给延迟创建留的别名 title: '数据网格' }); // 运行时实例化:每次调用都是独立对象,配置会与类默认值合并 var grid = Ext.create('MyApp.view.main.GridView', { title: '运行时覆盖的标题' // 覆盖 define 里的默认配置 }); // 更常见的写法:交给 xtype,由容器在渲染时按需创建 var another = Ext.widget('gridview');

逻辑说明:Ext.create的第二个参数是实例级配置,会和类定义里的配置做深合并,数组类型按下标覆盖,对象类型递归合并。这意味着在create里传columns会整体替换掉类里定义的列,而不是追加——这一点在动态改列的时候经常翻车。

Ext.widget('gridview')和Ext.create('MyApp.view.main.GridView')效果一样,前者靠xtype别名查找,后者靠全类名,写视图内部代码时用xtype更解耦。至于重复define同名类,不同版本表现不一致,有的覆盖有的告警,稳妥做法是改配置不要改定义,实在要换实现就换个类名。

4.2 Data Grid:Store 字段与列定义对齐

Grid 的核心是 Store 和列定义的字段映射,写错一个dataIndex就是整列空白。先看 Store:

// app/store/ExampleStore.js Ext.define('MyApp.store.ExampleStore', { extend: 'Ext.data.Store', alias: 'store.examplestore', // 对应配置里的 { type: 'examplestore' } fields: [ { name: 'id', type: 'int' }, { name: 'name', type: 'string' }, { name: 'value', type: 'float' }, { name: 'date', type: 'date', dateFormat: 'Y-m-d' } // Ext 的格式串 ], data: [ { id: 1, name: '条目 A', value: 12.5, date: '2025-01-08' }, { id: 2, name: '条目 B', value: 30.2, date: '2025-01-09' } ], proxy: { type: 'memory' } // 本地数据;接后端换成 ajax + url });

参数说明三处。fields里的type不是装饰,它决定了排序、筛选、聚合的行为,value写成string的话按字符串排序,9会排在10后面。dateFormat用的是 Ext 自己的格式串,Y是四位年、m是两位月、d是两位日,和常见日期库的YYYY-MM-DD写法不同,照搬会解析成Invalid Date。proxy用memory表示不请求后端,接真实接口时换成ajax并配url,Store 的其余部分不用动。

Grid 视图:

// classic/src/view/main/GridView.js Ext.define('MyApp.view.main.GridView', { extend: 'Ext.grid.Panel', xtype: 'gridview', title: '数据网格', store: { type: 'examplestore' }, // 按别名取 store,不用手动 new columnLines: true, selModel: 'rowmodel', // 行选择;需要多选换 checkboxmodel columns: [ { text: '#', dataIndex: 'id', width: 70 }, { text: '名称', dataIndex: 'name', flex: 1, filter: 'string' }, { text: '数值', dataIndex: 'value', flex: 1, filter: 'number', renderer: function (v) { // 只影响显示,不影响底层值,排序仍按原始数字 return Ext.util.Format.number(v, '0,000.00'); } }, { text: '日期', dataIndex: 'date', width: 120, xtype: 'datecolumn', format: 'Y-m-d' } ] });

逻辑说明:store: { type: 'examplestore' }是简写形式,等价于Ext.create('MyApp.store.ExampleStore')后赋给store,区别在于这种写法会在渲染时才创建。flex: 1和width同时给以flex优先,建议列宽要么全用flex均分要么全用width固定,混着写在不同分辨率下容易错位。renderer是纯展示层,返回值只影响单元格文本,filter: 'number'走的是 Store 里的原始值,这两者不能互相替代。

筛选器那一列配置了filter: 'number',但光配这个不会出现筛选图标,必须在app.json的requires里引入Ext.grid.filters包。这个静默失效是新手最容易踩的一个坑,配置全对、表头就是没有漏斗图标,也不报错。

4.3 Bar Chart:坐标轴配置与菜单挂载

图表在 Ext JS 7 里是独立包,先确认app.json里加了依赖:

{ "requires": [ "charts", "Ext.grid.filters" ] }

改完app.json要重新跑一次sencha app build development或者让app watch重启,否则依赖不会生效。数据部分和 Grid 一样的思路,单独建一个 ChartStore:

// app/store/ChartStore.js Ext.define('MyApp.store.ChartStore', { extend: 'Ext.data.Store', alias: 'store.chartstore', fields: [ { name: 'month', type: 'string' }, // 类目轴字段 { name: 'value', type: 'float' } // 数值轴字段 ], data: [ { month: '1 月', value: 120 }, { month: '2 月', value: 186 }, { month: '3 月', value: 152 }, { month: '4 月', value: 240 } ] });

图表类本身:

// classic/src/view/chart/ExampleChart.js Ext.define('MyApp.view.chart.ExampleChart', { extend: 'Ext.chart.CartesianChart', xtype: 'examplechart', title: '月度数据', store: { type: 'chartstore' }, insetPadding: 20, // 内边距,给轴标签留空间 axes: [{ type: 'numeric', position: 'left', fields: ['value'], // 必须与 series 的 yField 同名 title: { text: '数量' }, grid: true // 横向网格线 }, { type: 'category', position: 'bottom', fields: ['month'] // 必须与 xField 同名 }], series: [{ type: 'bar', xField: 'month', yField: 'value', style: { fill: '#3d7eff', minGapWidth: 8 }, label: { field: 'value', display: 'insideEnd' } }] });

参数说明:axes[].fields和series里的xField/yField必须是 Store 里真实存在的字段名,写错不报错,只是图上什么都没有。position决定轴在哪个边,类目轴一般放bottom,数值轴放left。grid: true只是画网格线,跟 Grid 组件没关系,别混淆。

往导航里挂的时候,在Main.js的treelist节点上加{ text: '柱状图', leaf: true, xtype: 'examplechart' }就行,第 3 章的控制器会自动按xtype打开。必须注意容器高度:图表的尺寸来自父容器,如果中央区域是tabpanel且没有给layout: 'fit'或者固定高度,图表会渲染成一个 0 高度的空壳,DOM 里有节点、控制台没报错、屏幕上什么都看不见。这是图表类问题里出现频率最高的一种。

4.4 Capture Form:View 与 Controller 的职责划分

表单是三个视图里唯一强依赖控制器的。视图负责字段和校验规则,控制器负责取值和提交,两者通过controller配置和handler字符串绑定:

// classic/src/view/form/CaptureForm.js Ext.define('MyApp.view.form.CaptureForm', { extend: 'Ext.form.Panel', xtype: 'captureform', controller: 'captureform', // 绑定控制器别名 title: '信息录入', bodyPadding: 16, layout: { type: 'vbox', align: 'stretch' }, defaults: { // 所有子字段的公共配置 xtype: 'textfield', labelWidth: 90, margin: '0 0 12 0' }, items: [ { fieldLabel: '名称', name: 'name', allowBlank: false }, { fieldLabel: '数量', name: 'qty', xtype: 'numberfield', minValue: 1, value: 1 }, { fieldLabel: '日期', name: 'date', xtype: 'datefield', format: 'Y-m-d' }, { fieldLabel: '备注', name: 'remark', xtype: 'textareafield', grow: true } ], buttons: [ { text: '重置', handler: 'onReset' }, { text: '提交', formBind: true, handler: 'onSubmit' } // 校验不通过时自动禁用 ] });

参数说明:defaults里的配置会下发给所有items,字段自己写的同名配置优先,这样能省掉大量重复。每个字段的name是取值时的键,不写name的字段getValues()拿不到,这是表单取值为空的第一大原因。formBind: true让按钮跟随表单校验状态,名称没填时提交按钮是灰的,属于零成本提升体验的配置。

控制器:

// classic/src/view/form/CaptureFormController.js Ext.define('MyApp.view.form.CaptureFormController', { extend: 'Ext.app.ViewController', alias: 'controller.captureform', onSubmit: function () { var form = this.getView().getForm(); // Panel 上先 getForm 才是 Form 实例 if (!form.isValid()) { return; } // 双保险,formBind 之外再挡一次 var values = form.getValues(); // 一次拿全部字段,别逐个 lookup console.log(values); Ext.Msg.alert('已提交', Ext.encode(values)); form.reset(); // 还原到字段的初始值 }, onReset: function () { this.getView().getForm().reset(); } });

逻辑说明:this.getView()返回的是Ext.form.Panel,它本身没有getValues方法,必须先getForm()拿到Ext.form.Basic实例再调。这一步混淆导致getValues is not a function的报错非常常见。form.getValues()返回的是纯对象,键就是字段的name,未渲染或隐藏的字段默认不包含在内,需要包含的话传参form.getValues(true)。form.reset()是恢复到字段的初始值而不是清空,value: 1的字段重置后还是 1。

5. 排查清单:五类高频翻车现场与定位顺序

Ext JS 的报错风格是"要么不说话,要么一次说一堆"。很多问题在控制台里连红字都没有,只是页面不动,所以排查顺序比排查技巧更重要。这一章按我实际踩过的顺序列五条,每条都按现象、原因、解决三段写。

5.1 命令不可用与首屏空白

现象:新开的终端里敲sencha -v提示"不是内部或外部命令",或者命令能用但生成完项目打开浏览器一片白。

原因:前者是安装时写入的 PATH 没有被当前终端读到,老窗口不会自动刷新环境变量。后者是跳过了构建步骤——sencha generate app只生成源码骨架,bootstrap.js和编译后的 CSS 是sencha app build阶段才产出的,index.html引用的恰恰是构建产物,所以直接双击打开必然是白的。

解决:命令问题换新终端,仍不行就查 PATH 或用绝对路径调用,先让流程能走。白屏问题先跑一次sencha app build development,再用sencha app watch起本地服务访问,不要用file://协议直接开 HTML——部分资源请求在文件协议下会被浏览器拦掉,表现同样是白屏,容易和构建问题混在一起。

5.2 Grid 有行无值、Chart 不出图

现象:Grid 渲染出了表头和行数,但每个单元格都是空的;或者 Chart 区域有边框有标题,里面什么都没有。

原因:Grid 这边九成是dataIndex和 Store 的fields.name对不上,或者 Store 用了model配置却把字段定义在 Store 的fields里,两套字段定义互相覆盖。Chart 这边通常是父容器高度塌陷,图表按 100% 撑开父容器,父容器本身高度是 0,结果就是 0。

解决:Grid 先在控制台打印grid.getStore().first().getData(),看拿到的是不是预期字段名,再回头核对列配置,字段定义只在model或fields里写一份。Chart 先给图表类加一个明确的height: 320,能出来就说明是布局问题,再回头给父容器加layout: 'fit',用布局解决而不是硬写高度。

还有一个连带项:Chart 的axes[].fields写错不会报错,图和轴都在,就是柱子没有高度。核对字段名的时候三个地方一起看——Store 的fields、axes的fields、series的xField/yField。

5.3 表单取值为 undefined、feature 静默失效

现象:表单填了内容,控制器里getValues()返回的对象里没有对应键;或者 Grid 列上配了filter,表头就是不出现筛选图标。

原因:表单字段漏写name,getValues()是按name收集的,没有name的字段直接被跳过。筛选图标不出现是Ext.grid.filters包没在app.json的 `requires

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

返回列表