Skip to content

Commit e42b3fb

Browse files
committed
Merge remote-tracking branch 'origin/master' into rn-template
2 parents 07f20aa + 2b4da25 commit e42b3fb

14 files changed

Lines changed: 734 additions & 282 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<view class="list">
3+
<view class="list-item list-item-first">
4+
<text class="title">标题</text>
5+
<text class="list-item-after">>></text>
6+
</view>
7+
<view class="list-item">
8+
<text class="title">内容</text>
9+
<text class="list-item-after">>></text>
10+
</view>
11+
</view>
12+
</template>
13+
<style lang="less">
14+
/* 展开嵌套选择器 */
15+
.list-item {
16+
color: red;
17+
}
18+
.list-item-first {
19+
color: blue;
20+
}
21+
.list-item-after {
22+
/* 这里可以添加对应的样式,例如 margin-left 等 */
23+
}
24+
</style>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<template>
2+
<view class="container">
3+
<view class="container-box active" hover-class="container-box-active"></view>
4+
</view>
5+
</template>
6+
<style>
7+
.container-box {
8+
/* 使用条件编译处理 1rpx 极细边框和 display: none */
9+
/* @mpx-if (__mpx_mode__ === 'wx' || __mpx_mode__ === 'ali' || __mpx_mode__ === 'web') */
10+
display: none;
11+
border: 1rpx solid #ccc;
12+
/* @mpx-elif (__mpx_mode__ === 'rn') */
13+
width: 0;
14+
height: 0;
15+
overflow: hidden;
16+
border-width: StyleSheet.hairlineWidth;
17+
border-color: #ccc;
18+
border-style: solid;
19+
/* @mpx-endif */
20+
21+
/* font-weight 避免数值 */
22+
font-weight: normal;
23+
}
24+
.container-box-active {
25+
background-color: #eee;
26+
}
27+
</style>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<view class="layout">
3+
<view class="left">Left</view>
4+
<view class="right">
5+
<!-- @mpx-if (__mpx_mode__ === 'rn') -->
6+
<text numberOfLines="{{1}}">Right</text>
7+
<!-- @mpx-else -->
8+
<text class="right-text">Right</text>
9+
<!-- @mpx-endif -->
10+
</view>
11+
</view>
12+
</template>
13+
<style>
14+
/* 替换 grid 和 float 为 flex 布局 */
15+
.layout {
16+
display: flex;
17+
flex-direction: row;
18+
}
19+
.left {
20+
/* rem 替换为 rpx */
21+
width: 100rpx; /* 假设 1rem = 10rpx,根据实际情况调整 */
22+
}
23+
.right {
24+
width: 50%;
25+
}
26+
/* @mpx-if (__mpx_mode__ === 'wx' || __mpx_mode__ === 'ali' || __mpx_mode__ === 'web') */
27+
.right-text {
28+
white-space: nowrap;
29+
text-overflow: ellipsis;
30+
overflow: hidden;
31+
}
32+
/* @mpx-endif */
33+
</style>
Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: mpx-rn-style-guide
3-
description: Mpx 跨端输出 RN (Mpx2RN or Mpx2DRN)的样式能力说明与实践建议,当用户询问 Mpx 跨端输出 RN 样式相关问题时调用,包括:Mpx2RN 样式兼容改造,Mpx2RN 样式能力支持详情,Mpx2RN 样式报错排查,Mpx2RN 样式开发最佳实践等
3+
description: Mpx 跨端输出 RN (简称为 Mpx2RN or Mpx2DRN)的样式适配开发指南,当用户问题或上下文中同时包含 Mpx、RN、样式三个关键要素时强制调用,如:Mpx2RN 样式适配、Mpx2RN 样式报错等。当用户问题不涉及 Mpx 跨端输出 RN 或与样式无关时不应调用,如:Mpx 输出小程序相关问题、RN 相关问题等
44
---
55

66
# Mpx 跨端输出 RN 样式开发指南
@@ -11,6 +11,15 @@ Mpx 是一个以微信小程序语法为基础、进行了类 Vue 语法拓展
1111

1212
Mpx 采用类 Vue 的单文件组件(SFC)格式 `.mpx` 进行组件与页面定义,详情查看:[Mpx 单文件组件](./references/single-file-component.md)
1313

