微信小程序

一、概述

1、小程序是什么?

小程序,最早是特指微信小程序,英文名MiniProgram,是一种不需要下载安装即可使用的应用。

官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/

  • 无需安装卸载
  • 即用即开
  • 对于h5 开发人员门槛低 ,使用html,css.js

小程序码:

image-20211015092233419

2、与网页程序h5开发的区别?

小程序的主要开发语言是 JavaScript ,对于前端开发者而言,上手容易。

  • 小程序

    • 依赖于微信或其他APP上(宿主)的一个应用形式,无法脱离其所在的APP

    • 在特定的环境中去开发,所用的组件、UI都是确定好了的,也不用去考虑兼容问题

    • 在速度上,小程序基于APP端去实现,在使用时就感到很流畅

  • 普通网页

    • 网页开发用在移动端或者PC端的网页开发技术
    • 开发时会有开发工具的选择、框架的选择、UI的选择等问题,还要兼顾到浏览器是否兼容的问题
    • 网页在不同的浏览器或设备中解析加载会比较慢一些
    • 网页运行在浏览器中,当然有一些App内嵌了浏览器也是可以运行的
    • 网页是需要通过网址来进行访问的

3、与传统App开发的区别?

  • 小程序

    • 依赖于微信或其他APP上的一个应用形式,无法脱离其所在的APP
    • 无需要安装,开发技术要求较低,无需考虑应用兼容适配问题
  • 传统App

    • 独立运行,不需要依赖于谁
    • 需要用户安装,开发技术要求较高且开发时还需要解决设备兼容适配问题

4、小程序框架结构

整个小程序框架系统分为两部分: 视图层 和 逻辑层

  • 逻辑层 js
  • 数据驱动视图 只需要在逻辑层更改数据,视图层内容就会相应更新。

二、帐号申请与登录设置

1、帐号申请

官网: https://mp.weixin.qq.com/

微信小程序允许个人开发者申请账号,申请成功后才能进行下一步的学习和开发。

打开上述地址,在首页点击右上角的“立即注册”:

然后注册类型选择小程序

随后按照页面提示进行注册信息的填写以完成注册。

2、登录及配置获取

申请成功后,回到网站首页,使用注册的邮箱账号和密码(或微信扫码)进入到微信小程序官方控制平台中。

进入微信小程序官方控制平台后,主要为了得到开发所要用到的appidsecret这两项值。这两项值后续需要用到:

需要注意:AppSecret不会明文存储并显示在页面上,需要点击生成按钮在显示弹窗后自行保存并记录,一旦离开当前页面(或刷新)就无法查看已经生成的AppSecret,只能对原先的进行重置。

三、微信开发者工具

1、工具的下载与安装

工具集成了公众号网页调试和小程序调试两种开发模式,开发者可以编译小程序在电脑上看到模拟器编译效果,此处根据电脑类型自行下载如下:

2、helloworld项目

在桌面上双击运行微信开发者工具,参考以下图示进行项目创建:

创建好项目后显示效果如下:

四、小程序目录结构及配置

1、目录结构

小程序包含一个描述整体程序(全局)的app和多个描述各自(局部)页面的page。

一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:

文件必填作用
app.js小程序逻辑-小程序入口文件
app.json小程序公共配置文件
app.wxss小程序公共样式表

一个小程序page页面由四个文件组成,分别是:

文件类型必填作用
js页面逻辑
wxml页面结构
wxss页面样式表
json页面配置

2、全局配置文件app.json

参考网址:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

app.json文件用来对微信小程序进行全局配置的,设置小程序页面数量、窗口表现、设置底部或顶部菜单、网络请求超时时间等。

app.json由于是json文件,所以其中不能添加任何注释,key和value字符串必须用双引号引起来,数组或对象最后一定不能有逗号。

常用的全局配置: 如何和页面的.json配置冲突,以页面为准

  • pages:注册小程序的页面路径列表

数组的第一项代表小程序的初始页面(首页)。小程序中新增/减少页面,都需要对 pages 数组进行修改。

  • window:全局的默认窗口表现

用于设置小程序的状态栏、导航条、标题、窗口背景色

  • tabBar:小程序底部或顶部菜单定义(换句话说,小程序的菜单是通过json配置来实现的)

  • networkTimeout:小程序网络请求超时时间设置

  • usingComponents:自定义组件配置

image-20211015095124049

3、全局样式文件app.wxss

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

如何和页面的样式文件冲突,以页面的配置文件为准

1
2
3
4
5
6
7
8
9
/**app.wxss**/
/* app.wxss 全局css样式*/
/* 引入公共css */
/* @import '/static/css/common.wxss'; */
page{
width: 750rpx;
font-size: 24rpx;
background:deeppink
}

4、小程序生命周期

每个小程序都需要在 app.js 中调用 App 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// app.js
// 注册整个小程序实例对象
// 必须调用且只能调用一次
App({
// 整个小程序得生命周期函数
onLaunch() {
console.log('监听小程序初始化')
},
onShow(){
console.log('监听小程序启动或切前台。')
},
onHide(){
console.log('监听小程序切后台。')
},
globalData: {
nameArr: ['张文静','岳小慧','宋江燕']
}
})

5、全局数据 globalData

整个小程序只有一个 App 实例,是全部页面共享的。开发者可以通过 getApp 方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App 上的函数。

在app.js 文件中,定义globalData 属性,该属性中存放的数据在每个页面都可以获取和修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在app.js 中定义全局数据
globalData: {
nameArr: ['张文静','岳小慧','宋江燕']
}

// 在 index.js 中获取
const app = getApp(); //获取App的实例
onLoad: function (options) {
console.log(app) //获取 App 实例,并获取全局变量数据 globalData
},
handleFn(){
app.globalData.nameArr.push('逯鑫') //可以在事件中修改 globalData全局数据
}

五、WXML语法

WXML:页面的视图结构文件。简单来说,其是基于html的基础之上,小程序框架自己设计的一套标记语言。使用这套标记语言,结合JS逻辑部分、样式部分(WXSS),可以允许开发者构建出页面的效果。

注意点:该语言标签严格要求,标签有开始,必须也有结束。

1、WXML数据绑定

a. 页面中的数据都是来自于页面的逻辑文件(js文件)的data属性

例如,我们也在page/index/index.js文件中新增一个数据“msg”,其值是“HTML5”。则如下:

b. 数据可以在页面结构文件(wxml文件)中展示,例如将刚才index页面的msg数据在index.wxml中展示输出(类似于vue的插值表达式)

1
<text class="user-motto">{{msg}}</text>

c. 使用 setData 方法来对数据进行修改。在使用时注意this关键词指向的问题,因为这个方法是对象实例里的方法。例如,假设需要msg数据在页面加载完成后5秒钟将值修改为“H5”,则代码如下:

针对数据的修改一般写在页面的生命周期函数中。

语法:

1
2
3
this.setData({
msg: "H5"
});

2、WXML循环

语法:其循环的实现与vue及其相似,也是通过标签的属性来实现循环的。其属性如下:

  • wx:for:该属性表示循环,其属性值是要循环的数据,默认下标名为 index,当前项为 item
  • wx:key:使用wx:for 渲染列表,必须循环体的唯一标识符,建议可以使用数组每一项得id。
  • wx:for-index:(可选)用于自定义索引变量名,默认值是index
  • wx:for-item:(可选)用于自定义循环到的元素的变量名,默认值是item

关于新建page的操作提示:

  • 在pages目录下新建一个空的文件夹
  • 右键新建好的空的文件夹,选择菜单中的“新建page”
  • 输入页面的名称,回车即可生成page需要的四个文件及其初始化的内容
  • 根据需要决定是否要设置页面为默认页面(在app.json文件中修改新建页面的顺序)

定义一个数据源,稍后演示循环操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
data: {
users: ['张三', '李四', '王五'],
obj: [
{
id: 1,
user: 'zhangsan'
},
{
id: 2,
user: 'lisi'
},
{
id: 3,
user: 'wangwu'
}
]
},

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--pages/wxfor/wxfor.wxml-->
<text>pages/wxfor/wxfor.wxml</text>

<!-- 在wxml里面不用div,div可以用view标签替代 -->
<!-- 使用默认的索引和元素变量循环 -->
<view wx:for="{{users}}" wx:key="index">
{{index}} - {{item}}
</view>

