字节跳动小程序 【开发】 自学总结

准备
  • 微信小程序开发文档 官网 - https://developers.weixin.qq.com/miniprogram/dev/framework/
    由于字节跳动小程序很类似微信小程序,但是文档的详细程度却差一些,所以需要微信小程序开发文档做对照
开发阶段

目录结构同微信小程序类似,app.json 为全局设置。

  1. 认证注意事项
    企业开发者:适用于企业,个体工商户,政府组织,海外机构等其他机构。每个企业主体可以验证【 10 个企业小程序 】。
    特别注意:认证主体后续需与支付主体、广告结算主体保持一致

此外,如果在广告中心开通了激励广告需要注意

1. 目前激励视频广告仅支持抖音端,接入需判断接入宿主及宿主版本, 在不支持的宿主及版本上需要将结果直接展示给用户。
2. 展示广告前向用户说明激励广告规则,明确告知用户看完视频广告后能获得相应奖励。
3. 一个页面最多出现一个激励视频广告。
4. 需要用户主动操作点击按钮,才能创建和获取激励视频广告。
5. 需判断广告异常情况,如不支持激励视频的低版本用户、广告调用失败等情况,应直接给予奖励。
激励广告规范

0-1. 关于激励广告
//--app.js

globalData:{
    'videoAd': null,
},
rewardedVideoAd(){
    var _that = this;
    var _videoAd = _that.globalData.videoAd;

    //兼容
    //目前只能在抖音使用该方法,今日头条等宿主暂不支持
    let version = tt.getSystemInfoSync().SDKVersion;
    if(_that.compareVersion(version,'1.57.0')){
      if(_videoAd==null){
        //初始化
        if(tt.createRewardedVideoAd){
            _videoAd = tt.createRewardedVideoAd({
                adUnitId: '申请的激励广告ID'
            });
            
            //广告显示成功,先解除绑定close事件的监听器,为后续添加准备
            if(typeof _videoAd != 'undefined'){
              _videoAd.offClose((res)=>{
                console.log('广告组件解绑');
              });
            };

            // return _videoAd;
            _that.globalData.videoAd = _videoAd;
        }else{
            tt.showModal({
                title: "提示",
                content:
                "当前客户端版本过低,无法使用该功能,请升级客户端或关闭后重启更新。"
            });
            // return null;
            _that.globalData.videoAd = null;
        }
      };
    }else{
      tt.showModal({
        title: '提示',
        content: '当前版本过低,无法获取激励广告功能,请升级到最新版本后重试。'
      });
    }; 
},
compareVersion(v1, v2){
    v1 = v1.split('.')
    v2 = v2.split('.')
    const len = Math.max(v1.length, v2.length)
    while(v1.length < len){
      v1.push('0')
    }
    while(v2.length < len){
      v2.push('0')
    }
    for(let i = 0; i < len; i++){
      const num1 = parseInt(v1[i])
      const num2 = parseInt(v2[i])
      if (num1 > num2){
        return true;
      }else if(num1 < num2){
        return false;
      }
    }
},

//需求页面,调用广告

//获取激励广告唯一对象      
var rewardedVideoAdObj = app.globalData.videoAd;

//用户点击触发的激励广告
showRewardedVideoAd(){
    //广告
    if(rewardedVideoAdObj){
      //版本符合,看广告
      rewardedVideoAdObj.show().then((res) => {
        rewardedVideoAdObj.offClose(res=>{});
        rewardedVideoAdObj.onClose(res => {
          clearTimeout(rewardedVideoAdObj.iTimer);
          rewardedVideoAdObj.iTimer = setTimeout(function(){
            if(res.isEnded) {
              // console.log('给与奖励');
              //观看完广告 -> 奖励流程
              adAllow();
            }else{
              // console.log('广告未观看完毕');
            }
          },500);
        });
      })
      .catch(err => {
        console.log("广告组件出现问题", err);
        //发生错误看不了广告 -> 奖励流程
        adAllow();
      });
    }else{
      //版本低,看不了广告 -> 奖励流程
      adAllow();
    }
  },
  //观看完激励广告的奖励流程
adAllow(){
  dosomething...
},

