微信小程序原生开发入门指南

1、初识微信小程序

  • 小程序为什么存在?为企业或个人提供便利的用户连接工具;它可以在一定程度上可以替代掉部分手机APP的作用(用完即走)。
  • 产品设计标准:小而美、开发周期较短。
  • 张小龙亲自“引爆”微信小程序(附演讲全文)

2、微信小程序开发前准备

  • 翻阅微信小程序官方文档
  • 下载、安装“微信者开发工具”
  • 注册一个小程序账号(管理后台)

3、小程序管理后台的基本操作

(一)版本管理

  • 小程序认证:填写基本信息、注意选择行业类目、备案付费300元。
  • 小程序有三个版本:开发版、审核版、线上版(默认代码体积不能超过2M)。
  • 小程序项目中用到的静态资源,可以放到CDN上,以减小代码体积。

(二)成员管理

  • 管理员(1人),是注册账号的微信用户。
  • 项目成员(15人),可以登录小程序管理后台,开发者必须是项目成员。
  • 体验成员(15人),只有体验的权限,没有开发的权限。

(三)开发管理

  • AppID,相当是小程序的身份证号码,创建项目、调试项目、小程序之间的跳转都要用到,还有比如支付等也要用到。
  • AppSecret,小程序密钥,一般要给后端,在登录、支付等功能中都要用到。
  • Request 地址,就是api 的 baseURL,本地开发时可以关闭https验证,上线时一定要小程序管理后台中添加上这个地址,并且要求https协议的。

4、微信开发者工具的基本使用

  • 如何创建新项目?
  • 如何导入旧项目?
  • 调试项目(真机调试、微信开发者工具调试)
  • 如何上传代码至开发版?
  • 关闭“开发环境下校检 https”的限制
  • 注意小程序api 版本库和微信版本之间兼容性问题。

5、认识四种文件

  • .wxml,类似 HTML 的角色。
  • .wxss,具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。
  • .js,编写脚本与用户交互,响应用户的点击、获取用户的位置等等。
  • .json,是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。

6、自带配置风格的小程序

微信小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

{
  "pages": [
    "pages/index/index",
    "pages/books/books"    
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "好程序员",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    "color": "#aaaaaa",
    "selectedColor": "#ff0000",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "福利",
        "iconPath": "/assets/tabbar/index.png",
        "selectedIconPath": "/assets/tabbar/index-on.png"
      },
      {
        "pagePath": "pages/books/books",
        "text": "书城",
        "iconPath": "/assets/tabbar/book.png",
        "selectedIconPath": "/assets/tabbar/book-on.png"
      }
    ]
  }
}

7、App类与应用级别的生命周期

注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

App({
  // 整个应用程序的入口
  onLaunch() {
    wx.login({
      success: res => console.log('登录', res.code)
    })
  },
  globalData: {
    userInfo: null,
  }
})

8、Page类与页面级别的生命周期

Page({
  data: { },
  onLoad: function (options) { },
  onReady: function () {},
  onShow: function () { },
  onHide: function () {},
  onUnload: function () {},
  onPullDownRefresh: function () { },
  onReachBottom: function () { },
  onShareAppMessage: function () { }
})

9、Component类与组件级别的生命周期

Component({
  properties: { },  // 父组件传递过来的属性
  data: { },  // 自有的状态数据
  ready: function(){ },   // 生命周期
  methods: { }  // 自有的方法
})

10、自定义Cate组件

# cate.wxml

    
        {{item.cate_zh}}
    

# cate.wxss
.cate_item {
    padding: 0 10rpx;
    display: inline-block;
    border: 1rpx solid #ccc;
    line-height: 45rpx;
}
.cate_item.on {
    color: red;
}
# cate.js
Component({
  properties: {
    list: {type: Array, value: [] },
    value: {type:String, value: ''}
  },
  methods: {
    tapClick(e) {
      // 父子组件事件通信
      // this.triggerEvent('input', e.target.dataset.cate)
      // model 双向绑定的写法
      this.setData({value: e.target.dataset.cate})
    }
  },
  // 生命周期
  lifetimes: {},
  pageLifetimes: {}
})
# cate.json
{
  "component": true
}

11、使用自定义封装的Cate组件

# study.json
{
  "usingComponents": {
    "qf-cate": "/components/cate/cate"
  }
}

12、列表渲染


  
    {{idx+1}}
    {{act.id*100}}
    {{act.title}}
  

13、条件渲染

第一行文字
第二行文字
第三行文字
第四行文字




14、动态样式


动态样式文字

15、事件绑定(bind / catch)



  
    测试事件(bind)
  
  测试事件(catch绑定)