<!-- 使用自定义的索引和元素变量循环 -->
<view wx:for="{{users}}" wx:for-index="key" wx:for-item="val" wx:key="key">
{{key}} - {{val}}
</view>

<!-- 循环数组对象的操作 -->
<view wx:for="{{obj}}" wx:key="index">
{{item.id}} - {{item.user}}
</view>

3、WXML判断

语法:与vue一样,小程序中的判断也是通过特定的属性来实现的。属性是:wx:if、wx:elif、wx:else。

例如,有以下的数据源:

1
2
3
data: {
age: 19
}

在视图结构代码中依据是否成年的规则去判定age是否成年了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--pages/wxif/wxif.wxml-->
<text>pages/wxif/wxif.wxml</text>

<view wx:if="{{age > 18}}">
成年了,可以去蹦迪了
</view>

<view wx:elif="{{age == 18}}">
刚好成年,不建议现在去蹦迪
</view>

<view wx:else>
未成年,回去写作业去
</view>

4、模板页面引用

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

  • 使用 name 属性,作为模板的名字。然后在<template/>内定义代码片段,使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入

  • WXML提供两种文件引用方式:import(高级)和include

  • import:有点类似以于二阶段,可以把一些常用自定义函数,写到一个文件中,在用的时候可以导入进来,然后再去调用里面的特定的函数。(按需使用)

  • include:将目标文件中所有的代码(除了template标签段、wxs标签段)统统在引入的位置直接用(直接使用)

4.1 页面内使用模板:

当前页面内定义模板,并在当前页面使用,数据item 为当前页js 中定义的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 定义模板 -->
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>

<!-- 使用模板 -->
<template is="msgItem" data="{{...item}}"/>

<!-- js -->
Page({
data:{
item: {
index: 0,
msg: '我是模板',
time: '2016-09-15'
}
},

六、WXSS样式

WXSS是一套样式语言,用于描述WXML的组件样式(有点CSS描述HTML样式的感觉)。

为了适应广大的前端开发者,WXSS具有CSS大部分特性。同时为了更适合开发微信小程序,WXSS对CSS进行了扩充以及修改。例如:

  • 新增了尺寸单位
    • WXSS在底层支持新的尺寸单位rpx,可以根据屏幕宽度进行自适应,响应式尺寸单位
    • 小程序中全屏尺寸数值是 : 750rpx
    • 与px的换算关系:
设备rpx换算px (屏幕宽度/750)px换算rpx (750/屏幕宽度)
iPhone51rpx = 0.42px1px = 2.34rpx
iPhone61rpx = 0.5px1px = 2rpx
iPhone6 Plus1rpx = 0.552px1px = 1.81rpx
  • 提供了全局的样式和局部样式

    • 定义在app.wxss中的样式为全局样式,作用于每一个页面
    • 在page的wxss文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖app.wxss中相同的选择器
  • 此外WXSS仅支持部分CSS选择器,目前支持的选择器有:

选择器样例样例描述
.class.intro选择所有拥有class=”intro”的组件
#id#firstname选择拥有id=”firstname”的组件
elementview选择所有view组件
element, elementview, checkbox选择所有文档的view组件和所有的checkbox组件
::afterview::after在view组件后边插入内容
::beforeview::before在view组件前边插入内容
  • 单位 rpx 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。在 iPhone6 上,屏幕宽度为375px,1rpx = 0.5px = 1像素,开发建议用 iPhone6 作为视觉稿的标准。

七、tabBar

文档地址:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar

tabBar就是小程序底部的导航菜单,小程序的tabBar使用比较简单,只需要在全局配置文件app.json中加上下面的配置即可,参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"tabBar": {
"color": "color",
"selectedColor": "selectedColor",
"list": [
{
"pagePath": "pagePath",
"iconPath": "iconPath",
"selectedIconPath": "selectedIconPath",
"text": "text"
},
{
"pagePath": "pagePath",
"iconPath": "iconPath",
"selectedIconPath": "selectedIconPath",
"text": "text"
},
]
}
}

参考配置实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"tabBar": {
"color": "#C0C0C0",
"selectedColor": "#000000",
"backgroundColor": "#FFFFFF",
"list": [
{
"pagePath": "pages/include/include",
"text": "导入",
"iconPath": "./assets/images/more.png",
"selectedIconPath": "./assets/images/more-active.png"
},
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "./assets/images/cookbook.png",
"selectedIconPath": "./assets/images/cookbook-active.png"
}
]
}
}

注意点:

  • pages数组中的默认页面(第一个元素)一定要出现在tabbar配置的list数组中,如果没有则底部菜单是不显示的;
  • 如果默认页面在菜单的list数组中,但是不是list数组的一个元素,那么默认页面在第一个元素,那个菜单就会被默认选中;
  • ==正常来讲,pages数组中的第一个元素(页面),即菜单list数组中的第一个栏目;==

八、页面生命周期

网址:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html

由框架自己触发的一系统事件函数(钩子函数)。

小程序数据互交就是通过不同的事件函数来完成的,了解生命周期函数,对于以后的数据交互是非常重要的。

  • data属性

页面的初始数据

  • onLoad(Object[json] query)

页面加载时触发。一个页面只会调用一次,可以在onLoad的参数中获取打开当前页面路径中的参数。

参数说明

名称类型说明
queryObject打开当前页面路径中的参数 json对象
  • onShow()

页面显示/切入前台时触发。一个页面可以触发N次。

  • onReady()

页面初次渲染完成时触发。一个页面只会调用一次。

  • onHide()

页面隐藏/切入后台时触发。一个页面可以触发多次

  • onPullDownRefresh

监听用户下拉动作,此事件需要在app.json文件中window节点中“开启全局的下拉刷新”

“enablePullDownRefresh”:true,才能触发它 作用:下拉加载更多

  • onReachBottom

页面上拉触底事件的处理函数,需要当前页面内容超过一屏显示 作用:上拉加载更多

  • onPageScroll

页面滚动触发事件的处理函数,需要当前页面内容超过一屏显示 作用:滚动事件监听

  • onShareAppMessage

用户点击右上角转发,触发此方法,在此方法中可以自定义转发的内容。 作用:自定义分享

分享页面的路径,必须以“/”开头。

九、自定义事件

1、事件绑定

文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#%E4%BA%8B%E4%BB%B6%E8%AF%A6%E8%A7%A3

  • 绑定事件两种写法

    • 绑定冒泡事件(不会帮我们阻止冒泡)【主要】
      • 语法:bind事件类型=”方法名” 方法名不能加括号,而且不能传参
    • 绑定非冒泡事件(会帮我们阻止冒泡)
      • 语法:catch事件类型=”方法名” 方法名不能加括号,而且不能传参
  • 上述语法还支持在属性名中间加上“:”写法,例如:

    • bind:事件类型
    • catch:事件类型
    • 自基础版本库2.8.1以后所有的事件都支持加“:”写法
  • 特别需要注意,事件类型有可能其名称与之前大不同,例如以前的点击事件类型名字是click,在这里点击事件其实就是手指触摸事件,官网规定触摸事件是tap

  • 小程序事件 有哪些?查看如下对应官网地址:

    https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

例如,声明一个view给其绑定一个点击事件(冒泡事件):

1
2
3
4
5
6
7
8
9
10
11
12
<!--pages/eventBind/eventBind.wxml-->
<text>pages/eventBind/eventBind.wxml</text>

<!-- 给view标签绑定点击事件 -->
<view bindtap="tapHandler">
点我一下有惊喜
</view>

<!-- 给view绑定长按事件 -->
<view bind:touchstart="start" bind:touchend="end">
蓄力发动技能
</view>

随后需要在页面的js逻辑层文件中写对应的处理程序:tapHandler,参考代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Page({

/**
* 页面的初始数据
*/
data: {
time: 0
},

/**
* tap事件的处理程序
*/
tapHandler() {
console.log('的确是点了,惊喜是:这节结束就下课。');
},

/**
* 蓄力开始触发的事件
*/
start() {
// 获取当前的时间并记录
let now = Date.now();
this.setData({
time: now
})
console.log('吟唱中.....');
},

/** 蓄力结束触发的事件 */
end() {
let diffTime = Date.now() - this.data.time;
console.log('技能发动完毕,cd....');
console.log('本次蓄力耗时' + diffTime + '毫秒');
},
// ......
});

2、事件对象

