【前端】移动端工程结构最佳实践(v4/v5)

阅读之前

你应该:

  • 了解node与npm相关内容
  • 了解lerna包管理工具的相关内容
  • 了解git仓库的相关内容
  • 了解rollup的相关内容

工程结构包示例

Vue项目结构包下载-v4.7.x
Vue项目结构包下载-v5.2.x

工程结构详解

工程结构
├── packages
│   ├── kunlun-mobile-boot
│   │   ├── package.json
│   │   ├── public
│   │   │   ├── favicon.ico
│   │   │   └── index.html
│   │   ├── src
│   │   │   ├── main.ts
│   │   │   └── shim-vue.d.ts
│   │   ├── tsconfig.json
│   │   └── vue.config.js
│   ├── kunlun-module-mobile-demo
│   │   ├── scripts
│   │   │   ├── postpublish.js
│   │   │   └── prepublish-only.js
│   │   ├── src
│   │   │   ├── index.ts
│   │   │   └── shim-vue.d.ts
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── rollup.config.js
│   │   └── tsconfig.json
│   └── kunlun-modules-mobile-demo
│       ├── scripts
│       │   ├── build.config.js
│       │   ├── postpublish.js
│       │   └── prepublish-only.js
│       ├── packages
│       │   ├── module-demo1
│       │   │   ├── index.ts
│       │   │   ├── package.json
│       │   │   ├── rollup.config.js
│       │   │   └── src
│       │   │       ├── index.ts
│       │   │       └── shim-vue.d.ts
│       │   ├── module-demo2
│       │   │   ├── index.ts
│       │   │   ├── package.json
│       │   │   ├── rollup.config.js
│       │   │   └── src
│       │   │       ├── index.ts
│       │   │       └── shim-vue.d.ts
│       │   └── module-dependencies
│       │       ├── index.ts
│       │       ├── package.json
│       │       └── src
│       │           └── dependencies.ts
│       ├── lerna.json
│       ├── tsconfig.json
│       └── package.json
├── lerna.json
└── package.json

壳工程

├── packages
│   └── ......
├── lerna.json
└── package.json

壳工程仅用于将多个包进行统一安装,并通过lerna包管理工具进行相关的软链接操作。

壳工程包含了除了node和npm外其他相关的安装依赖,如lernarimraf等。这样可以避免由于开发环境差异带来的一系列问题。

我们建议所有开发人员均使用相同的壳工程进行开发,因此,我们有必要将其提交并推送到git远程仓库进行共享。

壳工程目录下执行npm install命令即可完成一系列安装操作。

需要注意的是,lerna.json中定义了所有需要统一安装的子工程路径,安装前需要确认是否满足当前需要,但这些修改都应该只在本地完成,而不应该推送到git远程仓库

安装完成后,所有公共依赖均会安装在壳工程node_modules目录下,每个子工程的node_modules中仅包含软链接目录。并且在壳工程node_modules目录下不会出现本地已经存在的工程依赖。

单工程包管理

├── packages
│   └── kunlun-module-mobile-demo
│       ├── scripts
│       │   ├── postpublish.js
│       │   └── prepublish-only.js
│       ├── src
│       │   ├── index.ts
│       │   └── shim-vue.d.ts
│       ├── index.ts
│       ├── package.json
│       ├── rollup.config.js
│       └── tsconfig.json
├── lerna.json
└── package.json

作为一个独立的工程,它可以是一个项目的总工程,也可以是多个项目共同使用的子工程。这也是最常见的工程包结构。

通常情况下,kunlun-module-mobile-demo工程会使用一个独立的git仓库进行管理。

通过kunlun-module-mobile-demo打包生成的@kunlun/module-mobile-demo包,将可以被发布到npm仓库或其他地方,供其他工程或项目使用。

启动工程中的package.json中添加"@kunlun/module-mobile-demo": "~1.0.0"相关依赖进行引入。并在入口文件main.ts中导入相关的js和css相关内容。

