美文网首页小程序android开发学习微信小程序
40个必须搞懂的微信小程序知识点

40个必须搞懂的微信小程序知识点

作者: shine001 | 来源:发表于2022-06-13 15:55 被阅读0次

    1、初识微信小程序

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

    张小龙亲自“引爆”微信小程序 (附演讲全文)www.sohu.com/a/122892706_480349[图片上传失败...(image-8cbb8-1655106875266)]

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

    • 翻阅微信小程序官方文档

    微信开放文档developers.weixin.qq.com/miniprogram/dev/framework/

    • 下载、安装“微信者开发工具”

    稳定版 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、小程序管理后台的基本操作

    (一)版本管理

    • 小程序认证:填写基本信息、注意选择行业类目、备案付费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
    <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、动态样式

    <view class="box {{bol?'box':'box2'}}"></view>
    <view style='color:red;font-size:{{size+"rpx"}}'>动态样式文字</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() {
        // 创建动画
        // 第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画布(最新写法)

    <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步,绘制标题文字
                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、使用地理定位

    <button bindtap='getLngLat'>获取用户经纬度</button>
    <map class="map"
      longitude='{{latLng.longitude}}'
      latitude='{{latLng.latitude}}'
    ></map>
    <button bindtap='navTo'>导航去这里</button>
    
    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 实现转发

    <button open-type='share'>
      <view>拼团</view>
    </button>
    
    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})
        }
    })
    
    <view>{{msg}}</view>
    <button bindtap='updateMsg'>修改msg</button>
    

    23、onPageScroll 监听页面滚动

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

    24、<match-media> 实现媒体查询

    <!-- 媒体查询 -->
    <match-media min-width="315" max-width="600">
      <view>根据媒体设备来决定我是否显示</view>
    </match-media>
    

    25、<movable-area> 实现拖拽

    <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'>联系客服</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'>打开授权页面</button>
    <button open-type='feedback'>投诉建议</button>
    
    Page({
      data: {},
      getMobile(e) {
        // 目前已不支持个人版本的小程序
        console.log('获取手机号', e)
      },
      // 获取用户信息,会弹框请求用户授权
      // 即将过时,建议使用wx.getUserProfile获取用户信息
      getUserInfo(e) {
        console.log('获取用户信息', e)
      },
      getUser() {
        wx.getUserProfile({
          desc: '用于完善会员资料',
          success(e) {
            console.log('最新的用户信息', e)
            // 拿到用户信息之后,要调接口发送给后端数据库
            // 把用户保存在业务数据库
          }
        })
      }
    })
    

    27、使用 <picker> 组件选择省市区

    <view class="section">
      <view class="section__title">省市区选择器</view>
      <picker
        mode="region"
        bindchange="bindRegionChange"
        value="{{region}}"
        custom-item="{{customItem}}"
      >
        <view class="picker">
          当前选择:{{region[0]}},{{region[1]}},{{region[2]}}
        </view>
      </picker>
    </view>
    
    Page({
      data: {
        region: ['广东省', '广州市', '海珠区'],
        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">
          当前选择:{{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、小程序路由跳转

    <button bindtap='skipToTabPage'>跳转到Tab页</button>
    <button bindtap='skipToNotTabPage'>跳转到非Tab页</button>
    
    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

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

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

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

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

    <view bindlongtap='copy'>订单号:QF20120902093203023</view>
    <button bindtap='call'>打电话</button>
    <button bindtap='scan'>扫码</button>
    
    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 等应用。

    相关文章

      网友评论

        本文标题:40个必须搞懂的微信小程序知识点

        本文链接:https://www.haomeiwen.com/subject/zdvomrtx.html