14+
## 知识库索引
15+
16+
| 知识库 | 说明 |
17+
| --- | --- |
18+
| [Mpx 单文件组件](./references/single-file-component.md) | 描述了 Mpx 单文件组件的基本结构与语法,并提供简单示例,开发 Mpx 单文件组件时读取 |
19+
| [条件编译](./references/conditional-compile.md) | 描述了 Mpx 中包括模板、脚本、样式和配置等不同部分的条件编译语法,当跨端适配开发遇到兼容性问题需要分端处理时读取 |
20+
| [跨端输出 RN 样式能力参考](./references/rn-style-reference.md) | 描述了 Mpx 输出 RN 时详细的样式能力支持情况,当查询某项样式能力是否支持,遇到样式不生效、样式报错等问题时读取 |
21+
| [跨端输出 RN 样式开发最佳实践](./references/rn-style-practice.md) | 描述了 Mpx 输出 RN 时常用选择器和样式属性的跨端兼容方案和样式开发最佳实践,当进行已有组件跨端样式适配改造或新组件跨端样式开发时读取 |
22+
1423
## 跨端输出 RN 样式适配改造
1524

1625
当用户要求对已有 Mpx 组件进行跨端输出 RN 样式适配改造时,遵循以下流程与约束。
@@ -21,46 +30,81 @@ Mpx 采用类 Vue 的单文件组件(SFC)格式 `.mpx` 进行组件与页面
2130

2231
### 输出
2332