如示例中所示:

// 按需引入
import '@kunlun/module-mobile-demo/dist/kunlun-module-mobile-demo.css';

import '@kunlun/module-mobile-demo';

需要注意的是,在本地开发中,css文件相关内容应该是被临时注释的,因为通过import '@kunlun/module-mobile-demo';已经将相关内容引入,无需使用打包好的css文件。

多工程包管理

├── packages
│   └── kunlun-modules-mobile-demo
│       ├── scripts
│       │   ├── build.config.js
│       │   ├── postpublish.js
│       │   └── prepublish-only.js
│       ├── packages
│       │   ├── module-mobile-demo1
│       │   │   ├── index.ts
│       │   │   ├── package.json
│       │   │   ├── rollup.config.js
│       │   │   └── src
│       │   │       ├── index.ts
│       │   │       └── shim-vue.d.ts
│       │   ├── module-mobile-demo2
│       │   │   ├── index.ts
│       │   │   ├── package.json
│       │   │   ├── rollup.config.js
│       │   │   └── src
│       │   │       ├── index.ts
│       │   │       └── shim-vue.d.ts
│       │   └── module-mobile-dependencies
│       │       ├── index.ts
│       │       ├── package.json
│       │       └── src
│       │           └── dependencies.ts
│       ├── lerna.json
│       ├── tsconfig.json
│       └── package.json
├── lerna.json
└── package.json

单工程包管理类似,多工程包管理方案为了解决整体结构复杂,并且功能需要做最小化拆分的场景。在多工程包中提供的module-dependencies作为整个工程包的总入口向外提供功能。

如示例中所示:

// 视情况按需引入
// import '@kunlun/module-mobile-demo/dist/kunlun-module-mobile-demo1.css';
// import '@kunlun/module-mobile-demo/dist/kunlun-module-mobile-demo2.css';

import '@kunlun/modules-mobile-dependencies';

与单工程包唯一的区别在于启动工程的导入方式有所不同,仅需通过import '@kunlun/modules-mobile-dependencies';导入所有功能即可。在总入口处将对所有子工程包进行统一的导出并且包含了对应的css相关内容。

特别的是,如果多个工程间的css样式出现冲突,但又需要依赖js文件时,css相关内容将不能在总入口处进行导入,需要放在对应的启动工程中进行导入处理。原则上,我们应该避免这种情况的发生。

启动工程

├── packages
│   └── kunlun-mobile-boot
│       ├── package.json
│       ├── public
│       │   ├── favicon.ico
│       │   └── index.html
│       ├── src
│       │   ├── main.ts
│       │   └── shim-vue.d.ts
│       ├── tsconfig.json
│       └── vue.config.js
├── lerna.json
└── package.json

作为vue项目的启动工程,它应该仅用于工程组合,即选择性的使用某几个工程进行启动。

rollup脚本简述

通过package.json构建所需工程名称

const buildName = (name) => {
  const libraryName = name.replace('@', '').replace('/', '-');
  const pathName = libraryName.substring(packagePrefix.length);
  const camelCaseName = libraryName.replace(/-(\w)/g, (all, letter) => letter.toUpperCase());
  return { libraryName, pathName, camelCaseName };
};

使用方法:

import pkg from './package.json';

const res = buildName(pkg.name);
const libraryName = res.libraryName;

转换结果:@kunlun/module-mobile-demo -> kunlun-module-mobile-demo.esm.js

多工程包构建脚本的使用

方法签名
function rollupConfig(name = '', external = [], hasSCSS = true, extendPlugins)
示例脚本
import pkg from './package.json';
import rollupConfig from '../../scripts/build.config.js';

