40個必須搞懂的微信小程序知識點

1、初識微信小程序

  • 小程序為什么存在?為企業(yè)或個人提供便利的用戶連接工具;它可以在一定程度上可以替代掉部分手機APP的作用(用完即走)。
  • 產(chǎn)品設(shè)計標(biāo)準:小而美、開發(fā)周期較短。
  • 張小龍親自“引爆”微信小程序(附演講全文):

張小龍親自“引爆”微信小程序 (附演講全文)www.sohu.com/a/122892706_480349[圖片上傳失敗...(image-8cbb8-1655106875266)]

2、微信小程序開發(fā)前準備

  • 翻閱微信小程序官方文檔

微信開放文檔developers.weixin.qq.com/miniprogram/dev/framework/

  • 下載、安裝“微信者開發(fā)工具”

穩(wěn)定版 Stable Build 更新日志developers.weixin.qq.com/miniprogram/dev/devtools/stable.html

  • 注冊一個小程序賬號(管理后臺)

https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=

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

(一)版本管理

  • 小程序認證:填寫基本信息、注意選擇行業(yè)類目、備案付費300元。
  • 小程序有三個版本:開發(fā)版、審核版、線上版(默認代碼體積不能超過2M)。
  • 小程序項目中用到的靜態(tài)資源,可以放到CDN上,以減小代碼體積。

(二)成員管理

  • 管理員(1人),是注冊賬號的微信用戶。
  • 項目成員(15人),可以登錄小程序管理后臺,開發(fā)者必須是項目成員。
  • 體驗成員(15人),只有體驗的權(quán)限,沒有開發(fā)的權(quán)限。

(三)開發(fā)管理

  • AppID,相當(dāng)是小程序的身份證號碼,創(chuàng)建項目、調(diào)試項目、小程序之間的跳轉(zhuǎn)都要用到,還有比如支付等也要用到。
  • AppSecret,小程序密鑰,一般要給后端,在登錄、支付等功能中都要用到。
  • Request 地址,就是api 的 baseURL,本地開發(fā)時可以關(guān)閉https驗證,上線時一定要小程序管理后臺中添加上這個地址,并且要求https協(xié)議的。

4、微信開發(fā)者工具的基本使用

  • 如何創(chuàng)建新項目?
  • 如何導(dǎo)入舊項目?
  • 調(diào)試項目(真機調(diào)試、微信開發(fā)者工具調(diào)試)
  • 如何上傳代碼至開發(fā)版?
  • 關(guān)閉“開發(fā)環(huán)境下校檢 https”的限制
  • 注意小程序api 版本庫和微信版本之間兼容性問題。

5、認識四種文件

  • .wxml,類似 HTML 的角色。
  • .wxss,具有 CSS 大部分的特性,小程序在 WXSS 也做了一些擴充和修改。
  • .js,編寫腳本與用戶交互,響應(yīng)用戶的點擊、獲取用戶的位置等等。
  • .json,是一種數(shù)據(jù)格式,并不是編程語言,在小程序中,JSON扮演的靜態(tài)配置的角色。

6、自帶配置風(fēng)格的小程序

微信小程序根目錄下的 app.json 文件用來對微信小程序進行全局配置,決定頁面文件的路徑、窗口表現(xiàn)、設(shè)置網(wǎng)絡(luò)超時時間、設(shè)置多 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類與應(yīng)用級別的生命周期

注冊小程序。接受一個 Object 參數(shù),其指定小程序的生命周期回調(diào)等。App() 必須在 app.js 中調(diào)用,必須調(diào)用且只能調(diào)用一次。不然會出現(xiàn)無法預(yù)期的后果。

App({
  // 整個應(yīng)用程序的入口
  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: { },  // 自有的狀態(tài)數(shù)據(jù)
  ready: function(){ },   // 生命周期
  methods: { }  // 自有的方法
})

10、自定義Cate組件

# cate.wxml
<view class="cate">
    <text
        wx:for='{{list}}'
        class="cate_item {{value===item.cate?'on':''}}"
        wx:key='item'
        data-cate='{{item.cate}}'
        bindtap='tapClick'>
        {{item.cate_zh}}
    </text>
</view>
# 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"
  }
}
<qf-cate list='{{cateList}}' model:value='{{curCate}}'></qf-cate>

12、列表渲染

<block
  wx:for="{{actList}}"
  wx:for-item="act"
  wx:key="act"
  wx:for-index="idx"
>
  <view class="row">
    <text>{{idx+1}}</text>
    <text>{{act.id*100}}</text>
    <text>{{act.title}}</text>
  </view>
</block>

13、條件渲染

<view wx:if='{{idx===1}}'>第一行文字</view>
<view wx:elif='{{idx===2}}'>第二行文字</view>
<view wx:elif='{{idx===3}}'>第三行文字</view>
<view wx:else>第四行文字</view>
<button bindtap='toggle'>切換</button>