Page({
  data: {},
  click1(e) {
    console.log('click1', e)
    console.log('arg', e.target.dataset.msg)
  },
  click2(e) {
    console.log('click2', e)
  },
  clickOuter(e) {
    console.log('outer', e)
  }
})

16、表单绑定(单向绑定、双向绑定)


  
  
  

Page({
  data: {
    username: '小明',
    password: '123',
    },
    // 手动取值
  usernameChange(e) {
    console.log('e', e.detail.value)
    this.setData({username: e.detail.value})
  },
  submit() {
    const data = {
      username: this.data.username,
      password: this.data.password
    }
    console.log('提交', data)
  }
})

17、微信小程序动画(最新写法)



Page({
  data: { },
    startAnim() {
    // 创建动画
    // 第1参数是节点选择器
    // 第2个参数是动态帧(数组)
    // 第3个是过滤时间
    // 第4个参数是回调参数,用于清除动画,这是一种性能优化方案
    this.animate('.abc', [
      { opacity: 1.0, rotate: 0, backgroundColor: '#FF0000', scale: [1,1] },
      { opacity: 0.5, rotate: 45, backgroundColor: '#00FF00', scale: [0.6,0.6]},
      { opacity: 0.2, rotate: 80, backgroundColor: '#FF0000',scale: [0.2,0.2] },
    ], 5000, ()=> {
      // 清除动画
      // 它的第二个参数,用于控制动画的样式是否保留最后一帧的样式,默认是保留的
      this.clearAnimation('.abc', {
        opacity: false,
        rotate: false,
        // backgroundColor: false
      }, ()=> {
        console.log("清除动画成功")
      })
    })
  }
})

18、使用Canvas画布(最新写法)



Page({
  data: { },
    rpx2px(arg) {
        const res = wx.getSystemInfoSync()
        return res.screenWidth * arg / 750
    },
    // 用于支持最新drawImage()写法
    async getImage(path) {
        const c = wx.createOffscreenCanvas({type: '2d'})
        const image = c.createImage()
        await new Promise(resolve => {
            image.onload = resolve
            image.src = path
        })
        return image
    },
    onReady() {
        const $ = wx.createSelectorQuery()
        $.select('.ad')
        .fields({ node: true, size: true })
        .exec((res) => {
            this.canvas = res[0].node
            const ctx = canvas.getContext('2d')

            // 第1步,绘制一个和画布一样大的矩形
            ctx.fillStyle = 'orange'
            ctx.fillRect(0, 0, this.rpx2px(750), this.rpx2px(500))

            // 第2步,绘制标题文字
            ctx.font = "bold 25px serif"
            ctx.fillStyle = 'white'
            ctx.fillText('樱桃小丸子很可爱', this.rpx2px(0), this.rpx2px(50))

            // 第3步,绘制图片
            this.getImage('/assets/girl.png').then(img=>{
                ctx.drawImage(img,50,50)
            })
        })
    },
    // 保存到相册
    save() {
        // 把canvas画布转换成临时路径
        wx.canvasToTempFilePath({
            x: 0,
            y: 0,
            width: this.rpx2px(750),
            height: this.rpx2px(500),
            destWidth: 1500,
            destHeight: 1000,
            canvas: this.canvas,
            success(res) {
                // 把临时路径中的图片保存到相册
                wx.saveImageToPhotosAlbum({
                    filePath: res.tempFilePath
                })
            }
        })
    }
})

19、小程序初次启动时请求用户授权地理定位

# app.js
App({
  onLaunch() {
    // 获取用户的地理定位的授权
    wx.getSetting({
      success(res) {
        console.log('当前用户的授权列表', res.authSetting)
        // 如果用户未授权地理定位
        if (!res.authSetting['scope.userLocation']) {
          // 无须用户触发,直接弹框请求用户同意使用地理定位
          // 当用户拒绝过一次后,这段代码没用了
          wx.authorize({
            scope: 'scope.userLocation',
            success (res) {
              console.log('地理定位:用户点击了同意', res)
            },
            fail (err) {
              console.log('地理定位:用户点击了拒绝', res)
            }
          })
        }
      }
    })
  }
})
# app.json
{
  "pages": [],
  "permission": {
    "scope.userLocation": {
      "desc": "为了给你更好的服务,希望使用你的地理定位"
    }
  }
}

20、使用地理定位




Page({
  data: {
    latLng: {}
  },
  getLngLat() {
    var that = this
    wx.getSetting({
        success(res){
            if(res.authSetting['scope.userLocation']) {
                // 如果用户已经同意过地理定位,直接获取经纬度
                wx.getLocation({
                    success(res) {
                        console.log('用户位置', res)
                        that.setData({latLng: res})
                    }
                })
            }else{
                // 如果用户已经拒绝过地理定位,我们要打开微信内置的设置页面,让用户自己选择授权
                wx.openSetting({
                    success(res) {
                        console.log('用户手动选择授权', res)
                    }
                })
            }
        }
    })
  },
  navTo() {
    wx.openLocation({
        latitude: this.data.latLng.latitude,
        longitude: this.data.latLng.longitude,
        name: '深圳',
        address: '广东省深圳市'
    })
  }
})

