WikiWiki
首页
Java开发
Java面试
Linux手册
  • AI相关
  • Python Flask
  • Pytorch
  • youlo8
SEO
uniapp小程序
Vue前端
work
数据库
软件设计师
入门指南
首页
Java开发
Java面试
Linux手册
  • AI相关
  • Python Flask
  • Pytorch
  • youlo8
SEO
uniapp小程序
Vue前端
work
数据库
软件设计师
入门指南
  • uni-app-api
  • uni-app开发交友小程序
  • uni-app开发慕课博客
  • uni-app框架实战_基础
  • uni-app框架实战_项目
  • 微信小程序原生开发

小程序开发-微信原生

一、组成结构

1.小程序开发前的准备

  • 微信公众平台注册账号【微信公众平台 (qq.com)】
  • 下载【微信开发者工具】建议稳定版

2.创建微信小程序

  • 打开微信开发者工具

image-20240506201226364

image-20240506201348696

  1. 看到此页面,表示小程序已经创建成功

    image-20210503095033115

3.基本组成结构

pages所有的 **小程序页面,**每个页面以 单独的文件夹存在

  • index:index 页面文件夹,文件夹下面都包含 4 个 基本的页面文件,这四个文件共同组成 index 页面

    • index.js:.js 文件 -- 页面的脚本文件,存放页面的数据、事件处理函数、生命周期等
    • index.json:.json 文件 -- 当前页面的配置文件,配置页面的外观、表现等
    • index.wxml:.wxml 文件 -- 页面的模板结构文件
    • index.wxss:.wxss 文件 -- 当前页面的样式表文件
  • logs

    • ...
  • utils:工具类文件夹

    • utils.js
  • app.js:小程序的项目逻辑文件,用来 注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。

  • app.json:小程序公共配置文件,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

  • app.wxss:小程序公共样式表

  • project.config.json:是项目配置文件,用来记录我们对小程序开发工具所做的个性化配置

    • setting 中保存了编译相关的配置
    • projectname 中保存的是项目名称
    • appid 中保存的是小程序的账号 ID
  • sitemap.json:配置小程序及其页面是否允许被微信索引

    • rules中保存了索引规则列表的配置
    • rules规则中,action表示页面是否能被索引(allow,disallow),page表示生效的页面
  1. 一个小程序的页面由什么组成?
    1. 小程序的页面由 4 个文件组成
    2. 并且 这4个文件 应该被放入到 pages 文件夹下的 同一个文件夹中
  2. project.config.json 文件的作用是什么?
    1. 项目配置文件,用来记录我们对小程序开发工具所做的个性化配置
  3. sitemap.json 文件的作用是什么?
    1. 配置小程序及其页面是否允许被微信索引

4.wxml

  1. 什么是 WXML

    WXML(WeiXin Markup Language)是框架设计的一套标签语言(组件),用来构建小程序页面的结构,其作用类似于网页开发中的 HTML

  2. WXML 和 HTML 的区别

    • 标签名称不同

      • HTML(div, span, img, a)
      • WXML(view, text, image, navigator)
    • 属性节点不同

      • <a href="#">超链接</a>
      • <navigator url="/pages/home/home">跳转到home页</navigator>
    • 提供了动态渲染数据的模板语法

      • 数据绑定

      • 条件渲染

      • 列表渲染

  • 我是否可以在 wxml 文件中,写入 div 标签?
    • 可以写入 div 标签,但是不要使用。div 标签会被解析成 类 view 标签效果
    • wxml 中需要写入 小程序提供的组件,如果写入的为 非小程序组件,则会被解析为 类 view 标签效果