当组件触发事件时,逻辑层绑定该事件的处理方法会收到一个事件对象。通过此对象来进行小程序的自定义事件参数据传递。

事件对象在小程序中是非常有意义的,这点与vue和react不同。在小程序中,事件对象是给事件处理程序传递参数的唯一方式

BaseEvent基础事件对象属性列表:

属性类型说明
typeString事件类型
timeStampInteger页面打开到触发事件所经过的毫秒数。
targetObject触发事件的组件的一些属性值集合
currentTargetObject当前组件的一些属性值集合

注意点:target属性与currentTarget,在部分场景下是一样的,当然也存在不一样的情况。

  • 如果事件依附的这个组件(标签)不存在子组件(标签)并且子有属性的时候,两者一样

  • 如果事件依附的这个组件(标签)存在子组件(标签)并且子有属性的时候,则两者不一样

  • 如果以后要获取事件自身的组件(标签)的数据的时候,得使用currentTarget。

  • 在小程序中,如果希望在视图结构中通过事件给事件对象传递参数,则可以在标签上使用data-数据名=“数据值”的形式传递,例如参考代码:

    • <!-- 传递参数100 -->
      <view bindtap="tapHandler" id="efg" data-m="100">
          <!-- 传递参数abc -->
          <view id="abc" data-abc="abc">点我触发事件</view>
      </view>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      - ~~~js
      // 接收参数
      /**
      * 事件对象获取
      * 所有的事件处理程序都有一个默认的参数,这个参数就是事件对象eventObj
      */
      tapHandler(eventObj) {
      console.log(eventObj);
      // 接收事件组件/标签自己的数据
      console.log(eventObj.currentTarget.dataset.m);
      // 获取事件组件/标签子的数据
      console.log(eventObj.target.dataset.abc);
      }

​ 绑定文本框 e.detail.value

4.2 页面外使用模板

  • 把模板定义到外部,然后多个页面间可以共用使用定义的模板

  • import可以在当前文件中使用目标文件定义的template(代码区块[标签],可以有多个)。

语法:<import src="目标文件的路径"></import>

  1. 在pages 目录下新建 页面temp,其代码如下:
1
2
3
4
5
6
7
8
9
<!--pages/temp/temp.wxml-->
<view>
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
</view>
  1. 在其他页面如首页中引入模板temp,并使用模板,数据item1为使用页数据(如首页)
1
2
3
4
<!-- 引入模板 -->
<import src="../temp/temp.wxml"/>
<!-- 使用模板 -->
<template is="msgItem" data="{{...item1}}"/>

注意事项:

  • import的标签使用可以存在套娃行为,也就是说可以允许出现以下情况:在C文件中importB文件,在B文件中importA文件
  • import在套娃的时候需要注意,其使用template存在作用域的概念的。import导入,在使用时只能使用导入的目标文件的template,不能使用导入文件中的导入的目标文件的template。(不允许隔代使用)

4.3 include

include可以将目标文件除了<template/> <wxs/>外的整个代码引入,相当于是拷贝到include位置。

注意:只能引入静态html代码,如果代码中涉及data中得变量,需要在引入到得当前页面data中重新定义该变量。

语法:<include src="目标文件的路径"/>

  1. 在pages 中新建 temp1页面
1
2
3
4
5
6
<!--pages/temp1/temp1.wxml-->
<view class="box">
<view wx:for='{{fruitArr}}' wx:key='index'>
{{item}}
</view>
</view>

2.在使用的页面中引入模板:

1
2
<!-- 使用include 引入模板 -->
<include src="../temp1/temp1.wxml" />

小结:

  • include形式不支持对目标文件的template和wxs区块的解析
  • include引入即使用,import在引入后还需要再去单独使用

十、常用小程序组件

1、组件介绍

文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/view/component.html

框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行快速开发。一个组件通常包括开始标签和结束标签,属性用来修饰这个组件,内容在两个标签之间。

1.1、属性值类型

类型描述注解
Boolean布尔值组件写上该属性,不管该属性等于什么,其值都为true,只有组件上没有写该属性时,属性值才为false。如果属性值为变量,变量的值会被转换为Boolean类型**{{}}**
Number数字1, 2.5**{{}}**
String字符串“string”
Array数组[ 1, “string” ]{{}}
Object对象{ key: value }{{}}
EventHandler事件处理函数名“handlerName” 是 Page中定义的事件处理函数名

1.2、共同属性

所有组件都有的属性:

属性名类型描述注解
idString组件的唯一标示保持整个页面唯一
classString组件的样式类在对应的 WXSS 中定义的样式类
styleString组件的内联样式可以动态设置的内联样式
hiddenBoolean [false]组件是否显示所有组件默认显示,不需要加{{}}
data-*Any自定义属性组件上触发的事件时,会发送给事件处理函数
bind* / catch*EventHandler组件的事件详见事件

2、视图容器组件

2.1、swiper

文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html

案例:使用swiper展示轮播图

逻辑层定义数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data: {
background: ["https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/bce52a5f143cd3e25c6c39c7a0fd7f276ce43bad.png?sign=f4ec5771f7eabd11226fe5f4b7f0f6e8&t=5fa403f2",
"https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/6018ac895dd29437b1d023c121c7539ecf2e9221.jpeg?sign=47da092f8a6a1650df3da3dd3dd40cb3&t=5fa4041d",
"https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/f81d133833b89a18cb1842f449810d16ec5d3c78.jpeg?sign=22eadb72caac161df642aa18b84127a8&t=5fa40431"
],
// 是否显示原点
indicatorDots: true,
// 滑动方向是否为纵向
vertical: false,
// 是否自动轮播
autoplay: true,
// 自动切换时间间隔(单个图片显示的时长)
interval: 2000,
// 切换的持续时间
duration: 500
},

图片采用image组件进行展示,其支持对图片进行缩放、裁剪处理。关于image组件的信息,可以访问文档:https://developers.weixin.qq.com/miniprogram/dev/component/image.html

实际在wxml中展示

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--pages/swiper/swiper.wxml-->
<text>pages/swiper/swiper.wxml</text>

<view class="page-section page-section-spacing swiper">
<swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
<block wx:for="{{background}}" wx:key="*this">
<swiper-item>
<!-- image标签一般都需要配合图片处理属性去使用:mode。image标签除了展示图以外,还提供了一套对于图片的裁剪,缩放等功能 -->
<image style="height: 150px;" src="{{item}}"/>
</swiper-item>
</block>
</swiper>
</view>

在设计图片的时候,一定要和设计人员对接好图片的合适尺寸比例。

2.2、scroll-view

文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html

案例:实现滚动切换的效果

逻辑层代码

