小程序开发

1.基础

1.准备工作

2.开发步骤

1.第一个小程序

在这里插入图片描述

  • 1.小程序的AppID相当于小程序平台的一个身份证,这里使用的是测试号(注意区别于服务号或订阅号的AppID
  • 2.测试号默认不使用云服务,根据需求选择模板,这里选择JS-基础模板

2.小程序代码构成

1.JSON配置

在这里插入图片描述

  • 1.JSON是一种数据格式,小程序中的JSON用于静态配置
  • 2.配置文件说明(根目录)
    • 1.app.json:当前小程序的全局配置,具体可参考小程序全局配置说明
      • 1.页面路径
        • 1.pages:用于描述当前小程序所有页面路径,用于让微信客户端清楚当前小程序页面定义的目录
      • 2.界面表现
        • 2.window:用于定义小程序所有页面的顶部背景颜色,文字颜色定义等
      • 3.网络超时时间
      • 4.底部 tab
    • 2.project.config.json:当前小程序开发工具的个性化配置,具体可参考小程序个性化配置说明
    • 3.page.json:当前小程序独立页面的属性配置,具体可参考小程序独立页面配置说明
2.WXML + WXSS + JS
  • 1.网页编程采用的是HTML + CSS + JS组合
  • 2.小程序开发采用WXML + WXSS + JS组合
  • 3.WXML类似于HTML,具体可参考WXML语法参考WXSS类似于CSS
  • 4.MVVM开发模式(React,Vue):把渲染和逻辑分离
    • 1.网页开发流程中,通过JS操作DOM(对应 HTML的描述产生的树),以引起界面的一些变化响应用户的行为
    • 2.用户点击某个按钮的时候,JS会记录一些状态到JS变量里,同时通过DOM APIDOM的属性或者行为,进而引起界面一些变化
    • 3.当项目越来越大,代码会充斥着非常多的界面交互逻辑和程序的各种状态变量,显然这不是一个很好的开发模式
    • 4.不要让JS直接操控DOMJS只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系即可,小程序的框架也是用到了这个思路
3.小程序运行环境

在这里插入图片描述

  • 1.小程序的运行环境分为
    • 1.渲染层
      • 1.WXML模板和WXSS样式
    • 2.逻辑层
      • 1.JS脚本
  • 2.小程序的渲染层和逻辑层分别由2个线程管理
    • 1.渲染层的界面使用WebView进行渲染
    • 2.逻辑层采用JsCore线程运行JS脚本
    • 3.一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端Native做中转,逻辑层发送网络请求也经由微信客户端转发
4.框架
  • 1.小程序框架系统分为两部分
    • 1.逻辑层(App Service
    • 2.视图层(View
      • 说明:小程序提供了自己的视图层描述语言WXMLWXSS,以及基于JavaScript的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑
  • 2.小程序框架核心
    • 1.响应的数据绑定系统:将数据与视图非常简单地保持同步,当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新
  • 3.数据绑定
    • 1.通过{{ }}的语法把一个变量绑定到界面上,称为数据绑定
    • 2.仅仅通过数据绑定还不够完整的描述状态和界面的关系,还需要if/else, for等控制能力,小程序中这些控制能力都用wx:开头的属性来表达
    <text>{{msg}}</text>
    
    this.setData({ msg: "Hello World" })
    

1.列表渲染

<!--wxml-->
<view wx:for="{{array}}"> {{item}} </view>
// page.js
Page({
 data: {
   array: [1, 2, 3, 4, 5]
 }
})

2.条件渲染

<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>
// page.js
Page({
 data: {
   view: 'MINA'
 }
})

3.模板

<!--wxml-->
<template name="staffName">
 <view>
   FirstName: {{firstName}}, LastName: {{lastName}}
 </view>
</template>

<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>
// page.js
Page({
 data: {
   staffA: {firstName: 'Hulk', lastName: 'Hu'},
   staffB: {firstName: 'Shang', lastName: 'You'},
   staffC: {firstName: 'Gideon', lastName: 'Lin'}
 }
})

4.引用

  • 网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应
  • 小程序中,二者是分开的,分别运行在不同的线程中。网页开发者可以使用到各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作
  • 程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的
  • 网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ浏览器等,在移动端需要面对Safari、Chrome以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的
  • 在这里插入图片描述
  • ​网页开发者在开发网页的时候,只需要使用到浏览器,并且搭配上一些辅助工具或者编辑器即可。小程序的开发则有所不同,需要经过申请小程序账号、安装小程序开发者工具、配置项目等等过程方可完成

开始

  • 1.开发小程序的第一步,你需要拥有一个小程序账号,通过这个账号你就可以管理你的小程序
  • 2.申请账号
  • 3.进入小程序注册页 根据指引填写信息和提交相应的资料,就可以拥有自己的小程序账号。
    小程序注册
  • 4.登录 小程序后台 ,我们可以在菜单 “开发”-“开发设置” 看到小程序的 AppID 了

小程序的基本文件结构

在这里插入图片描述

  • 1.一个小程序项目必须有这3个描述App的文件,这3个文件是应用程序级别的文件
    在这里插入图片描述
    • 1.app.js
    • 2.app.json
    • 3.app.wxss
  • 2.它们必须放在应用程序的根目录下,否则小程序会提示找不到app.json文件
  • 3.接着是和这3个应用程序级别文件平行的pages文件夹
  • 4.一个小程序由若干个页面文件构成,比如图3-1中pages文件夹下就有2个页面,分别是index页面和logs页面。每个页面可以由4个文件构成,分别是:
    • .js
    • .wxml
    • .wxss
    • .json文件
      在这里插入图片描述
  • 1.wxml文件类似于我们熟悉的HTML文件,用来编写页面的标签和骨架,不同的是wxml文件里的标签元素不可以使用HTML标签,只能使用小程序自己封装的一些组件,这些组件也是我们后面要重点学习的知识。>* 2.wxss文件的作用类似于我们熟悉的CSS文件,用于编写小程序的样式,实际上小程序的样式编写语言就是CSS,只是把.css文件换成了.wxss文件。
  • 3.json文件用来配置页面的样式与行为。
  • 4.js文件类似于我们前端编程中的JavaScript文件,用来编写小程序的页面逻辑
  • 5.以上4种类型的页面文件的文件名称必须相同,这是要注意的一个地方
  • 小程序的4种页面级别文件同3个应用程序级别文件相比,多出了一个wxml页面标签文件,其他3个的作用基本相似,只不过页面文件作用于页面本身而应用程序文件作用于应用程序整体
  • 除了pages文件夹外,官方的示例项目中还有一个utils文件夹,这个文件夹用来存放一些公共的js文件,比如utils下面的util.js。我们可以任意定义类似于utils文件夹的目录,并放在小程序的任意位置,小程序对此并没有任何限制

从零开始编写一个小程序页面

  • 新建一个项目,并且不勾选【在当前目录中创建quick start项目】这个选项,因为我们要从零开始编写一个项目,所以每个文件都将由自己亲手创建。项目创建后,会出现一个如图3-2所示的错误提示,这是因为现在的项目里还没有任何文件,由于缺少必要的文件,所以小程序会报错。之所以完全新建一个全新的项目,是为了向开发者展示这些常见的错误消息,如果大家不想经历这些错误,那么可以在官方提供的quick start项目上修改

小程序配置

1.全局配置

  • 1.小程序根目录下的app.json文件用来对微信小程序进行全局配置,文件内容为一个JSON对象
    属性类型必填描述最低版本
    entryPagePathstring小程序默认启动首页
    pagesstring[]页面路径列表
    windowObject全局的默认窗口表现
    sitemapLocationstring指明 sitemap.json 的位置
    tabBarObject底部 tab 栏的表现
    debugboolean是否开启 debug 模式,默认关闭
    workersstringWorker代码放置的目录1.9.90
    pluginsObject使用到的插件1.9.6
    networkTimeoutObject网络超时时间
    functionalPagesboolean是否启用插件功能页,默认关闭2.1.0
    subpackagesObject[]分包结构配置1.7.3
    requiredBackgroundModesstring[]需要在后台使用的能力,如「音乐播放」
    requiredPrivateInfosstring[]调用的地理位置相关隐私接口
    preloadRuleObject分包预下载规则2.3.0
    resizablebooleanPC小程序是否支持用户任意改变窗口大小(包括最大化窗口);iPad 小程序是否支持屏幕旋转。默认关闭2.3.0
    usingComponentsObject全局自定义组件配置开发者工具 1.02.1810190
    permissionObject小程序接口权限相关设置微信客户端 7.0.0
    stylestring指定使用升级后的weui样式2.8.0
    useExtendedLibObject指定需要引用的扩展库2.2.1
    entranceDeclareObject微信消息用小程序打开微信客户端 7.0.9
    darkmodeboolean小程序支持 DarkMode2.11.0
    themeLocationstring指明 theme.json 的位置,darkmode为true为必填开发者工具 1.03.2004271
    lazyCodeLoadingstring配置自定义组件代码按需注入2.11.1
    singlePageObject单页模式相关配置2.12.0
    supportedMaterialsObject聊天素材小程序打开相关配置2.14.3
    serviceProviderTicketstring定制化型服务商票据
    embeddedAppIdListstring[]半屏小程序 appId2.20.1
    halfPageObject视频号直播半屏场景设置2.18.0
    debugOptionsObject调试相关配置2.22.1
    enablePassiveEventObject或booleantouch 事件监听是否为 passive2.24.1
    resolveAliasObject自定义模块映射规则
    rendererstring全局默认的渲染后端2.30.4
    rendererOptionsObject渲染后端选项2.31.1
    componentFrameworkstring组件框架,详见相关文档2.30.4
    miniAppObject多端模式场景接入身份管理服务时开启小程序授权页相关配置,详见相关文档
    staticObject正常情况下默认所有资源文件都被打包发布到所有平台,可以通过 static 字段配置特定每个目录/文件只能发布到特定的平台(多端场景) 相关文档

1.entryPagePath

  • 1.指定小程序的默认启动路径(首页)
  • 2.常见情景是从微信聊天列表页下拉启动、小程序列表启动等
  • 3.如果不填将默认为pages列表的第一项
  • 4.不支持带页面路径参数

2.pages

在这里插入图片描述

{
 "pages": ["pages/index/index", "pages/logs/logs"]
}
  • 1.指定小程序由哪些页面组成,每一项都对应一个页面的路径(含文件名) 信息
  • 2.文件名不需要写文件后缀,框架会自动去寻找对应位置的.json, .js, .wxml, .wxss四个文件进行处理
  • 3.未指定 entryPagePath 时,数组的第一项代表小程序的初始页面(首页)
  • 4.小程序中新增/减少页面,都需要对pages数组进行修改

3.window

属性类型默认值描述最低版本
navigationBarBackgroundColorHexColor#000000导航栏背景颜色
navigationBarTextStylestringwhite导航栏标题、状态栏颜色,仅支持 black/white
navigationBarTitleTextstring导航栏标题文字内容
navigationStylestringdefault导航栏样式,仅支持以下值:default默认样式;custom自定义导航栏,只保留右上角胶囊按钮iOS/Android 微信客户端 6.6.0,Windows 微信客户端不支持
homeButtonbooleandefault在非首页、非页面栈最底层页面或非tabbar内页面中的导航栏展示home键微信客户端 8.0.24
backgroundColorHexColor#ffffff窗口的背景色
backgroundTextStylestringdark下拉 loading 的样式,仅支持dark/light
backgroundColorTopstring#ffffff顶部窗口的背景色,仅 iOS 支持微信客户端 6.5.16
backgroundColorBottomstring#ffffff底部窗口的背景色,仅 iOS 支持微信客户端 6.5.16
enablePullDownRefreshbooleanfalse是否开启全局的下拉刷新。详见 Page.onPullDownRefresh
onReachBottomDistancenumber50页面上拉触底事件触发时距页面底部距离,单位为 px。详见 Page.onReachBottom
pageOrientationstringportrait屏幕旋转设置,支持 auto / portrait / landscape详见 响应显示区域变化2.4.0 (auto) / 2.5.0 (landscape)
restartStrategystringhomePage重新启动策略配置2.8.0
initialRenderingCachestring页面初始渲染缓存配置,支持 static / dynamic2.11.1
visualEffectInBackgroundstringnone切入系统后台时,隐藏页面内容,保护用户隐私。支持 hidden / none2.15.0
handleWebviewPreloadstringstatic控制预加载下个页面的时机。支持 static / manual / auto2.15.0
  • 1.注意
    • 1.HexColor(十六进制颜色值,如#ff00ff
    • 2.用于设置小程序的状态栏、导航条、标题、窗口背景色
    • 3.关于navigationStyle
      • 1.iOS/Android客户端7.0.0以下版本,navigationStyle 只在app.json中生效。
      • 2.iOS/Android客户端6.7.2版本开始,navigationStyle: customweb-view组件无效
      • 3.开启custom后,低版本客户端需做好兼容,开发者工具基础库版本切到1.7.0(不代表最低版本,只供调试用)可方便切到旧视觉
      • 4.Windows客户端3.0及以上版本,为了给用户提供更符合桌面软件的使用体验,统一了小程序窗口的导航栏,navigationStyle: custom不再生效
  • 2.实例
    {
      "window": {
        "navigationBarBackgroundColor": "#ffffff",
        "navigationBarTextStyle": "black",
        "navigationBarTitleText": "微信接口功能演示",
        "backgroundColor": "#eeeeee",
        "backgroundTextStyle": "light"
      }
    }
    
1.restartStrategy
可选值含义
homePage(默认值)如果从这个页面退出小程序,下次将从首页冷启动
homePageAndLatestPage如果从这个页面退出小程序,下次冷启动后立刻加载这个页面,页面的参数保持不变(不可用于 tab 页)

tabBar

  • 如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面
    tabBar
    如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
属性类型必填默认值描述最低版本
colorHexColortab 上的文字默认颜色,仅支持十六进制颜色
selectedColor HexColor 是 tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor HexColor 是 tab 的背景色,仅支持十六进制颜色
borderStyle string 否 black tabbar 上边框的颜色, 仅支持 black / white
list Array 是 tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
positionstring 否 bottom tabBar 的位置,仅支持 bottom / top
customboolean 否 false 自定义 tabBar,见详情 2.5.0

2.页面配置

  • 1.每一个小程序页面可使用同名.json文件对本页面的窗口表现进行配置,页面中配置项会覆盖根目录的app.jsonwindow中相同的配置项

小程序运行机制

1.小程序的生命周期

  • 1.小程序从启动到最终被销毁,会经历很多不同的状态,小程序在不同状态下会有不同的表现
    在这里插入图片描述

2.小程序启动

  • 1.从用户认知的角度看,广义的小程序启动可以分为两种情况,一种是冷启动,一种是热启动
    • 1.冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动,即冷启动
    • 2.热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态,这个过程就是热启动
  • 2.从小程序生命周期的角度来看,我们一般讲的「启动」专指冷启动,热启动一般被称为后台切前台

3.前台与后台

  • 1.小程序启动后,界面被展示给用户,此时小程序处于「前台」状态。
  • 2.当用户「关闭」小程序时,小程序并没有真正被关闭,而是进入了「后台」状态,此时小程序还可以短暂运行一小段时间,但部分 API 的使用会受到限制。切后台的方式包括但不限于以下几种:
    • 1.点击右上角胶囊按钮离开小程序
    • 2.iOS 从屏幕左侧右滑离开小程序
    • 3.安卓点击返回键离开小程序
    • 4.小程序前台运行时直接把微信切后台(手势或 Home 键)
    • 5.小程序前台运行时直接锁屏
  • 3.当用户再次进入微信并打开小程序,小程序又会重新进入「前台」状态

4.挂起

  • 1.小程序进入「后台」状态一段时间后(目前是 5 秒),微信会停止小程序 JS 线程的执行,小程序进入「挂起」状态。此时小程序的内存状态会被保留,但开发者代码执行会停止,事件和接口回调会在小程序再次进入「前台」时触发。
  • 2.当开发者使用了后台音乐播放、后台地理位置等能力时,小程序可以在「后台」持续运行,不会进入到「挂起」状态

5.小程序销毁

  • 1.如果用户很久没有使用小程序,或者系统资源紧张,小程序会被「销毁」,即完全终止运行。具体而言包括以下几种情形:
    • 1.当小程序进入后台并被「挂起」后,如果很长时间(目前是 30 分钟)都未再次进入前台,小程序会被销毁。
    • 2.当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。
      • 1.在 iOS 上,当微信客户端在一定时间间隔内连续收到系统内存告警时,会根据一定的策略,主动销毁小程序,并提示用户 「运行内存不足,请重新打开该小程序」。具体策略会持续进行调整优化。
      • 2.建议小程序在必要时使用 wx.onMemoryWarning 监听内存告警事件,进行必要的内存清理
        2.基础库 1.1.0 及以上,1.4.0 以下版本: 当用户从扫一扫、转发等入口(场景值为1007, 1008, 1011, 1025)进入小程序,且没有置顶小程序的情况下退出,小程序会被销毁。

6. 小程序冷启动的页面

  • 小程序冷启动时,打开的页面有以下情况
  • 1.(A 类场景)若启动的场景中不带 path
    基础库 2.8.0 以下版本,进入首页
    基础库 2.8.0 及以上版本遵循「重新启动策略」,可能是首页或上次退出的页面
  • 2.(B 类场景)若启动的场景中带有 path,则启动进入对应 path 的页面

7.重新启动策略

{
 "restartStrategy": "homePage"
}
可选值含义
homePage(默认值)如果从这个页面退出小程序,下次将从首页冷启动
homePageAndLatestPage如果从这个页面退出小程序,下次冷启动后立刻加载这个页面,页面的参数保持不变(不可用于 tab 页)
  • 1.小程序冷启动时,如果启动时不带 path(A 类场景),默认情况下将会进入小程序的首页。 在页面对应的 json 文件中(也可以全局配置在 app.json 的 window 段中),指定 restartStrategy 配置项可以改变这个默认的行为,使得从某个页面退出后,下次 A 类场景的冷启动可以回到这个页面
  • 2.注意:即使不配置为 homePage ,小程序如果退出过久(当前默认一天时间,可以使用退出状态来调整),下次冷启动时也将不再遵循 restartStrategy 的配置,而是直接从首页冷启动。
  • 3.无论如何,页面中的状态并不会被保留,如输入框中的文本内容、 checkbox 的勾选状态等都不会还原。如果需要还原或部分还原,需要利用退出状态

8. 小程序热启动的页面

  • 1.小程序热启动时,打开的页面有以下情况
    • (A 类场景)若启动的场景中不带 path,则保留上次的浏览的状态
    • (B 类场景)若启动的场景中带有 path,则 reLaunch 到对应 path 的页面
  • A 类场景常见的有下列场景值
    场景值ID说明
    1001 发现栏小程序主入口,「最近使用」列表(基础库2.2.4版本起包含「我的小程序」列表)
    1003星标小程序列表
    1023系统桌面小图标打开小程序
    1038从其他小程序返回小程序
    1056聊天顶部音乐播放器右上角菜单,打开小程序
    1080客服会话菜单小程序入口,打开小程序
    1083公众号会话菜单小程序入口 ,打开小程序(只有腾讯客服小程序有)
    1089聊天主界面下拉,打开小程序/微信聊天主界面下拉,「最近使用」栏(基础库2.2.4版本起包含「我的小程序」栏)
    1090长按小程序右上角菜单,打开小程序
    1103发现-小程序主入口我的小程序,打开小程序
    1104聊天主界面下拉,从我的小程序,打开小程序
    1113安卓手机负一屏,打开小程序
    1114安卓手机侧边栏,打开小程序
    1117后台运行小程序的管理页中,打开小程序

9.退出状态

{
  "restartStrategy": "homePageAndLatestPage"
}

Page({
  onLoad: function() {
    var prevExitState = this.exitState // 尝试获得上一次退出前 onSaveExitState 保存的数据
    if (prevExitState !== undefined) { // 如果是根据 restartStrategy 配置进行的冷启动,就可以获取到
      prevExitState.myDataField === 'myData' 
    }
  },
  onSaveExitState: function() {
    var exitState = { myDataField: 'myData' } // 需要保存的数据
    return {
      data: exitState,
      expireTimeStamp: Date.now() + 24 * 60 * 60 * 1000 // 超时时刻
    }
  }
})

onSaveExitState 返回值可以包含两项:

  • 1.基础库 2.7.4 开始支持,低版本需做兼容处理。
  • 2.每当小程序可能被销毁之前,页面回调函数 onSaveExitState 会被调用。如果想保留页面中的状态,可以在这个回调函数中“保存”一些数据,下次启动时可以通过 exitState 获得这些已保存数据
    字段名 类型 含义
    data Any 需要保存的数据(只能是 JSON 兼容的数据)
    expireTimeStamp Number 超时时刻,在这个时刻后,保存的数据保证一定被丢弃,默认为 (当前时刻 + 1 天)

WXML

  • 1.WXML(WeiXin Markup Language):微信小程序框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构

1.数据绑定

  • 1.数据绑定使用Mustache语法(双大括号)将变量包起来,为了使用户界面与业务数据(内容)分离
  • 2.WXML 中的动态数据均来自对应 Pagedata
  • 3.注意
    • 1.如果双大括号是作用于标签内部(属性)则需要带上双引号
    • 2.如果是作用于两个标签内部则不需要带上双引号
  • 4.数据绑定可以作用于以下几种类型

1.内容

<!--wxml-->
<view> {{message}} </view>
// page.js
Page({
 data: {
   message: 'Hello MINA!'
 }
})

2.组件属性

<view id="item-{{id}}"> </view>
Page({
 data: {
   id: 0
 }
})
  • 注意:双大括号需要在双引号之内

3.控制属性

<view wx:if="{{condition}}"> </view>
Page({
 data: {
   condition: true
 }
})
  • 注意:双大括号需要在双引号之内

4.关键字

  • 1.trueboolean 类型的 true,代表真值
  • 2.falseboolean 类型的 false,代表假值
    <checkbox checked="{{false}}"> </checkbox>
    
  • 注意:
    • 1.关键字需要在双引号之内
    • 2.不要直接写checked="false",其计算结果是一个字符串,转成boolean类型后代表真值

5.运算

  • 1.在 {{}} 可以内进行简单的运算,支持的有如下几种方式
1.三元运算
<view hidden="{{flag ? true : false}}"> Hidden </view>
2.算数运算
<!-- view中的内容为 3 + 3 + d -->
<view> {{a + b}} + {{c}} + d </view>
Page({
 data: {
   a: 1,
   b: 2,
   c: 3
 }
})

6.逻辑判断

<view wx:if="{{length > 5}}"> </view>

7.字符串运算

<view>{{"hello" + name}}</view>
Page({
 data:{
   name: 'MINA'
 }
})

8.数据路径运算

<view>{{object.key}} {{array[0]}}</view>
Page({
 data: {
   object: {
     key: 'Hello '
   },
   array: ['MINA']
 }
})

9.组合

  • 1.可以在Mustache(双大括号)内直接进行组合,构成新的对象或数组
1.数组
<!-- 最终组合成数组[0, 1, 2, 3, 4] -->
<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
Page({
 data: {
   zero: 0
 }
})
2.对象
<!-- 最终组合成的对象是 {for: 1, bar: 2} -->
<template is="objectCombine" data="{{for: a, bar: b}}"></template>

<!-- 用扩展运算符 ... 来将一个对象展开 -->
<!-- 最终组合成的对象是 {a: 1, b: 2, c: 3, d: 4, e: 5} -->
<template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>

<!-- 如果对象的 key 和 value 相同,也可以间接地表达 -->
<!-- 最终组合成的对象是 {foo: 'my-foo', bar:'my-bar'} -->
<template is="objectCombine" data="{{foo, bar}}"></template>
Page({
 data: {
   a: 1,
   b: 2
 }
})
// 用扩展运算符 ... 来将一个对象展开
Page({
 data: {
   obj1: {
     a: 1,
     b: 2
   },
   obj2: {
     c: 3,
     d: 4
   }
 }
})
// 如果对象的 key 和 value 相同,也可以间接地表达
Page({
 data: {
   foo: 'my-foo',
   bar: 'my-bar'
 }
})

2.列表渲染

1.wx:for

<view wx:for="{{array}}">
 {{index}}: {{item.message}}
</view>

<!-- wx:for 可以嵌套,下边是一个九九乘法表 -->
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
 <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
   <view wx:if="{{i <= j}}">
     {{i}} * {{j}} = {{i * j}}
   </view>
 </view>
</view>

<!-- 使用wx:for-item可以指定数组当前元素的变量名,使用wx:for-index可以指定数组当前下标的变量名 -->
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
 {{idx}}: {{itemName.message}}
</view>
Page({
 data: {
   array: [{
     message: 'foo',
   }, {
     message: 'bar'
   }]
 }
})
  • 1.组件上使用wx:for控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件,同时wx:for也可以嵌套
  • 2.默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
  • 3.使用wx:for-item可以指定数组当前元素的变量名
  • 4.使用wx:for-index可以指定数组当前下标的变量名

2.block wx:for

<block wx:for="{{[1, 2, 3]}}">
 <view> {{index}}: </view>
 <view> {{item}} </view>
</block>
  • 1.wx:for可以用在<block>标签上,以渲染一个包含多节点的结构块

3.wx:key

<switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;"> {{item.id}} </switch>
<button bindtap="switch"> Switch </button>
<button bindtap="addToFront"> Add to the front </button>

<switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;"> {{item}} </switch>
<button bindtap="addNumberToFront"> Add to the front </button>
Page({
 data: {
   objectArray: [
     {id: 2, unique: 'unique_2'},
     {id: 1, unique: 'unique_1'},
     {id: 0, unique: 'unique_0'},
   ],
   numberArray: [1, 2, 3, 4]
 },
 switch: function(e) {
   const length = this.data.objectArray.length
   for (let i = 0; i < length; ++i) {
     const x = Math.floor(Math.random() * length)
     const y = Math.floor(Math.random() * length)
     const temp = this.data.objectArray[x]
     this.data.objectArray[x] = this.data.objectArray[y]
     this.data.objectArray[y] = temp
   }
   this.setData({
     objectArray: this.data.objectArray
   })
 },
 addToFront: function(e) {
   const length = this.data.objectArray.length
   this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
   this.setData({
     objectArray: this.data.objectArray
   })
 },
 addNumberToFront: function(e){
   this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
   this.setData({
     numberArray: this.data.numberArray
   })
 }
})
  • 1.如果列表中项目的位置会动态改变或有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input中的输入内容,switch的选中状态),需要使用wx:key 指定列表中项目的唯一的标识符
  • 2.当数据改变触发渲染层重新渲染的时候,会校正带有 key的组件,框架会确保它们被重新排序而不是重新创建,以确保使组件保持自身的状态并且提高列表渲染时的效率
  • 3.wx:key的值以两种形式提供
    • 1.字符串:代表在for循环的arrayitem的某个 property,该property的值需要是列表中唯一的字符串或数字,且不能动态改变
    • 2.保留关键字*this:代表在for循环中的item本身,这种表示需要item本身是一个唯一的字符串或数字
  • 4.如不提供wx:key会报一个warning, 如果明确知道该列表是静态或不必关注其顺序则可以选择忽略
    在这里插入图片描述

4.注意

  • 1.花括号和引号之间如果有空格,将最终被解析成为字符串
    <view wx:for="{{[1,2,3]}} ">
    {{item}}
    </view>
    <!-- 等同于 -->
    <view wx:for="{{[1,2,3] + ' '}}">
    {{item}}
    </view>
    
  • 2.当wx:for的值为字符串时,会将字符串解析成字符串数组
    <view wx:for="array">
      {{item}}
    </view>
    <!-- 等同于 -->
    <view wx:for="{{['a','r','r','a','y']}}">
      {{item}}
    </view>
    

3.条件渲染

1.wx:if

<view wx:if="{{condition}}"> True </view>
<!-- 或 -->
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
  • 1.使用wx:if=""来判断是否需要渲染该代码块

2.block wx:if

<block wx:if="{{true}}">
 <view> view1 </view>
 <view> view2 </view>
</block>
  • 1.因为wx:if是一个控制属性,需要将它添加到一个标签上
  • 2.如果要一次性判断多个组件标签,可以使用一个<block> 标签将多个组件包装起来,并在上边使用wx:if控制属性
  • 3.注意:<block> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性

3.wx:if 区别 hidden

  • 1.wx:if
    • 1.因为wx:if之中的模板可能包含数据绑定,所以当wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染
    • 2.同时wx:if是惰性的,如果初始渲染条件为false,框架什么也不做,在条件第一次为真时才开始局部渲染
  • 2.hidden
    • 1.hidden组件始终会被渲染,只是简单的控制显示与隐
  • 3.总结:
    • 1.wx:if有更高的切换消耗
    • 2.hidden有更高的初始渲染消耗
    • 3.因此如果需要频繁切换的情景下用 hidden 更好,如果运行时条件不大可能改变则wx:if较好

4.模板

  • 1.WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用

1.定义模板

<!--
 index: int
 msg: string
 time: string
-->
<template name="msgItem">
 <view>
   <text> {{index}}: {{msg}} </text>
   <text> Time: {{time}} </text>
 </view>
</template>

2.使用模板

<template is="msgItem" data="{{...item}}"/>
Page({
 data: {
   item: {
     index: 0,
     msg: 'this is a template',
     time: '2016-09-15'
   }
 }
})

在这里插入图片描述

3.动态模板

<template name="odd">
 <view> odd </view>
</template>
<template name="even">
 <view> even </view>
</template>

<block wx:for="{{[1, 2, 3, 4, 5]}}">
 <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/>
</block>
  • 1.templateis属性可以使用Mustache语法,来动态决定具体需要渲染哪个模板
  • 2.注意需要带上双引号

4.模板的作用域

  • 1.模板拥有自己的作用域,只能使用data传入的数据以及模板定义文件中定义的<wxs />模块

5.引用

  • 1.WXML提供两种文件引用方式importinclude

1.import

<!--  1.在 item.wxml 中定义了一个叫 item 的 template -->
<template name="item">
 <text>{{text}}</text>
</template>
<!-- 2.index.wxml 中引用了 item.wxml,就可以使用 item 模板 -->
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
  • 1.import可以在该文件中使用目标文件定义的template
1.import 的作用域
<!-- C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template -->
<!-- A.wxml -->
<template name="A">
 <text> A template </text>
</template>
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
 <text> B template </text>
</template>
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>
  • 1.import有作用域的概念,即只会import目标文件中定义的template,而不会 import目标文件importtemplate

2.include

<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>

<!-- header.wxml -->
<view> header </view>

<!-- footer.wxml -->
<view> footer </view>
  • 1.include可以将目标文件除了<template/> <wxs/>外的整个代码引入,相当于是拷贝到include位置

WXSS

  • 1.WXSS (WeiXin Style Sheets)是一套样式语言,用于描述WXML的组件样式
  • 2.WXSS具有CSS大部分的特性,小程序在WXSS也做了一些扩充和修改
    • 1.新增尺寸单位
      • 1.写CSS样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位
      • 2.WXSS在底层支持新的尺寸单位 rpx ,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差
    • 2.提供全局的样式和局部样式
      • 1.类似app.json, page.json,可以写一个app.wxss作为全局样式,会作用于当前小程序的所有页面
      • 2.局部页面样式 page.wxss仅对当前页面生效
    • 3.此外WXSS仅支持部分CSS选择器

1.尺寸单位

设备rpx换算px (屏幕宽度/750)px换算rpx (750/屏幕宽度)
iPhone51rpx = 0.42px1px = 2.34rpx
iPhone61rpx = 0.5px1px = 2rpx
iPhone6 Plus1rpx = 0.552px1px = 1.81rpx
  • 1.rpx(responsive pixel)
    • 1.可以根据屏幕宽度进行自适应,规定屏幕宽为750rpx
    • 2.例: iPhone6屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素1rpx = 0.5px = 1物理像素
  • 2.建议开发微信小程序时可以用iPhone6作为视觉稿的标准
  • 3.注意:较小的屏幕上不可避免的会有一些毛刺,开发时尽量避免这种情况

2.样式导入

/** common.wxss **/
.small-p {
 padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
 padding:15px;
}
  • 1.使用@import语句可以导入外联样式表
  • 2.@import后跟需要导入的外联样式表的相对路径,用;表示语句结束

3.内联样式

<view style="color:{{color}};" />
<view class="normal_view" />
  • 1.框架组件上支持使用styleclass属性来控制组件的样式
    • 1.style:静态的样式统一写到class中,style接收动态的样式,在运行时会进行解析,尽量避免将静态的样式写进style中,以免影响渲染速度
    • 2.class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔

1.目前支持的选择器

选择器样例样例描述
.class.intro选择所有拥有 class=“intro” 的组件
#id#firstname选择拥有 id=“firstname” 的组件
elementview选择所有 view 组件
element, elementview, checkbox选择所有文档的 view 组件和所有的 checkbox 组件
::afterview::after在 view 组件后边插入内容
::beforeview::before在 view 组件前边插入内容

4.全局样式与局部样式

  • 1.定义在app.wxss中的样式为全局样式,作用于每一个页面
  • 2.pagewxss文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖app.wxss中相同的选择器

JS

  • 1.微信小程序通过编 JS脚本文件处理用户的操作,和用户做交互

1.事件

  • 1.事件是视图层到逻辑层的通讯方式
  • 2.事件可以将用户的行为反馈到逻辑层进行处理
  • 3.事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数
  • 4.事件对象可以携带额外信息(例:id, dataset, touches)

1.事件分类

  • 1.事件分为
    • 1.冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递
      类型触发条件最低版本
      touchstart手指触摸动作开始
      touchmove手指触摸后移动
      touchcancel手指触摸动作被打断,如来电提醒,弹窗
      touchend手指触摸动作结束
      tap手指触摸后马上离开
      longpress手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发1.5.0
      longtap手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
      transitionend会在 WXSS transition 或 wx.createAnimation 动画结束后触发
      animationstart会在一个 WXSS animation 动画开始时触发
      animationiteration会在一个 WXSS animation 一次迭代结束时触发
      animationend会在一个 WXSS animation 动画完成时触发
      touchforcechange在支持 3D Touch 的 iPhone 设备,重按时会触发1.9.90
    • 2.非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递
      • 1.除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件

2.普通事件绑定

<view bindtap="handleTap">
    Click here!
</view>
<view bindtap="{{ handlerName }}">
    Click here!
</view>
  • 1.事件绑定的写法类似于组件的属性 ,如果用户点击view组件,则页面的handleTap会被调用
  • 2.事件绑定函数可以是一个数据绑定,此时页面的this.data.handlerName必须是一个字符串,用于指定事件处理函数名
  • 3.如果this.data.handlerName是空字符串,则这个绑定会失效(可利用该特性来暂时禁用一些事件)
  • 4.自基础库版本1.5.0起,大多数组件和自定义组件中, bind后可跟一个冒号但其含义不变(例:bind:tap),自基础库版本2.8.1起所有组件都开始提供这个支持

3.互斥事件绑定

<view id="outer" mut-bind:tap="handleTap1">
 outer view
 <view id="middle" bindtap="handleTap2">
   middle view
   <view id="inner" mut-bind:tap="handleTap3">
     inner view
   </view>
 </view>
</view>
  • 1.自基础库版本2.8.2起,除bindcatch外还可使用mut-bind绑定事件
  • 2.mut-bind触发后,如果事件冒泡到其他节点上,则其他节点上的mut-bind绑定函数不会被触发,但bind绑定函数和 catch绑定函数依旧会被触发
  • 3.即所有mut-bind是互斥的,只会有其中一个绑定函数被触发,同时不影响bindcatch的绑定效果
  • 4.例:上述代码中点击inner view会先后调用handleTap3handleTap2 ,点击middle view会调用handleTap2handleTap1

4.绑定并阻止事件冒泡

<view id="outer" bindtap="handleTap1">
 outer view
 <view id="middle" catchtap="handleTap2">
   middle view
   <view id="inner" bindtap="handleTap3">
     inner view
   </view>
 </view>
</view>
handleTap1: function(event) {
     console.log('handleTap1')
     console.log(event)
},
handleTap2: function(event) {
     console.log('handleTap2')
     console.log(event)
},
handleTap3: function(event) {
     console.log('handleTap3')
     console.log(event)
},

在这里插入图片描述

  • 1.除bind外也可以用catch来绑定事件,与bind不同catch 会阻止事件向上冒泡
  • 2.例:上述代码点击inner view会先后调用handleTap3handleTap2
    • 1.因为tap事件会冒泡到middle view,而middle view中的catch阻止了tap事件冒泡,不再向父节点传递
    • 2.点击middle view会触发handleTap2,点击outer view会触发handleTap1

5.事件的捕获阶段

<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
 outer view
 <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
   inner view
 </view>
</view>

<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
 outer view
 <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
   inner view
 </view>
</view>
  • 1.自基础库版本1.5.0起,触摸类事件支持捕获阶段
  • 2.捕获阶段位于冒泡阶段之前,捕获阶段中事件到达节点的顺序与冒泡阶段恰好相反
  • 3.捕获阶段监听事件时可采用capture-bindcapture-catch关键字,注意capture-catch将中断捕获阶段并取消冒泡阶段
  • 4.例:上述代码中
    • 1.点击inner view会先后调用handleTap2handleTap4handleTap3handleTap1
    • 2.如果将第一个capture-bind改为capture-catch,将只触发handleTap2

2.事件对象

  • 1.如果无特殊说明当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象
    在这里插入图片描述

1.BaseEvent 基础事件对象属性列表

属性类型说明基础库版本
typeString事件类型
timeStampInteger事件生成时的时间戳,即页面打开到触发事件所经过的毫秒数
targetObject触发事件的组件的一些属性值集合/触发事件的源组件
currentTargetObject当前组件的一些属性值集合
markObject事件标记数据
  • 1.target说明
    在这里插入图片描述

    属性类型说明
    idString事件源组件的id
    datasetObject事件源组件上由data-开头的自定义属性组成的集合
  • 2.currentTarget说明
    在这里插入图片描述

    属性类型说明
    idString当前组件的id
    datasetObject当前组件上由data-开头的自定义属性组成的集合
  • 3.id说明:

    • 1.targetcurrentTarget可参考点击inner viewhandleTap3收到的事件对象targetcurrentTarget都是 inner
    • 2.而handleTap2收到的事件对象targetinnercurrentTargetmiddle
  • 4.dataset说明:

    • 1.组件节点中可附加一些自定义数据,事件中可获取自定义的节点数据用于事件的逻辑处理
    • 2.WXML中自定义数据以data-开头,多个单词由连字符-连接
    • 3.注意:连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符
      <view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
      
      Page({
        bindViewTap:function(event){
          event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法
          event.currentTarget.dataset.alphabeta === 2 // 大写会转>为小写
        }
      })
      
      • 1.data-element-type:最终会呈现为event.currentTarget.dataset.elementType
      • 2.data-elementType:最终会呈现为event.currentTarget.dataset.elementtype
  • 5.mark说明:

    <view mark:myMark="last" bindtap="bindViewTap">
      <button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
    </view>
    
    Page({
      bindViewTap: function(e) {
        e.mark.myMark === "last" // true
        e.mark.anotherMark === "leaf" // true
      }
    })
    
    • 1.基础库版本2.7.1以上可使用mark识别具体触发事件的target节点,此外mark还可用于承载一些自定义数据(类似于 dataset
    • 2.当事件触发时,事件冒泡路径上所有的mark会被合并,并返回给事件回调函数(即使事件不是冒泡事件也会mark
    • 3.markdataset区别:
      • 1.mark会包含从触发事件的节点到根节点上所有的mark:属性值
      • 2.dataset仅包含一个节点的data-属性值
    • 4.mark注意事项
      • 1.如果存在同名的mark,父节点的mark会被子节点覆盖。
      • 2.自定义组件中接收事件时, mark不包含自定义组件外的节点的mark
      • 3.不同于dataset,节点的mark不会做连字符和大小写转换

2.CustomEvent 自定义事件对象属性列表(继承BaseEvent)

属性类型说明
detailObject额外的信息
  • 1.detail说明:
    • 1.detail是自定义事件所携带的数据(例:表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息)
    • 2.点击事件的detail带有的 x, ypageX, pageY代表距离文档左上角的距离

3.TouchEvent 触摸事件对象属性列表(继承BaseEvent)

属性类型说明
touchesArray触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouchesArray触摸事件,当前变化的触摸点信息的数组

在这里插入图片描述
在这里插入图片描述

  • 1.touches说明
    属性类型说明
    identifierNumber触摸点的标识符
    pageX, pageYNumber距离文档左上角的距离,文档的左上角为原点 ,横向为X轴,纵向为Y轴
    clientX, clientYNumber距离页面可显示区域(屏幕除去导航条)左上角距离,横向为X轴,纵向为Y轴
    • 1.touches是一个数组,每个元素为一个Touch对象,表示当前停留在屏幕上的触摸点
  • 2.changedTouches说明
    • 1.changedTouches数据格式同touches,表示有变化的触摸点
      • 1.从无变有(touchstart
      • 2.位置变化(touchmove
      • 3.从有变无(touchend、touchcancel
  • 3.CanvasTouch说明
    属性类型说明
    identifierNumber触摸点的标识符
    x, yNumber距离 Canvas 左上角的距离,Canvas 的左上角为原点 ,横向为X轴,纵向为Y轴
    • 1.canvas 触摸事件中携带的touchesCanvasTouch数组
    • 2.注意:canvas中的触摸事件不可冒泡,所以没有 currentTarget

3.使用方式

<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! ></view>
Page({
 tapName: function(event) {
   console.log(event)
 }
})
  • 1.组件中绑定一个事件处理函数,当用户点击该组件时会在该页面对应的Page中找到相应的事件处理函数

1.点击事件

<view>{{ msg }}</view>
<button bindtap="clickMe">点击我</button>
Page({
 clickMe: function() {
   this.setData({ msg: "Hello World" })
 }
})
  • 1.点击button按钮时希望把界面上msg显示成Hello World
  • 2.因此在button上声明一个属性bindtap并在JS文件里声明clickMe方法响应点击操作
  • 3.此外还可在JS中调用小程序提供的API,利用API可以方便的调用微信提供的能力(例:获取用户信息wx.getUserInfo、本地存储、微信支付等)

1.使用WXS函数响应事件

<!-- 在组件中绑定和注册事件处理的WXS函数 -->
<wxs module="wxs" src="./test.wxs"></wxs>
<view id="tapTest" data-hi="Weixin" bindtap="{{wxs.tapName}}"> Click me! </view>
<!-- 注意:绑定的WXS函数必须用{{}}括起来 -->
// test.wxs文件实现tapName函数
function tapName(event, ownerInstance) {
 console.log('tap Weixin', JSON.stringify(event))
}
module.exports = {
 tapName: tapName
}
  • 1.从基础库版本2.4.4开始,支持使用WXS函数绑定事件(基础库2.4.4开始支持,低版本需做兼容处理)
  • 2.WXS函数接受2个参数,第一个是event,在原有的event的基础上加了event.instance对象,第二个参数是ownerInstance,和event.instance一样是一个ComponentDescriptor对象
  • 3.ownerInstance包含了一些方法,可以设置组件的样式和class

组件

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值