快速开始
本文档为基于 Flexue 快速开发一个单页面应用(SPA)提供指导。
单页应用(英语:Single-Page Application,缩写 SPA)
是一种网络应用程序模型,它通过动态重写当前页面来与用户交互,而非传统的从服务器重新加载整个新页面。单页面应用避免了页面之间切换时打断用户体验,使应用程序更像一个桌面应用程序,从而在交互上带来更好的用户体验(英语:User Experience, 缩写 UE)
。
基于 Flexue 开发一个单页面应用涉及以下几个过程:
- 创建 Flexue 开发项目,建立初始的页面模板和应用入口。
- 创建 应用元数据,定义应用的页面交互布局,如导航菜单、工具栏、内容窗口等。
- 应用组件
(App.vue)
绑定元数据,呈现整体框架。 - 创建 视图
(View)
,定义与导航菜单对应的每一个视图组件,用于承载我们需要设计实现的功能,并视图组件注册到应用上下文。 - 定义 视图
(View)
的路由入口,可以将视图组件绑定到导航菜单,这样点击导航菜单时能够动态加载视图组件。
建立 Flexue 开发项目
安装 Flexue 之前需要先安装 Vue3.0 。建议先安装 Vue Cli ,通过 Vue Cli 创建项目会更加方便。
安装 Vue Cli
# Vue Cli 采用全局安装;
npm install -g @vue/cli
安装 Vue Cli 的官方教程:https://cli.vuejs.org/zh/guide/installation.html
创建 Vue 项目
安装成功 Vue Cli 之后,可以使用 vue 命令来创建 Vue 项目。
新创建的项目具有完整的结构和示例的组件,可直接编译运行起来。
# 创建项目:quick-starter ;
# 执行 vue create 命令时首先会提示选择 vue 的版本,请选择 vue3.0 ;
# vue create 命令会在当前目录创建子目录 quick-starter 作为项目的根目录;
# 如果项目目录已存在,则会提供操作选项,可选值覆盖、合并等操作。
vue create quick-starter
# 进入项目目录,编译项目;
cd quick-starter
npm run build
# 运行项目可以看到新建项目的示例界面;
npm run serve
由 vue cli 创建的新项目默认包含一些示例性的组件代码,包括 src/App.vue
和 src/components/HelloWorld.vue
,其中多数的内容都是不必要的,可以简单地清理掉。
- 删除
HelloWorld.vue
cd quick-starter
rm -f src/components/HelloWorld.vue
- 拆分
App.vue
为模板、样式和代码
创建三个新文件: src/App.html
,src/App.css
,src/App.js
。 其中, App.html
模板文件的内容如下:
<!DOCTYPE html>
<h1>Hello Flexue!</h1>
App.css
样式文件的内容在此阶段保留空白即可。
App.js
代码文件的内容如下:
export default {
name: "App",
};
- 修改 src/App.vue 的内容如下:
<template src="./App.html"></template>
<style src="./App.css" scoped></style>
<script src="./App.js"></script>
创建完成后,重新运行 npm run serve
,可以看到初始的页面如下:
创建完成项目后,项目目录下 src 目录中包含了创建出来的一系列默认文件,最主要的有两个: 1. 入口代码:src/main.js
2. 应用组件:src/App.vue
、src/App.html
,src/App.css
,src/App.js
。
TIP
在相关的示例中,我们习惯于把 App.vue 分拆成 html
、css
、js
三个文件,以便将模板、样式和代码分别独立管理。因此,对于 App.vue ,对应地创建出App.html
、App.css
、App.js
三个文件,同时 App.vue 的内容只是简单地引用相应的三个文件。
我们推荐这种代码组织方式,这可以使项目代码的管理更加清晰有条理,也使得 IDE 可以根据文件的扩展名提供智能提示。但这并不是必须的。
安装 Flexue
截止文档更新时,flexue 的最新版本为 1.10.6
。
TIP
注:flexue 在 npm 官方源(https://registry.npmjs.org)
的只包含里程碑稳定版本,如需最新的版本,可将库的地址设置为我们架设的 npm 源 https://npm.linkgie.com/repository/npm-group/
。
- 设置 npm 源的方法 (这是可选步骤) :
npm config set registry https://npm.linkgie.com/repository/npm-group/
- 执行安装命令:
# 在项目根目录 quick-starter 下执行命令;
npm install --save flexue
npm install --save flexue-icons
安装成功后,在项目根目录的 package.json
文件中可以看到已经加入 flexue 的依赖。
引入 flexue
在项目的入口代码 main.js
增加以下内容:
// 导入 flex
import { runApp, Logger } from "flexue";
import { FlexueIcons, ElIcons } from "flexue-icons";
import galaxyAppMetadata from './application.metadata.js';
import LOGO from "./assets/logo.png";
// 导入应用的视图组件;
import { Views, DefaultView } from "./views/views.js";
runApp(
"#page",
{
title: "MyAPP",
logo: LOGO,
views: Views,
defaultView: DefaultView,
// 设置应用元数据;
galaxyAppMetadata: galaxyAppMetadata,
//是否接入 galaxy 服务 ;默认为 false;
// enableGalaxyService: false,
// galaxyService: {
// serviceMetadataUrl: "/v2/api-docs",
// },
// http: {
// pathPrefix: "/api",
// // 排除 swagger API ,不自动添加前缀;
// prefixExcludedList: ["/v2/api-docs"],
// },
},
// 初始化;
(context, vueApp) => {
// 注册 flexue 图标库; (推荐)
// 源自 bootstrap 5.0 的 SVG 图标库, 在 flexue 中被注册为 vue 组件供使用;
vueApp.use(FlexueIcons);
// 注册 element plus 的图标库;(可选)
vueApp.use(ElIcons);
}
);
应用元数据
export default {
metadataVersion: "v1",
applicationId: "galaxyplatform.linkgie.com",
applicationName: "我的 Flexue 应用",
profileNames: ["demo", "console"],
defaultProfileName: "demo",
profiles: {
demo: {
profile: "demo",
title: "开发示例",
page: "/index.html",
order: 100,
features: {
menus: [
{
key: "BASE-ELEMENTS",
name: "基本控件",
icon: "el-svg-magic",
submenus: [
{ href: "buttons-demo.view", icon: "el-svg-user", name: "按钮", key: "buttons-demo" },
{ href: "forms-demo.view", icon: "el-svg-collection", name: "表单", key: "forms-demo" },
],
},
],
toolbar: [
{ href: "account-profile", target: "VIEW", icon: "el-svg-position", name: "我的账号", key: "tbar-account-profile" },
{ href: "/logout", target: "LOCATION", icon: "el-svg-switch-button", name: "退出登录", key: "tbar-logout" },
],
},
},
console: {
profile: "console",
title: "控制台",
page: "/index.html",
order: 200,
features: {
menus: [
{ href: "summary.view", icon: "el-svg-home-filled", name: "信息总览", key: "summary" },
{
submenus: [
{ href: "ORG-REG.view", icon: "el-svg-flag", name: "机构注册", key: "ORG-REG" },
{ href: "orig-settings.view", icon: "el-svg-flag", name: "机构设置", key: "org-settings" },
{ href: "org-job.view", icon: "el-svg-guide", name: "职务设置", key: "org-job" },
{ href: "org-department.view", icon: "el-svg-house", name: "部门管理", key: "org-department" },
{ href: "org-position.view", icon: "el-svg-guide", name: "岗位管理", key: "org-position" },
{ href: "org-employee.view", icon: "el-svg-avatar", name: "人员管理", key: "org-employee" },
],
icon: "el-svg-office-building",
name: "机构管理",
key: "ORG-MGMT",
},
{
submenus: [
{ href: "business-register.view", icon: "el-svg-compass", name: "业务注册", key: "busi-register" },
{ href: "business-datatype.view", icon: "el-svg-coin", name: "数据类型", key: "busi-datatype" },
{ href: "business-log.view", icon: "el-svg-film", name: "业务日志", key: "busi-log" },
],
icon: "el-svg-promotion",
name: "业务管理",
key: "busi",
},
{
submenus: [
{ href: "account.view", icon: "el-svg-user", name: "账号管理", key: "security-account" },
{ href: "security-role.view", icon: "el-svg-view", name: "角色管理", key: "security-role" },
{ href: "security-authorize-account.view", icon: "el-svg-user-filled", name: "账号授权", key: "security-authorize-account" },
{ href: "security-authorize-role.view", icon: "el-svg-share", name: "角色授权", key: "security-authorize-role" },
{ href: "security-organization.view", icon: "el-svg-flag", name: "机构管理", key: "security-organization" },
{ href: "security-authorize-job.view", icon: "el-svg-operation", name: "岗位授权", key: "security-authorize-job" },
],
icon: "el-svg-key",
name: "安全控制",
key: "security",
},
{
submenus: [
{ href: "email-service.view", icon: "el-svg-message-box", name: "邮件服务", key: "4-1" },
{ href: "roles.view", icon: "el-svg-info-filled", name: "系统授权", key: "4-2" },
{ href: "functions-auth.view", icon: "el-svg-upload", name: "升级管理", key: "4-3" },
],
icon: "el-svg-setting",
name: "系统设置",
key: "4",
},
],
toolbar: [
{ href: "account-profile", target: "VIEW", icon: "el-svg-position", name: "我的账号", key: "tbar-account-profile" },
{ href: "/logout", target: "LOCATION", icon: "el-svg-switch-button", name: "退出登录", key: "tbar-logout" },
],
},
},
},
dataStructure: {
models: {
"com.linkgie.flexue.demo.FormVO": {
name: "com.linkgie.flexue.demo.FormVO",
primaryKey: "uri",
labelKey: "name",
fields: [
{ key: "uri", name: "标识符", readonly: false, order: 1, type: "TEXT", iterable: false, required: false },
{ key: "code", name: "编码", readonly: false, order: 1, type: "TEXT", iterable: false, required: true },
{ key: "name", name: "名称", readonly: false, order: 2, type: "TEXT", iterable: false, required: true },
{
key: "open",
name: "状态",
readonly: false,
order: 3,
type: "BOOL",
iterable: false,
required: true,
contentType: 'enum;strict=true;true="正常";false="关闭"',
},
{
key: "custom",
name: "来源",
readonly: false,
order: 3,
type: "BOOL",
iterable: false,
required: true,
contentType: 'enum;strict=true;true="用户";false="系统"',
},
{
key: "description",
name: "描述",
readonly: false,
order: 10,
type: "TEXT",
iterable: false,
required: false,
contentType: "plain;multiline=true",
},
],
interfaces: ["com.linkgie.flexue.demo.FormVO", "com.linkgie.flexue.demo.FormVI", "com.linkgie.flexue.demo.FormVI_1"],
},
},
interfaces: {
"com.linkgie.flexue.demo.FormVI": {
name: "com.linkgie.flexue.demo.FormVI",
topic: "FormVI",
fields: [
{ key: "code", name: "编码", readonly: false, order: 1 },
{ key: "name", name: "名称", readonly: false, order: 2 },
{ key: "open", name: "状态", readonly: false, order: 3 },
],
},
"com.linkgie.flexue.demo.FormVI_1": {
name: "com.linkgie.flexue.demo.FormVI_1",
topic: "FormVI_1",
fields: [
{ key: "custom", name: "来源", readonly: false, order: 3 },
{ key: "description", name: "描述", readonly: false, order: 10 },
],
},
"com.linkgie.flexue.demo.FormVO": {
name: "com.linkgie.flexue.demo.FormVO",
topic: "FormVO",
fields: [
{ key: "uri", name: "标识符", readonly: false, order: 1 },
{ key: "code", name: "编码", readonly: false, order: 1 },
{ key: "name", name: "名称", readonly: false, order: 2 },
{ key: "open", name: "状态", readonly: false, order: 3 },
{ key: "custom", name: "来源", readonly: false, order: 3 },
{ key: "description", name: "描述", readonly: false, order: 10 },
],
},
},
},
};
至此,已经成功创建了一个 Flexue 项目,下面可以开始运用 Flexue 的元数据和页面组件快速创建您的第一个单页面应用(SPA, Single-Page Application)
。
定义页面布局
页面布局 表示一种对界面进行功能区划分以实现高效的人机交互的界面功能分布方案。例如以下是一种典型的 页面布局 :
|----------------------------------------------------------|
| 图标 | 标题 | 工具 |
| |------------------------------------------------|
|---------| |
| | |
| | |
| | 视图 |
| 导航 | |
| | |
| |------------------------------------------------|
| | 页脚 |
|----------------------------------------------------------|
Flexue
通过 元数据 来定义 页面布局 ,这个过程只需关注界面交互模式的定义,而与界面样式完全无关。 元数据 是 JSON
对象,可以十分灵活地创建和维护。
定义导航菜单
导航菜单由 菜单分组 和 菜单项 组成。
- 菜单项的基本属性如下:
// 定义一项菜单项;
{
// 唯一键;
key: "0",
// 显示的标题;
title: "首 页",
// 显示的图标;
icon: "el-svg-house",
// 点击时候触发路由跳转的目标视图的 key;
// 可选属性;
href: "index",
}
- 菜单分组的基本属性如下:
// 定义一项菜单分组;
{
// 唯一键;
key: "1",
// 显示的标题;
title: "用户管理",
// 显示的图标;
// 可选属性;
icon: "el-svg-house",
// 子菜单列表;
submenus: [
{
key: "1-1",
title: "用户注册",
//href: "",
},
{
key: "1-2",
title: "用户审核",
href: "user-audit",
},
]
}
- 导航菜单的基本属性如下:
// 定义一组导航菜单;
{
// 菜单列表
menuItems: [
// 菜单项
{
key: "0",
title: "首 页",
icon: "el-svg-house",
href: "index",
},
// 菜单组
{
key: "1",
title: "用户管理",
icon: "el-svg-user",
submenus: [
{
key: "1-1",
title: "用户注册",
href: "user-registry",
},
{
key: "1-2",
title: "用户审核",
href: "user-audit",
},
],
},
]
}
定义工具栏
工具栏 实质是操作集(Actionset)
,由*操作集元数据(ActionsetMetadata)
定义,主要由多项操作(Action)
*元数据组成。
- 操作
(Action)
的基本属性如下:
// 定义一项操作;
{
// 唯一键;
key: "btn1",
// 显示的文本;
name: "分享",
// 显示的图标的标签;flexue 默认重定义 Element+ 提供的所有图标;
icon: "el-svg-share",
// 显示模式;
// text-icon 表示同时显示文本和图标;
pattern: "text-icon",
},
- 操作集
(Actionset)
的基本属性如下:
// 定义一个操作集;
{
// 操作列表;
actions: [
// 操作;
{
key: "btn1",
name: "分享",
icon: "el-svg-share",
pattern: "text-icon",
},
// 操作;
{
key: "btn2",
name: "设置",
icon: "el-svg-setting",
pattern: "text-icon",
},
]
}
至此,已完成定义主要的元数据。
清理不必要的文件
如果不打算自定义入口组件的话,可以采用 flexue 提供的默认实现,所以可以删除在初始化 vue 项目目录时自动创建的入口组件src/App.vue
、src/App.html
,src/App.css
,src/App.js
。
至此,已完成一个最基本的单页面应用。
(View)
创建视图在 Flexue 应用中,视图(View)
是显示在应用页面中除了导航菜单和工具栏之外的中间区域的组件。视图(View)
同时也是一个 VUE 组件,只是额外多了几项视图路由配置的属性。
出于代码规范化管理的目的,推荐采用以下文件布局风格来管理视图的相关源码:
- 将所有的视图的代码统一放到
src/views
目录下。 - 每一个视图有关的代码都放到以视图名称命名的子目录下,例如,
主视图 home
的代码都在src/views/home
目录下。 - 在视图目录下可以按照单文件
.vue
组件模式,或者把模板(.html
)、代码(.js
)、样式(.css
)分别独立维护的组合文件模式,例如,主视图 home
由 4 个文件组成,统一放在视图目录src/views/home
,分别是:home.vue
,home.html
,home.css
,home.js
。 - flexue 的插件自动对放到
src/views
目录下组件,根据组件目录的路径生成 view 视图的路由路径和路由名称。
(View)
的示例
视图作为示例,在 src/views
目录下分别创建两个视图 主视图 home
和 用户注册视图 user-registry
,对应地创建如下的目录和文件。
└── src
└── views
├── home
│ ├── home.html
│ ├── home.css
│ └── home.js
└── user-registry
├── user-registry.vue
├── user-registry.html
├── user-registry.css
└── user-registry.js
主视图
home
的示例home.vue
<template src="./home.html"></template> <style src="./home.css" scoped></style> <script src="./home.js"></script>
home.html
<!DOCTYPE html> <x-view> <h1>首页</h1> </x-view>
home.css
内容暂为空白home.js
// 定义一个VUE组件作为视图; export default { name: "home-view", }
用户注册视图
user-registry
的示例user-registry.vue
<template src="./user-registry.html"></template> <style src="./user-registry.css" scoped></style> <script src="./user-registry.js"></script>
user-registry.html
<!DOCTYPE html> <x-view title="视图标题"> <h1>用户注册</h1> </x-view>
user-registry.css
内容暂为空白user-registry.js
// 定义一个VUE组件作为视图; export default { name: "user-registry-view", }
(View)
注册 视图当将一个 VUE 组件指定了视图路由配置之后,还需要将该组件注册到视图上下文。注册操作需要用到flexue
提供的两个方法(function)
:registerViews
, setDefaultView
。
- 定义视图注册的方法
(function)
出于代码规范化管理的目的,我们推荐将视图注册的源码作为独立的文件放在 src/views/views.js
,具体示例如下:
import { registerViews, setDefaultView } from "flexue";
import HomeView from "./home/home.vue";
import UserRegistryView from "./user-registry/user-registry.vue";
const Views = [
HomeView,
UserRegistryView,
];
export { Views, HomeView as DefaultView };
- 在应用入口
(main.js)
调用视图注册方法
在 main.js
中增加一下内容:
+ import { Views, DefaultView } from "./views/views.js";
+ // 指定视图;
+ runApp("#app", { views: Views, defaultView: DefaultView });
完整的 main.js
如下:
import { runApp, Logger } from "flexue";
Logger.level = "WARN";
import entryApp from "./App.vue";
import { Views, DefaultView } from "./views/views.js";
// 参数说明:
// 0 - entryElementId: 入口挂载的 HTML 模板的元素Id;
// 1 - entryComponent: 作为单应用的入口组件;
// 2 - 应用配置:
// views: 视图列表;显示于 x-page 组件的视口区域,通常是跟导航菜单操作关联,在点击菜单时显示;
// defaultView: 默认视图;应用初始状态下显示的视图;
runApp("#app", { views: Views, defaultView: DefaultView });
(View)
设置从导航菜单进入视图如果希望在单击某项导航菜单时切换至某个特定视图,这就需要在菜单的元数据上通过routeView
属性指定关联视图的唯一键。
基于前面示例中的菜单元数据,将 “首页”菜单 和 “用户注册”菜单 分别关联到 “首页”视图 和 “用户注册”视图 ,具体如下:
- 关联 “首页”菜单
// “首页”菜单
{
key: "0",
title: "首 页",
icon: "el-svg-house",
// “首页”的视图的唯一键,与“首页”视图组件中的 viewRoute.key 属性一致;
href: "home.view",
}
- 关联 “用户注册”菜单
// “用户注册”菜单
{
key: "1-1",
title: "用户注册",
// “用户注册”的视图的唯一键,与“用户注册”视图组件中的 viewRoute.key 属性一致;
href: "user-registry.view",
},
至此,以上完成了两个功能视图的定义,以此类推,按照相同的方式还可以定义更多的功能视图。
编译和预览
编译
在项目根目录下执行编译命令:
npm run build
编译成功后,所有相关制品都输出到 dist
目录下,默认情况下,dist/index.html
为最终生成的应用页面.
预览
默认情况下,项目目录下的 package.json 文件中定义了 serve 命令,用于预览站点。
npm run serve
执行成功后,命令行下将提示预览站点的 URL ,如下图:
图6-1:运行站点
在浏览器下输入 http://localhost:8881/ 将呈现如下的界面。
图6-2:应用页面预览
至此,已经建立起了一个最基础的 Flexue 前端应用。