5.wxss

  1. 什么是 WXSS
    • WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML` 的组件样式
    • 类似于网页开发中的 CSS
    • WXSS 具有 CSS 大部分的特性
  2. 新增了尺寸单位 -- rpx,一个 rpx 为页面宽度的 1 / 750
  3. 提供了全局的样式和局部样式。
    1. 全局样式:写入到 根目录的wxss 中的样式
    2. 局部样式:写入到 **页面的 wxss ** 中的样式
  4. 此外 WXSS 仅支持部分 CSS 选择器
    1. .class(推荐使用) 和 #id
    2. element
    3. 并集选择器和后代选择器
    4. ::after 和 ::before 等伪类选择器
  1. wxss 新增了什么尺寸单位?
    1. rpx 尺寸单位,一个 rpx 为页面宽度的 1 / 750
  2. wxss 中推荐使用什么选择器?
    1. .class 类选择器

6..json 配置文件

小程序中,额外多出了一个 .json 的配置文件,.json 文件主要分为两个:

  1. 项目根目录下的 .json 文件
    1. 修改项目的基本配置
      1. 首页
      2. navigationBarBackgroundColor
  2. 页面中的 .json 文件
    1. 修改页面的基本配置(优先级高)
      1. navigationBarBackgroundColor
  1. 小程序中的 .json 文件主要分为哪两种?
    1. 项目根目录下的 .json 文件
    2. 页面中的 .json 文件
  2. 当这两种配置文件出现相同配置时,会出现什么结果?
    1. 以 页面的.json 文件 为主

7.小程序的宿主环境

  1. 宿主环境指的是 程序运行所必须的依赖环境
    1. web 前端的宿主环境为:浏览器
    2. android 软件的宿主环境为:android 系统
    3. IOS 软件的宿主环境为:IOS 系统1
  2. 小程序的宿主环境是什么?
    1. 小程序的宿主环境为:手机微信
      1. 所以:小程序可以调用 手机微信 中的
        1. 扫码
        2. 支付
        3. 登录
        4. 分享
        5. 等等功能
  3. 小程序的运行环境:
    1. image-20210503113010718
    2. 分成渲染层和逻辑层
      1. 其中 WXML 模板和 WXSS 样式工作在渲染层
        1. 渲染层的界面使用了WebView 进行渲染
        2. 一个小程序存在多个界面,所以渲染层存在**多个WebView**线程
      2. JS 脚本工作在逻辑层。
        1. 逻辑层采用JsCore线程运行JS脚本
    3. 这两个线程的通信会经由微信客户端做中转
      1. 逻辑层发送网络请求也经由 Native(手机原生系统) 转发

8.小程序的内置组件

视图容器 / view (qq.com)

  1. view
  2. scroll-view
  3. swiper && swiper-item
  4. text && rich-text
  5. button
  6. image

二、核心语法

1.数据驱动原则

数据驱动:

  // 商品
  let product = {
    price: 10,
    num: 5
  }
  // 总价格
  let total = 0;
  // 计算总价格的方法
  function getTotal(product) {
    return product.price * product.num
  }
  // 计算商品的总价格
  total = getTotal(product)
  // 进行打印
  console.log('总价格:' + total);
  // 50 太贵了,所以我们少购买了两个商品,也就是让 num = 3
  product.num = 3;
  // 问:总价格是多少?
  console.log('总价格:' + total); // 此时,打印发现总价格还是 50 元,如果要说原因的话,那么应该很简单,【因为我们没有重新进行价格的计算嘛】
  // 但是,此时大家有没有想过一点?我们为什么要进行价格的计算呢?
  // ----------------------------------------------------
  // 当商品的数量发生变化时,商品的总价格【理应发生变化】,不是吗?

上面的例子,就是我想要跟大家说的:【当数量发生变化时,商品的总价格理应发生改变】。

那么同样的道理,在我们的页面中,假如:

某一个 DOM 依赖于某个数据进行展示,那么【当数据发生变化时,视图也理应发生变化】。

而这个就是【响应式数据驱动】。

PS:如果大家想要跟深入的了解,那么可以查看博客:聊一聊响应式构建的那些经历

小程序中完成响应式:

  • 在 data 中定义数据

    // index.js
    // 获取应用实例
    const app = getApp()
    Page({
     data: {
       product: {
         price: 10,
         num: 5
       }
     }
    })
    
  • 在 wxml 中使用数据

    <view>
      <view>
        <!-- wxml 中访问数据,必须使用 {{}} 语法,{{}} 语法中可以放置【任意的、单一的 JavaScript 表达式】 -->  
        商品的单价:{{product.price}}
      </view>
      <view>
        商品的数量:{{product.num}}
      </view>
      <view>
        商品的总价格:{{product.price * product.num}}
      </view>
    </view>
    

现在我们已经可以在 js 的 data 中定义数据,并且在 wxml 中通过 {{}} 语法使用数据。

  1. 什么是数据驱动?
    1. 当数据发生变化时,视图理应发生变化
  2. 在小程序中如何完成数据绑定?
    1. 在 data 中定义数据
    2. 在 wxml 中通过 使用数据

2.常用事件与属性列表

处理点击事件

接下来我们希望做一件事情:

创建一个按钮

当用户点击按钮时

让 product 的 num + 1

创建按钮的方式非常简单:

<button type="primary">num + 1</button>

问题在于:我们如何给这个按钮添加点击事件呢?

有过开发经验的同学,可能会猜到:我们可以给 button 一个 click 事件来监听按钮的点击。

可是大家需要知道,现在我们是在【小程序】中,那么如果想要给 button 添加点击事件则不可以使用 click 而是 bind:tap / bindtap。

其中 bind: / bind 表示【绑定事件】,tap 为绑定的具体事件。小程序具体事件列表,可以点击 这里 查看。

  <button type="primary" bind:tap="onAddNum">num + 1</button>

接下来需要在 js 中定义对应的 事件

 /**
  * 定义事件处理的方法
  */
 onAddNum () {
  console.log('onAddNum')
 }

到目前:我们已经 监听了按钮的点击事件,并且写入了对应的处理函数 ,接下来就需要 **修改 num 的值 **

修改 data 的数据

想要修改 data 中的数据,那么我们需要借助一个函数 setData。

setData 接收一个 对象作为参数,这个对象就是最新的 data 数据。

其中 key 为要修改的数据, value 为最新的值

访问 data 的数据

因为我们想要让 num + 1 ,所以我们还需要拿到 num 的当前值,想要访问 num 的值,可以通过 this.data.product.num 的形式访问

所以最终的修改 num 的代码为:

 /**
  * 定义事件处理的方法
  */
 onAddNum () {
  this.setData({
    'product.num': this.data.product.num + 1
  })

此时,当我们点击 button ,可以发现:【当 num 发生改变时,总价格也发生了对应的变化】

  1. 如何为按钮添加点击事件?
    1. bindtap || bind:tap
  2. 如何修改 data 中数据的值?
    1. 通过 this.setData({}) 定义新的值
    2. 通过 this.data 访问具体的值

3.事件传参

现在让我们把需求变得更加复杂一些。

我们希望 onAddNum 方法可以接收一个参数,每次点击 num 增加的数量为传入的参数

那么如果想要实现这个需求的话,那么就需要涉及到一个知识点:【事件传参】。

如果大家有过开发经验的话,那么可能会认为这是一个非常简单的需求,顺便可以写下如下代码:

// html
<button type="primary" bind:tap="onAddNum(5)">num + 1</button>

// js
 onAddNum (step) {
  this.setData({
    'product.num': this.data.product.num + step
  })
 }

可是,假如我们真按照以上代码进行实现的话,那么 你应该会收到以下如下的警告:

image-20210505114301165

这个警告的意思是:没有一个叫做 onAddNum(5) 的方法用来处理当前的这个 tap 事件。

也即是说:onAddNum(5) 会被当做一个 完整的方法名字,而不是 方法名为:onAddNum,传入了参数为 5 !

那么如果我们想要传递参数应该怎么做呢?


在小程序中,如果想要给 **点击事件传递参数的话,**那么需要借助 event 对象 和 data- 属性 !

参数的传递包含两个部分:

  1. 形参
  2. 实参

形参:

首先先来看 形参,对于 点击事件的回调方法 而言,默认会接收一个参数 event (事件对象)。这个 event 对象为:回调方法的唯一参数

实参:

对于 小程序 中,我们不能直接为 回调方法传递实参。

而是需要通过:属性绑定的形式,把需要传递的参数绑定到 当前 DOM 元素中,绑定数据的属性需要以 data- 开头。该属性可以通过 e.target.dataset 进行访问。

// html
<button type="primary" bind:tap="onAddNum" data-step="5">num + 1</button>

// js
 onAddNum (e) {
  //  获取 data-step 的值
  let step = parseInt(e.target.dataset.step);
  this.setData({
    'product.num': this.data.product.num + step
  })
 }
  1. 如果想要在【点击事件中】传递参数,那么需要怎么做?
    1. 通过属性绑定(data-xx)的形式,把需要传递的参数绑定到 当前 DOM 元素中
    2. 在对应的回调函数中,通过 e.target.dataset 进行访问

4.实现【双向数据绑定】

上一章节中我们通过【事件传参】实现了【每次点击 + 5】 的功能,但是这样的功能未免还是有些太单调了。

所以我们接下来希望实现一个新的功能:

创建一个数字输入框,输入框 与【商品数量】完成 【双向数据绑定】。

即:

  1. 输入框内容发生变化时,商品数量同步跟随变化
  2. 商品数量发生变化时,输入框内容同步跟随变化

那么这样的功能我们应该如何去实现呢?


如果想要实现这个功能,那么我们需要先把这个功能进行拆解,【把一个复杂的功能拆解成多个简单的功能】是实现一个复杂逻辑的标准方式。

那么如何进行拆解呢? 大家可以先进行以下思考,然后再继续向下进行学习!


以上功能拆解如下:

  1. 创建一个【数字输入框】
  2. 设置 【商品数量】 为输入框的初始值
  3. 监听用户的输入行为
  4. 获取用户输入的值
  5. 赋值给【商品数量】
// html
<view>
    商品的数量:
    <!-- 1. 创建一个【数字输入框】 -->
    <!-- 2. 设置 【商品数量】 为输入框的初始值 -->
    <input class="num-input" type="number" value="{{ product.num }}" bindinput="onInput" />
</view>
  
// js
/**
  * 3. 监听 input 的输入事件
  */
 onInput (e) {
  //  4. 获取用户输入的值
   const val = parseInt(e.detail.value);
  //  5. 赋值给【商品数量】
  this.setData({
    'product .num': val
  })
:
  1. 什么叫做双向数据绑定?

    当视图发生变化时,数据跟随发生变化。

    当数据发生变化时,视图跟随发生变化.

  2. 小程序中如何实现双向数据绑定?

    通过 value 为 input 视图绑定数据

    通过监听 bindinput 获取视图的变化,在回调方法中修改数据

5.条件渲染

现在你已经买了很多的商品了,可是当你出去结账的时候,售货员小姐姐对你发出了一声惊呼:

  1. 如果【总价格 <= 100 】:hello 帅哥
  2. 如果【总价格 > 100 && 总价格 < 1000】:哇哦 有钱人哦
  3. 如果【总价格 >= 1000】:土豪你好

如果想要实现这么一个功能的话,那么就需要使用【条件渲染】的功能了。

小程序中提供了两个 API 都可以实现【条件渲染】的功能:

  1. wx:if ... wx:elif ... wx:else
  2. hidden

那么下面我们就分别用这两个语法来实现一下这个功能:

<!-- wx:if ... wx:elif ... wx:else:判断结果为 true 则进行渲染,否则不进行渲染 -->
  <view>
    售货员小姐姐惊呼:
    <text wx:if="{{ product.price * product.num <= 100 }}">hello 帅哥</text>
    <text wx:elif="{{ product.price * product.num > 100 && product.price * product.num < 1000 }}">哇哦 有钱人哦</text>
    <text wx:else>土豪你好</text>
  </view>
  <!-- hidden:结果为 true 则隐藏,否则不隐藏 -->
  <view>
    售货员小姐姐惊呼:
    <text hidden="{{ !(product.price * product.num <= 100) }}">hello 帅哥</text>
    <text hidden="{{ !(product.price * product.num > 100 && product.price * product.num < 1000) }}">哇哦 有钱人哦</text>
    <text hidden="{{product.price * product.num < 1000}}">土豪你好</text>
  </view>
  1. v-if 和 hidden 的区别是什么?
    1. v-if 用来控制 【组件是否会被渲染】
    2. hidden 用来控制【组件是否会被隐藏】
    3. 一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

6.列表渲染

新的需求:

如果我们有一组商品,并且希望把这组商品全部渲染出来得话,那么就需要使用到【列表渲染】的功能。

小程序中为我们提供了 v-for 指令,让我们进行【列表渲染】的实现。

同时也为我们提供了一个:包裹性质的容器 block 组件,当我们去循环多个元素时,可以使用 block 进行包裹,block 组件只起到包裹的其他组件的作用,本身并不会进行渲染。

// html
<!-- 
    利用 wx:for 循环渲染商品
    默认数组的当前项的下标变量名默认为 index,
    数组当前项的变量名默认为 item
   -->
  <view class="product-box">
    <block wx:for="{{ products }}" wx:key="index">
      <view class="product-item">
        <text>商品名:{{item.name}}</text>
        <text>价格:{{item.price}}</text>
      </view>
    </block>
  </view>
  
// js
data: {
   products: [
     {
       name: '苹果',
       price: 3.2
     },
     {
       name: '面包',
       price: 5.0
     },
     {
       name: '可乐',
       price: 2.5
     }
   ]
 }
  1. 使用 wx:for 时,当前项的【下标变量名】和【当前项变量名】默认分别是什么?
    1. 默认数组的当前项的下标变量名默认为 index
    2. 数组当前项的变量名默认为 item
  2. block 组件是否会被渲染?
    1. block 只是一个包裹性质的容器,不会被渲染。

7.配置文件解读

  1. app.json 配置文件:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
    1. pages 数组:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#pages
      1. 创建 list 页面
    2. window 对象:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window
    3. tabbar 对象:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
      1. index 页面
      2. list 页面
  2. 页面.json 配置文件:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html

8.数据请求

wx.request 发起网络请求,请求的方式主要分为两种:

  1. get 请求
  2. post 请求

这里准备了两个数据请求接口,可以用来测试 wx.request 的数据请求(详见接口文档):

  1. /api/test/getList
  2. /api/test/postData

那么接下来我们就根据 wx.request 来完成一个基本的接口请求

// html
<view>
  <button type="primary" bindtap="onGetClick">发起 get 请求</button>
</view>
// js
// index.js
// 获取应用实例
onGetClick () {
    wx.request({
        url: 'https://api.imooc-blog.lgdsunday.club/api/test/getList',
        method: 'GET',
        success: (res) => {
            console.log(res);
        }
    })
}

这样的代码看起来没有任何问题,但是我们却得到了一个错误(可测试的 APPID:wxf01e2ce0eb588aac):

image-20210508113751611

而要解决这个问题,我们就需要明确一个问题:小程序中的数据请求有什么限制?

  1. 只能请求 HTTPS 类型的接口
  2. 必须将接口的域名添加到信任列表中

解决方案:

  1. 生产环境:将想要请求的域名协议【更改为 HTTPS】并【添加到域名信任列表】
  2. 开发环境:通过勾选image-20210508103939068

当 get 请求完成,接下来来测试一下 post 请求:

// html 
  <button type="primary" bindtap="onPostClick">发起 post 请求</button>
  // js
   onPostClick () {
    wx.request({
      url: 'https://api.imooc-blog.lgdsunday.club/api/test/postData',
      method: 'POST',
      data: {
        msg: '愿大家心想事成,万事如意'
      },
      success: (res) => {
        console.log(res);
      }
    })
  }

题外话(扩展内容:针对有 web 前端开发经验的同学):

  1. 跨域问题: 跨域问题主要针对 浏览器 而言,而小程序宿主环境为【微信小程序客户端】,所以小程序中不存在【跨域问题】
  2. ajax 请求: ajax 依赖于 XMLHttpRequest 对象,而小程序宿主环境为【微信小程序客户端】,所以小程序中的【网络请求】不是 ajax 请求
  1. 小程序中的数据请求有什么限制?以及如何解决这种限制

    1. 限制:
      1. 只能请求 HTTPS 类型的接口
      2. 必须将接口的域名添加到信任列表中
    2. 解决方案:
      1. 生产环境:将想要请求的域名协议【更改为 HTTPS】并【添加到域名信任列表】
      2. 开发环境:通过勾选image-20210508103939068
  2. 小程序的数据请求会存在跨域问题吗?为什么?

    1. 不会

    【跨域问题】只存在于基于浏览器的 Web 开发中

    由于小程序的宿主环境不是浏览器,而是微信客户端

    所以小程序中不存在跨域问题

  3. 小程序的数据请求可以叫做 ajax 请求吗?为什么?

    不可以

    ajax 的核心是依赖于 【浏览器端】 的 XMLHttpRequest 对象

    由于小程序的宿主环境不是浏览器,而是微信客户端

    所以小程序的数据请求不可以叫做 ajax 请求

9.异步编程新方案 - promise

首先先去假设一个场景:

目前有一个需求,需要你按照以下的逻辑去进行接口请求:

  1. 先去请求接口 A
  2. 在接口 A 获取到数据之后,再去请求接口 B
  3. 在接口 B 获取到数据之后,再去请求接口 C
  4. 在接口 C 获取到数据之后,再去请求接口 D

如果按照上一小节学习到的内容,那么我们会得到以下的代码(接口代码请见:03-小程序核心语法/02-回调地狱.html):

   A(function (res) {
    console.log(res);
    B(function (res) {
      console.log(res);
      C(function (res) {
        console.log(res);
        D(function (res) {
          console.log(res);
        })
      })
    })
  })

在这个 颜值即正义 的世界里面,我们这样的代码结构应该是 没有前途的。 因为它太丑了,并且太难以阅读了。

假想一下,如果我们要请求 10 个接口的话,那么代码会变成什么样子?

所以在编程圈里对这样的代码有一个非常学术的名字:回调地狱 -> 回调函数的大量嵌套导致出现 复杂且难以阅读 的逻辑

点击 Promise 进入官方文档:

<!-- 
  使用 Promise 进行定义接口:
  Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
  它一个构造函数,所以可以通过 new 关键字来构建它,获取实例。
  在 Promise 中,分为了三种状态:
  1. 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  2. 已兑现(fulfilled): 意味着操作成功完成。
  3. 已拒绝(rejected): 意味着操作失败。
  
  可以通过 promise 实例的 
  1. 成功:promise.then()
  2. 失败:promise.catch() 
  3. 结束:promise.finally()
  的三个方法,进行链式调用来解决回调地狱的问题。
 -->
<script>
  const isA = true
  const isB = true
  const isC = true
  const isD = true

  function A() {
    // 1. 创建 Promise 实例
    return new Promise((resolve, reject) => {
      // 2. 当前处于 【待定(pending)】 状态下
      console.log('执行 A 接口的逻辑')
      setTimeout(() => {
        if (isA) {
          // 3. 进入 【已兑现(fulfilled)】 状态下
          resolve('接口 A 执行完成')
        } else {
          // 4. 进入 【已拒绝(rejected)】 状态下
          reject('接口 A 执行失败')
        }
      }, 1000)
    })
  }

  function B() {
    return new Promise((resolve, reject) => {
      console.log('执行 B 接口的逻辑')
      setTimeout(() => {
        if (isB) {
          resolve('接口 B 执行完成')
        } else {
          reject('接口 B 执行失败')
        }
      }, 1000)
    })
  }

  function C() {
    return new Promise((resolve, reject) => {
      console.log('执行 C 接口的逻辑')
      setTimeout(() => {
        if (isC) {
          resolve('接口 C 执行完成')
        } else {
          reject('接口 C 执行失败')
        }
      }, 1000)
    })
  }

  function D() {
    return new Promise((resolve, reject) => {
      console.log('执行 D 接口的逻辑')
      setTimeout(() => {
        if (isD) {
          resolve('接口 D 执行完成')
        } else {
          reject('接口 D 执行失败')
        }
      }, 1000)
    })
  }

  // 获取 Promise 实例
  A()
    // 通过 .then 方法获取当前 Promise 的执行结果
    .then(res => {
      console.log(res);
      // 标记下一步进入 B 方法
      return B()
    })
    // 继续 .then 进行下一次的异步操作
    .then(res => {
      console.log(res);
      // 标记下一步进入 C 方法
      return C()
    })
    // 继续 .then 进行下一次的异步操作
    .then(res => {
      console.log(res);
      // 标记下一步进入 D 方法
      return D()
    })
    // 继续 .then 进行下一次的异步操作
    .then(res => {
      console.log(res);
      // 结束
    })

Promise 与 回调地狱的结果代码对比截图

image-20210508152109839
  1. promise 是如何解决回调地狱的问题呢?
    1. 通过 .then 的方式进行 链式调用
  2. Promise 的状态分为几种,分别是什么?
    1. 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
    2. 已兑现(fulfilled): 意味着操作成功完成。
    3. 已拒绝(rejected): 意味着操作失败。
  3. 如何让 Promise 变成 已兑现(fulfilled)的状态,如何接收已兑现(fulfilled)的结果
    1. 通过 resolve 可以把 Promise 的状态,从 【待定(pending)】转变为 【已兑现(fulfilled)】
    2. 通过 promise实例.then 方法可以接收 已兑现(fulfilled) 的结果

但是看到这里之后,可能还会有很多同学 充满疑惑, “ 我并不感觉 promise 的这种方式更加简单呀? ”,如果你确实有这样的 疑问 的话,那么你应该相信这样的疑问在之前也被人提出过。

那么这个问题是怎么解决的呢?请看下一节 异步编程再升级 - async + await

10.异步编程再升级 - async + await

Promise 的方案解决了 回调地狱 的问题,但是 Promise 又带来了新的问题,那就是:大量的链式调用,让我们的代码变得又臭又长!

点击 async + await 进入官方文档:

  // 使用 async 和 awiat 可以简化 Promise 的异步操作,把 Promise 的异步操作变为同步写法
  // async:标记一个函数为异步函数
  // await:标记当前操作为异步操作,await 关键字只能使用在被 【async标记的函数中】
  async function test() {
    const resA = await A();
    console.log(resA);
    const resB = await B();
    console.log(resB);
    const resC = await C();
    console.log(resC);
    const resD = await D();
    console.log(resD);
  }

三种实现方案截图对比:

image-20210508155854567
  1. async 和 await 的作用是什么?
    1. async 和 await 可以简化 promise 操作
    2. 使 promise 的异步操作拥有 同步写法
  2. 使用 await 的注意事项是什么?
    1. await 必须在被 async 标记的函数中使用

11.小程序中使用 promise 解决异步编程

回过头来来看【小程序的代码】,同时回顾一下之前我们解决过的需求:

目前有一个需求,需要你按照以下的逻辑去进行接口请求:

  1. 先去请求接口 A
  2. 在接口 A 获取到数据之后,再去请求接口 B
  3. 在接口 B 获取到数据之后,再去请求接口 C
  4. 在接口 C 获取到数据之后,再去请求接口 D

那么接下来我们需要做的就很简单了,我们要 **使用 async 和 await ** 简化以上操作。

如果要达到我们的目标,那么我们需要分成两步来去操作:

  1. 获取到 promise 实例对象
  2. 使用 async 和 await 简化 promise 的操作

获取到 promise 实例对象:

因为 wx.request 不支持 promise 化,所以我们需要:使用 promise 封装 wx.request 请求

  pA () {
    return new Promise((resolve, reject) => {
      console.log('执行 A 接口的逻辑');
      wx.request({
        url: 'https://api.imooc-blog.lgdsunday.club/api/test/A',
        success: (res) => {
          resolve(res)
        },
        fail: (err) => {
          reject(err)
        }
      })
    })
  }

使用 async 和 await 简化 promise 的操作(PS:注意不要勾选 ES6 转 ES5)

async onPromiseGetClick () {
    const resA = await this.pA()
    console.log(resA.data.data.msg);
    const resB = await this.pB()
    console.log(resB.data.data.msg);
    const resC = await this.pC()
    console.log(resC.data.data.msg);
    const resD = await this.pD()
    console.log(resD.data.data.msg);
  }
  1. 如何使 wx.request 配合 async 和 await 使用?
    1. 使用 promise 封装 wx.request 请求
    2. 使用 async 和 await 简化 promise 的操作

12.生命周期

到现在为止我们已经学习了非常多的小程序核心知识点,那么接下来我们就需要去实现一个小的案例了。

那么接下来我们就先去实现这个案例的第一个功能:

我们希望 页面出现之后,可以获取接口数据,并进行渲染

那么这样的一个简单需求,根据我们现在所学到的知识是:没有办法实现的。

如果想要实现这个功能,就需要掌握 页面的生命周期

什么是生命周期:

想要学习【小程序】的生命周期,那么我们必须要先搞清楚,什么是【生命周期】

所谓 生命周期 就是:一件事物由 创建 到 销毁 的全过程。

在这个过程中会有很多 ” 关键的时刻 “,这些关键的时刻就是 生命周期函数


在 【小程序】中,生命周期主要分为两部分:

  1. 页面的生命周期(本章节内容)
  2. 组件的生命周期(后续章节讲解)

创建新的页面 list,在新创建的页面中,我们可以发现在 js 文件中已经默认生成了很多的代码:

// pages/list/list.js
Page({
	...
    /**
     * 生命周期函数--监听页面加载
     */
    onLoad: function (options) {
        console.log('onLoad');
    },

    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady: function () {
        console.log('onReady');
    },

    /**
     * 生命周期函数--监听页面显示
     */
    onShow: function () {
        console.log('onShow');
    },

    /**
     * 生命周期函数--监听页面隐藏
     */
    onHide: function () {
        console.log('onHide');
    },

    /**
     * 生命周期函数--监听页面卸载
     */
    onUnload: function () {
        console.log('onUnload');
    },
	...
})

在这些代码中,我们重点关注 生命周期函数--xx 相关的内容。

这 5 个函数,就是 【小程序中的生命周期函数】,我们把鼠标放入到【函数上】,那么【小程序开发工具】会提示出对应的【函数解释】。

这些生命周期函数不需要全部掌握,我们只需要着重掌握其中两个就可以:

  1. onLoad:最先被调用,可以用来【接收别的页面传递过来的数据】。在后面的【页面跳转】中会再去进行讲解。
  2. onReady:页面初次渲染完成后调用。我们可以 在这里从服务端获取数据

那么知道了这个之后,回到我们最初的需求上,我们希望 页面出现之后,可以获取接口数据,并进行渲染。 那么怎么去进行实现呢?

很简单!只需要在 onReady 中调用获取接口数据的方法就可以了。

那么现在 我们已经在页面出现之后,获取到了接口的数据,所以接下来我们只需要根据数据完成页面的渲染就可以了:

// html
<scroll-view class="list-box" scroll-y>
    <block wx:for="{{ listData }}" wx:key="index">
        <view class="list-item">{{ index }} -- {{ item.title }}</view>
    </block>
</scroll-view>

// js
    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady: async function () {
        console.log('onReady');
        const data = await this.getList()
        this.setData({
            listData: data.list
        })
    },
    getList() {
        return new Promise((resolve, reject) => {
            wx.request({
                url: 'https://api.imooc-blog.lgdsunday.club/api/test/getList',
                method: 'GET',
                success: (res) => {
                    resolve(res.data.data)
                }
            })
        })
    }
// css
.list-item {
    padding: 26px;
    font-size: 20px;
    font-weight: bold;
    border-bottom: 1px solid #cccccc;
}
  1. 什么是生命周期?什么是生命周期函数?
    1. 所谓 生命周期 就是:一件事物由 创建 到 销毁 的全过程。
    2. 在这个过程中会有很多 ” 关键的时刻 “,这些关键的时刻就是 生命周期函数
  2. onReady 的调用时机是什么?
    1. 页面初次渲染完成后调用。我们可以 在这里从服务端获取数据

13.pullToRefresh - 下拉刷新与上拉加载

那么如果我们想要在当前项目中实现【分页请求】,就需要借助【下拉刷新于上拉加载】的功能,也就是 pullToRefresh

整个【分页加载】分为两个部分:

  1. 上拉加载
  2. 下拉刷新

这两部分需要分别来进行处理,首先我们先来看【上拉加载】

上拉加载:

在 【小程序】中,默认已经实现了【上拉加载】的功能,可以直接通过监听 onReachBottom 函数,来监听:页面上拉触底事件。当页面滑动到底部时,会触发 onReachBottom 函数。

	/**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom: function () {
        console.log('onReachBottom');
    },

在【用户上拉】时,我们希望获取【下一页】的数据,所以我们需要对当前的数据进行分页:

    /**
     * 页面的初始数据
     */
    data: {
        // 当前页数
        page: 1,
        // 每页的数据量
        size: 10
    },

然后【当页面进入时】,我们获取第一页的数据,所以我们需要对代码进行一下修改:

    getList() {
        return new Promise((resolve, reject) => {
            wx.request({
                url: 'https://api.imooc-blog.lgdsunday.club/api/test/getList',
                method: 'GET',
                // 请求当前页的数据
                data: {
                    page: this.data.page,
                    size: this.data.size
                },
                success: (res) => {
                    resolve(res.data.data)
                }
            })
        })
    }

然后在【上拉操作】时,持续进行后续的数据请求:

    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom: async function () {
        console.log('onReachBottom');
        // 修改 page
        this.setData({
            page: this.data.page + 1
        })
        // 获取最新数据
        const data = await this.getList()
        // 将最新的数据补充到现有数据的后面
        this.setData({
            listData: [...this.data.listData, ...data.list]
        })
    },

同时我们希望 数据加载完成后,给用户一个提示,同时不在发起数据请求

// html
    <!-- 底线 -->
    <view class="bottom" wx:if="{{ listData.length === total }}">-- 我也是有底线的! --</view>
// js
data: {
    // 总数据量
    total: -1
},
onReady: async function () {
    const data = await this.getList()
    this.setData({
        listData: data.list,
        // 为总数据量赋值
        total: data.total
    })
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: async function () {
    console.log('onReachBottom');
    // 如果当前数据量已经 === 总数据量,则表示数据已经加载完成了
    if (this.data.listData.length === this.data.total) {
        return;
    }
    ...
}

那么到目前,我们已经完成了【上拉加载】的操作,但是我们知道,我们还缺少一个【下拉刷新】的操作!

大家如果看到这里已经感觉代码量很大了,那么可以先暂停一下,把以上代码实现一遍,完成之后,继续往下去看!


下拉刷新:

想要在【小程序】中实现【下拉刷新】不同于上拉加载,需要首先开启【下拉刷新】:

// 页面.json
{
  "backgroundColor": "#cccccc",
  "enablePullDownRefresh": true
}

当我们开启了【下拉刷新】之后,我们就可以去监听 onPullDownRefresh 函数,这个函数会在:用户下拉刷新时进行回调

    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh: function () {
        console.log('onPullDownRefresh');
    },

在此回调中,我们需要进行的操作就非常简单了,我们只需要:重置页数,重置数据源,关闭下拉动画 就可以了:

/**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh: async function () {
        console.log('onPullDownRefresh');
        // 重置页数
        this.setData({
            page: 1
        })
         // 获取最新数据
         const data = await this.getList()
         // 将最新的数据补充到现有数据的后面
         this.setData({
             listData: data.list
         })
        //  关闭下拉刷新的动作(在真机中,下拉刷新动作不会自动关闭)
        wx.stopPullDownRefresh()
    },
  1. 何开启下拉刷新
    1. 对 页面对应的 json 文件 中通过 "enablePullDownRefresh": true 开启
  2. 在 onPullDownRefresh 中,一般进行什么操作
    1. 重置页数
    2. 重置数据源
    3. 关闭下拉动画
  3. 在 onReachBottom 中,一般进行什么操作
    1. 判断数据是否已经加载完成
    2. 自增页数
    3. 累加数据

14.页面跳转

我们现在已经把【数据列表】全部展示出来了,然后接下来我们需要完成【文章详情页的渲染】,也就是点击【item 跳转到文章详情页面】

小程序的页面跳转分为两种方式:

  1. 声明式导航
    1. 跳转到 tabbar 页面
    2. 跳转到 非tabbar 页面
    3. 后退页面
  2. 编程式导航
    1. 跳转到 tabbar 页面
    2. 跳转到 非tabbar 页面
    3. 后退页面

声明式导航:

【小程序】中提供了一个:跳转页面的组件 navigator ,使用这个组件可以完成【声明式导航】

<!-- 跳转到 非 tabbar 页面 -->
<block wx:for="{{ listData }}" wx:key="index">
        <view class="list-item">
            <!-- 注意:url 的表达式必须为 / 开头的页面路径 -->
            <navigator url="/pages/detail/detail">{{ index }} -- {{ item.title }}</navigator>
        </view>
    </block>

----

<!-- 跳转到 tabbar 页面 -->
<!-- 注意:跳转到 tabbar 页面,必须要指定 open-type="switchTab"-->
<navigator open-type="switchTab" url="/pages/index/index">跳转到首页</navigator>

-----

<!-- 后退页面 -->
<!-- 注意:后退页面必须指定 open-type="navigateBack" -->
<navigator open-type="navigateBack">后退</navigator>

编程式导航:

【小程序】中提供了三个 API ,用来帮助我们进行 编程式导航:

  1. wx.switchTab:跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面

    <!-- 编程式导航跳转到首页 -->
    <button type="primary" bindtap="onSwitchToHome">利用 switchTab 跳转到首页</button>
    
    onSwitchToHome () {
        wx.switchTab({
            url: '/pages/index/index',
        })
    }
    
  2. wx.navigateTo:保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面

    <!-- 编程式导航跳转到详情页面 -->
    <button type="primary" bindtap="onNavigateToDetail">利用 navigateTo 进入详情页</button>
    
    onNavigateToDetail () {
        wx.navigateTo({
            url: '/pages/detail/detail',
        })
    }
    
  3. wx.navigateBack:关闭当前页面,返回上一页面或多级页面。

    <!-- 编程式导航后退页面 -->
    <button type="primary" bindtap="onNavigateBack">利用 navigateBack 后退页面</button>
    
    onNavigateBack () {
        wx.navigateBack({
            delta: 1,
        })
    }
    

导航传参:

【小程序】的导航传参遵循:get 请求的标准 。

  1. 以 ? 分割 url 和参数
  2. 以 = 连接参数的 key 和 value
  3. 以 & 来拼接参数

那么下面我们来完成案例的最后一个功能:点击跳转时,传递当前 item 的索引和标题,并且在 detail 页面中展示:

// 声明式导航传递参数
<navigator url="/pages/detail/detail?index={{index}}&title={{item.title}}">{{ index }} -- {{ item.title }}</navigator>
// 编程式导航传递参数
<button type="primary" bindtap="onNavigateToDetail" data-index="{{index}}" data-title="{{item.title}}">利用 navigateTo 进入详情页</button>
onNavigateToDetail (e) {
    const { index, title } = e.target.dataset
    wx.navigateTo({
        url: `/pages/detail/detail?index=${index}&title=${title}`,
    })
}
// 在 detail 中接收数据,并展示
<view class="msg">index:{{index}} -- title:{{title}}</view>
onLoad: function (options) {
    const {index, title} = options;
    this.setData({
        index,
        title
    })
}

  1. 页面跳转的方式有哪几种?
    1. 声明式导航
    2. 编程式导航
  2. 跳转到 【tabbar】页面和【非 tabbar】页面的方式分别是什么?
    1. 声明式导航
      1. <navigator open-type="switchTab" url="xx" />
      2. <navigator open-type="navigate"(默认可不指定) url="xx" />
    2. 编程式导航
      1. wx.switchTab({ url: 'xx'})
      2. wx.navigateTo({ url: 'xx'})
  3. 如何进行导航传参
    1. 【小程序】的导航传参遵循:get 请求的标准 。

      1. 以 ? 分割 url 和参数
      2. 以 = 连接参数的 key 和 value
      3. 以 & 来拼接参数

小结

  1. 数据驱动原则:由数据来驱动视图 是 现代前端开发的核心思想之一
  2. 商品案例:
    1. 数据驱动
    2. 组件的事件处理
    3. 处理事件方法传递参数
    4. 双向数据绑定
    5. 条件渲染
    6. 列表渲染
  3. 列表展示案例:
    1. 利用配置文件生成 tabbar
    2. 数据请求的限制
    3. 如何封装 promise 的数据请求
    4. 利用 async + await 简化异步请求
    5. 页面的生命周期概念
    6. 实现 pullToRefresh
    7. 页面跳转

三、进阶概念

1.什么是组件化思想

一个大的项目,由多个小的组件组成,每一个组件都封装了单独的【结构】、【样式】和【行为】。

这就是现在的组件化思想。

如果之前大家有过 vue、react、angular 的开发经验的化,那么应该很好理解 组件化 指的是什么意思。

如果大家之前只有过 html + css + js 的开发经验得话,那么可以把组件理解为:包含了【结构】和【样式】的模块。

2.创建组件

  1. 创建组件

    1. 创建 components 文件夹
    2. 创建 tabs 和 list 文件夹
    3. 右键 新建 Component
  2. 使用组件

    1. 找到页面的 .json 文件

    2. 在 usingComponents 选项下 注册组件

      1. key 为当前组件在该页面中的标签名

      2. value 为组件的代码路径

        index.json

          "usingComponents": {
            "navigation-bar": "/components/navigation-bar/navigation-bar",
            "tabs":"/components/tabs/tabs",
            "list":"/components/list/list"
          }
        

        index.vxml

        <view>
          <tabs></tabs>
          <list></list>
        </view>
        
    3. 在页面的 wxml 文件中,以 注册的 key 为标签名,使用组件

  1. 组件应该被放入到哪个文件夹中?
    1. components
  2. 如何在页面中使用【自定义组件】?
    1. 在页面的 .json 文件中,通过 usingComponents 进行注册
    2. 在 wxml 文件中,以注册的 key 为 标签名 进行使用

3.组件的生命周期

组件 的生命周期应该被定义在 lifetimes 中,而方法必须要放入到 methods 中。

/**
* 生命周期函数
*/
lifetimes: {
    attached() {
 	   this.loadTabsData()
    }
}

组件的生命周期一共有三个:

  1. created : 组件实例刚刚被创建好。此时还不能调用 setData
  2. attached:组件完全初始化完毕、进入页面节点树后。绝大多数初始化工作可以在这个时机进行
  3. detached:在组件离开页面节点树后

image-20240527210531560

    /**
     * 组件的初始数据
     */
    data: {
        // 数据源
        listData: [],
        // 选中项
        active: -1
    },
	/**
     * 生命周期函数
     */
    lifetimes: {
        attached() {
            this.loadTabsData()
        }
    },
    /**
     * 组件的方法列表(组件中的方法必须定义到 methods 中)
     */
    methods: {
        /**
         * 获取数据的方法
         */
        loadTabsData() {
            wx.request({
                url: 'https://api.imooc-blog.lgdsunday.club/api/hot/tabs',
                success: (res) => {
                    this.setData({
                        listData: res.data.data.list,
                        active: 0
                    })
                }
            })
        }
    }

<scroll-view class="tabs-box" scroll-x>
    <view wx:for="{{ listData }}" wx:key="index" class="tab {{ active === index ? 'active' : '' }}">
        {{item.label}}
    </view>
</scroll-view>
.tabs-box {
    /* 指定宽度 + 不换行 */
    width: 750rpx;
    white-space: nowrap;
    border-bottom: 1px solid #cccccc;
}

.tab {
    /* 指定 display */
    display: inline-block;
    padding: 12px 22px;
}

.active {
    color: #f94d2a;
}

  1. 组件的 生命周期 和 方法 分别应该被放入到哪个节点下?
    1. 生命周期应该被定义在 lifetimes 中
    2. 方法必须要放入到 methods 中
  2. created 函数中可以调用 setData 吗?
    1. 不可以
  3. 获取数据的操作应该在哪个函数中进行?
    1. attached

4.数据监听器

场景

通过 接口文档 我们可以看出,如果想要获取 list 那么我们需要传递一个 type 的参数,而这个 type 就是用户选中的 tab 项 的 id

所以接下来我们就需要来做一件事情:监听用户选中的 tab,根据用户选中的 tab 来切换底部 list 的数据

目标:监听用户选中的 tab,根据用户选中的 tab 来切换底部 list 的数据

当我们面临一个复杂的需求时,我们需要把 复杂的需求,拆解为几个可执行的步骤

大家看到这里,可以先思考一下,我们如何拆解以上需求...


步骤拆解如下:

  1. 监听用户选中项的变化
  2. 获取用户选中的数据的 id
  3. 把 id 传递给 list 组件
  4. list 组件根据接收到的 id 获取对应的数据
<view wx:for="{{ listData }}" wx:key="index" class="tab {{ active === index ? 'active' : '' }}" bindtap="onItemClick" data-index="{{index}}">
        {{item.label}}
    </view>
        /**
         * 1. 监听用户选中项的变化
         * item 点击事件处理
         */
        onItemClick(e) {
            // 1.1:获取用户选中的 下标
            const {
                index
            } = e.target.dataset;
            // 1.2:修改选中项
            this.setData({
                active: index
            })
        }


	/**
     * 1.3:监听 active 的变化
     * 通过 observers 定义数据监听器
     */
    observers: {
        // key 为要监听的数据
        // value 为当数据发生变化时,调用的函数
        active: function (active) {
            // 2:获取用户选中的数据的 `id`
            const {id} = this.data.listData[active]
        }
    }

到目前,我们已经实现了需求的前两步,那么后面的两步怎么做呢?

  1. 小程序中通过哪个选项来声明数据监听器
    1. observers
  2. 数据监听器的使用场景是什么?(需要同学自己思考)
    1. 需要监听数据的变化
    2. 在数据变化之后,进行一些操作的时候

5.组件之间的关系与通讯

组件之间的关系:

组件之间的关系和 html 标签之间的关系其实是相同的:

  1. 父子关系 image-20210513101931968
  2. 兄弟关系 image-20210513101957706

不同关系之间的传递数据方式:

  1. 父子关系

    1. 父向子传参:

      // 子组件:通过 properties 声明要从父组件中接收的数据
          /**
           * 组件的属性列表
           */
          properties: {
              tabId: String
          },
      
      // 父组件:通过自定义属性的形式传递数据,以子组件中定义的 key 为属性名,以要传递的数据为属性值
         <list tabId="{{tabSelectId}}">
      
    2. 子向父传参:

      // 子组件:通过 triggerEvent 方法发送一个通知,通知父组件接收数据。
      // 方法的第一个参数为:通知名
      // 方法的第二个参数为:要传递的数据
      this.triggerEvent('change', {
          id
      })
      
      // 父组件:通过 bind 监听子组件中发送的通知
      // bind 后的内容为 子组件发送的通知名,表达式为接收到该通知时所触发的方法
      <tabs bind:change="onTabChange"></tabs>
      // 方法被触发后可以通过 e.detail 的形式获取子组件传递过来的数据对象
      onTabChange (e) {
          const {id} = e.detail;
          this.setData({
              tabSelectId: id
          })
      }
      
  2. 兄弟关系

    1. 兄弟组件之间传参:兄弟关系 === 没有关系。 兄弟组件只是为了方便称呼的一个叫法而已。 image-20210513103334265

      所以想要完成兄弟组件之间的传参,就需要:为它们建立关系。

      而建立关系说白了就是为了找到一个:和这两个组件都认识的 ”中间人“ 。

      这个中间人一般为:统一的父组件。

      而最后:兄弟组件之间想要传递数据,就需要利用 ”中间人进行传递“,也就是:

      1. 【兄弟 A 组件】传递数据给 父组件(中间人)
      2. 父组件(中间人)再把数据传递给 【兄弟 B 组件】
  1. 组件之间数据传递的关系可以分为哪几种?
    1. 父向子传递数据
    2. 子向父传递数据
    3. 兄弟组件之间传递数据
  2. 兄弟组件之间传递数据的方式是什么?

6.组件的插槽

1:定义:

1.1:定义单一插槽:

在 组件 中使用 slot 组件定义插槽。

表示:占据了这一块空间,等待父组件填充。

1.2:定义多个插槽:

小程序默认只能定义一个插槽,如果要定义多个插槽那么需要:在组件中指定 options 选项的 multipleSlots 选项为 true

然后通过 slot 的 name 属性为插槽命名。例如:<slot name="header"></slot>

2:使用:

2.1:使用单一插槽:

在组件使用时,以 innerHTML 的形式插入内容:

<component>
    <view>单一插槽插入的 DOM</view>
</component>

2.2:使用多个插槽:

在组件使用时,以 innerHTML 的形式插入内容,以 slot 属性标记当前 DOM 插入到哪个插槽中:

<component>
    <view slot="header">该元素将被插入到 name=header 的插槽中</view>
    <view slot="footer">该元素将被插入到 name=footer 的插槽中</view>
</component>
  1. 什么时候需要使用插槽?
    1. 由 父组件 来指定 子组件 中某一部分展示的内容和样式时
  2. 小程序中如何定义多个插槽?
    1. 指定 options 的 multipleSlots 为 true

本章小结

  1. 组件化思想
  2. 创建组件
  3. 组件的生命周期
  4. 数据监听器
  5. 组件的关系
  6. 组件的通讯
  7. 组件的插槽
最近更新:: 2025/8/22 15:05
Contributors: yanpeng_
Prev
uni-app框架实战_项目