小程序开发

222 阅读9分钟

小程序

一、目录结构

pages

index

...

utils

utile.js

app.json

{
  //所有页面都在pages注册
  //在pages新建页面, 这里会自动注册
  //在这里注册, 也会自动新建页面
  "pages":[
    "pages/index/index",
    "pages/favor/favor",
    "pages/order/order"
  ],
  //对窗口进行配置
  //
  "window":{
    "backgroundTextStyle":"light",
    //导航颜色
    "navigationBarBackgroundColor": "#ff8189",
    //名字
    "navigationBarTitleText": "Weixin",
    //名字的颜色
    "navigationBarTextStyle":"black"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

app.js

app.wxss

三个配置json文件

二、案例

1. 数据绑定

对于页面来说, wxml文件写文本, wxss文件写样式, 如果需要绑定数据, 在js文件中的page的data中写数据, 在wxml用{{}}语法绑定, view块级元素、text行内级元素

2. 列表数据

在js的page的data中写一个数组: movies: ["少年派", "大话西游", "星际穿越", "独行月球"]

在wxml文件中展示:

<view class="movies">
  <block wx:for="{{movies}}" wx:key="*this">
  	<view>
  		{{ item }}-{{ index }}
  	</view>
  </block>
</view>

3.计数器

wxml:

<view class="counter">
	<view class="count">当前计数: {{ counter }}</view>
  <button bindtap="increment">+1</button>
  <button bindtap="decrement">-1</button>
</view>

js:

Page({
  data: {
    counter: 0
  },
  //监听的事件方法
  increment() {
    //修改data数据, 不会引起页面刷新, 不会自动检测数据变更重新渲染数据, 与vue不同
  	//this.data.counter += 1
    //修改了data使页面重新渲染: this.setData()
    this.setData({
      counter: this.data.counter + 1 //+1后赋值
    })
	},
  decrement() {
    this.setData({
      counter: this.data.counter - 1 //-1后赋值
    })    
  }
})

三、小程序的双线程

小程序分为视图层和逻辑层,视图层的相关任务全都在WebView里执行。

一个小程序存在多个界面,所以视图层存在多个WebView线程。

而逻辑层采用JsCore线程运行JS脚本

他们之间通过系统层的WeixinJsBridge进行通信,也就是逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。

小程序的渲染层和逻辑层分别由2个线程管理:

  • 渲染层: 界面渲染相关的任务全都在WebView里执行。一个小程序存在多个界面,所以渲染层存在多个WebView线程。

  • 逻辑层:采用JsCore线程运行JS脚本。 视图层和逻辑层通过系统层的WeixinJsBridge进行通信:逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。

页面渲染的具体流程是:

在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的DOM树上,渲染出正确的UI界面。

四、全局APP配置文件

在pages中的页面的json文件, "enablePullDownRefresh": true 下拉刷新

tabBar配置:

//app.json
"tabBar": {
  //选中的颜色
  "selectedColor": "#ff8189"
  //tabbar列表
  "list": [
    {
      "text": "首页",
      "pagePath": "pages/index/index",
      "iconPath": "assets/tabbar/home.png",
      "selectedIconPath": "assets/tabbar/home_active.png"
    },
    ...
  ]
}

页面page的配置:

//pages/index/index.json
{
  "usingComponents": {},
  "navigationBarTitleText": "我的首页",
  "navnavigationBarBackgroundColor": "#f00",
  //设置下拉刷新
  "enablePullDownRefresh": true,
  //设置滚动到底部(距离底部100时)
  "onReachBottomDistance": 100
}

监听下拉刷新、上拉上拉加载:

//index.js
Page({
  data: {
    avatarURL: "",
    listCount: 30
  },
  //监听下拉刷新
  onPullDownRefresh() {
    setTimeout(() => {
      //停止下拉刷新
			this.setData({ listCount: 30 })
      //下拉之后listCount恢复到显示30条
      wx.stopPullDownRefresh({
        success: (res) => {
          console.log("成功停止了下拉刷新", res)
        },
        fail: (err) => {
          console.log("失败停止了下拉刷新", err)
        }
      })
    },1000)
  },
  //监听页面滚动到底部
  onReachBottom() {
    this.setdata({
      listCount: this.listCounter + 30
      //滚到底部, listCounter数据加30
    })
  }
})

五、app.js文件

1. 判断小程序进入的场景

onLaunch 全局执行一次

onShow 隐藏后再打开重新执行

onHide 隐藏时执行

App({
  onLaunch(options) {
    
  },
  onShow(options) {
    
  },
  onHide(options) {
    
  }
})

3. 监听生命周期函数

App({
  globalData: {
    token: "",
    userInfo: {
      name: ""
    }
  },
  onLaunch(options) {
    // 1.先从本地获取token/userInfo
    const token = wx.getStorageSync("token")
    const userInfo = wx.getStorageSync("userInfo")
    // 2.进行登录操作(判断逻辑)
    // 3.将登陆成功的数据保存到storage
    if (!token || !userInfo) {
    	wx.setStorageSync("token", "mytoken")
    	wx.setStorageSync("userInfo", { name: "abc" })      
    }
    // 4.将获取到的数据保存到globalData中
    this.globalData.token = token
    this.globalData.userInfo = userInfo
    // 5.发送网络请求, 拿到数据保存到globalData
  },
  onShow(options) {
    
  },
  onHide(options) {
    
  }
})

2. App()实例只有一个, 并且是共享的

App({
  //存放共享数据
  globalData: {
    token: "",
    userInfo: {
      name: "abc"
    }
  },
})
//index.js
Page({
  data: {
    userInfo = {}
  },
  onLoad() {
    //获取共享的数据: App实例中的数据
    //1. 获取app实例对象
    const app = getApp()
    //2. 从app实例对象获取数据
    const token = app.globalData.token
    const userInfo = app.globalData.userInfo
    //3. 拿到token发送网络请求
    //4. 将数据展示到界面上面
    //this.data.userInfo = userInfo 不能改变
    this.setData({ userInfo })//对象增强语法
  }
})
//index.wxml
<view class="user">name: {{ userInfo.name }}</view>

以上数据不是响应式的, 所以共享的数据通常是一些固定的数据

六、网络请求

//pages/index/index.js
Page({
  data: {
    recommends: []
  },
  //发送网络请求
  onLoad() {
    wx.request({
      url: "",
      success: (res) => {
        console.log(this)
        //拿到onLoad的this
        console.log(res)
        //拿到res
        const recommends = res.
        //把res的数据给recommends
        this.setData({ recommends })//对象增强语法
      }
    })
  }
})

七、常见组件

1. Text文本组件(行内)

类似span标签

常见属性:

user-select 可选属性

decode 解码属性

<text user-select>Hello World</text>
<text>{{ message }}</text>
<text decode>&gt;</text>

2. Button按钮组件(块)

常见属性:

size 大小

type 样式

plain 镂空

disabled 禁止交互

loading 加载效果

hover 按钮按上去(样式自己设置)

<button size="mini">size属性</button>
<button type="primary">type属性</button>
<button plain disabled loading>其他属性</button>
<button hover-class="active">hover效果</button>

open-type 属性: 用于获取特殊权限, 绑定特殊事件

contact 打开会话

getUserInfo 获取用户信息

getPhoneNumber 获取手机号码

<button open-type="contact">
  打开会话
</button>
<button bind:tap="getUserInfo">
  获取用户信息
</button>
<button open-type="getPhoneNumber" bindgetPhoneNumber="getPhoneNumber">
  获取手机号码
</button>
//pages/index/index.js
Page({
  getUserInfo(event) {
    wx.getUserProfile({
      desc: 'desc',
    }).then(res => {
      console.loh(res)
    })
  },
  getPhoneNumber(event) {
    
  }
})

3. View视图组件(块)

类似div标签

4. Image图片组件(块)

常见属性:

src

mode 只显示图片占默认宽高的一部分

lazy-load 懒加载

<image mode="top right">显示图片右上角</image>
<image mode="aspectFill">宽度固定 高度默认裁剪</image>
<image mode="aspectFit">适配图片宽高</image>
<image mode="widthFix">宽度固定 高度自适配</image>

选择本地图片:

<button bindtap="onChooseImage">选择图片</button>
<image src="{{chooseImageUrl}}"></image>
//pages/index/index.js
Page({
  data: {
    chooseImageUrl: ""
  }
	onChooseImage() {
    wx.chooseMedia({
      mediaType: "image"
    }).then(res => {
      const imagePath = res.tempFiles[0].tempFilePath
      this.setData({ chooseImageUrl: imagePath })
    })
  }
})

5. scroll-view滚动组件

局部滚动

scroll-view要有固定高度

当内容高度大于scroll-view的高度时, 就会滚动

<scroll-view class="container" scroll-y>上下滚动</scroll-view>
<scroll-view scroll-x>左右滚动</scroll-view>

scroll-view滚动监听

bindscroll 发生了滚动

bindscrolltoupper 滚动到顶部/左边

bindscrolltolower 滚动到底部/右边

<scroll-view
	class="container"
  scroll-x
  enable-flex
  bindscrolltoupper="onScrollToUpper"
  bindscrolltolower="onScrollToLower"
  bindscroll="onScroll"
>
  
</scroll-view>
//pages/index/index.js
Page({
  onScroll(event) {
    console.log("发生了滚动", event)
    //event含有滚动了多少详细数据
  },
	onScrollToUpper() {
    console.log("滚动到最顶部/左边")
  },
  onScrollToLower {
  	console.loh("滚动到最底部/右边")
	}
})

6. 组件的共同属性

id

class

style

hidden

data- * 自定义属性

bind */catch * 组件的事件

7. input的双向绑定

<input type="text" model:value="{{message}}"/>
//pages/index/index.js
Page({
	data: {
    message: "abc"
  }
})

八、wxss

1. 在app.wxss编写的样式对所有pages都生效, 相当于全局样式

2. wxss支持的选择器:

.class

#id

element

::after

::before

3. 选择器的优先级

!important

style="" 1000

#id 100

.class 10

element 1

4. wxss自适应单位-rpx

rpx: 根据屏幕宽度进行自适应, 规定屏幕宽为750rpx, 如在iphone6上, 屏幕宽度为375px, 共有750个物理像素, 则750rpx = 375px = 750物理像素

对iphone6的设计稿: 1rpx = 0.5px

对iphone5的设计稿: 1rpx = 0.42px

对iphone6 Plus的设计稿: 1rpx = 0.552px

九、wxml

1. mustache语法

2. 逻辑条件判断-指令

wx:if - wx:elif - wx:else

3. hidden属性和if的区别

写上hidden属性默认值为true 但是组件树中元素还存在 所以是隐藏

用if可以直接把元素从组件树中移除

<view hidden="{{isHidden}}">123</view>
<view wx:if="{{!isHidden}}">123</view>

isHidden: false, this.setData({ isHidden: !this.data.isHidden )}

4. 列表渲染 - wx:for

<view class="books">
	<block wx:for="{{books}}" wx:key="id">
    <!-- item: 每项内容, index: 每项索引 -->
  	{{item.name}}-{{item.price}}
  </block>
</view>

books为对象数组数据, name、price为books属性

除了遍历data中的数组, 还可以遍历数字

对象类型key绑定id, 其他绑定*this

for放在block标签上, block不是组件, 只是一个包装元素, 不会在页面中做任何渲染, 只接受控制属性

给item重命名: wx:for-item="book" , 把item改成book

5. WXS

WXS是小程序的一套脚本语言, 结合wxml, 可以构建出页面的结构

WSX不依赖于运行时的基础库版本, 可以在所有版本的小程序中运行

有两种写法:

写在<wxs>标签中

写在以.wxs结尾的文件中

<wxs module="format">
  // 编写纯JS代码 不要写es6语法
  function formatPrice(price) {
  	return "$" + price
  }
  // 必须导出后, 才能被其他地方调用: 必须使用CommonJS导出
  module.exports = {
  	formatPrice: formatPrice // 不能用对象增强语法
  }
</wxs>
<wxs module="format" src="/">引入dormat.wxs文件</wxs>

十、组件化开发

事件的监听

1. 常见事件

bindtouchstart 手指触摸动作开始

bindtouchmove 手指触摸后移动

bindtouchcancel 手指触摸动作被打断, 如来电提醒, 弹窗

bindtouched 手指触摸动作结束

bindtap 点击

bindlongpress 长按后离开

2. 事件对象event

当某个事件触发时, 会产生一个事件对象, 并且这个对象会被传入到回调函数中

常见属性: type、timeStamp(时间戳)、target(触发事件的元素)、currentTarget(处理事件的元素)

自定义属性data-*可以在event中获取: event.currentTarget.dataset.

组件化

1. 组件化的使用

全局组件: 最外层文件夹components

局部组件: pages文件夹里创建cpns文件夹

自定义组件的引入: 在要引入组件的json文件配置 "usingComponents": {"my-cpn": "路径"} 然后可以使用组件 <my-cpn/>

自定义组件也是由4个文件组成, 常见的一个组件命名 my-cpn

2. 步骤:

在json文件中进行自定义组件声明(将component字段设为true 可这一组文件设为自定义组件)

在wxml中编写组件的模板, wxss中编写样式

在js文件中, 可以定义数据或组件内部的相关逻辑

3. 组件的通信

页面可以向组件传递内容: 数据(properties) 样式(externalClasses) 标签(slot)

//组件的js文件
Component({
  properties: {
    title: {
      type: String,
      value: "默认标题"
    },
    content: {
      type: String,
      value: "默认内容"     
    }
  },
  externalClass: ["info"],
  methods: {
  }
})
//使用组件的wxml
<my-cpn title="abc" content="cba"></my-cpn>
//组件的wxml文件
<view>
	<view></view>
</view>

4. 插槽

在components中创建插槽文件夹my-slot, 在该文件夹的wxml文件中预留插槽<slot></slot>, view标签给my-slot的class, 在index中构建<my-slot>编写插槽的内容</my-slot>即可显示以上的view, 并且可以向插槽中编写内容

小程序的slot不支持默认值(技术难点): 创建view使用:empty伪类+兄弟选择器使插槽有插入则隐藏view

十一、API

1. 网络请求: wx.request(Object object)

url

data: 请求参数

method: 请求的方式

success: 成功时的回调

fail: 失败时的回调

Page({
	onLoad() {
	wx.request({
		url: "",
		success: (res) => {
    },
    fail: (err) => {
    }
	})
	}
})

2. 设备信息和位置信息: wx.getSystemInfo(Object object)

需要在app.json中授权

"permission": {
	"scope.userLocation": {
		"desc": "获取你的位置信息"
	}
}

3. 本地存储storage

4. 跳转和页面返回