21、onShareAppMessage 实现转发


Page({
  data: {},
  // 实现转发的两种方案(胶囊按钮、button.open-type='share')
  onShareAppMessage(e) {
    console.log('转发', e)
    if(e.from==='menu') {
      return {
        title: '招聘年薪百万',
        path: 'pages/listen/listen',
        imageUrl: 'https:70.jpg.webp'
      }
    }else if(e.from==='button') {
      return {
        title: '我正在拼团...',
        path: 'pages/listen/listen',
        imageUrl: 'https://img20.0.jpg.webp'
      }
    }
  }
})

22、globalData 全局数据

App({
  globalData: { msg: 'hello' }
})
const app = getApp()
Page({
    data: {
        msg: app.globalData.msg
    },
    updateMsg() {
        app.globalData.msg = '456'
        this.setData({msg: app.globalData.msg})
    }
})
{{msg}}

23、onPageScroll 监听页面滚动



App({
  // 页面滚动
  onPageScroll(e) {
    console.log('页面滚动', e.scrollTop)
  },
  // 使用scrollTop滚动到页面的任何位置
  backToTop() {
    wx.pageScrollTo({
      scrollTop: 133,
      duration: 2000
    })
  }
})

24、 实现媒体查询



  根据媒体设备来决定我是否显示

25、 实现拖拽


  
    text
  

Page({
    data: {
        point: {x:0,y:0}
    },
    areaChange(e) {
    this.setData({point: e.detail})
  }
})

26、功能极其强大的

Page({
  data: {},
  getMobile(e) {
    // 目前已不支持个人版本的小程序
    console.log('获取手机号', e)
  },
  // 获取用户信息,会弹框请求用户授权
  // 即将过时,建议使用wx.getUserProfile获取用户信息
  getUserInfo(e) {
    console.log('获取用户信息', e)
  },
  getUser() {
    wx.getUserProfile({
      desc: '用于完善会员资料',
      success(e) {
        console.log('最新的用户信息', e)
        // 拿到用户信息之后,要调接口发送给后端数据库
        // 把用户保存在业务数据库
      }
    })
  }
})

27、使用 组件选择省市区


  省市区选择器
  
    
      当前选择:{{region[0]}},{{region[1]}},{{region[2]}}
    
  

Page({
  data: {
    region: ['广东省', '广州市', '海珠区'],
    customItem: '全部',
  },
  bindRegionChange(e) {
    console.log('region picker', e)
    this.setData({region: e.detail.value})
  }
})

28、 组件使用再举例


  选择品类
  
    
      当前选择:{{cateArr[cateIdx].cate_zh}}
    
  

Page({
    data: {
        cateArr: [
            {id:0,cate:'all',cate_zh:'全部'},
            {id:1,cate:"car",cate_zh:"汽车生活"},
            {id:1,cate:"office",cate_zh:"办公用品"}
        ],
        cateIdx: 0
    },
    bindCateChange(e) {
    console.log('cate picker', e)
    this.setData({cateIdx: parseInt(e.detail.value)})
  }
})

29、使用



Page({
    startPlay() {
    const audioCtx = wx.createInnerAudioContext()
    console.log('ctx', audioCtx)
    audioCtx.play()
  }
})

30、使用 相机组件




Page({
  data: {avatar:''},
  takeCamera() {
    const ctx = wx.createCameraContext()
    ctx.takePhoto({
      quality: 'high',
      success: (res) => {
        this.setData({
          avatar: res.tempImagePath
        })
      }
    })
  }
})

31、小程序路由跳转



Page({
    // 跳转到Tab页,使用switchTab,不能传参
    skipToTabPage() {
        wx.switchTab({
            url: '/pages/listen/listen'
        })
    },
    // 跳转到非Tab页,使用navigateTo,可以传参
    // 在另一个页面中,使用 onLoad 生命周期来接收参数
    skipToNotTabPage() {
        wx.navigateTo({
            url: '/pages/user/user?id=123&name=abc'
        })
    }
})

32、自定义ActionSheet


Page({
    selectMethod() {
    wx.showActionSheet({
      itemList: ['使用积分兑换', '直接支付'],
      success (res) {
        console.log('用户选择的兑换方式是:', res.tapIndex)
      }
    })
  }
})

33、使用小程序的功能 API


Page({
    testWXApi() {
        wx.setNavigationBarTitle({title:'1234'})
        wx.setBackgroundColor({ backgroundColor: '#ff0000' })
        wx.hideTabBar()
    }
})

