uniapp小程序改网页笔记

H5 端 uniapp 是默认开启 scoped 的!!


常见标签的编译转换规则

小程序原生标签 H5 编译结果 结构变化特点 潜在样式风险
<view> <uni-view> 直接平替,无额外嵌套 最小风险
<navigator> <uni-navigator> 包裹一层 <a> 标签 样式可能失效
<button> <uni-button> 直接平替,无额外嵌套 最小风险
<image> <uni-image> 包含双重结构 (div 背景层+img 样式层级依赖问题
<text> <uni-text> 内部增加 <span> 包裹 文本样式继承可能改变
<input> <uni-input> 复杂结构包裹原生 <input> 焦点/边框样式异常
<scroll-view> <uni-scroll-view> 嵌套多层级容器元素 滚动条样式错位
<swiper> <uni-swiper> 生成复杂结构 全局样式污染风险
<picker> <uni-picker> 生成复杂结构 定位/动画异常
ini 复制代码
<navigator
  class="category-item"
  hover-class="none"
  open-type="switchTab"
  url="/pages/category/category"
>
    ...
</navigator>

会被编译成以下代码:

ini 复制代码
<uni-navigator data-v-44ae5a81="" class="category-item">
  <a class="navigator-wrap" href="/pages/category/category">
    ...
  </a>
</uni-navigator>

这就有个坑的地方,注意到了 uni-navigator 身上出现了 data-v-44ae5a81 属性,但是新生成的 a 标签上面没有生成这个属性,如果你在 css 使用了 .category-item a {...},这种用法会报错的,因为这样子会被编译成 .category-item a[data-v-44ae5a81] {...},可是 a 实际上没有自定义属性。方案就是在 navigator 标签内部再加上一个 view 标签,然后所有的样式作用在这个 view 标签身上,这样也可以实现多端兼容。

其实一般情况下作用在 navigator 的样式都是正常的,只有你在 navigator 标签上使用了 flex 布局,并且 flex 布局没有传递到新增的 a 标签,此时页面样式就会乱。

确实可以起作用,可以发现 a 标签已经完全消失,不见了。

支持平台

平台 支持版本要求 说明
Web平台 uni-app 3.7.6+ 使用Vue2或Vue3渲染的H5页面
App-vue3 uni-app 3.7.6+ 使用Vue3渲染的App平台
其他平台 ❌ 不支持 小程序(微信、支付宝等)、其他渲染模式

arduino 复制代码
<text class="text">猜你喜欢</text>

会被编译成以下代码:

xml 复制代码
<uni-text data-v-e94a02da="" class="text">
  <span>猜你喜欢</span>
</uni-text>

arduino 复制代码
<image class="image" :src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/dfc11bb0-4af5-4e9b-9458-99f615cc685a.jpg">
</image>

会被编译成以下代码:

xml 复制代码
<uni-image data-v-17cc3903="" class="image">
    <!-- 背景图 -->
    <div style="background-image: url(&quot;http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/dfc11bb0-4af5-4e9b-9458-99f615cc685a.jpg&quot;); background-position: 0% 0%; background-size: 100% 100%;"></div>
    
    <!-- img 标签 -->
    <img src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/dfc11bb0-4af5-4e9b-9458-99f615cc685a.jpg" draggable="false">
</uni-image>

uni-image 这个标签还会有默认样式,导致页面所有的 image 样式都会乱掉,直接手动修改 uniappuni-image 标签样式就好了。

image 标签添加 lazy-load 属性,使用懒加载:

对于 H5 一点作用没有,只有在小程序里有用,我说为什么设置了懒加载还是疯狂请求,最终想到方案了:

xml 复制代码
<!-- #ifdef APP-PLUS || MP-WEIXIN -->
<image class="image" :src="item.picture" lazy-load></image>
<!-- #endif -->
<!-- #ifdef H5 -->
<img class="image" :src="item.picture" loading="lazy" alt="" />
<!-- #endif -->

使用条件编译配合 img 标签的 loading="lazy" 实现 H5 图片的懒加载。


ini 复制代码
<input placeholder="搜索小兔鲜商品" />

会被编译成以下代码:

xml 复制代码
<uni-input data-v-cdfa925e=""> <!-- 根元素有父组件的scoped属性 -->
  <div class="uni-input-wrapper"> 
    <!-- 内部元素继承父组件作用域 -->
    <div class="uni-input-placeholder" 
         data-v-6412fd5e=""       <!-- 子组件自己的scoped -->
         data-v-cdfa925e=""       <!-- 继承的父组件scoped -->
    >搜索小兔鲜商品</div>
  </div>
</uni-input>

其实有可能就是 uni-input-placeholder 是一个子组件,继承了父组件 scoped,同时生成了自己子组件的 scoped。


xml 复制代码
<scroll-view
  scroll-y
  class="scroll-view"
>
  <!-- 内容区域 -->
</scroll-view>

会被编译成以下代码:

xml 复制代码
<uni-scroll-view data-v-83a5a03c="" class="scroll-view">
  <div class="uni-scroll-view">
    <div class="uni-scroll-view" style="overflow: hidden auto;">
      <!-- 下拉刷新区域 -->
      <div class="uni-scroll-view-refresher" 
           style="background-color: rgb(255, 255, 255); height: 0px; transition: height 0.3s;">
        <div class="uni-scroll-view-refresh">
          <div class="uni-scroll-view-refresh-inner"></div>
        </div>
      </div>
      
      <!-- 实际内容容器 -->
      <div class="uni-scroll-view-content">
        <!-- 这里包含原内容组件 -->
      </div>
    </div>
  </div>
</uni-scroll-view>

ini 复制代码
<picker
  class="picker"
  mode="date"
  start="1900-01-01"
  :end="new Date()"
  :value="form.birthday"
  @change="changeBirthday"
>
  <view v-if="form.birthday">{{ form.birthday }}</view>
  <view class="placeholder" v-else>请选择日期</view>
</picker>

会编译成以下代码:

xml 复制代码
<uni-picker class="picker">
  <!-- 选择器弹出层 -->
  <div class="uni-picker-container uni-region-select">
    <!-- 遮罩层 -->
    <div class="uni-mask uni-picker-mask" style="display: none;"></div>
    
    <!-- 选择器主体 -->
    <div class="uni-picker-custom">
      <!-- 标题栏 -->
      <div class="uni-picker-header">
        <div class="uni-picker-action uni-picker-action-cancel">取消</div>
        <div class="uni-picker-action uni-picker-action-confirm">完成</div>
      </div>
      
      <!-- 选项区域,包含选项数据 -->
      <div class="uni-picker-select">...</div>
      <div></div> <!-- 布局占位 -->
    </div>
  </div>
  
  <!-- 当前值展示区 -->
  <div>
    <uni-view class="placeholder">请选择日期</uni-view>
  </div>
</uni-picker>

省市区选择器 app 和 H5 端用不了。


ruby 复制代码
<swiper :circular="true" :autoplay="true" :interval="3000" @change="onChange">
  <swiper-item v-for="item in list" :key="item.id">
    <navigator url="/pages/index/index" hover-class="none" class="navigator">
      <image class="image" :src="item.imgUrl"></image>
    </navigator>
  </swiper-item>
</swiper>

会编译成以下代码:

xml 复制代码
<uni-swiper>
  <!-- 轮播容器 -->

  <div class="uni-swiper-wrapper">
    <!-- 幻灯片组 -->
    <div class="uni-swiper-slides">


      <!-- 幻灯片框架 (循环5次) -->
      <div class="uni-swiper-slide-frame" style="transform: translate(-200%, 0px);">
        <!-- 实际幻灯片项 -->
        <uni-swiper-item style="transform: translate(100%, 0px);">
          <!-- 导航元素 -->
          <uni-navigator>
            <a href="/pages/index/index">
              <!-- 图像组件 (双重渲染) -->
              <uni-image>
                <div style="background-image:url(http://...)"></div>
                <img src="http://..." draggable="false">
              </uni-image>
            </a>
          </uni-navigator>
        </uni-swiper-item>
      </div>
      
      <!-- 其他重复结构... -->
      
    </div>

  </div>
</uni-swiper>

UniApp 样式编译机制详解:

1. 标签前缀转换规则

原始标签 编译后标签
view uni-view
navigator uni-navigator
input uni-input
scroll-view uni-scroll-view
swiper uni-swiper
image uni-image
button uni-button

2. 页面样式转换规则

特征 原始样式 编译后样式 变化原因
选择器作用域 普通类选择器 [data-v-xxxx] 属性选择器 Vue Scoped CSS 作用域隔离
单位转换 rpx 单位 rem 单位 跨平台适配
特殊选择器 page 选择器 转为 uni-page-bodybody 小程序特有选择器转换

在小程序 vs H5 的页面结构

  1. 小程序页面结构

    xml 复制代码
    <!-- 每个小程序页面的根元素 -->
    <page>
      <view>页面内容</view>
    </page>

    核心概念 :小程序的 page 元素是整个页面的唯一根容器,直接包含所有内容。

  2. H5 实际结构

    xml 复制代码
    <body> <!-- 整个网页的基础容器 -->
      <div class="app-container">
        <uni-page-head>顶部导航</uni-page-head>
        <uni-page-body> <!-- 这是真正的内容容器 -->
          页面内容
        </uni-page-body>
        <uni-tab-bar>底部导航</uni-tab-bar>
      </div>
    </body>

    关键区别 :H5 中的 uni-page-body 对应小程序 page

    也就是说 uni-page-body 是网页端项目的页面容器,此时可以在全局样式文件中通过条件编译来把小程序中 page 的样式作用在 uni-page-body 元素身上:

    css 复制代码
    /* #ifdef H5 */
    uni-page-body {
      height: 100%;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      background-color: #f7f7f8;
    }
    
    /* #endif */

    核心目标 :通过设置 uni-page-body 的样式,使其成为内容区域的布局容器 ,在垂直方向上自动填充顶部导航栏与底部 TabBar 之间的可视区域(即视口的可操作区域),同时保证背景色和布局能覆盖整个内容区域。这样既保持了与小程序的 page 容器相似的布局行为,又能正确适配 H5 的多组件嵌套结构。

css 复制代码
如果你在 H5 端,页面底部有个按钮,比如支付按钮使用了 fixed 布局,并且因为 H5 端的 bottom: 0 是在页面底部的,会和用 `dom` 元素生成的 `tabBar` 重叠,此时 `uniapp` 给你提供了一个帮助:
go 复制代码
我们可以给按钮设置这么一个 `css` 变量,来让按钮往上抬高一个 `tabBar` 的高度,但这个只是某些情况才这么做,一般页面不这么进行布局,而且 `css` 变量支持动态替换。

UniApp 如何转换 page 选择器

当你在不同页面的CSS中写:

css 复制代码
page {
  display: flex;
  background-color: #f4f4f4;
}

UniApp 会编译为:

css 复制代码
/* 针对内容区域的样式 */
uni-page-body[data-v-db0227ed] {
  display: flex;
  background-color: #f4f4f4;
}

/* 针对整个页面底层的样式,只保留背景颜色样式 */
body[data-v-db0227ed] {
  background-color: #f4f4f4;
}

运行时如何区分页面

  1. 进入A页面时:

    • body标签添加属性:<body data-v-db0227ed>
    • 此时A页面的样式body[data-v-db0227ed]生效
  2. 切换到B页面时:

    • 移除body上的data-v-db0227ed
    • 添加新属性:<body data-v-8749bc2f>
    • 此时B页面的样式body[data-v-8749bc2f]生效

关键问题的原因(为什么背景显示不全)

当设置page{background:black;}时实际发生了什么:

  1. body[data-v-xxxx] {background:black;}

    • 使整个网页底层变为黑色
    • 影响整个浏览器窗口的背景
  2. uni-page-body[data-v-xxxx] {background:black;}

    • 使中间内容区域容器的背景变为黑色

问题根源:

  • uni-page-body 这个容器默认可能有白色背景

  • 假设你设置了黑色背景但内容区还是白色,就像这样:

    lua 复制代码
    +---------------------- 浏览器窗口(黑色背景)----------------------+
    |                                                               |
    |   [顶部导航] - 可能透明也可能有自己背景                             |
    |   +-------------------------------------------------------+   |
    |   | uni-page-body容器(默认白色背景,覆盖了黑色)              |    |
    |   |   | 页面内容 |                                         |    |
    |   +-------------------------------------------------------+   |
    |   [底部导航] - 可能透明也可能有自己背景                             |
    |                                                               |
    +---------------------------------------------------------------+
  • 效果:底层背景是黑色,但因为中间内容区的白色容器挡在上面,你实际看到的是白色背景。


rpx 单位转 rem


ini 复制代码
<uni-swipe-action-item v-for="address in addressList" :key="address.id">

对于这种组件标签,不要在他身上添加事件监听器,因为你不知道在编译为 H5 代码后这个标签还有没有存在,最好就是在 view 标签,text 标签身上去绑定事件监听器,因为在编译后是一定存在的。


css 复制代码
:host {...}

这个选择器在小程序中可以直接作用在组件根元素,但是在 H5 就一点作用没有,会被编译成:

markdown 复制代码
[data-v-ccde5587]:host {...}

而且因为 H5 也没有使用 shadow-dom 所以这个选择器选中不了任何元素。那怎么办呢?

css 复制代码
/* #ifdef APP-PLUS || MP-WEIXIN */
// 根元素,box-sizing 保证高度不超出 100vh
:host {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #f7f7f8;
  box-sizing: border-box;
}

/* #endif */

使用条件渲染来控制组件样式,小程序端是可以正常使用 :host 标签的,那 H5 不行怎么办呢?

css 复制代码
/* global.scss 全局添加样式 */
/* #ifdef H5 */
uni-page-body {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #f7f7f8;
}

/* #endif */

然后我还发现了一个 bug:

热更新前:

css 复制代码
.scroll-view[data-v-db0227ed] { flex: 1; }

热更新后:

css 复制代码
.scroll-view { flex: 1; }

比如删除文件里的一个选择器,整个文件所有选择器的自定义属性就全都丢了,刷新页面也没用,其实这也不算 bug,因为我的 vue 文件实际上是没有添加 scoped 的,估计是 uniapp 会在最开始的阶段给我的样式添加一个 scoped,然后因为我的 style 没有添加 scoped,后续热更新就给他刷掉了。


小程序特有功能,比如微信登录,又比如微信专属的动画效果:

还有微信特有的按钮:

这些都得用条件编译给他去掉。


uniapp 打包的网页实现了和微信小程序一样的缓存页面栈,就是会自动缓存已经访问并且没有销毁的页面,核心原理是什么?


接下来解决 H5 端和 app 端骨架屏失效的问题,为什么微信小程序没问题,在这边就会有问题呢?先来介绍微信小程序的骨架屏为什么可以正常显示:

接下来再来介绍为什么 H5 端跟 App 端不能正常显示骨架屏:

scss 复制代码
/* #ifdef H5 || APP-PLUS */
/* 引入骨架屏宽高样式 */
@import './style/CategoryPanel.scss';
@import './style/HotPanel.scss';
@import '@/components/style/XtxSwiper.scss';
@import '@/components/style/XtxGuess.scss';
/* #endif */

在骨架屏组件内部使用这种方式来引入骨架屏的宽高样式,记住啊,这里后缀要加 .scss,不然会找不到这个文件,因为 vite 默认不会解析 .scss 后缀文件,

ini 复制代码
*// Vite 默认的解析器会尝试查找这些扩展名* const defaultResolveExtensions = [  '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json',  '.vue', '.svelte', '.marko', '.astro'  *// 框架文件扩展* ];

也可以在 vite 中配置:

这样就可以正确识别了,不过最简单的还是把路径写全。


uniCloud前端网页托管异常

📁 问题描述

  1. 部署场景

    • 项目类型:uni-app 开发的多端项目(H5+小程序+App)
    • 部署路径:https://你的域名/web/
    • 后端:uniCloud(无法修改服务器配置)
  2. 问题表现

    • 访问 https://你的域名/web/
    • 服务器返回的是根目录的index.html(而非/web目录下的)
    • 所有资源路径错误(缺少/web/前缀)
    • 控制台报错404(资源加载失败)

其实就是 uniCloud 默认支持了历史模式,不管你访问什么页面,只要不是静态资源,它就直接给你返回 index.html

💡 解决方案

manifest.json中添加网页端基础路径配置:

json 复制代码
// manifest.json
{
  "h5": {
    "router": {
      "base": "/web/" // 关键配置!
    },
}

生效原理

路由基础路径 base: "/web/" 会让 vue-router 所有路由自动添加/web/前缀

这里还有个坑啊,如果是 /web,那路径拼装会变成 /webstatic/...,坑比啊。

💎 解决方案优势

  1. 多端兼容 - 只影响H5端,不影响小程序/App
  2. 配置简单 - 只需修改 manifest.json
  3. 维护方便 - 路径配置集中化管理
  4. 自动生效 - 无需修改业务代码

就上面配置了 base: "/web/",接下来会发生什么事情呢?首先来看一下开发服务器的项目结构:

可以注意到在服务器根目录下生成了 web 目录,然后还可以注意到 images 静态资源是放在 /web/src 目录下,tabs 静态资源是放在 /web 目录下,我们接下来模拟访问开发服务器的整个流程:

步骤 1:用户输入根路径并加载首页

  1. 用户在地址栏输入: http://localhost:5173/ (根路径)

  2. 浏览器发起请求: GET http://localhost:5173/

  3. 服务器处理请求:

    • 服务器配置:重定向到 /web,请求直接返回 /web 目录下的 index.html 文件。
    • 服务器没有为其他路径做任何路由处理。
  4. 服务器响应:index.html 文件的内容返回给浏览器。

  5. 浏览器渲染 HTML: 开始解析收到的 index.html

步骤 2:加载 JavaScript 入口文件

  1. HTML 解析到 JS 加载标签:

    • index.html 中有一行关键代码:<script type="module" src="/web/src/main.ts"></script>
  2. 浏览器发起 JS 请求:

    • 浏览器将 src 属性解释为相对于服务器根目录的路径。
    • 请求 URL: GET http://localhost:5173/web/src/main.ts
  3. 服务器处理 JS 请求:

    • Vite 开发服务器在其项目目录结构中查找 /web/src/main.ts 文件。
  4. 服务器响应: 找到文件 /web/src/main.ts 后,将其内容(以及可能需要的转换/热更新信息)返回给浏览器。

  5. 浏览器执行 JS: 浏览器接收到 JS 文件内容并开始执行它。此时您的 Vue 应用代码 (main.ts 或其导入的模块) 开始运行。

步骤 3:前端路由初始化并修改 URL

  1. 路由库初始化:

    • 在前端框架(Vue)初始化的过程中,前端路由库(Vue Router)被创建和挂载。
    • 路由库读取到配置项 base: "/web/"
  2. 识别前端当前路径:

    • 路由库检查当前的浏览器地址栏路径:http://localhost:5173/ (根路径)。
    • 因为它匹配了路由 base ( /web/ ) 对应的应用根目录(实际上当前路径 //web/ 更短)。
  3. 前端路径重定向:

    • 改变地址栏(客户端行为): 它将地址栏路径替换http://localhost:5173/web/#/注意:这个过程完全在浏览器内部进行,并未向服务器发送新的请求! 浏览器只是显示了新 URL。
  4. 当前视图: 前端路由库根据当前匹配的路由路径(比如 /#/ ),渲染对应的组件到页面上。

步骤 4:加载应用所需的其他资源(CSS, 图片)

  1. 应用渲染到 DOM:

    • 随着对应组件的渲染,浏览器开始生成最终的 DOM 结构。
    • 组件可能包含类似代码: <img src="/web/static/tabs/cart_default.png">。这个 src 也是以根路径开头的 /web/...
  2. 浏览器发起静态资源请求:

    • 浏览器遇到 <img> 标签需要加载图片。它将 src 属性 /web/static/tabs/cart_default.png 解释为相对于服务器根目录的路径。
    • 请求 URL: GET http://localhost:5173/web/static/tabs/cart_default.png
  3. 服务器处理静态资源请求:

    • 开发服务器识别请求路径 /web/static/tabs/cart_default.png
  4. 服务器响应: 找到文件后,将图片数据返回给浏览器。

  5. 浏览器显示图片:

    浏览器接收到图片数据,并将其渲染到页面上的 <img> 标签位置。

步骤 5:用户尝试直接访问或刷新子路由

  1. 场景1:刷新当前应用页面: 用户在应用内部(URL 为 http://localhost:5173/web/#/pages/category/category)按下 F5 键刷新页面。

  2. 场景2:直接访问子路由: 用户在地址栏直接输入 http://localhost:5173/web/#/pages/category/category 并回车。

  3. 浏览器发起请求: GET http://localhost:5173/web/ (注意:URL hash #/pages/category/category 不会被发送到服务器!)

  4. 服务器处理请求:

    • 服务器识别请求路径 /web/
    • 用户补充的关键点生效: 服务器配置为访问 /web 路径时会返回根目录下的 index.html
  5. 服务器响应: 再次返回 index.html

  6. 浏览器加载过程和初始化: 重复 步骤 1(解析HTML)-> 步骤 2(加载JS)-> 步骤 3(路由初始化)

  7. 前端路由接管:

    • 前端路由库初始化时读取 base: "/web/"
    • 它获取到浏览器地址栏中的完整URL (包括 Hash):http://localhost:5173/web/#/pages/category/category
    • 路由库在客户端直接匹配到 /pages/category/category 路由,并渲染对应的页面组件。
  8. 页面渲染完成: 应用成功显示 /pages/category/category 页面。

上面讲述的这个流程对开发环境和生产环境都是适用的,因为开发环境其实也是生成了和上线后一样的项目结构,下次遇到同样的问题,其实可以在开发者工具的源代码中去看我们的项目结构,一下子就一目了然了。

最后进行验证,设置 base: "./" 可以看见开发者服务器生成的目录,就不包含 web 目录了:

此时的路径可以看到是相对路径:

相对谁呢?你想想,先添加 index.html,再添加的 js,渲染页面,然后加载图片资源,也是说图片是加载在 index.html 这个文件内部的,那图片的相对路径就是 index.html,毕竟是单页应用嘛,所有的相对路径其实都是相对于唯一的 index.html 文件。

这是 SPA 应用的基础设计原则

  1. index.html 是应用的唯一入口点

  2. 所有资源请求都基于此文件的路径

  3. 前端路由变化不影响已加载资源的相对路径解析

  4. 浏览器的路径解析机制:

    • 当前文档的 URL 为基准
    • 解析所有相对路径(./../

这种设计保证了应用的路径一致性,无论客户端路由如何变化,资源加载路径始终基于初始 HTML 文件位置。


uni-data-picker 组件工作流程

案例背景:

用户选择的行政区信息:

  • 省:北京市(code=110000)
  • 市:市辖区(code=110100)
  • 区:东城区(code=110101)

实际上,传递进入组件的只有 :value="form.countyCode" 也就是 区:东城区(code=110101) 的数据。

xml 复制代码
<uni-data-picker
  <!-- 绑定最终选择的区/县级编码 -->
  <!-- 示例:form.countyCode = '110101'(东城区代码) -->
  :value="form.countyCode"  
  
  <!-- 指定使用的云数据库表名 -->
  collection="opendb-city-china"

  <!-- field:定义查询返回的字段和映射关系 -->
  <!-- 数据库会执行:SELECT code, name FROM region ... -->
  <!-- 映射为:{ value: code, text: name } -->
  field="code as value, name as text"

  <!-- orderby:查询排序规则 -->
  <!-- 数据库会执行:ORDER BY code ASC -->
  orderby="value asc"

  <!-- step-search:是否启用分步搜索(建议更正拼写为step-search) -->
  :step-searh="true"

  <!-- 用于标识传递的 value 其实对应的是数据库中的哪个字段,参与数据库查询 -->
  <!-- 注意,这里得是一个唯一标识符,code = '110101' 类似这样子 -->
  self-field="code"
  
  <!-- 用于标识怎么关联上一级节点的字段 -->
  <!-- 举个例子,比如现在我们有一个东城区的json数据,我们要怎么找到他上一级市的数据呢? -->
  <!-- 在东城区的json数据中有一个parent_code字段可以直接拿到上一级的数据 -->
  parent-field="parent_code"

  @change="changeRegionAppH5"
>
  <view v-if="form.fullLocation">{{ form.fullLocation }}</view>
  <view class="placeholder" v-else>请选择城市</view>
</uni-data-picker>

这个组件其实还支持插槽,插槽内部的内容会覆盖掉主件的默认显示,默认显示是一个 input 表单,并且带有清空按钮:

就长这样。


一、未打开状态(回显路径)

具体步骤:

  1. 查询区级记录

    vbnet 复制代码
    SELECT * FROM region WHERE code = '110101' 
    /* 返回:{ code:"110101", name:"东城区", parent_code:"110100" } */
  2. 查询市级记录

    vbnet 复制代码
    SELECT * FROM region WHERE code = '110100'  /* 由上条记录的parent_code获得 */
    /* 返回:{ code:"110100", name:"市辖区", parent_code:"110000" } */
  3. 查询省级记录

    sql 复制代码
    SELECT * FROM region WHERE code = '110000'  /* 由上条记录的parent_code获得 */
    /* 返回:{ code:"110000", name:"北京市", parent_code:null } */

结果生成:

arduino 复制代码
// 组建selected数组
selected = [
  { value: "110000", text: "北京市" },   // 省
  { value: "110100", text: "市辖区" },   // 市
  { value: "110101", text: "东城区" }    // 区
]

// 显示文本
"北京市 / 市辖区 / 东城区"

关键特点:

  • 顺序查询:区→市→省(从终端节点向上溯源)
  • 字段映射 :自动转换 code → valuename → text

二、打开状态(级联加载)

具体步骤:

  1. 查询省级列表

    vbnet 复制代码
    SELECT code as value, name as text 
    FROM region 
    WHERE parent_code IS NULL
    /* 返回所有省份:北京、天津、河北... */
  2. 查询市级列表

    vbnet 复制代码
    SELECT code as value, name as text 
    FROM region 
    WHERE parent_code = '110000' /* 来自selected[0].value */
    /* 返回北京市的下级:市辖区、县区... */
  3. 查询区级列表

    vbnet 复制代码
    SELECT code as value, name as text 
    FROM region 
    WHERE parent_code = '110100' /* 来自selected[1].value */
    /* 返回市辖区的下级:东城区、西城区... */

这里前端做了优化,根据选中元素列表,直接把三次查询的语句拼成一个数组传递给云函数,云函数可以直接调用三次查询,而不是前端调用三次云函数进行查询,这样就省了请求数,因为本来需要三次请求能实现的事情,现在通过调用一次云函数请求就实现了。

结果展示:

lua 复制代码
| 省级列表      | 市级列表     | 区级列表     |
|-------------|-------------|-------------|
| ☑ 北京市     | ☑ 北京市    | ☑ 东城区     |
|   天津市     |             |   西城区     |
|   河北省     |             |             |

关键特点:

  • 层级依赖

    • 省级查询: parent_code IS NULL
    • 市级查询: parent_code=省级value
    • 区级查询: parent_code=市级value

三、特殊场景处理

场景1:新用户首次打开(无预选值)

用户操作路径

  1. 选择「河北省」
  2. 自动触发市级查询 (parent_code=130000)
  3. 选择「石家庄市」
  4. 自动触发区级查询 (parent_code=130100)

场景2:中途变更选择

ini 复制代码
-- 用户将北京改为天津时:
1. 取消未完成的市/区查询
2. 查询天津的市级: WHERE parent_code='120000'
3. 自动查询首市的区级: WHERE parent_code='120100'

相关推荐
FlyingBird~14 分钟前
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
java·javascript·typescript
江城开朗的豌豆1 小时前
前端性能救星!用 requestAnimationFrame 丝滑渲染海量数据
前端·javascript·面试
江城开朗的豌豆1 小时前
src和href:这对'双胞胎'属性,你用对了吗?
前端·javascript·面试
江城开朗的豌豆1 小时前
forEach遇上await:你的异步代码真的在按顺序执行吗?
前端·javascript·面试
漂流瓶jz9 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
鱼樱前端10 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js
coding随想10 小时前
JavaScript中的原始值包装类型:让基本类型也能“变身”对象
开发语言·javascript·ecmascript
满分观测网友z11 小时前
vue的<router-link>的to里面的query和params的区别
前端·javascript·vue.js
BillKu11 小时前
Vue3 + TypeSrcipt 防抖、防止重复点击实例
前端·javascript·vue.js