1
2
3
4
5
6
// pages/scroll/scroll.js
const order = [
{id:"a",url:"https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/bce52a5f143cd3e25c6c39c7a0fd7f276ce43bad.png?sign=f4ec5771f7eabd11226fe5f4b7f0f6e8&t=5fa403f2"},
{id:"b",url:"https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/6018ac895dd29437b1d023c121c7539ecf2e9221.jpeg?sign=47da092f8a6a1650df3da3dd3dd40cb3&t=5fa4041d"},
{id:"c",url:"https://storage.lynnn.cn/assets/markdown/91147/pictures/2020/11/f81d133833b89a18cb1842f449810d16ec5d3c78.jpeg?sign=22eadb72caac161df642aa18b84127a8&t=5fa40431"}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Page({
/**
* 页面的初始数据
*/
data: {
// 默认要显示的元素
toView: 'a',
// 绑定数据给页面
imgs: order
},

// 监听是否滚动到顶部
scrollToTop() {
this.setData({
scrollTop: 0
})
},

// 触摸事件
tap() {
for (let i = 0; i < order.length; ++i) {
// this.data.toView此时是图片的id
if (order[i].id === this.data.toView) {
this.setData({
toView: order[i + 1].id,
scrollTop: (i + 1) * 200
})
break
}
}
},

// 触摸并拖拽事件
tapMove() {
this.setData({
scrollTop: this.data.scrollTop + 10
})
},
})

wxml文件参考代码

1
2
3
4
5
6
7
8
<!--pages/scrollView/scrollView.wxml-->
<text>pages/scrollView/scrollView.wxml</text>

<scroll-view scroll-y="true" style="height: 560rpx;" scroll-into-view="{{toView}}" scroll-top="{{scrollTop}}">
<block wx:for="{{imgs}}" wx:key="*this">
<image src="{{item.url}}" />
</block>
</scroll-view>

3、表单组件

小程序项目中但凡是涉及到表单的地方,都需要使用表单组件。表单组件的作用就是用于组成表单的。

注意点:

  • 这里面表单组件部分的标签继续沿用html的标签,与之前完全同名
  • 组件标签的部分属性也与之前html属性一致,但是更多的是不一致的

文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/button.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!--pages/formComponent/form.wxml-->
<text>pages/formComponent/form.wxml</text>

<!-- 表单组件:按钮组件 -->
<!-- 常规按钮:底色灰白的,不易见,type值为default(可以不写) -->
<button>常规按钮</button>
<!-- 主要按钮:绿色按钮,最常用的按钮 -->
<button type="primary">主要按钮</button>
<!-- 警告按钮:底色灰色,但是字是红色 -->
<button type="warn">警告按钮</button>
<!-- 按钮开放功能:打开授权,需要配合授权的回调函数,综合案例中再讲 -->
<button open-type="getUserInfo" bindgetuserinfo="getInfo">授权</button>

<!-- 单选按钮组 -->
<!-- 由于单选按钮组中包含了多个redio,因此需要被包裹 -->
<radio-group>
<radio value="1"></radio>
<radio value="2"></radio>
</radio-group>

<!-- 复选框组 -->
<!-- 与单选按钮组类似,也需要被包裹 -->
<checkbox-group>
<checkbox value="吃饭">吃饭</checkbox>
<checkbox value="睡觉">睡觉</checkbox>
<checkbox value="打DD">打DD</checkbox>
</checkbox-group>

<!--表单组件的标签的样式是需要自己去调节设置的 -->
<input type="text" value="zhangsan" />

提醒:按钮button组件有一个非常实用的属性open-type,后续可以通过该属性赋予按钮高级的功能(微信开放能力),例如:

  • 获取用户信息的时候
  • 唤起其他App的时候
    • 不是任意app都可以唤起,只能唤起来源app
  • 获取用户手机号
  • ….

4、 媒体组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 页面部分
<camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;" bindscancode='getscandata'></camera>
<button type="primary" bindtap="takePhoto">拍照</button>
<view>预览</view>
<image mode="widthFix" src="{{src}}"></image>

// 逻辑部分
takePhoto() {
const ctx = wx.createCameraContext()
ctx.takePhoto({
quality: 'high',
success: (res1) => {
console.log(res1);
this.setData({
src: res1.tempImagePath
})
// 将照片保存到手机相册中
wx.saveImageToPhotosAlbum({
filePath:res1.tempImagePath,
success(res) {
console.log(res);
}
})

}
})
}

video组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<view class="page-body">
<view class="page-section tc">
<video
id="myVideo"
src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400"
binderror="videoErrorCallback"
danmu-list="{{danmuList}}"
enable-danmu
danmu-btn
show-center-play-btn='{{false}}'
show-play-btn="{{true}}"
controls
picture-in-picture-mode="{{['push', 'pop']}}"
bindenterpictureinpicture='bindVideoEnterPictureInPicture'
bindleavepictureinpicture='bindVideoLeavePictureInPicture'
></video>
<view style="margin: 30rpx auto" class="weui-label">弹幕内容</view>
<input bindblur="bindInputBlur" class="weui-input" type="text" placeholder="在此处输入弹幕内容" />
<button style="margin: 30rpx auto" bindtap="bindSendDanmu" class="page-body-button" type="primary" formType="submit">发送弹幕</button>
<navigator style="margin: 30rpx auto" url="picture-in-picture" hover-class="other-navigator-hover">
<button type="primary" class="page-body-button" bindtap="bindPlayVideo">小窗模式</button>
</navigator>
</view>
</view>

video.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
function getRandomColor() {
const rgb = []
for (let i = 0; i < 3; ++i) {
let color = Math.floor(Math.random() * 256).toString(16)
color = color.length === 1 ? '0' + color : color
rgb.push(color)
}
return '#' + rgb.join('')
}

Page({
onShareAppMessage() {
return {
title: 'video',
path: 'page/component/pages/video/video'
}
},

onReady() {
this.videoContext = wx.createVideoContext('myVideo')
},

onHide() {

},

inputValue: '',
data: {
src: '',
danmuList:
[{
text: '第 1s 出现的弹幕',
color: '#ff0000',
time: 1
}, {
text: '第 3s 出现的弹幕',
color: '#ff00ff',
time: 3
}],
},

bindInputBlur(e) {
this.inputValue = e.detail.value
},

bindButtonTap() {
const that = this
wx.chooseVideo({
sourceType: ['album', 'camera'],
maxDuration: 60,
camera: ['front', 'back'],
success(res) {
that.setData({
src: res.tempFilePath
})
}
})
},

bindVideoEnterPictureInPicture() {
console.log('进入小窗模式')
},

bindVideoLeavePictureInPicture() {
console.log('退出小窗模式')
},

bindPlayVideo() {
console.log('1')
this.videoContext.play()
},
bindSendDanmu() {
this.videoContext.sendDanmu({
text: this.inputValue,
color: getRandomColor()
})
},

videoErrorCallback(e) {
console.log('视频错误信息:')
console.log(e.detail.errMsg)
}
})

5、 地理定位组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 地图组件 -->
<!-- <map longitude="{{long}}" latitude="{{lat}}" scale='20'></map> -->

onLoad: function (options) {
// 在该位置获取当前位置的经纬度
wx.getLocation({
type: 'wgs84',
success: (res)=> {
console.log(res);
this.setData({
long: res.longitude,
lat:res.latitude
})
}
})
},

app.json

1
2
3
4
5
6
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
},
"requiredPrivateInfos": ["getLocation"],

十一、自定义组件

文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/

开发者可以将页面内的功能模块抽象成自定义组件(思想与vue和react一样),以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。

1. 组件传参

步骤:

  • 1.首先创建组件, 新建文件创建组件,右键创建文件夹,然后新建组件生成相应4个文件同页面文件一样。

  • 2.在组件js文件中,定义组件得属性,如下图tag,且在页面中输出变量tag如下

    image-20211015182708911

  • 3.在其他页面使用组件,首先需要在该页面得json配置文件中引入该组件如下图

  • 4.将引入得组件名放到对应使用位置,父组件添加属性,给子组件传递数据,子组
    件通过 properties 接收,如此处tag 属性,注意:传递属性名和接受得属性名
    必须一致。

    image-20211015182826480

  • 5.子组件向父组件传递参数一般通过事件传递。

  • 6.子组件绑定事件,父组件页面bind:子组件事件名。

image-20211015182956059

2. 组件生命周期

1.组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。

  • 组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data此时还不能调用 setData 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
  • 在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。
  • 在组件离开页面节点树后, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Component({
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
// ...
})

分享:引入第三方组件vant

https://blog.csdn.net/m0_67841039/article/details/125828689

app.json

1
"style": "v2",  去掉

十二、导航方式

小程序的路由章节并不是需要学习怎么定义路由,因为路由已经在app.json全局配置文件中声明好了。在本章节我们需要掌握的就是如何实现page的切换。

page的切换小程序提供了两种实现方式,一种是通过组件来实现(声明式导航),另外一种是通过api实现(编程式导航)

1、基于组件

基于组件式的导航方式其实就是之前的声明式导航。

文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html

组件:navigator

该组件的属性比较多,需要注意下open-type,这里属性值稍微常用一点的就是switchTab,其是用来切换tabBar的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--pages/navigator/navigator.wxml-->
<text>pages/navigator/navigator.wxml</text>

<!--
声明式导航:基于组件的导航方式
组件:navigator
可以将其看作是html的a标签
url属性:去的地址,地址要带“/”
-->
<navigator url="/pages/index/index">去首页</navigator>

<!-- 声明式导航也支持传递参数 -->
<navigator url="/pages/index/index?id=1&age=22">带参数去首页</navigator>

<!-- 菜单的切换 -->
<!-- 无效方式 -->
<navigator url="/pages/scrollView/scrollView">尝试切菜单</navigator>
<!-- 可用 -->
<navigator url="/pages/logs/logs" open-type="switchTab">尝试切菜单</navigator>

跳转去其他页面的时候,如果带参,则接收参数的页面需要在onLoad生命周期函数中接收形参options,其是一个普通对象,里面存放着传递过来的数据。