34、从手机相册中选取照片


Page({
    selectAvatar() {
        // 可以先使用wx.getSetting先判断当前用户是否有访问相机和相册的权限
        wx.chooseImage({
            count: 9,
            sizeType: ['original', 'compressed'],
            sourceType: ['album', 'camera'],
            success (res) {
                // tempFilePath可以作为img标签的src属性显示图片
                const tempFilePaths = res.tempFilePaths
                console.log('tempFilePaths', tempFilePaths)
            }
        })
    }
})

35、微信小程序支付(伪代码)


Page({
    pay () {
        // 前提:先开通微信支付平台(收款平台),小程序要认证
        // 1、在小程序管理后台绑定已有的支付账号
        // 2、使用wx.request把订单信息发送给业务服务器,后端会返回支付信息
        // 3、使用wx.requestPayment()请求完成支付
    }
})

36、小程序中实现复制黏贴、打电话、扫码功能

订单号:QF20120902093203023


Page({
    // 复制黏贴
  copy() {
    wx.setClipboardData({
      data: 'QF20120902093203023'
    })
  },
  call() {
    wx.makePhoneCall({
      phoneNumber: '0755-89890909'
    })
  },
  scan() {
    wx.scanCode({
      scanType: 'barCode',
      success(res){
        console.log('扫码结果', res)
      }
    })
  }
})

37、下拉刷新与触底加载

Page({
    // 触底加载
  onReachBottom() {
    console.log('到底了,我准备调接口')
  },
  // 下拉刷新
  onPullDownRefresh() {
    console.log('正在下拉刷新')
    setTimeout(()=>{
      // 当调接口完成时,手动停止掉下拉刷新
      wx.stopPullDownRefresh()
    }, 1000)
  },
})
# .json 局部配置
{
  "navigationStyle": "custom",
  "onReachBottomDistance": 100,
  "enablePullDownRefresh": true
}

38、Request 封装

const baseUrl = 'http://localhost:8888'
const version = '/api/v1'

module.exports = function(url,method,data) {
    return new Promise(function(resolve, reject){
        wx.request({
          url: baseUrl+version+url,
          data,
            method,
          header: {
            Authorization: wx.getStorageSync('token')
          },
          success (res) {
            // 成功后数据过滤
                resolve(res.data.data)
          },
            fail(err) {
                wx.showToast({title:'网络异常'})
                reject(err)
            }
        })
    })
}

39、微信小程序的登录流程

# app.js
const api = require('./utils/api')
App({
  // 整个应用程序的入口
  onLaunch() {
    // 登录
    wx.login({
      success: res => {
        console.log('登录', res.code)
        // 使用 wx.request 调接口,用code换取token
        api.fetchLogin({code: res.code}).then(res=>{
          // console.log('登录token', res)
          wx.setStorageSync('token', res.token)
        })
      }
    })
  }
})
# Node.js + Koa 代码示例接口
const axios = require('../utils/axios')
const jwt = require('../utils/jwt')
// 引入model
const userModel = require('../model/user')

class UserController {
    // 登录接口
    static async login(ctx) {
        // 接收入参
        console.log('post body', ctx.request.body)
        let { code } = ctx.request.body
        console.log('code', code)
        // 第1步,用code+appid+secret换取微信服务器的openid+session-key
        const res = await axios({
            url: '/sns/jscode2session',
            method: 'get',
            params: {
                appid: 'w2x2f8b3892386e5e2ccf',
                secret: '345bc1b923ae7423bbf28146e31ff372e',
                js_code: code,
                grant_type: 'authorization_code'
            }
        })
        // 第2步,openid不能重复入库     
        const list = await userModel.find({openid: res.openid})     
        if(list.length > 0) {
            const token = jwt.createToken(res)
            ctx.body = {
                err: 0,
                msg: 'success',
                data: { token }
            }
        }else{
            const ele = {
                openid: res.openid,
            }
            await userModel.insertMany([ele])
            const token = jwt.createToken(res)
            ctx.body = {
                err: 0,
                msg: 'success',
                data: { token }
            }
        }
    }
}
module.exports = UserController

40、写在最后

微信小程序原生写法比较麻烦,推荐使用 Taro 3.0开发微信小程序,它不仅支持 React风格、也支持 Vue 和 Vue3.0 的语法风格。

Taro 是一个开放式跨端跨框架解决方案(由京东凹凸实验室开源),支持使用 React/Vue/Nerv 等框架来开发 微信、京东、百度、支付宝、字节跳动、QQ小程序、H5、RN 等应用。


本篇结束,感谢关注!!!

你可能感兴趣的:(微信小程序原生开发入门指南)