24-
1. 经过跨端适配改造后的 `{name}.mpx` 组件或 `{name}-rn.mpx`(根据用户要求决定在原文件中进行修改还是创建新的文件)
25-
2. 记录跨端改造详情的 `{name}-log.md` 文件,包含以下内容:
26-
- 对不兼容样式属性或选择器进行的处理详情
27-
- 对不兼容样式属性的条件编译改造详情
28-
- 对选择器的检查详情
29-
30-
33+
以用户指示为准,若无特殊指示则默认在原文件 `{name}.mpx` 中进行修改。在输出修改后的代码时,应输出完整的组件代码,避免使用省略号,确保用户可以直接复制或应用修改。
34+
35+
### 约束指引
36+
37+
进行样式适配改造时需严格遵循以下约束指引:
38+
39+
1. **选择器单类化(示例)**:禁止使用复合选择器(如 `.list .item`),必须转换为单类名等效实现(如 `.list-item`),并确保同步修改 `<template>` 结构和 `<script>`(如 `createSelectorQuery`)中的对应引用。
40+
* **Bad Example**: `<style> .list .item { color: red; } </style>`
41+
* **Good Example**: `<style> .list-item { color: red; } </style>`
42+
2. 模板样式类名动态绑定尽可能使用 `wx:style``wx:class` 模版指令,尽量避免在 `style``class` 属性中使用 `{{}}` 插值表达式进行动态绑定。
43+
* **Bad Example**: `<view class="item {{isActive ? 'active' : ''}}">`
44+
* **Good Example**: `<view class="item" wx:class="{{ {active: isActive} }}">`
45+
3. 对于存在跨端兼容方案的选择器和样式属性,读取[跨端输出 RN 样式开发最佳实践](./references/rn-style-practice.md),优先改造为跨端兼容方案,以便于后期维护。
46+
* **选择器的跨端兼容方案**:
47+
- 复合选择器(后代/交集等)→ 等效单类选择器:[参考](./references/rn-style-practice.md#11-复合选择器替换为等效单类选择器)
48+
- 子元素伪类(:first-child / :last-child / :nth-child)→ 使用 `wx:class` + `index` 判断:[参考](./references/rn-style-practice.md#12-子元素伪类替代方案-first-child--last-child--nth-child)
49+
- 伪元素选择器(::before / ::after)→ 使用真实节点替代:[参考](./references/rn-style-practice.md#13-伪元素选择器替代方案-before--after)
50+
- 点击态伪类(:active)→ 使用 `hover-class`/`hover-stay-time`[参考](./references/rn-style-practice.md#14-点击态处理-active)
51+
* **样式属性的跨端兼容方案**:
52+
- 极细边框:原平台 `1rpx`,RN 使用 `hairlineWidth` 并通过条件编译切换:[参考](./references/rn-style-practice.md#23-1像素边框极细线)
53+
- 单位兼容:`rem/em` → 使用 `rpx`[参考](./references/rn-style-practice.md#24-避免使用不兼容的单位-remem)
54+
- 字体粗细:避免数值型 `font-weight`,使用 `normal`/`bold`[参考](./references/rn-style-practice.md#25-谨慎使用-font-weight-数值)
55+
- 文本溢出:`white-space`/`text-overflow` → 原平台保留样式,RN 使用 `numberOfLines` 模板属性条件编译:[参考](./references/rn-style-practice.md#4-文本溢出处理)
56+
- 隐藏元素:避免 `display: none`,使用尺寸归零与 `overflow: hidden` 等组合样式:[参考](./references/rn-style-practice.md#5-隐藏元素)
57+
- 布局属性:避免 `grid`/`float`,统一使用 Flex 布局:[参考](./references/rn-style-practice.md#32-避免使用-grid-布局) [参考](./references/rn-style-practice.md#33-避免使用-float-布局)
58+
4. 非必要时减少条件编译的使用,**避免出现大面积连续的条件编译**,因为这会严重破坏代码的可读性和后期维护性。
59+
5. **保留原始样式定义中的 `/*use rpx*/``/*use px*/` 注释**,此类注释用于编译期间批量切换样式单位。
3160

3261
### 任务流程
3362

34-
1. 分析输入 Mpx 组件的样式代码,参考[跨端输出 RN 样式能力支持详情](./references/rn-style-reference.md),识别出所有不兼容的选择器和样式属性
35-
2. 对不兼容的选择器进行改造处理,尝试将其转换为 RN 平台支持的选择器类型(仅允许包含**单类选择器**`page` 选择器和 `:host` 选择器),同时更新 `template``script` 中对于类名的相关引用。
36-
37-
2. 对不兼容的样式属性进行条件编译改造,保留原样式属性并添加 todo 注释提示用户手动处理
38-
3. 对所有选择器进行检查,确保仅包含**单类选择器**`page` 选择器和 `:host` 选择器
39-
4. 检查是否存在大块的条件编译,仅在必要时局部使用,保持代码的可读性和维护性
40-
5. 检查是否保留了样式定义中/*use rpx*/和/*use px*/注释,用于编译期间批量切换样式单位
41-
42-
### 强约束条件
43-
44-
对已有的 Mpx 组件进行跨平台样式适配改造时,需要严格遵循以下约束条件:
45-
46-
1. style 区块中的所有选择器必须全部改造为 RN 平台支持的选择器类型,仅允许包含**单类选择器**`page` 选择器和 `:host` 选择器
47-
2. 对于不兼容的样式属性,**必须通过条件编译局部保留原样式属性**,同时添加 todo 注释信息提示用户手动处理
48-
3. **禁止出现大块的条件编译**,仅在必要时局部使用,保持代码的可读性和维护性
49-
4. **保留样式定义中/*use rpx*/和/*use px*/注释**,用于编译期间批量切换样式单位
50-
51-
### 适配改造检查项
52-
53-
对已有的 Mpx 组件进行跨平台样式适配改造后,需要执行并通过以下全部检查项:
54-
55-
- [ ] style 区块中不包含 RN 平台不支持的选择器类型,仅允许包含**单类选择器**`page` 选择器和 `:host` 选择器
56-
- [ ] 所有 RN 平台中不兼容的样式属性都必须被条件编译局部保留,避免输出到 RN 平台,并且包含 todo 注释信息
57-
- [ ] 禁止出现大块的条件编译,仅在必要时局部使用,保持代码的可读性和维护性
58-
59-
## 跨端输出 RN 样式能力支持详情
60-
61-
[跨端输出 RN 样式能力支持详情](./references/rn-style-reference.md)
62-
63-
## 跨端输出 RN 样式开发最佳实践
64-
65-
[跨端输出 RN 样式开发最佳实践](./references/rn-style-practice.md)
66-
63+
1. **选择器适配改造**
64+
- 1.1 分析组件的 `<style>` 部分,对于 `sass``less``stylus` 等支持嵌套选择器写法的预处理语言,先将原代码中所有的嵌套选择器写法展开铺平为传统的选择器写法,便于后续进行选择器的兼容性判断及适配改造。
65+
* **Bad Example (嵌套选择器未展开)**:
66+
```less
67+
.list {
68+
.item {
69+
color: red;
70+
&.active {
71+
color: blue;
72+
}
73+
}
74+
}
75+
```
76+
* **Good Example (嵌套选择器已展开铺平)**:
77+
```less
78+
.list .item { color: red; }
79+
.list .item.active { color: blue; }
80+
```
81+
- 1.2 读取 [Mpx 跨端输出 RN 样式开发最佳实践#选择器使用建议](./references/rn-style-practice.md#1-选择器使用建议),分析组件的 `<style>` 部分,将 RN 平台不支持的选择器改造为跨端兼容的等效实现,并同步更新 `<template>` 和 `<script>` 中对应的类名引用。
82+
- 1.3 对于无法进行跨端兼容等效实现的选择器,使用原平台条件编译对该选择器样式片段进行局部包裹,保留在原平台输出产物中,并添加 `todo` 注释记录不兼容 RN 平台的详情。
83+
* **Good Example (局部条件编译)**:
84+
```css
85+
/* @mpx-if (__mpx_mode__ === 'wx' || __mpx_mode__ === 'ali' || __mpx_mode__ === 'web') */
86+
/* todo: RN 不支持 ::-webkit-scrollbar 伪元素,仅在原平台保留 */
87+
.scroll-view::-webkit-scrollbar { display: none; }
88+
/* @mpx-endif */
89+
```
90+
91+
92+
2. **样式属性适配改造**
93+
- 2.1 读取 [跨端输出 RN 样式能力支持详情](./references/rn-style-reference.md),对 `<style>`、`<template>` 和 `<script>` 中定义的样式属性进行分析,检查是否存在 RN 平台不支持的样式属性。
94+
- 2.2 对于 RN 平台不支持的样式属性,读取 [Mpx 跨端输出 RN 样式开发最佳实践](./references/rn-style-practice.md),将其改造为跨端兼容的等效实现。
95+
- 2.3 对于无法进行跨端兼容等效实现的样式属性,使用原平台条件编译对该样式属性进行局部包裹,保留在原平台输出产物中,并添加 `todo` 注释记录不兼容 RN 平台的详情。
96+
* **Good Example (局部条件编译)**:
97+
```css
98+
.animation-box {
99+
/* @mpx-if (__mpx_mode__ === 'wx' || __mpx_mode__ === 'ali' || __mpx_mode__ === 'web') */
100+
/* todo: RN 不支持 keyframes 动画,仅在原平台保留 */
101+
animation: slideIn 0.5s ease-in-out;
102+
/* @mpx-endif */
103+
}
104+
```
105+
106+
3. **检查与确认**
107+
- 3.1 检查确保 `<style>` 中不存在任何嵌套选择器写法。
108+
- 3.2 检查确保 `<style>`、`<template>` 和 `<script>` 中所有存在跨端兼容方案的选择器和样式属性都已被改造为跨端兼容的等效实现。
109+
- 3.3 检查确保 `<style>`、`<template>` 和 `<script>` 中所有 RN 平台不支持的选择器和样式属性都已被原平台条件编译所包裹。
110+
- 3.4 检查确保 `<style>`、`<template>` 和 `<script>` 中不存在大面积的条件编译,所有添加的条件编译仅最小包裹不兼容的样式片段。

.agents/skills/mpx-rn-style-guide/references/rn-style-detail.md renamed to .agents/skills/mpx-rn-style-guide/others/rn-style-detail.md

File renamed without changes.

.agents/skills/mpx-rn-style-guide/references/rn-style-diff.md renamed to .agents/skills/mpx-rn-style-guide/others/rn-style-diff.md

File renamed without changes.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# 条件编译
2+
3+
对于跨平台无法兼容的部分,局部使用条件编译进行分平台定义是可以接受的,跨平台输出 RN 时通常原平台使用 `__mpx_mode__ === 'wx' || __mpx_mode__ === 'ali' || __mpx_mode__ === 'web'` 作为条件,而 RN 平台则使用 `__mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony'` 作为条件。
4+
5+
## 样式条件编译
6+
7+
```html
8+
<style>
9+
.container {
10+
/* @mpx-if (__mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony') */
11+
padding-top: 0; /* RN 自行处理安全区域 */
12+
/* @mpx-else */
13+
padding-top: 88rpx; /* 包含导航栏高度 */
14+
/* @mpx-endif */
15+
}
16+
</style>
17+
```
18+
19+
## 模板条件编译
20+
21+
模板条件编译提供了两种,常用的有 `wx:if` 条件编译和 `@mode` / `@_mode` 节点/属性维度的条件编译。
22+
23+
### wx:if 条件编译
24+
25+
直接使用 `wx:if``__mpx_mode__` 变量进行条件渲染,逻辑直观但灵活性较低。
26+
27+
```html
28+
<template>
29+
<!-- 此处的 __mpx_mode__ 不需要声明数据,编译时会基于当前编译 mode 进行替换 -->
30+
<view wx:if="{{__mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony'}}">
31+
仅 RN 平台可见
32+
</view>
33+
<view wx:else>
34+
原平台可见
35+
</view>
36+
</template>
37+
```
38+
39+
### 节点/属性维度条件编译
40+
41+
使用 `@``|` 符号来指定某个节点或属性只在某些平台下有效。这种方式更加灵活简洁。
42+
43+
- **显式声明 (`@mode`)**:节点或属性仅在目标平台下输出,节点或属性为目标平台原生支持,**框架会跳过对该节点或属性的跨平台语法转换**
44+
- **隐式声明 (`@_mode`)**:节点或属性仅在目标平台下输出,节点或属性为 Mpx 转换支持,**框架仍然会对其进行正常的跨平台语法转换**
45+
- **标签名动态替换 (`mpxTagName@mode`)**:结合 `@mode` 属性,可以对模板组件的 `tagName` 进行分平台条件编译。当节点存在该属性时,在输出到对应平台时会将节点标签修改为该属性的值。
46+
47+
**示例:**
48+
49+
```html
50+
<template>
51+
<!-- 属性维度条件编译,仅在 RN 平台注入 numberOfLines,该属性为 RN 平台原生支持,使用 @mode 进行显式声明,输出 RN 时跳过属性跨平台语法转换-->
52+
<text
53+
class="title"
54+
numberOfLines@ios|android|harmony="{{1}}"
55+
>
56+
{{title}}
57+
</text>
58+
<!-- 属性维度条件编译,仅在 RN 平台注入 is-simple,该属性为 Mpx 转换支持,使用 @_mode 进行隐式声明,输出 RN 时保留属性跨平台语法转换-->
59+
<text
60+
class="content"
61+
is-simple@_ios|_android|_harmony
62+
>
63+
{{content}}
64+
</text>
65+
</template>
66+
```
67+
68+
```html
69+
<template>
70+
<!-- 节点维度条件编译,使用 @_mode 进行隐式声明,仅在目标平台输出,并且保留节点与属性的跨平台语法转换 -->
71+
<view @_ios|_android|_harmony bindtap="handleTap" class="rn-only">仅 RN 可见</view>
72+
</template>
73+
```
74+
75+
```html
76+
<template>
77+
<!-- 标签名动态替换,原平台输出 view,而在 RN 平台输出时,标签名将被替换为 mpx-custom-view -->
78+
<view mpxTagName@ios|android|harmony="mpx-custom-view">will be mpx-custom-view in RN</view>
79+
</template>
80+
```
81+
82+
## 脚本条件编译
83+
84+
`<script>` 中,可以通过访问 `__mpx_mode__` 获取当前编译 mode,进行平台差异逻辑编写(例如处理 RN 与原平台在选择器 API 上的差异等)。
85+
86+
**基础用法:**
87+
```javascript
88+
if (__mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony') {
89+
// 执行 RN 环境相关逻辑
90+
} else {
91+
// 执行原平台相关逻辑
92+
}
93+
```
94+
95+
**三元表达式用法:**
96+
```javascript
97+
// 对于简单的变量赋值或传参,推荐使用三元表达式
98+
const isRN = __mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony'
99+
const apiUrl = isRN ? 'https://api.rn.com' : 'https://api.original.com'
100+
```
101+
102+
## 配置条件编译
103+
104+
我们可以在 `<script name="json">` 中编写 `JS` 逻辑动态定义组件的 `JSON` 配置,可以访问 `__mpx_mode__``__mpx_env__` 环境变量进行条件编译,在不同的平台和环境下导出不同的 `JSON` 配置。
105+
106+
**示例:**
107+
```html
108+
<script name="json">
109+
const isRN = __mpx_mode__ === 'ios' || __mpx_mode__ === 'android' || __mpx_mode__ === 'harmony'
110+
const isDidiEnv = __mpx_env__ === 'didi'
111+
112+
// 动态定义引用的子组件
113+
const usingComponents = isRN ? {
114+
'mpx-sticky-header': '../components/mpx-sticky-header',
115+
'mpx-sticky-section': '../components/mpx-sticky-section'
116+
} : {
117+
'wx-sticky-header': '../components/wx-sticky-header',
118+
'wx-sticky-section': '../components/wx-sticky-section'
119+
}
120+
121+
module.exports = {
122+
// 结合 __mpx_env__ 动态配置页面或组件的标题
123+
navigationBarTitleText: isDidiEnv ? '滴滴出行' : '示例页面',
124+
// 结合 __mpx_mode__ 动态配置平台差异化属性
125+
disableScroll: isRN ? false : true,
126+
usingComponents
127+
}
128+
</script>
129+
```

0 commit comments

Comments
 (0)