navigateTo : 跳转非tabbar页面,不关闭当前页面

redirectTo : 关闭当前当前页面,跳转到其它页面

switchTab 跳转到其他页面,关闭非tabbar页面

reLaunch 关闭所有页面,打开到应用内的某个页面

open-type 的合法值

说明最低版本
navigate对应 wx.navigateTowx.navigateToMiniProgram 的功能
redirect对应 wx.redirectTo 的功能
switchTab对应 wx.switchTab 的功能
reLaunch对应 wx.reLaunch 的功能1.1.0
navigateBack对应 wx.navigateBack 的功能1.1.0
exit退出小程序,target="miniProgram"时生效2.1.0

2、基于Api

基于Api的导航实质上就是基于事件+编程的方式来实现导航。

文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.switchTab.html

具体的api可以参考文档,例如演示2个常用的api:

1
2
3
4
<!-- 基于Api形式的导航实现 -->
<view bindtap="tapHandler1">去tab中的index</view>
<view bindtap="tapHandler2">去nav1</view>
<view bindtap="tapHandler3">去nav1</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 编程式导航方式
// wx.switchTab 等于 navigator + open-type=switchTab
tapHandler1: function(){
wx.switchTab({url: "/pages/index/index"});
},

// wx.navigateTo 等于 navigator
// navigateTo,保留当前页面(目标页面左上角出现“返回”按钮),去应用内其它页面,但不能是tabbar中页面
tapHandler2: function(){
wx.navigateTo({url: "/pages/nav1/nav1"});
},

// wx.redirectTo 等于 navigator + open-type=redirect
// redirectTo,不保留当前页面(目标页面左上角出现“首页”按钮),去应用内其它页面,但不能是tabbar中页面
tapHandler3: function(){
wx.redirectTo({url: "/pages/nav1/nav1"});
},

十三、界面交互方法

1、显示消息提示框

1
2
3
4
5
wx.showToast({
title: '成功',
icon: 'success',
duration: 2000
})

2、显示模态对话框

1
2
3
4
5
6
7
8
9
10
11
wx.showModal({
title: '提示',
content: '这是一个模态弹窗',
success (res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})

3、显示 loading 提示框

注意:需主动调用 wx.hideLoading 才能关闭提示框

1
2
3
4
5
6
7
wx.showLoading({
title: '加载中',
})

setTimeout(function () {
wx.hideLoading()
}, 2000)

云开发(serveless技术) 云数据库和云函数

十四、网络请求

文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html

请注意:小程序只支持httpswss(WebSocket,其具备ws协议与wss协议)协议的网络请求。但是允许开发者在开发环境下使用http请求,不过在最终上线时必须要修改为小程序支持的协议类型。

1、网络请求的相关设置

虽然小程序只支持httpswss协议的网络请求。但是允许开发者在开发环境下使用http请求。如果需要在开发环境下使用http协议进行开发测试并且不对域名合法性进行校验,请记得开启支持:

最终上线时,需要有https的接口服务,并且需要在小程序官方后台中设置接口域名(为了安全,添加域名白名单),设置路径:小程序管理后台 / 开发 / 开发设置 / 服务器域名。如果条件允许,建议尽量在开发时就使用符合要求的域名配置而不是等到上线的时候再返工。

如果使用的测试号,请在测试号管理页面添加白名单。

域名可以使用给定测试域名:

2、发起网络请求

在小程序的wx对象中,其已经提供了网络请求方法。

语法:wx.request(请求参数的对象)

文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
wx.request({
url: 'test.php', //仅为示例,并非真实的接口地址
data: {
x: '',
y: ''
},
method: "GET",
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
console.log(res.data)
}
})

注:在发起请求之前,需要在app.json文件中设置request请求的超时时间以防止网络堵塞导致程序卡死,时间单位为毫秒:

1
2
3
"networkTimeout": {
"request": 10000
}

wx.request()的方法返回的对象信息包含如下几个属性:

  • cookies:返回的cookies
  • data:响应体
  • header:响应头
  • statusCode:http状态码
  • errMsg:错误信息

例如:点击按钮发送一个网络请求请求数据并循环渲染

wxml代码

1
2
3
4
5
6
7
8
<!--pages/request/request.wxml-->
<text>pages/request/request.wxml</text>

<button type="primary" bindtap="send">发送请求</button>

<block wx:for="{{data}}" wx:key="index">
<view>地区id:{{item.id}},地区名:{{item.name}}</view>
</block>

js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Page({

/**
* 页面的初始数据
*/
data: {
data: []
},

/**
* 发送网络请求
*/
send() {
wx.request({
// 请求地址
url: 'https://api.i-lynn.cn/area',
// 请求方式
method: "GET",
// 请求参数
data: {
id: 1
},
// 成功请求的回调
success: (res) => {
console.log(res);
this.setData({
data: res.data
})
}
})
}
})

请求的封装

1
2
3
4
5
6
7
8
9
10
11
12
const http=(url,method,data)=>{
return new Promise((reslove,reject)=>{
wx.request({
url,
data,
method,
success: (result) => {
reslove(result)
}
})
})
}

3、缓存

文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorage.html

小程序是有缓存的,但是它缓存没有过期时间,单个key允许存储的最大数据长度为1MB,所有数据存储上限为10MB。如果缓存达到了上限,清除缓存,清除缓存机制:最久未被使用。

例如:在wxml结构中设置2个按钮,分别用于设置和获取缓存的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- 设置缓存 -->
<button type="primary" bindtap="setCache">设置</button>
<!-- 获取缓存 -->
<button type="primary" bindtap="getCache">获取</button>

setCache: function () {
// 同步存储
wx.setStorageSync('key', 'value')
// 异步存储
wx.setStorage({
key:"key",
data:"value"
})
},

getCache: function () {
// 同步获取
var value = wx.getStorageSync('key')
// 异步获取
wx.getStorage({
key: 'key',
success (res) {
console.log(res.data)
}
})
},

重要的数据不要放在小程序缓存中。

需要注意,获取缓存数据的方法wx.getStorage方法为异步方法,在获取的时候要么加.then进行处理,要么加async + await进行处理。(推荐:再或者就是在方法名后面加上Sync来使用其同步的方法)在小程序开发工具中默认是不允许使用async和await的,如有使用需求,请开启增强编译功能,如图:

十五、综合案例

目标:实现一个简易的新闻小程序,包括如下功能:

  • 热点新闻
  • 搜索
  • 新闻列表-加载更多
  • 新闻详情
  • 实现登录

效果演示

image-20211109220019102

​ (新闻页)

详情页

​ (详情页)

我的

​ (我的页)

1、封装

本案例需要使用的地址信息参考如下(此处仅约定地址,更为详尽的参数信息见对应功能的接口介绍):

基础地址:https://mpapi.iynn.cn/api/v1

各功能地址:

  • 个人中心(我的)

    • 登录:login
    • 授权:auth
    • 获取个人信息:user
    • 修改个人信息:user
    • 获取记录
      • 浏览:record
      • 收藏:fav
      • 点赞:like
  • 新闻

    • 新闻列表:news

    • 新闻搜索:news/search

    • 热点新闻:news/hot

    • 新闻详情:news/:id

    • 添加记录

      • 浏览:addRecord
      • 收藏:addFav
      • 点赞:addLike

在小程序开发的环节中,也需要应用模块化的思想,拆成3个部分即可:

  • 请求地址的封装吗,形成地址配置文件
  • 请求封装
  • 模型封装(处理业务,将业务独立,需要时候的时候在页面的js文件中导入使用即可)

1.1、请求封装

地址配置文件:utils/request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const ajax = (url,method,data)=>{
// 开始转圈圈
wx.showLoading({
title: '加载中...',
})
return new Promise((resolve,reject)=>{
wx.request({
url: url, //仅为示例,并非真实的接口地址
data: data,
method:method,
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
// console.log(res.data)
resolve(res.data)
// 关闭转圈圈
wx.hideLoading()
},
fail(err){
reject(err)
}
})
})
}

export default ajax

1.2、接口封装

请求封装的文件:utils/http.js

1
2
3
4
5
6
7
8
9
10
11
import ajax from './request'

// 公共的基础域名地址
const baseUrl= 'https://mpapi.iynn.cn/api/v1';

//所有的请求集合
// 新闻列表
export function newList(params){
return ajax(baseUrl+'/news','get',params)
}
...

2、功能实现