0-2. 关于插屏广告
值得注意的是:

  • 不能打断用户的完整操作过程,例:不能在快速的信息流下拉刷新过程中插入广告
  • 不能在用户刚打开小程序时就插入广告,即便不在首屏,也需要等待一会儿再进入广告所在页面
    否则报错,APP刚刚启动,广告不能显示
//抖音插屏广告
interstitialAd(){
    //目前只能在抖音使用该方法,今日头条等宿主暂不支持
    //插屏广告组件每次创建都会返回一个全新的实例,默认是隐藏的,需要调用 InterstitialAd.show 将其显示
    //基础库 1.70.0 开始支持本方法
    let version = tt.getSystemInfoSync().SDKVersion;
    if(app.compareVersion(version,'1.70.0')){
      //创建插屏广告
      // console.log('创建插屏广告');
      let interstitialAd = tt.createInterstitialAd({
        adUnitId: "19j5e8eiiae4h5fa17",
      });

      if(typeof interstitialAd != 'undefined'){
        interstitialAd.load().then(() => {
          interstitialAd.show();
        }).catch((err) => {
          console.log(err);
        });
      };
    }else{
      tt.showModal({
        title: '提示',
        content: '当前版本过低,无法获取插屏广告功能,请升级到最新版本后重试。'
      });
    };
}

0-3. 关于上下架
手动下架的小程序(小游戏),需要再次审核,审核通过后,方可再次上架
暂时没有微信的 临时下架功能,如 下架进行维护,再自己操作上架,而无需审核

全局设置
  1. 如增加Tab需要设置图标,
    图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片
"tabBar": {
    "color":"#666666",
    "selectedColor":"#2a76ff",
    "backgroundColor":"#FFF",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "标题1",
        "iconPath":"pages/resource/images/icon1.png",
        "selectedIconPath":"pages/resource/images/icon1_ac.png"
      },
      {
        "pagePath": "pages/second/index",
        "text": "标题2",
        "iconPath":"pages/resource/images/icon3.png",
        "selectedIconPath":"pages/resource/images/icon3_ac.png"
      }
    ]
  }
  1. 页面跳转分为标签跳转和程序跳转
    标签跳转

...

navigator-hover 默认为 {background-color: rgba(0, 0, 0, 0.1); opacity: 0.7;},的子节点背景色应为透明色。

2-0-1. 删除 navigator 默认点击样式
法1

.navigator-hover { background-color:rgba(0,0,0,0); opacity:1;}

法2


程序跳转

tt.navigateTo({
      url: '/pages/match_expert/index?planid='+_planId,      //绝对路径
      url: '../match_expert/index?planid='+_planId,                //相对路径
      success(res) {
        console.log(`${res}`);
      },
      fail(res) {
        console.log(`navigateTo调用失败`);
      }
 });

【注】: 不能跳转到 TabBar 页面

2-0. 跳转到TabBar的方法

tt.switchTab({
        url: '../my/index',
        success(res){},
        fail(err){
          console.log(err);
          console.log(`navigateTo调用失败`);
        }
});

2-1. 跳转只要到达对应文件夹即可,不用指定到xxx.ttml
2-2. 获取跳转的get参数 , 在被跳转页的js文件,生命周期onload参数中获取

onLoad: function(option){
    let pageId = option.detailId || "";
    if(pageId){
      //获取信息
      this.getMes(pageId);
    }
  }

2-3. 如果需要跳转的页面,在app.json中的tabBar内,则需要使用

tt.switchTab({
        url: `pages/my`,
        success(res){},
        fail(err) {
          console.log(`switchTab调用失败`);
        }
});

2-4. 返回上一页,如果目标页面为非TAB页,则可以使用

tt.navigateBack({
  delta: 1,
  success(res) {
    console.log(`${res}`);
  },
  fail(res) {
    console.log(`navigateBack调用失败`);
  },
});
  1. 小程序对Javascript语法的 支持程度 - https://microapp.bytedance.com/dev/cn/mini-app/develop/framework/mini-app-runtime/javascript-support

  2. 模块化,提取公共脚本
    导出