export default rollupConfig(pkg.name, [
  'vue',
  'lodash-es',
  'vant',
  '@ant-design/icons-vue',
  '@kunlun/mobile-dependencies',
  '@kunlun/vue-ui-mobile-vant'
], false);
  • pkg.name:通过当前路径的package.json获取name属性。
  • external:打包时排除当前工程中导入的第三方包,原则上需要全部排除,否则会出现内存变量不一致的问题。
  • hasSCSS:是否进行scss编译。当使用scss时,必须开启。
  • extendPlugins:扩展插件。

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/49.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
nation's avatarnation数式员工
Previous 2023年6月20日 pm4:07
Next 2023年11月2日 pm1:58

相关推荐

  • Oinone移动端快速入门

    介绍 oinone的pc端的页面默认都可以在移动端直接访问。自定义mask、layout、视图组件、字段组件、动作组件方式都参考pc端实现。目前移动端的UI组件是基于vant@3.6.0版本开发,如有自定义部分的代码,推荐使用该组件库。 “注意”: 由于移动端和pc端在交互上的巨大差异,两端用的是不同的UI组件库是,按照此约定开发的自定义组件在两端也是无法相互兼容的,在pc端自定义的组件或者页面,不会在移动端自动适配,需要自行开发对应的移动端组件或者页面。 工程搭建 移动端很多交互跟pc端差异很大,所以移动端的我们采用的方案是独立用一套移动端的UI框架实现,而不是简单的做页面布局自适应,所以移动端需要跟pc端一样独立部署一套前端工程。 参考文档:【前端】移动端工程结构最佳实践(v4/v5) 如何区分多端 在界面设计器设计页面的时候,可以通过顶部的多端设备的图标切换在各端的页面效果。 pc端页面 切换为移动端后的页面 注意:大部分情况下,pc端和移动端可以共享一个设计的页面,然后通过上面的方法区分两端,如果移动端的页面交互差异很大,那更推荐独立新建一个页面专门给移动端使用。 模块 模块在定义的时候可以通过注解@Module.clientTypes决定当前模块在哪些端展示 package pro.shushi.pamirs.demo.api; import org.springframework.stereotype.Component; import pro.shushi.pamirs.business.api.BusinessModule; import pro.shushi.pamirs.core.common.CommonModule; import pro.shushi.pamirs.file.api.FileModule; import pro.shushi.pamirs.meta.annotation.Module; import pro.shushi.pamirs.meta.base.PamirsModule; import pro.shushi.pamirs.meta.common.constants.ModuleConstants; import pro.shushi.pamirs.meta.enmu.ClientTypeEnum; import pro.shushi.pamirs.user.api.UserModule; @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", // 客户端类型,默认是PC和MOBILE端都展示 clientTypes = {ClientTypeEnum.PC, ClientTypeEnum.MOBILE}, // 登录后默认访问 priority 值最小的模块 priority = 1, dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, BusinessModule.MODULE_MODULE, FileModule.MODULE_MODULE } ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) public class DemoModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core"; public static final String MODULE_NAME = "DemoCore"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.demo" }; } } 菜单 界面设计器设置方式 在菜单设置的时候可以选择“显示设备” 低代码设置方式 通过注解@UxMenu.clientTypes设置显示设备 package pro.shushi.pamirs.demo.core.init.menu; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.demo.api.model.DemoItemCategory; import pro.shushi.pamirs.meta.enmu.ClientTypeEnum; @UxMenus() public class DemoMenus implements ViewActionConstants { // 同时在pc端和移动端显示 @UxMenu(value = "商品中心", clientTypes = {ClientTypeEnum.PC, ClientTypeEnum.MOBILE}) class ItemPMenu{ @UxMenu("商品类目") class DemoItemAndCateMenu { // 只在pc端显示 @UxMenu(value = "商品管理", clientTypes = {ClientTypeEnum.PC}) @UxRoute(DemoItem.MODEL_MODEL) class DemoItemMenu { } // 只在移动端显示 @UxMenu(value = "类目管理", clientTypes = {ClientTypeEnum.MOBILE}) @UxRoute(DemoItemCategory.MODEL_MODEL) class DemoItemCategoryMenu { } } } } 组件 界面设计器选中组件后,可以在右边属性面板看到“显示设备”的配置,默认为空,为空则表示在pc端和移动端都显示

    2024年9月19日
    2.2K00
  • 自定义组件之手动渲染基础(v4)

    阅读之前 你应该: 了解DSL相关内容。母版-布局-DSL 渲染基础(v4) 了解SPI机制相关内容。组件SPI机制(v4.3.0) 了解组件相关内容。 Class Component(ts)(v4) 自定义组件之自动渲染(组件插槽的使用)(v4) 为什么需要手动渲染 在自定义组件之自动渲染(组件插槽的使用)(v4)文章中,我们介绍了带有具名插槽的组件可以使用DSL模板进行自动化渲染,并且可以用相对简单的方式与元数据进行结合。 虽然自动化渲染在实现基本业务逻辑的情况下,有着良好的表现,但自动化渲染方式也有着不可避免的局限性。 比如:当需要多个视图在同一个位置进行切换。 在我们的平台中,界面设计器的设计页面,在任何一个组件在选中后,需要渲染对应的右侧属性面板。每个面板的视图信息是保存在对应的元件中的。根据元件的不同,找到对应的视图进行渲染。在单个视图中使用自动化渲染是无法处理这一问题的,我们需要一种可以局部渲染指定视图的方式,来解决这一问题。 获取一个视图 使用ViewCache获取视图 export class ViewCache { /** * 通过模型编码和名称获取视图 * @param model 模型编码 * @param name 名称 * @param force 强制查询 * @return 运行时视图 */ public static async get(model: string, name: string, force = false): Promise<RuntimeView | undefined> /** * 通过模型编码、自定义名称和模板获取编译后的视图(此视图非完整视图,仅用于自定义渲染使用) * @param model 模型编码 * @param name 名称(用作缓存key) * @param template 视图模板 * @param force 强制查询 * @return 运行时视图 */ public static async compile( model: string, name: string, template: string, force = false ): Promise<RuntimeView | undefined> } ViewCache#get:用于服务端定义视图,客户端直接获取完整视图信息。 ViewCache#compile:用于客户端定义视图,通过服务端编译填充元数据相关信息,但不包含视图其他信息。 自定义一个带有具名插槽的组件,并提供切换视图的相关按钮 以下是一个自定义组件的完整示例,其使用ViewCache#compile方法获取视图。 view.ts const template1 = `<view> <field data="id" invisible="true" /> <field data="code" label="编码" /> <field data="name" label="名称" /> </view>`; const template2 = `<view> <field data="id" invisible="true" /> <field data="name" label="名称" /> <field data="code" label="编码" /> </view>`; export const templates = { template1, template2 }; ManualDemoWidget.ts import { BaseElementWidget, createRuntimeContextForWidget, FormWidget, RuntimeView, SPI, ViewCache, ViewType, Widget } from '@kunlun/dependencies'; import ManualDemo from './ManualDemo.vue'; import { templates } from './view'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' })) export class ManualDemoWidget extends BaseElementWidget { private formWidget: FormWidget | undefined; public…

    2023年11月1日
    1.2K00
  • 自定义表格支持合并或列、表头分组

    本文将讲解如何通过自定义实现表格支持单元格合并和表头分组。 点击下载对应的代码 在学习该文章之前,你需要先了解: 1: 自定义视图2: 自定义视图、字段只修改 UI,不修改数据和逻辑3: 自定义视图动态渲染界面设计器配置的视图、动作 1. 自定义 widget 创建自定义的 MergeTableWidget,用于支持合并单元格和表头分组。 // MergeTableWidget.ts import { BaseElementWidget, SPI, ViewType, TableWidget, Widget, DslRender } from '@kunlun/dependencies'; import MergeTable from './MergeTable.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MergeTableWidget' }) ) export class MergeTableWidget extends TableWidget { public initialize(props) { super.initialize(props); this.setComponent(MergeTable); return this; } /** * 表格展示字段 */ @Widget.Reactive() public get currentModelFields() { return this.metadataRuntimeContext.model.modelFields.filter((f) => !f.invisible); } /** * 渲染行内动作VNode */ @Widget.Method() protected renderRowActionVNodes() { const table = this.metadataRuntimeContext.viewDsl!; const rowAction = table?.widgets.find((w) => w.slot === 'rowActions'); if (rowAction) { return rowAction.widgets.map((w) => DslRender.render(w)); } return null; } } 2. 创建对应的 Vue 组件 定义一个支持合并单元格与表头分组的 Vue 组件。 <!– MergeTable.vue –> <template> <vxe-table border height="500" :column-config="{ resizable: true }" :merge-cells="mergeCells" :data="showDataSource" @checkbox-change="checkboxChange" @checkbox-all="checkedAllChange" > <vxe-column type="checkbox" width="50"></vxe-column> <!– 渲染界面设计器配置的字段 –> <vxe-column v-for="field in currentModelFields" :key="field.name" :field="field.name" :title="field.label" ></vxe-column> <!– 表头分组 https://vxetable.cn/v4.6/#/table/base/group –> <vxe-colgroup title="更多信息"> <vxe-column field="role" title="Role"></vxe-column> <vxe-colgroup title="详细信息"> <vxe-column field="sex" title="Sex"></vxe-column> <vxe-column field="age" title="Age"></vxe-column> </vxe-colgroup> </vxe-colgroup> <vxe-column title="操作" width="120"> <template #default="{ row, $rowIndex }"> <!– 渲染界面设计器配置的行内动作 –> <row-action-render :renderRowActionVNodes="renderRowActionVNodes" :row="row" :rowIndex="$rowIndex" :parentHandle="currentHandle" ></row-action-render> </template> </vxe-column> </vxe-table> <!– 分页 –> <oio-pagination :pageSizeOptions="pageSizeOptions" :currentPage="pagination.current"…

    2025年1月9日
    2.0K00
  • OioNotification 通知提醒框

    全局展示通知提醒信息。 何时使用 在系统四个角显示通知提醒信息。经常用于以下情况: 较为复杂的通知内容。 带有交互的通知,给出用户下一步的行动点。 系统主动推送。 API OioNotification.success(title,message, config) OioNotification.error(title,message, config) OioNotification.info(title,message, config) OioNotification.warning(title,message, config) config 参数如下: 参数 说明 类型 默认值 版本 duration 默认 3 秒后自动关闭 number 3 class 自定义 CSS class string –

    2023年12月18日
    1.1K00
  • 前端-如何修改指定页面的内组件的css样式

    为组件加自定义class,用该class作为父选择器写特定的css样式 以form为例,自定义了以下class <view/>标签的表单视图(FormView)组件 <element/>标签的form(FormWidget)组件 <element/>标签的actionBar(ActionBarWidget)组件 import { registerLayout, ViewType } from '@kunlun/dependencies'; export const install = () => { registerLayout( ` <view type="FORM" class="my-form-view"> <element widget="form" slot="form" class="my-form-widget"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" class="my-action-bar" slotSupport="action" > <xslot name="actions" slotSupport="action" /> </element> </view> `, { viewType: ViewType.Form, model: 'resource.k2.Model0000000109', actionName: 'uiViewb2de116be1754ff781e1ffa8065477fa' } ); }; install(); 查看修改后的页面html结构 编写样式的css .my-form-view .oio-form { /** TODO **/ } .my-form-widget .oio-row { /** TODO **/ } .my-action-bar .oio-col { /** TODO **/ }

    2024年6月17日
    1.6K00

Leave a Reply

Please Login to Comment