<!-- 顯示與隱藏、類似v-show -->
<view hidden='{{bol}}'>第十行文字</view>

14、動態(tài)樣式

<view class="box {{bol?'box':'box2'}}"></view>
<view style='color:red;font-size:{{size+"rpx"}}'>動態(tài)樣式文字</view>

15、事件綁定(bind / catch)

<!-- 事件綁定、冒泡、事件傳參、事件對象 -->
<view bindtap='clickOuter'>
  <view
    data-msg='hello bind'
    bindtap='click1'>
    測試事件(bind)
  </view>
  <view catchtap='click2'>測試事件(catch綁定)</view>
</view>
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、表單綁定(單向綁定、雙向綁定)

<view>
  <input type="text" value='{{username}}' bindblur='usernameChange' />
  <input type="text" model:value='{{password}}' />
  <button bindtap='submit'>提交</button>
</view>
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、微信小程序動畫(最新寫法)

<view class="abc"></view>
<button bindtap='startAnim'>開始你的表演</button>
Page({
  data: { },
    startAnim() {
    // 創(chuàng)建動畫
    // 第1參數(shù)是節(jié)點選擇器
    // 第2個參數(shù)是動態(tài)幀(數(shù)組)
    // 第3個是過濾時間
    // 第4個參數(shù)是回調(diào)參數(shù),用于清除動畫,這是一種性能優(yōu)化方案
    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, ()=> {
      // 清除動畫
      // 它的第二個參數(shù),用于控制動畫的樣式是否保留最后一幀的樣式,默認是保留的
      this.clearAnimation('.abc', {
        opacity: false,
        rotate: false,
        // backgroundColor: false
      }, ()=> {
        console.log("清除動畫成功")
      })
    })
  }
})

18、使用Canvas畫布(最新寫法)

<canvas type="2d" class="ad" id="myCanvas"/>
<button bindtap='save'>保存到相冊</button>
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步,繪制標(biāo)題文字
            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畫布轉(zhuǎn)換成臨時路徑
        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、小程序初次啟動時請求用戶授權(quán)地理定位

# app.js
App({
  onLaunch() {
    // 獲取用戶的地理定位的授權(quán)
    wx.getSetting({
      success(res) {
        console.log('當(dāng)前用戶的授權(quán)列表', res.authSetting)
        // 如果用戶未授權(quán)地理定位
        if (!res.authSetting['scope.userLocation']) {
          // 無須用戶觸發(fā),直接彈框請求用戶同意使用地理定位
          // 當(dāng)用戶拒絕過一次后,這段代碼沒用了
          wx.authorize({
            scope: 'scope.userLocation',
            success (res) {
              console.log('地理定位:用戶點擊了同意', res)
            },
            fail (err) {
              console.log('地理定位:用戶點擊了拒絕', res)
            }
          })
        }
      }
    })
  }
})
# app.json
{
  "pages": [],
  "permission": {
    "scope.userLocation": {
      "desc": "為了給你更好的服務(wù),希望使用你的地理定位"
    }
  }
}

20、使用地理定位

<button bindtap='getLngLat'>獲取用戶經(jīng)緯度</button>
<map class="map"
  longitude='{{latLng.longitude}}'
  latitude='{{latLng.latitude}}'
></map>
<button bindtap='navTo'>導(dǎo)航去這里</button>
Page({
  data: {
    latLng: {}
  },
  getLngLat() {
    var that = this
    wx.getSetting({
        success(res){
            if(res.authSetting['scope.userLocation']) {
                // 如果用戶已經(jīng)同意過地理定位,直接獲取經(jīng)緯度
                wx.getLocation({
                    success(res) {
                        console.log('用戶位置', res)
                        that.setData({latLng: res})
                    }
                })
            }else{
                // 如果用戶已經(jīng)拒絕過地理定位,我們要打開微信內(nèi)置的設(shè)置頁面,讓用戶自己選擇授權(quán)
                wx.openSetting({
                    success(res) {
                        console.log('用戶手動選擇授權(quán)', res)
                    }
                })
            }
        }
    })
  },
  navTo() {
    wx.openLocation({
        latitude: this.data.latLng.latitude,
        longitude: this.data.latLng.longitude,
        name: '深圳',
        address: '廣東省深圳市'
    })
  }
})

21、onShareAppMessage 實現(xiàn)轉(zhuǎn)發(fā)

<button open-type='share'>
  <view>拼團</view>