// common.js
function hello(name) {
  console.log(`Hello ${name} !`);
}
module.exports.hello = hello;

导入

var common = require("common.js");
Page({
  helloWorld: function() {
    common.hello("world");
  }
})
  1. 标签有限,基本这三个就够用了
    view - div text - span image - img

5-1. 如果渲染的文本内包含html标签,则需要使用rich-text。官方连接文档 - https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/component/basic-content/rich-text
如:


  1. 布局单位与实际操作
    单位 rpx 据说可以根据屏幕比例变化
    | 设备 | rpx 换算 px (屏幕宽度/750) | px 换算 rpx (750/屏幕宽度) |
    | iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
    建议:
    设计师可以用 iPhone6 作为视觉稿的标准。
    即使用PSD尺寸为750px的设计稿,然后1:1测量,单位使用rpx即可

  2. 图片标签新裁切属性属性,同web中CSS3的object-fit

7-1. 如果希望图片宽度100%,高度自适应,
则需要给图片添加mode="widthFix"和样式 width;100% 即可,
如果不设置mode="widthFix",高度样式不设置,那么会图片会走默认的系统预制高度
如果不设置mode="widthFix",高度样式设置auto,那么高度为0
两种都不是图片高度自适应


7-2. 如果希望固定宽高内,不镂空,则需要图片设置宽高尺寸后,使用 mode使用aspectFill 即可

  1. 渲染转译符 增加属性decode
     

  2. 调试工具缓存有时候很大,通过开发者工具清除缓存按钮不好使,直接退出再重进,即可

9-1. setData给对象内属性赋值

Page({
  data: {
    gameData:{
      level:0, //评价等级
      totalScore:0, //总分数
      questionIndex:0 //当前题目
    }
  }
});

设置数据
this.setData({
    ['gameData.totalScore']: 1000
});

9-2. setData给对象内属性赋值,且子对象为变量

data:{
    totalMatchData:[ [],[],[],[],[] ]
}

//indexNum为数组索引,且为变量
var _mdata =  'totalMatchData['+indexNum+']';
_that.setData({
  [_mdata]: xxx
});

BUG:这种方式如果设置多个动态数据,只有最后一个会生效???!!!!

  1. 返回顶部 按钮怎么操作页面
  • 针对scroll-view
    可以使用重置这个属性的值 scroll-into-view ,值为页面上目标点的元素的id即可,注意这个元素必须是 scroll-view 包含结构内的
    注:
    如果元素的在底部触发了bindscrolltolower触底行为,通过scroll-into-view指向这个元素的ID,
    会将scorll-view拉回到这个元素位置,但是其实这样做,会多滚动一屏的高度,需要修正这个位置
    所以,可以在这个元素上添加position:absolute定位元素到 负一屏的高度,如果还有偏差可以用padding做二次修正

补充:

  • 对于 必须要添加一个高度限制,否则效果不会生效
  • 指定锚点元素的id (不能以数字开头)
  • 如果在内动态添加循环元素,该组件会回到 scrollTop 为 0 的位置,需要将 scroll-view 组件的全部子元素包裹一层 view 可避免该问题 (官方提示)
  • 锚点ID元素可以放置在循环元素内部
//--- ttml


    
        
        
        
    


//--- js
data: {
    returnTopEle:"topEleTar"
},
returnFn(){
  this.setData({
    returnTopEle:"topEleTar"
  });
}
  • 针对非scroll-view
    使用API
tt.pageScrollTo({
  scrollTop: 0,
  duration: 1000,
  success(res) {
    //console.log(`pageScrollTo调用成功`);
  },
  fail(res) {
    console.log(`pageScrollTo调用失败`);
  }
});
  1. 网络请求需要配置白名单 位置 - https://developer.toutiao.com/dev/cn/mini-app/develop/api/other/network-request-reference
    特别注意:线上环境,网络请求仅支持 https 协议的 URL

  2. 小程序点击,不支持事件处理函数传参,带上参数会认定整体为 函数名,报错未定义
    解决通过自定义属性


    
        罗纳尔多
    
 

事件处理函数默认参数为事件对象,通过其可以过得自定义属性