2.1、接口说明

2.2、新闻页面

实现的功能:

  • 搜索框
  • 轮播图
  • 新闻列表
    • 加载更多
  • 新闻详情展示
    • 新闻基本信息
    • 用户访问统计(获取用户标识符openid)

需求1:实现搜索框的展示和搜索功能

搜索框的排版可以参考别人已经写好的代码:https://www.jb51.net/article/165269.htm

小程序的input框的回车事件,其实就是手机端的收入法弹出后按键上的“搜索”按键,对应的事件是:bindconfirm

搜索框结构代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 搜索框 -->
<view class="weui-search-bar">
<view class="weui-search-bar__form">
<!--点击之后,出现input框 -->
<view class="weui-search-bar__box">
<icon class="weui-icon-search_in-box" type="search" size="14"></icon>
<input type="text" class="weui-search-bar__input" placeholder="搜索" value="{{inputVal}}"
focus="{{inputShowed}}" bindinput="inputTyping" bindconfirm="search" />
<!--输入款字数大于0,则显示清除按钮 -->
<view class="weui-icon-clear" wx:if="{{inputVal.length > 0}}" bindtap="clearInput">
<icon type="clear" size="14"></icon>
</view>
</view>
<!--没点击之前,只是一些文字和图标 -->
<label class="weui-search-bar__label" hidden="{{inputShowed}}" bindtap="showInput">
<icon class="weui-icon-search" type="search" size="14"></icon>
<view class="weui-search-bar__text">搜索</view>
</label>
</view>
<!--动态出现的“取消”键 -->
<view class="weui-search-bar__cancel-btn" hidden="{{!inputShowed}}" bindtap="hideInput">取消</view>
</view>

逻辑代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 三方搜索框方法
*/
showInput: function () {
this.setData({
inputShowed: true
});
},
hideInput: function () {
this.setData({
inputVal: "",
inputShowed: false
});
},
clearInput: function () {
this.setData({
inputVal: ""
});
},
inputTyping: function (e) {
//搜索数据
this.setData({
inputVal: e.detail.value
});
},
// 搜索输入框的确认事件
search: function () {
// 思路:获取搜索框中的值,带着值去搜索页面,交给搜索页面去发送ajax获取对应的结果
let keyword = this.data.inputVal
// 跳转到搜索页面
wx.navigateTo({
url: '/pages/search/search?keyword=' + keyword,
})
}

样式代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* pages/index/index.wxss */
.weui-search-bar {
position: relative;
padding: 8px 10px;
display: -webkit-box;
display: -webkit-flex;
display: flex;
box-sizing: border-box;
background-color: #EFEFF4;
border-top: 1rpx solid #D7D6DC;
border-bottom: 1rpx solid #D7D6DC;

}

.weui-icon-search {
margin-right: 8px;
font-size: inherit;
}

.weui-icon-search_in-box {
position: absolute;
left: 10px;
top: 7px;
}

.weui-search-bar__text {
display: inline-block;
font-size: 14px;
vertical-align: middle;
}

.weui-search-bar__form {
position: relative;
-webkit-box-flex: 1;
-webkit-flex: auto;
flex: auto;
border-radius: 5px;
background: #FFFFFF;
border: 1rpx solid #E6E6EA;
}

.weui-search-bar__box {
position: relative;
padding-left: 30px;
padding-right: 30px;
width: 100%;
box-sizing: border-box;
z-index: 1;
}

.weui-search-bar__input {
height: 28px;
line-height: 28px;
font-size: 14px;
}

.weui-icon-clear {
position: absolute;
top: 0;
right: 0;
padding: 7px 8px;
font-size: 0;
}

.weui-search-bar__label {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
border-radius: 3px;
text-align: center;
color: #9B9B9B;
background: #FFFFFF;
line-height: 28px;
}

.weui-search-bar__cancel-btn {
margin-left: 10px;
line-height: 28px;
color: #09BB07;
white-space: nowrap;
font-size: 30rpx;
}

需求2:轮播图的实现

在实现轮播图的时候需要注意俩个细节:

  • 点击轮播图后需要去往详情页面
  • 轮播图需要显示与之对应的新闻标题

页面结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 轮播图 -->
<view>
<swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}"
duration="{{duration}}">
<block wx:for="{{hotNews}}" wx:key="index">
<!-- 点击轮播图跳转到详情页面 -->
<navigator url="/pages/detail/detail?id={{item.id}}">
<swiper-item>
<!-- 使用image组件展示图片 -->
<image src="{{item.pic}}" style="height:150px;" />
<!-- 轮播图对应的新闻标题 -->
<view class="title">{{item.title}}</view>
</swiper-item>
</navigator>
</block>
</swiper>
</view>

逻辑部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// pages/index/index.js
import {
getSearchResult,
getHotNews,
getNewsList
} from "../../models/news_model"

Page({

/**
* 页面的初始数据
*/
data: {
indicatorDots: false,
vertical: false,
autoplay: true,
interval: 2000,
duration: 500,
// 存放热点新闻的,swiper的数组
hotNews: []
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取热点新闻
getHotNews().then(res => {
// 将数据保存
if (res.status === 0) {
this.setData({
hotNews: res.data
})
}
})
},
})

样式部分:

1
2
3
4
5
6
7
8
9
10
11
/* pages/index/index.wxss */
/* 轮播图标题的样式 */
.title {
position: absolute;
bottom: 0;
left: 0;
color: #FFFFFF;
font-size: 12px;
background-color: rgba(0,0,0,0.3);
width: 100%;
}

需求3:实现新闻列表,及加载更多

页面结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 新闻列表 -->
<view style="margin-top: 8rpx;">
<!-- 每个view就是一个记录 -->
<view wx:for="{{newsList}}" wx:key="index" style="margin-left: 5rpx;display:flex;" bindtap="goto" data-id="{{item.id}}">
<!-- 新闻图片 -->
<view style="margin-right:10rpx;">
<image src="{{item.pic}}" style="width: 330rpx;height:220rpx;" />
</view>
<!-- 新闻的文字相关 -->
<!-- 导入wxs模块处理过长的字符串内容 -->
<wxs src="/utils/common.wxs" module="tool"></wxs>
<view style="margin-right:5rpx;font-size: 13px;">
<view>{{tool.jq(item.title,11)}}</view>
<view style="color:#999d9c">{{tool.jq(item.desn,37)}}</view>
<view style="color:#d1c7b7">{{item.author}}</view>
</view>
</view>
</view>

<!-- 加载完毕的提示 -->
<view style="text-align:center;margin-top:20rpx;margin-bottom:20rpx;color:grey;">{{over}}</view>

逻辑部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// pages/index/index.js
// 导入模型中的方法(根据需要)
import {
getSearchResult,
getHotNews,
getNewsList
} from "../../models/news_model"
Page({

/**
* 页面的初始数据
*/
data: {
indicatorDots: false,
vertical: false,
autoplay: true,
interval: 2000,
duration: 500,
// 存放热点新闻的,swiper的数组
hotNews: [],
// 加载页码
page: 1,
// 新闻列表的默认数据
newsList: [],
// 是否加载完成
finished: false,
// 加载完毕的提示
over: ""
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取热点新闻
getHotNews().then(res => {
// 将数据保存
if (res.status === 0) {
this.setData({
hotNews: res.data
})
}
})
// 初次加载新闻,主动加载
this.loadNews()
},

/**
* 加载新闻,结合初次加载(主动)及后续的被动加载(加载更多)
*/
loadNews: function () {
if (!this.data.finished) {
// 表示有数据要请求
getNewsList(this.data.page).then(res => {
if (this.data.page < res.data.last_page) {
// 后面还可以接着请求,页码+1,保存数据
this.setData({
page: this.data.page + 1,
newsList: [...this.data.newsList, ...res.data.data]
})
} else {
this.setData({
finished: true,
newsList: [...this.data.newsList, ...res.data.data],
over: "------- 别扒拉了 -------"
})
}
})
}
},

/**
* 实现页面的跳转,眺详情
*/
goto: function (e) {
// 获取新闻的id
let id = e.currentTarget.dataset.id
// 跳转
wx.navigateTo({
url: '/pages/detail/detail?id=' + id,
})
},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
// 加载更多
this.loadNews()
}
})

需求4:新闻详情展示和点击量自增

修改config/url.js关于地址的配置:

1
2
3
4
5
6
// 新闻详情的地址
news_detail: prefix + "news/",
// 用户登录地址
login: prefix + "login",
// 记录新闻的点击量
addRecord: prefix + "addRecord"

修改文件models/news_model.js增加模型方法:

1
2
3
4
5
6
7
8
9
10
11
12
// 获取新闻详情的方法
export const getNewsDetail = (id) => {
return http({
// 传参请注意,采用的restful形式
url: url.news_detail + id
})
}

// 实现用户登录获取用户的唯一标识符openid


// 增加点击量

了解openid:是用户身份的标识符,为了方便开发者识别用户的。其是一个字符串,包含了数字和字母。

openid有一个特征,同一个用户在使用很多个应用的时候,每个应用获取到这个用户的openid值是不一样的。例如,张三在使用App1,App1程序获取到其openid可能是123456789,App2获取到张三的openid可能是234567890。

openid的获取需要依赖于小程序控制台获取到的俩个信息:appid、appsecret。appid是存在小程序端的,用户可见。但是appsecret在实际开发的时候是存储在后端的(不是小程序端)。

openid的获取:

  • a. 调用wx.login的api获得code
  • b. 将code与appid及appsecret(后端)三个参数一起去腾讯换openid【由后端去换】
  • 【后端要做】c. 在换取到openid之后,去数据库中判断该openid是否存在(判断当前这个用户是新用户还是老用户),如果在则直接登录,如果不在后端会往数据库中建立一个用户账号。最终返回给前端openid及用户在项目中标识符(一般是用户名或用户id号)

a. 新建配置文件,存储公共的配置项

1
2
3
4
5
6
7
8
9
// config/config.js
// 存放通用的配置
const appid = "wxf8173895cc084401"
const appsecret = "826fd887304fbaffcf8e8b9ee3f5a73d"

export default {
appid,
appsecret
}

b. 在模型中新加对应的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 作用:根据业务需要,进行数据请求和处理,返回最终需要的数据
// 本质上来说,就是一堆方法的集合

// 导入地址和请求方法(注意是否以“/”开头)
import url from "../config/url"
import http from "../services/http"
// 导入配置文件
import cfg from "../config/config"


// 实现用户登录获取用户的唯一标识符openid
function getCode() {
return new Promise((resolve, reject) => {
wx.login({
success: res => resolve(res.code)
})
})
}
// 获取openid,要使用async/await务必要开启增强编译,否则报错
export const getOpenId = async () => {
// 获取缓存数据
let openid = wx.getStorageSync('openid')
if (!openid) {
let code = await getCode();
// 请求后端,拿code+appid+appsecret去交换openid
openid = await http({
url: url.login,
data: {
code,
appid: cfg.appid,
appsecret: cfg.appsecret
},
method: "POST"
})
// 存储到缓存中
wx.setStorageSync('openid', openid)
}
return openid
}

// 增加点击量
export const addRecord = (user_id, article_id) => {
return http({
url: url.addRecord,
data: {
user_id,
article_id
},
method: "POST"
})
}

c. 在页面的js文件中添加对应的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// pages/detail/detail.js
// 导入模型中的方法
import {
getNewsDetail,
getOpenId,
addRecord
} from "../../models/news_model"
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
// 获取新闻的id
let id = options.id
// 主动依据新闻id获取新闻的内容
getNewsDetail(id).then(res => {
this.setData({
newsInfo: res.data
})
})
// 获取openid
let openid = await getOpenId()
// 记录访问
addRecord(openid.data.id,id).then(res => {
console.log(res);
})
}
})

2.3、我的页面

  • tabBar

  • 获取用户的头像和用户名,并且展示在页面上(授权)

知识点:微信小程序授权

预期实现目标:如果用户已经授权了,则显示用户的头像,如果没有授权,给用户展示授权按钮。

授权接口文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html

授权注意点

获取用户信息。页面产生点击事件(例如 buttonbindtap 的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回 userInfo。该接口用于替换 wx.getUserInfo

  • 需要通过给用户一个按钮,来进行授权
  • 在获取到用户的信息之后,我们也建议做一下缓存操作

实现:wxml代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--pages/center/center.wxml-->
<!-- 在此页面展示用户的基本信息(用户已经登录)/没有登录则展示登录按钮 -->
<!-- 类似于小程序刚初始化好的index页面 -->

<!--
button组件有一个属性:open-type,加上这个属性后,其支持开放功能,用户登录就属于开放功能
接口:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html

-->

<view wx:if="{{!isLogin}}">
<button type="primary" open-type="getUserInfo" bindtap="getInfo">登录</button>
</view>

<view wx:if="{{isLogin}}">
<image src="{{user_info.avatarUrl}}" style="width: 132px;height:132px;" />
<view>昵称:{{user_info.nickName}}</view>
<view>性别:{{user_info.gender === 1 ? "男" : "女"}}</view>
<view>国家:{{user_info.country}}</view>
</view>

逻辑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// pages/center/center.js
Page({

/**
* 页面的初始数据
*/
data: {
isLogin: false
},

/**
* 获取用户信息
*/
getInfo: function () {
var _this = this
wx.getUserProfile({
desc: '获取用户信息以登录',
success: res => {
// 做一个缓存数据的处理
// 将返回的用户信息转成对象形式
let obj = JSON.parse(res.rawData)
wx.setStorageSync('user_info', obj)
_this.setData({
isLogin: true,
user_info: obj
})
}
})
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取缓存中的用户信息
let user_info = wx.getStorageSync('user_info');
if (user_info) {
// 如果有则使用
this.setData({
user_info,
isLogin: true
})
}
// console.log(user_info);
}
})

小程序在2019年发布了对于登录授权的规范建议,要求不再强制用户一打开小程序就要求登录授权,而是通过按钮点击触发授权,需要用户主动操作。文档见:https://developers.weixin.qq.com/community/operate/doc/000640bb8441b82900e89f48351401

3、作业

4、小程序的发布

a. 右键小程序开发者工具界面右上角的上传按钮,将代码上传

b. 填写小程序代码的版本及备注信息

c. 完成代码上传

如果小程序的帐号并不是我们自己管理的,则我们的工作到此就为止了,后续由管理员去进行(但是你得通知到)

d. 使用小程序管理帐号进入开发者控制台提交小程序信息,待腾讯审核通过后即可发布

审核需要时效。后续操作,按照页面的引导操作即可。

十六、uni-app

网址:https://uniapp.dcloud.io/

uni-app是一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。

image-20211015153116722

1、uniapp 发展史

读 you ni,是统一的意思,DCloud于2012年开始研发小程序技术,优化 webview 的功能和性能,并推出了 Hbuilder 开发工具

  • 2015年,Dcloud 正式商用了自己的小程序,产品名为“流应用”
  • DCloud持续在业内普及小程序理念,推进各大流量业务
  • 开发者面对如此多的私有标准,造成混乱的局面
  • 开发一个免费开源的框架,通过这个框架为开发者抹平各平台差异

![](https://storage.lynnn.cn/assets/markdown/91147/pictures/2021/06/60822f2bc36e09b79992b4b8eb9c322b8c7b655b.png?sign=3bb72e4c9dd28c23df47f8029da531a8&t=60c24b54

2、uniapp 优缺点

  • 优点:
    1. uni-app 对前端开发人员比较友好,学习成本比较低,基于 vue.js
    2. uni-app 使用vue的语法+小程序的标签和API。
    3. 使用 hbx 进行开发,hbx 对于 vue 语法等支持可以说是比较完备
    4. 拓展能力强,封装了H5+,支持 nvue,也支持原生Android,ios开发
  • 缺点:
    1. 问世的时间还比较短,有很多地方还不是完善,坑很多
    2. 对于使用中的一些 bug 及问题,官方回应的不是很及时

3、uniapp 开发规范

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了开发规范

4、基本使用

4.1、创建小程序项目

两种创建方式:

如果使用uniapp开发小程序,后续需要使用文档,请去uniapp官网文档去复制,不要再去各自小程序的文档中复制了。

使用hbuilderX 编辑工具创建项目:

image-20211015154138752

4.2、目录结构

image-20211015154523076

4.3、配置选项

文档地址:https://uniapp.dcloud.io/collocation/pages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
"pages": [{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle":"custom"
}
}, {
"path": "pages/fen/fen",
"style": {
"navigationBarTitleText": "分类",
"navigationStyle":"custom"
}
}, {
"path": "pages/car/car",
"style": {
"navigationBarTitleText": "购物车",
"navigationStyle":"custom"
}
}, {
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle":"custom"
}
}]
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/img/index1.png",
"selectedIconPath": "static/img/index2.png",
"text": "首页"
}, {
"pagePath": "pages/fen/fen",
"iconPath": "static/img/fen1.png",
"selectedIconPath": "static/img/fen2.png",
"text": "分类"
},{
"pagePath": "pages/car/car",
"iconPath": "static/img/car1.png",
"selectedIconPath": "static/img/car2.png",
"text": "购物车"
},{
"pagePath": "pages/mine/mine",
"iconPath": "static/img/mine1.png",
"selectedIconPath": "static/img/mine2.png",
"text": "我的"
}]
}

