• 小程序开发


    送给写小程序的你。

     

    声明:本人也是菜鸟一只,写的不好的地方,大佬们指点指点。

    微信小程序商城,持续更新。

    先说说写小程序的渊源吧,我之前认识的大佬写了一个比较简单的小程序,我改了改样式于是成功的有了我的第一个小程序。然后我在找工作的时候,简历上写了这么一回事,结果进来之后,leader语重心长的对我说,公司的小程序商城就教给你了,重点是公司没有一个人会微信小程序。WTF,不是招我进来写VUE的吗?

    于是就开始了我从零开始写微信小程序商城之路。

    微信小程序的官方文档什么的我就不说了,那是肯定要看的。说说我这几天开始写我遇到的问题。希望对将要写小程序的你能有一丝丝帮助,因为本人也很菜。

    1、微信小程序之rpx

    在微信官方的文档中,我们看到这样一句话:

    在 iPhone 6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。开发微信小程序时设计师可以用 iPhone 6 作为视觉稿的标准。也就是说,设计师在设计小程序时,可以这样做:

    • 直接以 iPhone 6 的屏幕尺寸(375×667)用作视觉稿尺寸,1 px = 0.5 rpx;
    • 以 1 px = 1 rpx 的标准,将设计稿尺寸设定为 750×1334。

    其实总的来说就是,你跟UI小姐姐说一声,然后就可以很爽的写样式了。

    2、微信小程序之容器视图

    • view 其实就是div,嘿嘿嘿就是这么简单粗暴的解释。
    • scroll-view 可滚动视图区域。 这个需要注意的是,横向滚动的视图区域。重要的是这两个行内样式,其他的官方文档也说的比较清楚。

    <view class="supply-scroll bg">
    <scroll-view class="scroll-box" scroll-x="true" style=" 100%;white-space: nowrap; display: flex" >
    <view class="scroll-items" wx:for="{{scrollList}}" wx:key="index" data-index="{{index}}" style="display: inline-block">
    <image src="{{item.url}}" class="scroll-image"/>
    </view>
    </scroll-view>
    </view>

    3、微信小程序之UI框架

    这个,很有意思,当我写完一个我觉得很有灵性的选项卡的时候,以前哥们告我微信小程序有个框架你看看去。。。

    <view wx:for="{{tab}}" wx:key="index" data-id="{{item.id}}" data-index="{{index}}"
    bindtap="switchTab" class="tab-list {{tabIndex === index?'on':''}}">
    {{item.name}}
    </view>

    不BB,直接上地址 github.com/Tencent/weu…

    先写到这里,周六加班的午休时间,冒着被同时打死的危险写的,如果有什么写的不好的地方,请大家多多包涵,我也还会分享我遇到的问题。

    最近又看到有赞的微信小程序UI还是老规矩直接上地址:

    youzan/zanui-weapp

    4、微信小程序之switchTab

    页面跳转大家官网上看的navigator用法你套着用就可以了,今天分享一下我在小程序中遇到的页面跳转的问题。我写的是一个电商商城小程序,跳转到详情页之后有两个点击跳转的icon如图:

    聪明的你肯定猜出来这两个icon是跳转到哪的,肯定也是bindtap,然后wx.navigator就搞定了。两个简单的页面跳转嘛,然后居然就是这两个问题卡了我一个多小时,后来还是在微信群里问大佬们才解决的,因为自己看文档不仔细,跳转的这两个页面是微信官方底部tab的路径如图:

    问题是出在官方的tabbar中你用了这几个路径如图:

    然后解决方法为:

    handleGoIndex: function() {
    wx.switchTab({
    url: '/pages/index/index'
    })
    },
    handleGoCart: function() {
    wx.switchTab({
    url: '/pages/cart/index'
    })
    }

    5、微信小程序之禁止页面下拉

    有的时候你会遇到不让页面下拉,如果你以为我说的是下拉刷新的api的话那就图样了。就是不让你下滑的时候出来那个白条。直接上代码了:

    <view catchtouchmove="stopDrag" class="container">
    stopDrag(){
    return false;
    },

    但是这个慎用,我也想在这里如果有大神能指点指点就最好了,这个的效果是可视区域全部静止滑动... 拼多多的小程序首页就是不会出现那个下拉的白条,但能上滑看商品信息,求教~

    最新解决方法,在app。json 里面增加一条这个属性,就可以禁止滚动。

    6、微信小程序之页面深度

    在测试自己的项目的时候,发现明明逻辑没问题,语法也没问题。突然想起来小程序的页面栈是五个,于是特别中二的数了一下自己的页面跳转哈哈... 才发现当时自己的理解是错误的,刚开始不细心看文档,我以为这五个页面是超过五个会把新的加进页面栈,第一个打开的销毁。我就无脑用wx.navigateTo,遇到这个问题后用了 wx.redirectTo就解决了。但是谁让我好学呢,于是发现一个大神写的特别完美的理解小程序页面跳转的原理。

    (现在深度,变成了10层,但是在ios 10的部分系统下,还是有bug的,还是要考虑页面深度这种场景的。)

    依然是嘿嘿:小程序基础篇之页面路由-微信小程序俱乐部 www.wxappclub.com

    7、微信小程序之上拉加载

    我现在公司的项目做的是电商的小程序,下拉刷新,官网写的很明确不能与scroll-view同时使用。我是在脑子瓦特的情况下,onReachBottom 和 scroll-view的bindscrolltolower 都用了。经过我的实践,推荐大家用 onReachBottom。如果是一些tab里面也有下拉刷新,这个就很方便直接上代码:

    onReachBottom() {
            let isPush = this.data.index,
                val = this.data.inputVal;
            this.setData({
                isBtnShow: true
            });
            if (isPush ==1) {
                let num = this.data.limitIndex;
                this.setData({
                    limitIndex: num+1
                })
            //关于上拉加载的性能优化
                setTimeout(()=>{
                        // 给后端传下拉刷新的次数+1
                        const data = {
                            limitIndex: this.data.limitIndex
                        };
                        utils.sendRequest(api.AllGoodsUrl, data, this.handleReachBottom.bind(this));
                },1500)
            };
            if (val != '') {
                setTimeout(()=>{
                    let num = this.data.limitIndex;
                        this.setData({
                            limitIndex: num+1
                        })
                        // 给后端传下拉刷新的次数+1
                        const data = {
                            limitIndex: this.data.limitIndex,
                             data:{
                                name: this.data.inputVal,
                            }
                        };
                        utils.sendRequest(api.AllGoodsUrl, data, this.handleLoadMore.bind(this));
                },1500)
            };
        },
    这段代码主要就是判断tab的状态来延时请求接口。实现效果如下图:
    

    这就是我用上拉加载的实践,希望有大神能提出更好的建议。

    8、微信小程序之数据交互

    说到这里顺便把我的一些关于数据交互的一些经验分享一下

    function sendRequest(path, data, callback) {
        wx.request({
            url: path, 
            data: data,
            header: {
                'content-type': 'application/json'
            },
            method: "POST",
            success: callback,
            fail:(res)=>{
              console.log(res)
            }
        })
    }  把微信请求封装起来
    还有项目里面要用到的接口如下
    
    把接口暴露出去在其他页面调用

    希望有大神能帮忙一下小程序的组件化最佳实践~

    9、微信小程序之Android请求失败的坑

    我当时遇到的情况是,在模拟器和IOS环境下请求数据都是没有任何问题的,在测试android环境时发现请求失败如下图

    这个错还让我跟运维的大哥撕了半天哈哈, 首先出现这个错,兄弟这个锅完完全全就能甩给运维的大哥,不留任何余地。 然后可以帮大哥提供一写资料,一步步排查,先查你项目绑定域名:

    1、https证书问题 :ssl证书配置需要使用pem,不使用crt, ,

    2、服务器端的版本信息:TLS版本的问题,该问题在微信小程序官方文档中已经提及到,服务器TLS版本必须支持 1.2 (启用1.2,禁用1.1和1.0等低版本),

    3、前两种方法是在查阅资料有人说是可解决的,但是对于我们公司最后在排查前两项之后,运维大哥用Nginx转发了之后,解决了安卓请求失败的问题。希望可以帮到大家吧~

    10、微信小程序之地址页面三级联动

    说道这个问题,现给大家贴一个地址,非常感谢大神的开源:blog.csdn.net/sinat_17775…

    大家可以看到代码之后,先理清你要处理的后端数据和存地址的要求等等,我遇到的问题是

    后端给我的数据里面有每一个地址对应的ID,比如北京:2 北京:50 东城区:500

    处理这个数据的时候,我根据原来大神的代码做了一些修改,

    let val = e.detail.value,
                t = this.data.values,
                cityData = this.data.cityData,
                index = this.data.id,
                list = this.data.addrList;
                list[index].area = true;
                try {
                    if (val[0] != t[0]) { //当val是选择省份的时候
                    const citys = [];
                    const countys = [];
                        cityData[val[0]].child.map(item => citys.push({name:item.name,id:item.id}));
                        cityData[val[0]].child[0].child.map(item => countys.push({name:item.name,id:item.id}));
                        list[index].provinceName = this.data.provinces[val[0]].name;//省份
                        list[index].cityName = cityData[val[0]].child[0].name;//城市
                        list[index].districtName = cityData[val[0]].child[0].child[0].name;//地区
                        list[index].province = this.data.provinces[val[0]].id;//对应的传值ID
                        list[index].city = cityData[val[0]].child[0].id;//对应的传值ID
                        list[index].district = cityData[val[0]].child[0].child[0].id;//对应的传值ID
                    this.setData({
                        citys: citys,
                        countys: countys,
                        values: val,
                        value: [val[0], 0, 0],
                        addrList: list
                    })
                        return;
                    }
                    if (val[1] != t[1]) {//当val是选择城市的时候
                        const countys = [];
                        cityData[val[0]].child[val[1]].child.map(item => countys.push({name:item.name,id:item.id}));
                        list[index].cityName = this.data.citys[val[1]].name;// 选择城市
                        list[index].city = this.data.citys[val[1]].id;//对应的传值ID
                        list[index].districtName = cityData[val[0]].child[val[1]].child[0].name;//选择城市对应的地区
                        list[index].district = cityData[val[0]].child[val[1]].child[0].id;//对应的传值ID
                        this.setData({
                            countys: countys,
                            values: val,
                            value: [val[0], val[1], 0],
                            addrList: list
                        })
                        return;
                    }
                    if (val[2] != t[2]) {//当val是选择地区的时候
                        list[index].districtName = this.data.countys[val[2]].name;//选择地区
                        list[index].district = this.data.countys[val[2]].id;//对应的传值ID
                        this.setData({
                            county: this.data.countys[val[2]].name,
                            values: val,
                            addrList: list
                        })
                        return;
                    }    
    
                } catch(e) {
                    // statements
                    console.log(e);
                }
    list里面是有 收货人,电话,等等信息  但是我只操作改变数组里面地址改变的信息,
    

    布局方面需要做一些修改的地方就是

    我贴了这么多图,是因为我真不会说了,调这个页面调了两天。只能提供个大概方向,还是得一步步处理数据,goodluck~

    11、微信小程序之Android环境下的横向滚动

    <view class="tab bg">
              <scroll-view class="" scroll-x="true" style=" 100%;white-space: nowrap; display: flex;overflow-x: auto;">
                  <view style="display: inline-block" bindtap="switchIndex" class="tab-list {{index === 1 ?'on':''}}">首页</view>
                  <view wx:for="{{tab}}" wx:key="index" data-id="{{item.id}}" data-index="{{index}}"
                     style="display: inline-block" bindtap="switchTab" class="tab-list {{tabIndex === index?'on':''}}">
                      {{item.nav_name}}
                  </view>
              </scroll-view>
          </view>
    代码一贴其实当测试小哥告诉你安卓tab不能滑的时候,你只需要加一个overflow-x: auto;哈哈哈
    

    12、微信小程序之Ios环境下mp4播放问题

    当你遇到你的mp4格式有的能播放,有的不能播放的话,你只需要看一下你的mp4编码格式,必须是h264格式才行。

    13、微信小程序之微信支付的坑

    这个因为我也是第一次写小程序嘛,后端的大哥,在H5的商城里什么都实现了一次了,非说各种没问题,只贴两个图就明白了第一个

    这个api里面的所有数据都是后端返给你的,不要接受他的甩锅哈哈。

    还有就是一定要让后端好好看微信支付的文档,一般公司开发商城都是服务商版的支付服务,这里就是我和后端大哥的甩锅之路,他没有绑定我得小程序appid,然后各种说调不通。。。

    去这里配置好,前端只需要调API传值就好

    14、微信小程序之图片上传

    好久没更新了,被公司业务搞得焦头烂额。

    老规矩,直接上代码了。

    handleCancelPic() {
            let id = this.data.dbId;
            wx.chooseImage({
              count: 3, // 默认9
              sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
              sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
              success: res => {
                // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
                var tempFilePaths = res.tempFilePaths;
    
                this.setData({
                    src: tempFilePaths
                })
                upload(this,tempFilePaths,'','');
              }
            })
        }
    然后一个封装好的方法
    function upload(page, path,way,id) {
        console.log(path)
      wx.showToast({
        icon: "loading",
        title: "正在上传"
      });
      var test = [],
        that = this;
      for (var i = 0; i<path.length; i++) {
            wx.uploadFile({
              url: api.CancelImg,
              filePath: path[i],          
              name: 'file',
              header: { "Content-Type": "multipart/form-data" },
              success: res => {
                test.push(res);
                wx.setStorageSync('cancelImg',test)
                console.log(test)
                if (res.statusCode != 200) { 
                  wx.showModal({
                    title: '提示',
                    content: '上传失败',
                    showCancel: false
                  })
                  return;
                }else {
                    wx.showModal({
                        title: '提示',
                        content: '上传成功',
                        showCancel: false
                    }) 
                }
              },
              fail: function (e) {
                console.log(e);
                wx.showModal({
                  title: '提示',
                  content: '上传失败',
                  showCancel: false
                })
              },
              complete: function () {
                wx.hideToast();  //隐藏Toast
              }
            })
        }
    这个是多个图片上传的方法,单个图片上传的话,把循环去掉就好。主要是因为微信官方默认的就是一次上传一张图片这个很蛋疼。只能这么搞了。。。
    

    15、微信小程序之电商购物车逻辑

    接着再给大家分享一个关于小程序购物车全选的逻辑处理,这个还是要感谢我的老妹教导我,一个开发人员做东西一定要严谨,不管UI设计的有多丑,该有的逻辑你一定要做到。

    首先我们要做到的就是,当用户点击第三个商品时 全选按钮自动选中,或者全选之后,只要有一个商品不选中,全选按钮也得变动。先给大家看一下代码:

    你要在页面onload时候定义一些你需要每次渲染的数据

    data: {
    likeList: [],
    carts:[], // 购物车列表
    hasList:false, // 列表是否有数据
    //totalPrice:0, // 总价,初始为0
    selectAllStatus:false, // 全选状态,默认全选,
    goodsNums:0,
    allclick:[]
    }
    每件商品单个选中的的逻辑处理
    selectList(e) {
      const index = e.currentTarget.dataset.index;// 获取每一个点击的购物车ID
      let carts = this.data.carts,
      selected = carts[index].select,
      all = this.data.allclick;
      carts[index].select = !selected;
    carts[index].select == true ? all.push(index):all.splice(index,1);
    all.length == carts.length ?
    this.setData({
      selectAllStatus: true
    }):this.setData({
      selectAllStatus: false
    });
      this.getTotalPrice();
    },
    上面的代码,先做的就是单选的页面渲染效果。判断部分的代码就是最主要的处理全选逻辑的一步。相信你看到这里也注意到我在data里定义了一个allclick的空数组,然后就是接下来的逻辑:
    
    • 按钮选中时取出对应item的角标放到新的arr里,这里因为我之前结算的逻辑已经搞好了,我就随便往数组里push数据,但其实可以作为对应商品的更重要的一些数据处理。
    • 按钮不选中是从新的arr里找到这个item对应下标的数据移除
    • 完成上面两步处理之后,每次按钮状态发生变化的时候判断arr的长度和cart的长度。

    这就是我的处理,也可循环,实现的方式有很多,只是拿出来让没有接触过的小伙伴做个参考~

    data: {
    likeList: [],
    carts:[], // 购物车列表
    hasList:false, // 列表是否有数据
    //totalPrice:0, // 总价,初始为0
    selectAllStatus:false, // 全选状态,默认全选,
    goodsNums:0,
    allclick:[]
    }
    每件商品单个选中的的逻辑处理
    selectList(e) {
      const index = e.currentTarget.dataset.index;// 获取每一个点击的购物车ID
      let carts = this.data.carts,
      selected = carts[index].select,
      all = this.data.allclick;
      carts[index].select = !selected;
    carts[index].select == true ? all.push(index):all.splice(index,1);
    all.length == carts.length ?
    this.setData({
      selectAllStatus: true
    }):this.setData({
      selectAllStatus: false
    });
      this.getTotalPrice();
    },
    这段代码也还是先处理全选的状态,然后就是关联状态的处理,
    
    • 当全选没有勾选的时候全部改变商品信息里的按钮为false,直接清空allclick数组。
    • 当全选勾选的时候全部改变商品信息里的按钮为true,先清空,接着重新push,再赋值。

    经过这几步操作之后就解决了全选这方面的所有逻辑,觉得有用的小伙伴,记得评论,关注哟~

    16、微信小程序之使用Map对象

    这是我的梁master跟我提的这件事,我是小程序,她是Android,项目一样。我俩关系好,然后她教我的用的这个Map对象,其实这个是java的map对象。用起来真的很爽,不废话了直接上代码。

    let localMap = new Map(),// 定义一个全局的MAP对象 
    typeCode = ''; // 点击分类里面的细分选项卡
    handleClickTabs(e) {
     let id = e.target.dataset.index,
     code = e.target.dataset.id; 
    typeCode = code;// 这里定义code,在请求回调里面使用 
    this.setData({ leftTab : id }); 
    if (localMap!=null) {// map对象 
    let list = localMap.get(typeCode);//获取对应分类的type的code 
    if (list!=null) {//map里面有值,渲染页面 
    this.setData({ tabSonList:list }) }else {//map里面没有值,去请求接口 const data = { goodsTypeCode: code }; 
    utils.sendRequest(api.ClassifySon, data, this.handleGoodsSon.bind(this)); }
     }else { const data = { goodsTypeCode: code }; 
    utils.sendRequest(api.ClassifySon, data, this.handleGoodsSon.bind(this)); } }, //分类里面的内容 handleGoodsSon(res) {
     let list = res.data; localMap.set(typeCode,list)//存对应typeCode的list 
    this.setData({ tabSonList:list }) },
    

    这样就实现了,对选项卡的优化,如果接口数据变多了的话,会重新赋值。 虽然是自己给自己加戏,但这是也是我的一种工作态度,学习了新东西,还让优化了项目。

    17、小程序之全局变量缓存的问题

    这个问题的出现是因为,在input的事件,我没有找到更好的能监听键盘收回的方法,也是为了更好的用户体验吧,所以随之而来的问题就是全局变量,在赋值之后用户退出这个页面,全局变量被微信缓存了,然后造成,用户输入过一次之后,修改手机号失败的bug。

    let Btel = '',//防止用户恶意修改手机号
        Byzm = '';
    
    handleTel(e) {// 这里修改用户输入的手机号
            this.setData({
                tel:e.detail.value 
            })
        },
        handleYzm(e) {
            this.setData({
                Yzm:e.detail.value
            })
            Byzm = e.detail.value;
            let psd = this.data.psd,
                yzm = this.data.Yzm;
            yzm == psd ? this.setData({isYzm:0}):'';
        },
        handleGetYzm() {
            let reg = common.telReg,
                val = this.data.tel;    
            if (!reg.test(val)) {
                   wx.showModal({
                    content:'请输入正确的手机号',
                    showCancel:false,
                    confirmColor:'#3cc51f',//默认值为#3cc51f
                    success:res =>{
                        if(res.confirm){
                            this.setData({
                                tel:''
                            })         
                        }
                    }
                })
            }else {//发送验证码的时候用全局变量的手机号
                Btel = val;// 这里是全局的手机号    
                this.setData({
                    isReset : true,
                    isNoClick: true
                })
                const data ={
                    tel:val//传的是全局变量 
                };
                utils.sendRequest(api.YanZhengMa, data, this.handleGetYzmSucc.bind(this));
                //button 定时器
                let time = setInterval(()=>{
                let phoneCode = this.data.time;
                    phoneCode --
                    this.setData({
                        time : phoneCode
                    })
                    if(phoneCode == 0){
                         clearInterval(time)
                         this.setData({
                            isReset : false,
                            isNoClick: false,
                            time:60
                         })
                    }
                },1000)
            }
            
                
        },
    
    //然后这一步是校验了用户在请求完验证码接口后 有没有修改手机好 然后保存
    handleSave() {
            let name = this.data.name,
                telNum = this.data.tel,
                yzm = Byzm,
                status = this.data.isYzm,
                card = wx.getStorageSync('UserCard');
            let timestamp= new Date().getTime();
            if (yzm == '') {
                wx.showModal({
                    content:'请输入验证码.',
                    showCancel:false,
                    confirmColor:'#3cc51f'
                })
                return false;
            }
            if (name!=''&&telNum!='') {
                if(Btel != telNum) {
                    utils.showModal('手机号发生变化,请重新获取验证码。');
                }else {
                    const data ={
                    distribution_id:card.distribution_id,
                    post:{
                        user_name:name,
                        user_tel:Btel,
                        user_code:yzm
                    },
                    user_id:card.user_id,
                    password:yzm+timestamp
                    };
                    utils.sendRequest(api.BindTel, data, this.handleSaveTel.bind(this));
                }
            }else {
                utils.showModal('请填写完整信息哟');
            }
        },
    
    // 然后在 保存成功之后 用户点击确定  清空 全局变量  也可以在隐藏和卸载的生命周期里面清空全局变量。
    handleSaveTel(res) {
            if (res.data.error == 0) {
                let go = this.data.go,
                    id = res.data.data.id,
                    lv = res.data.data.level;
                wx.showModal({
                    content:'绑定成功~',
                    showCancel:false,
                    confirmColor:'#3cc51f',//默认值为#3cc51f
                    success:res =>{
                        if(res.confirm){
                            Byzm = '';//对小程序全局变量缓存进行清除
                            if (go) {
                                wx.redirectTo({
                                  url: '/pages/user/cash/cash'
                                })
                            }else {
                                if (id != 0) {
                                    let card = wx.getStorageSync('UserCard');
                                    card.distribution_id = id;
                                    card.distribution_level = lv;
                                    wx.setStorageSync('UserCard',card);
                                    wx.setStorageSync('seller', true)
                                }
                                wx.switchTab({
                                  url: '/pages/user/index'
                                })
                            }         
                        }
                    }
                }) 
                return false
            }else {
                utils.showModal(res.data.err_msg);
            }
        }
    

    18、小程序之微信支付的深坑

    事情是这样的,因为公司主体变更,避税之类的事。我们公司的小程序,需要重新换绑微信支付,在财务给我们申请号商户号之后,我们排期半夜来切环境。 我记得是一个周四的晚上,十二点开始切环境,然后 大概后端代码数据库都准备好之后,我们重新绑定微信支付,以为大功告成。周五还能弹性一上午美滋滋~

    没想到 刚交个测试,说微信支付不能用,提示我们没有授权。wtf,刚绑定好的你这样提示,不合适吧。按照报错一查,我去小程序 社区里一看。

    你敢信,微信开放平台的商户号和公众号商户号,是两回事。 小程序之支持公众号的商户号,但是你在开放平台绑定小程序微信支付的时候,官方给你的提示是 绑定成功~。

    然后 就变成了 早上七点下班。。。

    血的教训呀!!!

    19、小程序之登陆改版

    https://juejin.im/post/5b0e9c6351882515887ed760

    嘿嘿,写完贴个地址得了~

    -------------------------------------------------------------------------------------------

    你都看到这了?!不点个关注,加个收藏再走嘛~
    我的小程序,自律更自由,如果你也喜欢锻炼的话在这里寻找你的小伙伴吧。
    一只喜欢锻炼的程序猿,嘿嘿。

     

    一步步记录自己的踩坑历程~我要做到我技术不是最好的,但我给你总结的小程序的东西是最简单粗暴的哈哈哈

    推广一下自己的小程序,如果你也喜欢锻炼的话在这里寻找你的小伙伴吧。还有告诉大家一个微信小程序的框架,有vue开发经验的小伙伴很容易上手:github.com/wepyjs/wepy

  • 相关阅读:
    与 字符串 相关的操作 -- lua语言实现(带有浓厚的 lua 语法特性)
    03-BFC
    11-canvas
    JavaScript知识点 思维导图
    文件系统 函数
    Meta标签大全
    PHP 快速排序
    天气预报API开发
    Zend Studio XDebug调试配置
    启程
  • 原文地址:https://www.cnblogs.com/zhangycun/p/9232993.html
Copyright © 2020-2023  润新知