selectClickFn(ev){
    console.log(ev.currentTarget.dataset.index); //1
}

12-1. 阻止事件冒泡的行为要通过修改事件本身来做,不支持修饰符,不支持事件对象stopPropagation

触摸点击事件: bindtap --> catchtap 即可
小程序中存在冒泡的事件:

冒泡事件

注:
事件绑定后没有反应的情况,可能是以下情况

  • text标签内嵌套text标签,在里面的text上绑定事件,无法触发
  1. canvas在小程序(非小游戏)中,存在开发工具渲染正常,真机尺寸错误的情况
    表现 以 iphone6 为基准绘制的页面,在iphone7 plus上表现 canvas变小,在小米5上表现 canvas变大
    因此需要对canvas绘图尺寸进行二次修改
    如:
tt.getSystemInfoSync().windowWidth -- 真机宽度 
tt.getSystemInfoSync().windowHeight -- 真机高度 

开发者工具中,如选择iphone6为开发参照,则获取设备宽高做参照
以iphone6为例
375 -- 模拟器上宽度 
603 -- 模拟器上高度 

在真机上,模拟器数据需要修正的比例值
宽度修正比例值 = 真机宽度 / 模拟器上宽度 
高度修正比例值 = 真机高度 / 模拟器上高度 

【实操】: iphoneX修正异常、如果修正后的高度大于宽度,需要调整高度为宽度的数值
再次测试,iPhone与小米真机表现一致了


修正cy值
  1. 小程序支持的canvas属性 字节跳动小程序官网 - https://microapp.bytedance.com/dev/cn/mini-app/develop/api/interface/canvas-draw/tt.createcanvascontext

  2. 小程序图片预加载
    循环数组数据创建图片,通过图片的bindloadbinderror的变化,来判断加载进度与是否加载完毕,
    暂时没有发现 new Image() 或类似 小游戏的 tt.createImage 的东西




    
        
        



//图片加载
bindloadFn(res){
    let _that = this;
    if(res.type=="load"){
      loadNum+=1;
      this.setData({
        allImgLoad:Math.floor(loadNum/sumLoadNum*100)
      });
      if(loadNum==sumLoadNum){
        this.setData({
          allLoadText:'加载完毕'
        });
        setTimeout(function(){
          _that.setData({
            scene:2
          });
        },800);
      }
    }
},
binderrorFn(err){
    console.log('图片加载错误');
    console.log(err);
}
  1. 音频
  • 分为背景音频
const backgroundAudioManager = tt.getBackgroundAudioManager();
backgroundAudioManager.src = "https://xxx/0000-0001.mp3";
BackgroundAudioManager.play();
BackgroundAudioManager.pause();
BackgroundAudioManager.stop();
//音频加载回调
BackgroundAudioManager.onWaiting(function callback)
//音频播放中
paused  --- boolean | 当前音频是否处于暂停状态,只读
  • 普通音频
const innerAudioContext = tt.createInnerAudioContext();
innerAudioContext.src = "https://someaudiourl";
innerAudioContext.volume = 0.5;  //范围 0~1。默认为 1 只读
innerAudioContext.onPlay(() => {
  console.log("开始播放回调");
});
innerAudioContext.pause();
InnerAudioContext.stop();
  1. 动画 animate.css 需要修改下后缀名 如 .ttss
  
  
  1. 获取 基础库版本 (' 论坛提交问题需要基础库版本号 ');
    所有参数 - https://developer.toutiao.com/dev/cn/mini-app/develop/api/device/system-information/tt.getsysteminfo
//获取基础库版本
tt.getSystemInfo({
      success(res) {
        console.log(res.SDKVersion);
      }
});
  1. 全局数据的获取与设置
    在app.js中
//获取
onLaunch: function () {
    console.log(this.globalData.some_data);  // 10
  },
  //全局数据
  globalData: {
    some_data:10
  },
  ...

//设置
this.globalData.some_data= 1000;  

在非app.js中

const app = getApp()
//获取
console.log(app.globalData.some_data);

//设置
app.globalData.some_data = 1000;

19-1. 关于onLaunch生命周期,有且仅加载一次,可以通过二维码带参数,参数的获取方式为
以自定义参数'channel'为例