4.4、组件

文档地址:https://uniapp.dcloud.io/component/README

4.5、页面布局

同微信小程序布局一样,使用的单位也是rpx ,可以以iphone6 为视觉稿。

4.6、样式

在 App.vue 中的样式为全局样式,作用于每一个页面。

在 pages 目录下 的 vue 文件中定义的样式为局部样式。

局部样式只作用在对应的页面,并会覆盖 App.vue 中相同的选择器。

1.外部样式导入

在static 静态目录下可以导入外联样式表,使用@import 后跟需要导入的外联样式表的相对路径,用;表示语句结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
/* 导入外链样式 */
@import url("static/css/common.css");
/* 全局css样式 */
page{
width: 750rpx;
font-size:24rpx
}

/* 解决头条小程序组件内引入字体不生效的问题 */
/* #ifdef MP-TOUTIAO */
@font-face {
font-family: uniicons;
src: url('/static/uni.ttf');
}
/* #endif */
</style>

注意:如果在app.vue 中导入,则此外联样式为全局样式

2. style和class 样式

  • style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度

    1
    2
    3
    <view :style='{color:color}'>

    </view>
  • class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔

4.7 选择器

注意:在 uni-app 中不能使用 * 选择器,page 相当于 body 节点,支持的选择器有:

1
2
3
4
5
6
7
选择器	样例	样例描述
.class .intro 选择所有拥有 class="intro" 的组件
#id #firstname 选择拥有 id="firstname" 的组件
element view 选择所有 view 组件
element, element view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件
::after view::after 在 view 组件后边插入内容,仅 vue 页面生效
::before view::before 在 view 组件前边插入内容,仅 vue 页面生效

4.8 页面栈

  • uni.navigateTo({ url: ‘test?id=1&name=uniapp’ });
  • uni.redirectTo({ url: ‘test?id=1’ });
  • uni.reLaunch({ url: ‘test?id=1’ });
  • uni.switchTab({ url: ‘/pages/index/index’ });
  • uni.navigateBack({ delta: 2 });

4.9、网络请求

文档地址:https://uniapp.dcloud.io/api/request/request

4.10、生命周期

uni-app 完整支持 Vue 实例的生命周期,还新增应用生命周期及页面生命周期,即uniapp 对vue 和小程序的生命周期都支持。

  • created

  • mounted

  • updated

  • destroyed

  • onload

  • onshow

  • onReady

  • onHide

  • onUnload

4.11、globalData

Vue 之前是没有这类概念的,但 uni-app 引入了globalData概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// app.vue 定义全局变量globalData
<script>
export default {
// 整个uniapp 项目的生命周期
onLaunch: function() {
console.log('App Launch');
},
onShow: function() {
console.log('App Show');
},
onHide: function() {
console.log('App Hide');
},
globalData:{
// 这是全局变量
nameArr:['迪迦','盖亚','塞罗','泰罗']
}
};
</script>

// 获取globalData 全局变量
<script>
let app = getApp();
</script>

2、跨端和条件编译

2.1 条件编译的必要性

uni-app 已将常用的组件、JS API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足,但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况,大量写 if else,会造成代码执行性能低下和管理混乱,编译到不同的工程后二次修改,会让后续升级变的很麻烦

  • 编译期判断 编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码

    image-20211015142820413

  • 条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台

    语法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾

    ​ #ifdef:if defined 仅在某平台存在

    ​ #ifndef:if not defined 除了某平台均存在

    ​ %PLATFORM%:平台名称

2.2 条件编译类型

  • 页面的条件编译

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- #ifdef H5 -->
    <view class="">
    h5端
    </view>
    <!-- #endif -->

    <!-- #ifdef MP-WEIXIN -->
    <view class="">
    微信端
    </view>
    <!-- #endif -->
  • 编译样式的条件

    1
    2
    3
    4
    5
    6
    /* #ifdef H5 */
    .on{
    font-size: 50rpx;
    background: purple;
    }
    /* #endif */
  • pages.json的条件编译

    1
    2
    3
    4
    5
    6
    7
    // #ifdef H5
    ,{
    "path" : "pages/detail/detail",
    "style" : {}
    }
    // #endif

  • static目录的条件编译

    image-20211015144459111

3、uniapp 插件使用

在项目中安装less 插件,<style lang='less'></style>

uni-search-bar 插件

4.1、项目工作环境初始化

uniapp的两种使用方式推荐使用基于HBuilder X的形式,因此在操作之前请先做好其安装工作。

a. 使用HBuilder x新建一个项目

b. 填入项目的基本信息,项目名称根据自身需要自行决定

c. 后续我们将使用HBuilder X进行编码、开发,而小程序开发者工具作为小程序的预览工作;为了将让HBuilder X能够将编译好的小程序代码准确的传给小程序开发者工具,此处我们需要设置小程序工具,以允许小程序开发者工具被其他程序调用:

d. 在HBuilder X中设置运行,以体验执行效果

启动时可能会弹出窗口询问小程序开发者工具的安装位置,如果弹出该窗口,请根据自己电脑上安装的位置选中安装目录,随后点击确定

最终设置操作完毕,后续我们每次在HBuilder X中修改的代码一经保存,微信小程序开发者工具就会实时更新显示效果。

另外不要忘了给我们的最终输出程序类型做配置:

如果需要其他类型的小程序输出,操作也是如此。

4.2、重构swiper案例

此时已经不再是使用原生小程序语法,因此请务必:参考uni-app官网文档,结合vue框架语法

a. 新建页面,右键pages,选择新建页面

b. 去pages.json文件中设置项目的启动页(将启动页的内容移动第一个元素)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
// 将重构的案例放到最前面
"path" : "pages/swiper/swiper",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}

},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}

参考代码

文档地址:https://uniapp.dcloud.io/component/swiper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template>
<!-- 在uniapp中,template标签就相当于之前的wxml文件 -->
<view>
<swiper class="swiper" :indicator-dots="indicatorDots" :autoplay="autoplay" :interval="interval" :duration="duration">
<!-- 循环输出获取到的数据 -->
<swiper-item v-for="(item,index) in swiper" :key="item.id">
<navigator :url="'/pages/detail/detail?id=' + item.id">
<image :src="item.pic" />
</navigator>
</swiper-item>
</swiper>
</view>
</template>

<script>
// 在uniapp中,script段落就相当于之前的js文件
export default {
data() {
return {
// 是否显示轮播图的圆点
indicatorDots: true,
// 是否自动播放
autoplay: true,
// 一张图要显示的持续时间(单位是毫秒)
interval: 2000,
// 切换图片需要的时间(单位是毫秒)
duration: 500,
// swiper列表的数据
swiper: []
}
},
// vue的生命周期:created
created() {
uni.request({
url: 'https://mpapi.iynn.cn/api/v1/news/hot', //仅为示例,并非真实接口地址。
success: (res) => {
// console.log(res.data);
if (res.data.status == 0) {
let arr = [];
res.data.data.forEach(el => {
arr.push({
id: el.id,
pic: el.pic
})
})
// 将组装好的数据赋值给data中的swiper(vue的赋值,不是react的setState,也不是小程序的setData)
this.swiper = arr;
}
}
});
},
methods: {

}
}
</script>

<style>
/* 在uniapp中,style标签就相当于之前的wxss文件 */
</style>

5、uniapp下的小程序发布

随后依据弹窗的窗口提示填写需要的信息,按照步骤进行即可。

6、uniapp下的app打包

1.首先在mainfest.json 中配置app 应用对应的配置项。

image-20211125174258910

2.点击hbuilderX 发行,选择 原生App-云打包