Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/didi/mpx
Browse files Browse the repository at this point in the history
  • Loading branch information
hiyuki committed Mar 21, 2024
2 parents 10e71c6 + c25b527 commit 1093a51
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 35 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
欢迎访问[https://mpxjs.cn](https://mpxjs.cn),跟随我们提供的文档指南使用Mpx进行跨端小程序开发。

## 近期更新
基于 Mpx 的移动端基础组件库 [mpx-cube-ui](https://www.mpxjs.cn/mpx-cube-ui/guide/intro.html) 已经开源,更多详情查看[这里](https://mpxjs.cn/articles/mpx-cube-ui.html)

Mpx 2.9 版本正式发布,支持原子类、SSR和构建产物体积优化,更多详情查看[这里](https://mpxjs.cn/articles/2.9-release.html),迁移指南查看[这里](https://mpxjs.cn/guide/migrate/2.9.html),相关指南及 API 参考文档已更新。

Expand Down Expand Up @@ -196,6 +197,7 @@ Mpx的核心设计思路为增强,不同于业内大部分小程序框架将we
|@mpxjs/mock|[![npm version](https://badge.fury.io/js/%40mpxjs%2Fmock.svg)](https://badge.fury.io/js/%40mpxjs%2Fmock)|结合mockjs提供数据mock能力|
|@mpxjs/utils|[![npm version](https://badge.fury.io/js/%40mpxjs%2Futils.svg)](https://badge.fury.io/js/%40mpxjs%2Futils)|mpx运行时工具库|
|@mpxjs/babel-plugin-inject-page-events|[![npm version](https://badge.fury.io/js/%40mpxjs%2Fbabel-plugin-inject-page-events.svg)](https://badge.fury.io/js/%40mpxjs%2Fbabel-plugin-inject-page-events)|组合式API页面事件处理插件|
|@mpxjs/mpx-cube-ui|[![npm version](https://badge.fury.io/js/%40mpxjs%2Fmpx-cube-ui.svg)](https://badge.fury.io/js/%40mpxjs%2Fmpx-cube-ui)|基于 Mpx 的移动端基础组件库|
## 开发团队
Expand Down
1 change: 1 addition & 0 deletions docs-vuepress/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const sidebar = {
{ title: 'Mpx2.7 版本正式发布,大幅提升编译构建速度', path: '2.7-release' },
{ title: 'Mpx2.8 版本正式发布,使用组合式 API 开发小程序', path: '2.8-release' },
{ title: 'Mpx2.9 版本正式发布,支持原子类、SSR 和包体积优化', path: '2.9-release' },
{ title: '小程序跨端组件库 Mpx-cube-ui 开源啦', path: 'mpx-cube-ui' },
{ title: 'Mpx-cli 插件化改造', path: 'mpx-cli-next' },
{ title: 'Mpx 小程序单元测试能力建设与实践', path: 'unit-test'}
]
Expand Down
91 changes: 91 additions & 0 deletions docs-vuepress/articles/mpx-cube-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 小程序跨端组件库 Mpx-cube-ui 开源啦

> 作者:[CommanderXL](https://github.com/CommanderXL)
Mpx-cube-ui 是一款基于 [Mpx 小程序框架](https://www.mpxjs.cn/) 的移动端基础组件库,一份源码可以跨端输出所有小程序平台及 Web,同时具备良好的拓展能力和可定制化的能力来帮助你快速构建 Mpx 应用项目。

Mpx-cube-ui 提供了灵活配置的主题定制能力,在组件设计开发阶段对表现层的结构和样式进行抽离,利用预编译器和 CSS 变量的能力,提供细粒度(颜色、字体、圆角、阴影等)的样式定制能力,你的项目可以按需使用主题的编译方案还是运行时方案来满足不同样式风格的业务场景开发。

Mpx-cube-ui 提供了开箱即用的跨端输出能力,源码基于 Mpx 小程序框架进行开发,依托于 Mpx 提供的[跨平台能力](https://mpxjs.cn/articles/2.0.html#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91)即基于微信小程序跨平台编译输出为支付宝、百度、QQ、头条等目标平台的小程序代码,同时还可以输出到 Web。

接下来会通过这篇文章去分享一下 Mpx-cube-ui 是如何诞生的。

## 业务现状
### 集团产品的迭代
越来越多的业务产品开始借助小程序的渠道来拓展产品的推广和使用,不同的技术团队承接的不同业务产品方向的需求。在滴滴所有的小程序产品当中,滴滴出行小程序作为最大的C端流量入口,承接了不同业务产品流量分发。在具体到小程序产品的研发环节,不同业务的小程序都被集成到了滴滴出行小程序当中,一同打包上线并发布。
当然在这种跨业务、技术部门的产品研发当中,不同的技术团队都会有自身配套的基础能力建设。就拿小程序的组件库来说,每个独立的业务产品都会依据对应的交互设计规范来搭建一套满足自身业务述求的基础组件用以提高日常的业务开发效率,例如按钮、半浮层弹窗、Toast、Dialog、表单等等。

### 团队内的业务迭代
除了跨业务产品研发的场景外,在同一个技术团队内部也可能会承接来自不同业务产品方向的需求,这些不同业务产品同样也会有独立发展迭代的述求。由于业务场景的约束,团队内部同样也会面临如何做基础能力的沉淀和复用的议题,在节省研发资源的同时去提升业务项目交付的质量和效率。
<hr>

那么在不同的业务产品发展初期,为了快速交付产品的功能保障上线时间,研发侧会尽量利用之前已有的基础能力来快速搭建产品功能。就拿小程序组件库的复用来说,常见的方式有:

- 直接侵入到原有的组件代码当中进行 Hard Code,不同的业务产品依赖同一份组件代码,利用一些 if/else 或者条件编译等手段来使得原有的组件在满足最新的业务述求的同时,还要确保不会影响到原有的业务使用;
- Fork 一份原有组件库的代码,在此基础上单独迭代维护,做到和原有组件的彻底隔离而不会造成污染;

对于第一种方案来说:组件本身因为增量的差异化需求使得组件本身的维护成本变高,同时对于差异化的代码处理难免在编译打包环节出现代码冗余问题,也就是原本不属于当前业务产品的代码实现也被一同打包;
对于第二种方案来说:组件库维护的数量变多,使得后续的组件迭代和更新需要修改多处代码;

此外,在小程序的场景下还面临着包体积等平台规范的硬性约束,特别是对于像滴滴出行小程序这种体量大的小程序来说,众多小程序业务产品被集成到同一个小程序当中,包体积也是日常研发过程中重点关注的一个指标。模块或组件的可复用性好、可拓展性强也意味着不需要重复去开发同一份功能相同代码,随之代码包体积也不会因重复实现相同功能而增速过快。
## Mpx 技术生态
在 Mpx 整个技术生态当中,组件体系目前是不完备的。

在我们目前使用 Mpx 去搭建实际的业务产品的过程中,我们基于小程序平台的基础组件去搭建了特定业务场景的基础业务组件,因为业务场景、功能和代码实现等各方面的原因,这些业务场景的基础组件是没法开放给社区的 Mpx 开发者来使用的。因此对于社区来说,要么只能同样基于平台的基础组件去开发上层基础组件,或者是使用第三方的原生小程序组件库,不管哪种方式对于社区的开发者而言,都有不少的上手和开发成本。

因此在社区当中也有比较强的述求,期望 Mpx 能提供更为基础通用的组件来更好的支持上层的业务开发。
## 组件库自身的维护
- 文档示例一体化

不管是我们内部维护的业务组件库还是之前我们开源的基于 Vue 的 Web 组件库 [Cube-ui](https://didi.github.io/cube-ui/#/zh-CN),组件本身的文档示例也是在组件库迭代过程中也是比较占用时间精力的,在示例和文档方面往往需要重复写多份文案,还要确保它们之间是同步更新的。那么针对组件库自身的维护,如何尽量减少组件库本身的维护成本也是我们需要深入思考的一部分工作。

以上这些问题促使我们重新思考整个 Mpx 组件体系的构建和发展。

## Mpx 组件体系
经过业务的长期迭代和验证,Mpx-cube-ui 作为 Mpx 技术生态当中组件体系的基础设施:脱离于业务的基础组件库,同时又要有良好的拓展能力和可定制化的能力来更好的支持上层业务。

![组件分层设计](https://gift-pypu-cdn.didistatic.com/static/driver_miniprogram/do1_6wEnx7fVXGlK8hILtuYa)
在整个组件体系的搭建过程中,我们依据组件的特性将组件拆分为如下几种类型:

- 业务组件一般来源于某一个具体的产品功能,用以解决特定问题域,更加强调关注点分离,代码的维护成本;
- 基础业务组件一般来源于某一类的业务产品,在特定的业务背景下抽象的比较通用的,较少糅合业务逻辑,介于基础组件和业务组件之间,强调解决效率,可复用性,可维护性;
- 基础组件一般来源于我们设计交互元语言,用以定义业务产品和用户的交互、反馈,更加强调一致性,效率,可复用性;

底层的基础组件(可组合性、可拓展性、稳定性)是为了更好的服务于上层的(基础)业务组件。

从底层的基础组件到业务组件(由下至上),组件的业务属性越来越强,所要解决的问题域更加聚焦,更贴合具体的问题和场景,同时它们的可复用性也随之降低。
## Mpx-cube-ui 组件设计
这里通过乘客交易、司机运营两个不同业务方向的组件来举例说明 Mpx-cube-ui 在组件的设计和开发当中所做的一些思考。
在这两个业务场景当中都有 Modal 半浮层组件,不过在司乘业务不同的设计规范约束下,组件间的差异还是比较大的。

![mpx-cube-ui-modal](https://gift-pypu-cdn.didistatic.com/static/driver_miniprogram/do1_1EABWsPUeVHTim2iwTUf)

那么为了解决上文当中组件库在跨业务产品的的可复用性、可拓展性的问题,核心所遵循的原则是:

**弱化不同业务场景的设计规范(去业务),回归到组件本身的行为(逻辑)、结构和样式(表现):**

- 行为,组件所特有的行为方法:展示(show),隐藏(hide),关闭(close);
- 结构,方便组件间的重新组合:头部区,标题区,内容区,底部操作区;
- 样式,方便业务场景的主题定制:颜色、字号、圆角、边距等;

![组件设计原则](https://gift-pypu-cdn.didistatic.com/static/driver_miniprogram/do1_ceWLvE8RbZIIduHHTdtu)

对于 Mpx-cube-ui 组件的主题定制有个简单的逻辑关系:
- 全局基础样式变量:提供了基本色值、字号等,会影响到所有组件的展示;
- 组件样式变量:对于基本的色值、字号等继承于全局基础变量,涉及到组件自身的结构、样式变量会单独定义;
- 组件渲染样式:直接依赖组件样式变量;

![mpx-cube-ui-modal](https://gift-pypu-cdn.didistatic.com/static/driver_miniprogram/do1_b4uBKbP8yIwdip7pBZAq)

通过定制[全局基础样式变量](https://www.mpxjs.cn/mpx-cube-ui/guide/design-tokens.html)[组件样式变量](https://www.mpxjs.cn/mpx-cube-ui/components/base/button.html#css-variable)都能达到定制主题的目的,有关主题定制的功能具体参见[文档](https://www.mpxjs.cn/mpx-cube-ui/guide/theme.html)

在 Mpx-cube-ui 实现的定制主题的方案当中是利用预编译器和 Css Varaibles 的能力来提供细粒度的样式定制能力:

![业务架构图](https://gift-pypu-cdn.didistatic.com/static/driver_miniprogram/do1_3ZVcQITsryAG4k96hFrH)
- 利用预编译器可编程的能力,在编译阶段就可以完成主题能力的定制,当你的业务项目作为独立应用迭代,可以只利用预编译器的能力,从而使得你的 css 代码体积尽可能的小;
- 利用 Css Varaibles 的能力,可以解决更为复杂的场景,例如同一个组件在巨型应用当中,在不同业务场景页面需要有不同的主题样式;

[官网示例](https://www.mpxjs.cn/mpx-cube-ui/guide/intro.html)当中可以直接体验主题切换功能。
## 未来的规划
1. 开源更多我们内部所沉淀出的基础组件,去满足多样化的业务场景开发;
2. 将文档示例一体化的能力独立打包开源,用以组件库的快速建站以及降低文档、示例代码的维护成本。
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"packages": [
"packages/*"
],
"version": "2.9.21"
"version": "2.9.25"
}
2 changes: 1 addition & 1 deletion packages/api-proxy/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mpxjs/api-proxy",
"version": "2.9.21",
"version": "2.9.23",
"description": "convert miniprogram API at each end",
"module": "src/index.js",
"types": "@types/index.d.ts",
Expand Down
23 changes: 9 additions & 14 deletions packages/api-proxy/src/common/js/promisify.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getEnvObj, noop } from './utils'
import { getEnvObj } from './utils'

const envObj = getEnvObj()

Expand Down Expand Up @@ -68,25 +68,20 @@ function promisify (listObj, whiteList, customBlackList) {
}

result[key] = function (...args) {
const obj = args[0]
if (promisifyFilter(key) && !(obj.success || obj.fail || obj.complete)) {
if (!args[0]) {
args.unshift({ success: noop, fail: noop })
}
const obj = args[0] || {}
// 不需要转换 or 用户已定义回调,则不处理
if (!promisifyFilter(key) || obj.success || obj.fail) {
return listObj[key].apply(envObj, args)
} else { // 其他情况进行转换
if (!args[0]) args.unshift(obj)
let returned
const promise = new Promise((resolve, reject) => {
obj.success = function (res) {
resolve(res)
}
obj.fail = function (e) {
reject(e)
}
obj.success = resolve
obj.fail = reject
returned = listObj[key].apply(envObj, args)
})
promise.__returned = returned
return promise
} else {
return listObj[key].apply(envObj, args)
}
}
})
Expand Down
2 changes: 1 addition & 1 deletion packages/api-proxy/src/platform/api/lifecycle/index.ali.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function getEnterOptionsSync () {
my.getEnterOptionsSync()
return my.getEnterOptionsSync()
}

export {
Expand Down
52 changes: 50 additions & 2 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,52 @@
# 小程序框架 [**MPX** 介绍文档](https://didi.github.io/mpx)
[Mpx](https://mpxjs.cn), 一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架。
欢迎访问[https://mpxjs.cn](https://mpxjs.cn),跟随我们提供的文档指南使用Mpx进行跨端小程序开发。

## 跨平台代码编写指南
Mpx具有以下功能特性:
* [数据响应](https://www.mpxjs.cn/guide/basic/reactive.html) (赋值响应 / [watch](https://www.mpxjs.cn/api/global-api.html#watch) / computed)
* [组合式 API](https://mpxjs.cn/guide/composition-api/composition-api.html)
* 增强模板语法 ([动态组件](https://www.mpxjs.cn/guide/basic/component.html#%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6) / [样式绑定 / 类名绑定 ](https://www.mpxjs.cn/guide/basic/class-style-binding.html#%E7%B1%BB%E5%90%8D%E7%BB%91%E5%AE%9A) / [内联事件函数](https://www.mpxjs.cn/guide/basic/event.html) / [双向绑定](https://www.mpxjs.cn/guide/basic/two-way-binding.html) / [refs](https://www.mpxjs.cn/guide/basic/refs.html))
* 极致性能 ([运行时性能优化](https://www.mpxjs.cn/guide/understand/runtime.html) / [包体积优化](https://www.mpxjs.cn/guide/understand/compile.html#%E5%88%86%E5%8C%85%E5%A4%84%E7%90%86) / 框架运行时体积14KB)
* [高效强大的编译构建](https://www.mpxjs.cn/guide/understand/compile.html#%E5%88%86%E5%8C%85%E5%A4%84%E7%90%86) (基于webpack5 / 支持持久化缓存 / 兼容webpack生态 / 兼容原生小程序 / 完善支持npm场景下的分包输出 / 高效调试)
* [单文件组件开发](https://www.mpxjs.cn/guide/basic/single-file.html)
* [渐进接入 / 原生组件支持](https://www.mpxjs.cn/guide/advance/progressive.html)
* [状态管理](https://www.mpxjs.cn/guide/advance/store.html) (Vuex规范 / 支持多实例Store)
* 跨团队开发 ([packages](https://www.mpxjs.cn/guide/advance/subpackage.html))
* 逻辑复用 ([mixins](https://www.mpxjs.cn/guide/advance/mixin.html))
* [周边能力](https://www.mpxjs.cn/guide/extend/) (fetch / api增强 / mock / webview-bridge)
* 脚手架支持
* 多平台增强 (支持在微信、支付宝、百度、qq、头条小程序平台中进行增强开发)
* [跨平台编译](https://www.mpxjs.cn/guide/advance/platform.html) (一套代码跨端输出到微信、支付宝、百度、字节、QQ、京东、快应用(web) 和 [web平台](https://www.mpxjs.cn/guide/advance/platform.html#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E8%BE%93%E5%87%BAweb) 中运行)
* [TypeScript支持](https://www.mpxjs.cn/guide/tool/ts.html) (基于ThisType实现了完善的类型推导)
* [I18n国际化](https://www.mpxjs.cn/guide/tool/i18n.html)
* [单元测试](https://www.mpxjs.cn/guide/tool/unit-test.html)
* [E2E测试](https://www.mpxjs.cn/guide/tool/e2e-test.html)
* [原子类](https://mpxjs.cn/guide/advance/utility-first-css.html)
* [SSR](https://mpxjs.cn/guide/advance/ssr.html)
* [组件维度运行时渲染方案](https://github.com/didi/mpx/pull/919) (即将到来)
## @mpxjs/core

Mpx 小程序框架运行时核心库。

## 快速开始

```bash
# 安装mpx脚手架工具
npm i -g @mpxjs/cli

# 初始化项目
mpx create mpx-project

# 进入项目目录
cd mpx-project

# 安装依赖
npm i

# development
npm run serve

# production
npm run build
```

使用小程序开发者工具打开项目文件夹下dist中对应平台的文件夹即可预览效果。
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mpxjs/core",
"version": "2.9.21",
"version": "2.9.25",
"description": "mpx runtime core",
"keywords": [
"miniprogram",
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/core/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ function preProcessRenderData (renderData) {
export default class MpxProxy {
constructor (options, target, reCreated) {
this.target = target
// 兼容 getCurrentInstance.proxy
this.proxy = target
this.reCreated = reCreated
this.uid = uid++
this.name = options.name || ''
Expand Down Expand Up @@ -622,7 +624,7 @@ export default class MpxProxy {
export let currentInstance = null

export const getCurrentInstance = () => {
return currentInstance && { proxy: currentInstance?.target }
return currentInstance
}

export const setCurrentInstance = (instance) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/size-report/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mpxjs/size-report",
"version": "2.9.14",
"version": "2.9.24",
"description": "mpx size report plugin",
"main": "src/index.js",
"scripts": {
Expand Down
9 changes: 4 additions & 5 deletions packages/size-report/src/SizeReportPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class SizeReportPlugin {
const moduleEntriesMap = new Map()

function setModuleEntries (module, entryModule, noEntry) {
getModuleEntries(module, noEntry).add(entryModule)
getModuleEntries(module, noEntry).add(entryModule.rootModule || entryModule)
}

function getModuleEntries (module, noEntry) {
Expand Down Expand Up @@ -210,7 +210,7 @@ class SizeReportPlugin {
// 处理ConcatenatedModule
const resource = entryModule.resource || (entryModule.rootModule && entryModule.rootModule.resource)
if (resource && reportGroup.entryRules && matchCondition(parseRequest(resource).resourcePath, reportGroup.entryRules)) {
reportGroup.entryModules.add(entryModule)
reportGroup.entryModules.add(entryModule.rootModule || entryModule)
}
})
}
Expand All @@ -222,7 +222,7 @@ class SizeReportPlugin {
const resource = module.resource || (module.rootModule && module.rootModule.resource)
if (resource && matchCondition(parseRequest(resource).resourcePath, reportGroup.noEntryRules)) {
reportGroup.noEntryModules = reportGroup.noEntryModules || new Set()
reportGroup.noEntryModules.add(module)
reportGroup.noEntryModules.add(module.rootModule || module)
walkEntry(module, (module, noEntryModule) => {
setModuleEntries(module, noEntryModule, true)
})
Expand All @@ -247,8 +247,7 @@ class SizeReportPlugin {
const sharedSet = new Set()
const otherSelfEntryModules = new Set()
entryModules.forEach((entryModule) => {
// 处理ConcatenatedModule
const entryNode = mpx.getEntryNode(entryModule.rootModule || entryModule)
const entryNode = mpx.getEntryNode(entryModule)
if (entryNode) {
selfSet.add(entryNode)
} else {
Expand Down
Loading

0 comments on commit 1093a51

Please sign in to comment.