if(options.query && options.query.channel){
    _that.globalData.channel = options.query.channel;
};

返回参数的种类 - https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/api/foundation/lifecycle/tt-get-launch-options-sync/

参数的种类

19-2. 当页面通过二维码加载出错时候,需要重定向

onPageNotFound(res) {
    //如果不是 tabbar 页面
    tt.redirectTo({
      url: "pages/index/index",
    }); 
    //如果是 tabbar 页面,请使用 tt.switchTab
    tt.switchTab({
      url: "pages/index/index",
    }); 
},
  1. 获取自定义属性
    20-1. 在非组件标签上挂载属性时候,可以使用 event.currentTarget.dataset.index来获取;
//--ttml

  按钮1

//--js
function navClickFn(event){
  if(typeof event.currentTarget.dataset.index!= 'undefined'){
    let _index = event.currentTarget.dataset.index;
    console.log(_index); //100
  }
}

20-2.获取组件
区别与使用普通标签,在组件上挂载data属性,可以使用 event.target.dataset.index来获取; 如: pinker组件

  1. 导入ttml模板

  1. InnerAudioContext与BackgroundAudioManager冲突,音效会终止BackgroundAudioManager背景音
    解决的方法:
    将背景音频(BackgroundAudioManager)用音效(InnerAudioContext)代替,
//背景音函数
let bgsrc = 'https://www.aaa.com/bgm1.mp3';
function createBGMObj(_src){
  var bgmOb = tt.createInnerAudioContext();
  bgmOb.src = _src;
  bgmOb.autoplay = true;
  bgmOb.loop = true;
  //音频加载中
  bgmOb.onWaiting(function(res){ });
  //音频可播放
  bgmOb.onCanplay(function(){
    bgmOb.play(); 
  });
  return bgmOb;
};

const gameBGM = createBGMObj(bgsrc);

if(!gameBGM.paused){
      this.setData({
        bgmBtnStatue:true //音频播放的UI类名
      });
      gameBGM.stop && gameBGM.stop();
      console.log('暂停');
    }else{
      this.setData({
        bgmBtnStatue:false //音频暂停的UI类名
      });
      gameBGM.play && gameBGM.play();
      console.log('播放');
}
  1. 小程序轮播图指示点颜色 ( 默认: indicator-color ,选中:indicator-active-color)
    更多参数配置 - https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html

    
        
            
                
                {{item.title}}
            
        
    

  1. 修改swiper容器高度
//--ttml

   
...

//--ttss
.homePage .ourCup .bannerBox -- 包裹swiper容器高度
.homePage .ourCup .bannerBox tt-swiper  -- swiper自身高度
  1. 背景图 -- ( 图片精灵可以放在这里 )
    样式中使用本地背景图,可以将背景图文件与app.json同级,然后通过
background:url('resource/image/bell.png') no-repeat left top;

或者使用 base64、或者网络图片

  1. 下载远程图片至本机相册
//--- ttml

//--- js
  downloadImgFn(){
    var _src = 'https://www.xxx.com/upload/images/temp/3.d5aabd23.jpg'
    tt.getImageInfo({
      src: _src,
      success(res) {
        var _path = res.path
        tt.saveImageToPhotosAlbum({
          filePath: _path,
          success(res) {
            console.log(`saveImageToPhotosAlbum调用成功`);
          },
          fail(res) {
            console.log(`saveImageToPhotosAlbum调用失败`);
          }
        });
      }
    });
  }

26-1. 方式2,通过 tt.downloadFile 下载网络图片至本地缓存,获取tempFilePath,然后通过
tt.saveImageToPhotosAlbum,配置上参数tempFilePath,下载至本地相册

这个过程中会询问用户相册的权限,此外需要配置下载文件白名单
且白名单为https协议,不能加端口号,否则失效

白名单不要加端口号

  1. 组件 switch
    修改背景色 使用 属性 color设置
    修改大小,使用transform:scale(xxx) 调整

  2. 定位为fixed的元素不要放在scroll-view中,真机不会生效(不像模拟器)

  3. 表单输入框、文本域限制字数
    ttml