</button>
Page({
  data: {},
  // 實現(xiàn)轉(zhuǎn)發(fā)的兩種方案(膠囊按鈕、button.open-type='share')
  onShareAppMessage(e) {
    console.log('轉(zhuǎn)發(fā)', 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 全局數(shù)據(jù)

App({
  globalData: { msg: 'hello' }
})
const app = getApp()
Page({
    data: {
        msg: app.globalData.msg
    },
    updateMsg() {
        app.globalData.msg = '456'
        this.setData({msg: app.globalData.msg})
    }
})
<view>{{msg}}</view>
<button bindtap='updateMsg'>修改msg</button>

23、onPageScroll 監(jiān)聽頁面滾動

<!-- 使用scrollTop -->
<button bindtap='backToTop'>回到指定位置</button>
App({
  // 頁面滾動
  onPageScroll(e) {
    console.log('頁面滾動', e.scrollTop)
  },
  // 使用scrollTop滾動到頁面的任何位置
  backToTop() {
    wx.pageScrollTo({
      scrollTop: 133,
      duration: 2000
    })
  }
})

24、<match-media> 實現(xiàn)媒體查詢

<!-- 媒體查詢 -->
<match-media min-width="315" max-width="600">
  <view>根據(jù)媒體設(shè)備來決定我是否顯示</view>
</match-media>

25、<movable-area> 實現(xiàn)拖拽

<movable-area class="area">
  <movable-view
    direction='all'
    x='{{point.x}}'
    y='{{point.y}}'
    bindchange='areaChange'>
    <view class="area-text">text</view>
  </movable-view>
</movable-area>
Page({
    data: {
        point: {x:0,y:0}
    },
    areaChange(e) {
    this.setData({point: e.detail})
  }
})

26、功能極其強大的 <button>表單組件

<!-- button是表單,功能極其豐富 -->
<button open-type='contact'>聯(lián)系客服</button>
<button open-type='getPhoneNumber' bindgetphonenumber='getMobile'>登錄</button>
<!-- <button open-type='getUserInfo' bindgetuserinfo='getUserInfo'>上傳圖像</button> -->
<button bindtap='getUser'>獲取用戶信息</button>
<button open-type='launchApp'>打開抖音</button>
<button open-type='openSetting'>打開授權(quán)頁面</button>
<button open-type='feedback'>投訴建議</button>
Page({
  data: {},
  getMobile(e) {
    // 目前已不支持個人版本的小程序
    console.log('獲取手機號', e)
  },
  // 獲取用戶信息,會彈框請求用戶授權(quán)
  // 即將過時,建議使用wx.getUserProfile獲取用戶信息
  getUserInfo(e) {
    console.log('獲取用戶信息', e)
  },
  getUser() {
    wx.getUserProfile({
      desc: '用于完善會員資料',
      success(e) {
        console.log('最新的用戶信息', e)
        // 拿到用戶信息之后,要調(diào)接口發(fā)送給后端數(shù)據(jù)庫
        // 把用戶保存在業(yè)務(wù)數(shù)據(jù)庫
      }
    })
  }
})

27、使用 <picker> 組件選擇省市區(qū)

<view class="section">
  <view class="section__title">省市區(qū)選擇器</view>
  <picker
    mode="region"
    bindchange="bindRegionChange"
    value="{{region}}"
    custom-item="{{customItem}}"
  >
    <view class="picker">
      當(dāng)前選擇:{{region[0]}},{{region[1]}},{{region[2]}}
    </view>
  </picker>
</view>
Page({
  data: {
    region: ['廣東省', '廣州市', '海珠區(qū)'],
    customItem: '全部',
  },
  bindRegionChange(e) {
    console.log('region picker', e)
    this.setData({region: e.detail.value})
  }
})

28、<picker> 組件使用再舉例

<view class="section">
  <view class="section__title">選擇品類</view>
  <picker
    mode="selector"
    bindchange="bindCateChange"
    value="{{cateIdx}}"
    range='{{cateArr}}'
    range-key='cate_zh'
  >
    <view class="picker">
      當(dāng)前選擇:{{cateArr[cateIdx].cate_zh}}
    </view>
  </picker>
</view>
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、使用 <audio> 音頻組件

<audio    
  poster="http://y.592000.png"
  name="此時此刻"
  author="許巍"
  src="http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E06DCBDC9AB7C49FD713D632D313AC4858BACB8DDD29067D3C601481D36E62053BF8DFEAF74C0A5CCFADD6471160CAF3E6A&fromtag=46"
  id="myAudio"
  controls
  loop>
</audio>
<button bindtap='startPlay'>播放</button>
Page({
    startPlay() {
    const audioCtx = wx.createInnerAudioContext()
    console.log('ctx', audioCtx)
    audioCtx.play()
  }
})

30、使用 <camera> 相機組件

<camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera>
<button bindtap='takeCamera'>拍照</button>
<image src='{{avatar}}'></image>
Page({
  data: {avatar:''},
  takeCamera() {
    const ctx = wx.createCameraContext()
    ctx.takePhoto({
      quality: 'high',
      success: (res) => {
        this.setData({
          avatar: res.tempImagePath
        })
      }
    })
  }
})

31、小程序路由跳轉(zhuǎn)

<button bindtap='skipToTabPage'>跳轉(zhuǎn)到Tab頁</button>
<button bindtap='skipToNotTabPage'>跳轉(zhuǎn)到非Tab頁</button>
Page({
    // 跳轉(zhuǎn)到Tab頁,使用switchTab,不能傳參
    skipToTabPage() {
        wx.switchTab({
            url: '/pages/listen/listen'
        })
    },
    // 跳轉(zhuǎn)到非Tab頁,使用navigateTo,可以傳參
    // 在另一個頁面中,使用 onLoad 生命周期來接收參數(shù)
    skipToNotTabPage() {
        wx.navigateTo({
            url: '/pages/user/user?id=123&name=abc'
        })
    }
})

32、自定義ActionSheet

<button bindtap='selectMethod'>兌換禮品</button>
Page({
    selectMethod() {
    wx.showActionSheet({
      itemList: ['使用積分兌換', '直接支付'],
      success (res) {
        console.log('用戶選擇的兌換方式是:', res.tapIndex)
      }
    })
  }
})

33、使用小程序的功能 API

<button bindtap='testWXApi'>測試API</button>
Page({
    testWXApi() {
        wx.setNavigationBarTitle({title:'1234'})
        wx.setBackgroundColor({ backgroundColor: '#ff0000' })
        wx.hideTabBar()
    }
})

34、從手機相冊中選取照片

<button bindtap='selectAvatar'>選擇照片</button>
Page({
    selectAvatar() {
        // 可以先使用wx.getSetting先判斷當(dāng)前用戶是否有訪問相機和相冊的權(quán)限
        wx.chooseImage({
            count: 9,
            sizeType: ['original', 'compressed'],
            sourceType: ['album', 'camera'],
            success (res) {
                // tempFilePath可以作為img標(biāo)簽的src屬性顯示圖片
                const tempFilePaths = res.tempFilePaths
                console.log('tempFilePaths', tempFilePaths)
            }
        })
    }
})

35、微信小程序支付(偽代碼)

<button bindtap='pay'>立即支付</button>
Page({
    pay () {
        // 前提:先開通微信支付平臺(收款平臺),小程序要認證
        // 1、在小程序管理后臺綁定已有的支付賬號
        // 2、使用wx.request把訂單信息發(fā)送給業(yè)務(wù)服務(wù)器,后端會返回支付信息
        // 3、使用wx.requestPayment()請求完成支付
    }
})

36、小程序中實現(xiàn)復(fù)制黏貼、打電話、掃碼功能

<view bindlongtap='copy'>訂單號:QF20120902093203023</view>
<button bindtap='call'>打電話</button>
<button bindtap='scan'>掃碼</button>
Page({
    // 復(fù)制黏貼
  copy() {
    wx.setClipboardData({
      data: 'QF20120902093203023'
    })
  },
  call() {
    wx.makePhoneCall({
      phoneNumber: '0755-89890909'
    })
  },
  scan() {
    wx.scanCode({
      scanType: 'barCode',
      success(res){
        console.log('掃碼結(jié)果', res)
      }
    })
  }
})

37、下拉刷新與觸底加載

Page({
    // 觸底加載
  onReachBottom() {
    console.log('到底了,我準備調(diào)接口')
  },
  // 下拉刷新
  onPullDownRefresh() {
    console.log('正在下拉刷新')
    setTimeout(()=>{
      // 當(dāng)調(diào)接口完成時,手動停止掉下拉刷新
      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) {
            // 成功后數(shù)據(jù)過濾
                resolve(res.data.data)
          },
            fail(err) {
                wx.showToast({title:'網(wǎng)絡(luò)異常'})
                reject(err)
            }
        })
    })
}

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

# app.js
const api = require('./utils/api')
App({
  // 整個應(yīng)用程序的入口
  onLaunch() {
    // 登錄
    wx.login({
      success: res => {
        console.log('登錄', res.code)
        // 使用 wx.request 調(diào)接口,用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) {
        // 接收入?yún)?        console.log('post body', ctx.request.body)
        let { code } = ctx.request.body
        console.log('code', code)
        // 第1步,用code+appid+secret換取微信服務(wù)器的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不能重復(fù)入庫     
        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開發(fā)微信小程序,它不僅支持 React風(fēng)格、也支持 Vue 和 Vue3.0 的語法風(fēng)格。

Taro 是一個開放式跨端跨框架解決方案(由京東凹凸實驗室開源),支持使用 React/Vue/Nerv 等框架來開發(fā) 微信、京東、百度、支付寶、字節(jié)跳動、QQ小程序、H5、RN 等應(yīng)用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容