15、电脑相关、移动端基础(术语|长度单位|设备像素|倍图)、vant-ui相关、微信产品、微信小程序-目录说明|WXML语法|WXS语法|事件系统|组件|自定义组件|API、uniapp跨端框架-简介|教程|组件|全局文件、postcss.config.js(2800行)

附、电脑相关
  (1)查看命令行:win+r; cmd; 确定
  (2)查看电脑配置:win+r; msinfo32; 确定
  (3)设置自动锁屏时长,Windows+I>设置>系统>电源和睡眠
  (4)windows10系统输入法切换的快捷键设置步骤:至少有2种输入法同时备用
    A、开始-齿轮-时间和语言-语言-中文(简体中国)-选项-(添加键盘-微软拼音-)微软拼音-选项-按键-模式切换
  (5)搜狗输入法的快捷键界面:S图标-更多设置-属性设置-按键
    A、中英切换
    B、系统功能快捷键-系统功能快捷键设置
  (6)广告屏保的位置与删除
    A、重启
    B、此电脑-(上方中间的)查看-显示-隐藏的项目
    C、打开,C:\Users\制造商(NINGMEI)\AppData\Local\Temp
    D、删除该位置下的所有文件
  (7)谷歌截全屏的步骤
    A、F12
    B、ctrl+shift+p
    C、输入full
    D、点击Capture full size screenshot
    另,免费在线网页转图片,https://www.url2pic.com/url2pic/index.html

一、移动端基础
1、移动端的常用术语
  (1)Uni,统一的
  (2)IDE,集成开发环境(Integrated Development Environment),如VS Code、HBuilderX等
  (3)SDK,软件开发工具包(Software Development Kit),是软件开发工具的集合,通常包括编译器、调试器、API文档和代码示例等
  (4)uniCloud,基于serverless模式和js编程的云开发平台,为开发者提供免费服务器空间,由DCloud(数字天堂)联合阿里云、腾讯云等云厂商共同推出
2、移动端的长度单位
  (1)不常用(dot、dpi、dip)
    A、dot,点,印刷行业的最小单位,也可用于描述屏幕显示的最小光点
    B、dpi,每英寸点数(Dots Per Inch),最初表示打印精度或屏幕点密度,在计算机显示领域常被与ppi混用
    C、dip,设备独立像素(Device-Independent Pixel),不依赖于具体显示设备的物理像素密度
  (2)换算依据(ppi、dpr)
    A、ppi,每英寸像素数(Pixels Per Inch),表示屏幕像素密度,硬件物理指标
    B、dpr,设备像素比(Device Pixel Ratio),表示物理像素与逻辑像素的映射关系,软件换算系数
      a、定义,物理像素/逻辑像素
      b、获取,window.devicePixelRatio
      c、大于1,标志着设备具备更高密度的显示能力
  (3)通用(px、vw、vh、vmin、vmax、em、%、rem)
    A、px,像素(pixel),是图像显示的最小单位,包含物理像素和逻辑像素,读作/ˈpɪksl/
      a、物理像素:又叫设备像素,设备的最小显示单元,由屏幕硬件决定。分辨率指屏幕上物理像素的数量(如1920×1080)
      b、逻辑像素:又叫CSS像素,Web开发中使用的抽象像素单位,独立于设备硬件,用于在浏览器中定义元素尺寸
    B、vw,视口宽度(viewport width),1vw等于视口宽度的1%
    C、vh,视口高度(viewport height),1vh等于视口高度的1%
    D、vmin,视口宽度和高度中较小值的1%
    E、vmax,视口宽度和高度中较大值的1%
    F、em,相对于当前元素的字体大小,如.size{width:3em}
    G、%,相对于父元素的尺寸
    H、rem,相对于根元素(html)的字体大小,通常需要通过JS或CSS媒体查询动态设置html的font-size来实现适配
  (4)专用(dp、sp、pt)
    A、Android(dp、sp)
      a、dp,安卓原生应用开发中的逻辑长度单位,用于控件尺寸,其与px的换算依据是【屏幕密度,ppi,5级】
        a1、在屏幕密度为160ppi的设备上,1px物理像素渲染1dp逻辑像素
        a2、在屏幕密度为320ppi的设备上,2px物理像素渲染1dp逻辑像素
        a3、在屏幕密度为480ppi的设备上,3px物理像素渲染1dp逻辑像素
        a4、屏幕密度,5级分别是:120ppi(0.75px=1dp)、160ppi、240ppi(1.5px=1dp)、320ppi、480ppi
        a5、dp=px×(160/ppi),Device-Independent Pixel,设备独立像素
        a6、确保不同密度屏幕上元素视觉大小一致
      b、sp,安卓原生应用开发中的专属字体单位,用于字体尺寸,基本与dp等价
        b1、Scaled Pixels,可缩放像素
        b2、随系统“字体大小”设置同步缩放   
    B、iOS(pt)
      a、pt,苹果原生应用开发中的逻辑长度单位,其与px的换算依据是【设备像素比,dpr】
        a1、在设备像素比为1的设备上,1px物理像素渲染1pt
        a2、在设备像素比为2的设备上,2px物理像素渲染1pt
        a3、在设备像素比为3的设备上,3px物理像素渲染1pt
        a4、pt,Point,点
        a5、在印刷领域,1pt=1/72英寸,与编程领域的pt是不同的概念
      b、Retina显示技术
        b1、2010年苹果公司首次在iPhone4上推出的高像素密度(ppi)显示技术
        b2、通过提升ppi使肉眼在正常使用距离下难以分辨单个像素
        b3、其命名“Retina”(视网膜)即由此而来,配合系统适配实现更清晰的画面
3、移动端的系统与应用
  (1)系统
    A、安卓系统
      a、2003年,安卓公司成立,开始研发安卓系统
      b、2005年,谷歌收购安卓,将其转为移动操作系统项目
      c、2007年,谷歌正式公布安卓系统
      d、2008年,首款安卓手机发布,搭载Android1.0系统 
      e、2011年,Android4.0,实现手机与平板系统统一
      f、2014年,Android5.0,引入Material Design设计语言
      g、2019年,Android10,首次采用数字版本号命名
    B、苹果系统
      a、2007年,首款苹果手机发布,系统名为iPhone OS
      b、2010年,系统更名为iOS,并推出适用于iPad的版本
      c、2013年,iOS7采用扁平化设计风格
      d、2016年,iOS10开放更多系统功能接口
      e、2019年,苹果将iPad的系统独立为iPadOS,专注于平板体验优化
      f、2020年,iOS14引入主屏幕小组件、App资源库等新功能
      g、3种系统,macOS(台式机电脑、笔记本电脑)、iPadOS(平板电脑)、iOS(手机)
  (2)应用
    A、原生应用(Native)
      a、完全使用平台专属语言(安卓用Kotlin/Java,苹果用Swift/Objective-C)编写的应用程序
      b、直接调用系统提供的原生UI组件和API
      c、能获得最佳的性能、流畅的交互体验和最完整的系统功能访问权限
    B、原生应用的缺陷与解决方案
      a、缺陷,需用两种平台专属语言,为安卓和iOS开发两套代码
      b、解决,用前端熟悉的React Native技术,写一套JS/JSX代码,最终由双平台各自渲染为原生组件,降低开发成本与门槛
    C、混合应用(Hybrid)
      a、把系统内置的网页渲染容器(如安卓的WebView、苹果的WKWebView)作为外壳
      b、大部分的业务逻辑和用户界面是通过运行在容器中的Web技术(HTML、CSS、JavaScript)来实现的
      c、一套Web代码可同时运行在两个平台,开发效率高,其它方面如性能、体验、集成能力,与原生应用相比显弱
    D、网页渲染容器(网页渲染组件)
      a、封装了网页渲染引擎和(应用与网页)双向通信接口的组件/类,能识别px、rem,能渲染H5网页
      b、postcss-pxtorem把开发人员设置的固定值px转换为相对值rem,以实现对不同分辨率网页容器的适配
    E、操作系统分配执行环境给应用的过程
      a、通过识别应用程序的组成结构(如是否包含WebView组件或特定框架的运行时库)
      b、来动态地为其分配合适的执行环境(如原生的ART虚拟机或混合模式下的WebView引擎)
      c、以确保应用能够正确且高效地运行
    F、react.js与React Native的关系
      a、同属Facebook(现Meta)开发的技术体系
      b、共享React核心思想(如组件化、声明式编程、虚拟DOM)和JSX语法
      c、状态管理逻辑(如Hooks)一致
      d、React.js用于Web端DOM渲染
      e、React Native用于移动端原生组件渲染,组件类型和渲染底层不同
      f、开发者可复用React知识快速上手
  (3)QQ
    A、1999年,腾讯QQ(当时名为OICQ,后因侵权诉讼改名)正式上线
    B、2002年,QQ群功能诞生,开启“多对多”社交模式,用户量迅速增长
    C、2004年,QQ注册用户突破3亿,腾讯在香港主板上市
    D、2010年,3Q大战
      a、1月21日,腾讯推QQ医生3.2仿360,赠诺顿并借春节强推
      b、5月31日,QQ医生升4.0为QQ电脑管家,含360主流功能
      c、9月27日,360发“隐私保护器”,查QQ是否窥用户隐私
      d、10月27日,腾讯联合多公司发声明,要求调查360
      e、10月29日,360推“360扣扣保镖”,称保QQ用户安全
      f、11月3日,腾讯发信,装360电脑停QQ,逼用户二选一
      g、11月4日,四家厂商召开发布会,客户端不兼容360
      h、11月10日,经协调,360召回软件,腾讯恢复QQ与360兼容
    E、2023年,推出QQ9.0版本,实现多端统一
  (4)微信
    A、H5,使用(微信基于系统网页渲染容器加工后的)专属容器加载运行
    B、小程序(rpx)
      a、小程序使用自研的双线程容器运行
      b、小程序规定屏幕宽度为750rpx,即Responsive Pixel,响应式像素
      c、小程序根据实际屏幕宽度的px值和元素尺寸的rpx值,计算出元素尺寸的px值
      d、计算公式为,设备实际屏幕宽度px值÷750×元素尺寸rpx值=元素尺寸px值
    C、微信的发展历程
      a、2011年,1月,微信上线,实现文字、图片即时通讯;5月,新增语音对讲
      b、2012年,新增语音通话、视频通话、朋友圈、公众号
      c、2013年,新增微信支付,绑定银行卡,开启移动支付
      d、2014年,新增微信红包功能
      e、2017年,新增小程序,整合服务与内容,构建生态闭环
4、当依据二倍图(@2x)设计稿开发H5网页时,正确的适配逻辑是
 (1)设置尺寸
    A、尺寸没有类似于background-size的属性可以自动压缩
    B、尺寸使用设计稿标注的1/2,通过CSS设置宽度、高度、字体大小、间距等
    C、尺寸使用设计稿的原标注,以行业通用的@2x设计稿宽度750px为基准
      a、通过postcss-pxtorem(需配置rootValue=75,对应750px设计稿)自动转换为rem!!!
      b、通过postcss-px-to-viewport(需配置viewportWidth=750)自动转换为vw/vh
    D、尺寸不能使用scale(0.5),因为它
      a、只会在视觉上缩小显示,元素仍然占据原始尺寸
      b、导致布局错乱(如原位置会有大块空白)和点击区域不准确等问题
  (2)设置图片
    A、背景有background-size属性,通过该属性把@2x图片压缩到(1)中盒子里显示
    B、4个(2×2)物理像素显示1个逻辑像素,可充分利用高分辨率屏幕的显示能力,避免图片模糊      
    C、对于<img>标签,可使用srcset属性提供不同倍率的图片,由浏览器根据dpr自动选择加载最合适的一张,这是更现代的解决方案      
5、多倍图适配方案
  (1)使用srcset属性实现自动适配
    <img src="image@1x.png" 
       srcset="image@1x.png 1x, image@2x.png 2x, image@3x.png 3x" 
       alt="示例图片">
  (2)使用CSS媒体查询适配
    <div class="retina-image"></div>
    <style>
      .retina-image {
        width: 200px; /* 实际显示宽度 */
        height: 150px; /* 实际显示高度 */
        background-image: url('image@1x.png');
        background-size: 200px 150px; /* 与元素尺寸一致 */
      }
      /* 2倍图适配 */
      @media (-webkit-min-device-pixel-ratio: 2),(min-resolution: 192dpi) {
        .retina-image {
          background-image: url('image@2x.png');
        }
      }
      /* 3倍图适配 */
      @media (-webkit-min-device-pixel-ratio: 3),(min-resolution: 288dpi) {
        .retina-image {
          background-image: url('image@3x.png');
        }
      }
    </style>
  (3)使用JavaScript动态适配
    <img id="js-adapt-image" alt="JS适配图片">
    <script>
      //获取设备像素比
      const dpr = window.devicePixelRatio || 1;
      let imageUrl = 'image@1x.png';
      //根据像素比选择合适的图片
      if (dpr >= 3) {
        imageUrl = 'image@3x.png';
      } else if (dpr >= 2) {
        imageUrl = 'image@2x.png';
      }
      //设置图片源
      document.getElementById('js-adapt-image').src = imageUrl;
    </script>
6、移动端开发注意事项
  (1)iOS页面跳转位置问题:
    A、原因:浏览器会保持前一个页面的滚动位置
    B、解决方案:window.scrollTo(0, 0);或document.documentElement.scrollTop = 0;

二、vant-ui相关
1、<input type="value">
  (1)button,定义可点击按钮(多数情况下,用于通过 JavaScript 启动脚本)
  (2)checkbox,定义复选框
  (3)file,定义输入字段和 "浏览"按钮,供文件上传
  (4)hidden,定义隐藏的输入字段,发送表单的时候,隐藏域的信息也被一起发送到服务器,
    https://blog.csdn.net/weixin_38098192/article/details/90265302
  (5)image,定义图像形式的提交按钮
  (6)password,定义密码字段,该字段中的字符被掩码
  (7)radio,定义单选按钮
  (8)reset,定义重置按钮,重置按钮会清除表单中的所有数据
  (9)submit,定义提交按钮,提交按钮会把表单数据发送到服务器
  (10)text,定义单行的输入字段,用户可在其中输入文本,默认宽度为20个字符
2、深度选择器
  当<style scoped>只作用于当前组件中的元素,可采用下列方式影响到子组件
  附、全局样式
    <style>
      .wrap .child {
        color: red;
      }
    </style>
  (1)本页样式 >>> ,原生css支持,sass/less可能无法识别
    <style scoped>
      .wrap >>> .child {
        color: red;
      }
    </style>
  (2)本页样式 /deep/ ,sass/less可识别,在vue 3.0会报错
    <style scoped>
      .wrap /deep/ .child {
        color: red;
      }
    </style>
  (3)本页样式 ::v-deep ,vue 3.0支持,编译速度快 
    <style scoped>
      .wrap ::v-deep .child {
        color: red;
      }
    </style>
3、vant-ui动态内容
  (1)notify 消息提示,没有遮罩,页面顶部显示,有样式变化,3秒后自动消失
  (2)toast 轻提示,没有遮罩,页面居中显示,无样式变化,3秒后自动消失
  (3)NoticeBar 通知栏,没有遮罩,固定位置显示,有样式变化,不消失
  (4)Popover 气泡弹出框,没有遮罩,点击处显示,会基于reference插槽的内容进行定位,常用于下拉选项,
  (5)overlay 遮罩层,全屏罩住
  (6)popup 弹出层,有遮罩,常用触底显示,确认后消失,常用于省市县、日期选择
  (7)dialog 弹出框,有遮罩,常用居中显示,确认后消失
4、van-field
  (1)输入框,正常
  (2)文本框,type="textarea"
  (3)下拉框的逻辑
    输入框禁用,右侧添加三角,
    点击事件让下方气泡popup出现、传出序号、计算-从总数据中找出的相关数据-成下拉选项,
    点击事件从自身数据中找出的相关数据-成为下拉选项,气泡popup出现
    点击确定时,给对应的v-model赋值,气泡消失
  (4)其它,用input插槽,
    自定义输入框,使用此插槽后,与输入框相关的属性和事件将失效
    在Form组件进行表单校验时,会使用input插槽中子组件的value,而不是Field组件的value
  (5)<van-uploader/>的部分属性
    capture,图片选取模式,可选值为camera,直接调起摄像头
    after-read,文件读取完成后的回调函数
    before-read,文件读取前的回调函数,返回false可终止文件读取,支持返回Promise
4、Rule数据结构,只有一个对象项的数组
  (1)required,是否为必选字段,boolean
  (2)message,错误提示文案
  (3)trigger,本项规则的触发时机,可选值为onChange、onBlur
  (4)formatter,格式化函数,将表单项的值转换后,再用“pattern”或“validator”进行校验
  (5)pattern,通过正则表达式进行校验,返回值为false时,红色的message出现在输入框的下方
  (6)validator,通过函数进行校验,返回值为false时,机制让红色的message出现在输入框的下方;
    返回值为promise实例时,机制向其注入resolve、reject,在reject里让红色的message出现在输入框的下方
    validator:function(rule, val, callback) {
      return new Promise(function(resolve, reject) {
        setTimeout(function() {
          const dataNum = Date.now()
          if (dataNum % 2 === 0) {
            resolve('成功')
          } else {
            reject('失败')
          }
        }, 2000)
      })
    }
5、插槽input示例
(1)html部分
  <van-form @submit="beforeSubmit" ref="form">
    <template v-for="(item,idx) in formList">
      <div class="sign-form" :key="idx" v-if="item.inputType == 0">
        <van-field name="name" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" show-word-limit
              :label="item.topicName" v-model="formData[idx]" :placeholder="'请输入'+item.topicName" :maxlength="item.numLimit" />
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 1">
        <van-field rows="4" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" autosize
              v-model="formData[idx]" :label="item.topicName" type="textarea" show-word-limit />
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 2">
        <van-field :label="item.topicName" v-model="formData[idx]" right-icon="arrow-down" readonly placeholder="请选择"
              :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" @click="changeCol(idx)" />
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 3 && item.isCheckbox == 0">
        <van-field :label="item.topicName" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]">
          <template #input>
            <van-radio-group v-model="formData[idx]" direction="horizontal" class="hor-style">
              <van-radio v-for="(v,k) in JSON.parse(item.optionInfo)" :name="v.option" :key="k">{{v.option}}</van-radio>
            </van-radio-group>
          </template>
        </van-field>
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 3 && item.isCheckbox == 1">
        <van-field :label="item.topicName" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]">
          <template #input>
            <van-checkbox-group v-model="formData[idx]" :max="item.selectLimitMax ? item.selectLimitMax : 0" direction="horizontal">
              <van-checkbox v-for="(value,key) in JSON.parse(item.optionInfo)" :name="value.option" shape="square" :key="key">{{value.option}}</van-checkbox>
            </van-checkbox-group>
          </template>
        </van-field>
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 4">
        <div class="van-form_card">
          <div class="field__label">{{item.topicName}}</div>
          <div class="field__desc">支持“图片”扩展名:png、gif、jpeg、pcx、psd、tiff</div>
        </div>
        <van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" v-model="formData[idx]">
          <template #input>
            <div class="upload-preview">
              <van-image v-if="formData[idx]" width="4rem" height="3rem" fit="contain" :src="formData[idx]" />
              <van-uploader :before-read="uploadFile" accept="image/png,image/gif,image/jpeg,image/pcx,image/psd,image/tiff" :name="idx">
                <van-icon name="plus" />
              </van-uploader>
            </div>
          </template>
        </van-field>
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 5">
        <div class="van-form_card">
          <div class="field__label">{{item.topicName}}</div>
          <div class="field__desc">支持“视频”扩展名:avi、MP4、rmvb</div>
        </div>
        <van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" v-model="formData[idx]">
          <template #input>
            <div class="flex-col">
              <div class="video-upload" v-if="showUpload">
                <span>视频上传中...</span>
                <van-progress :show-pivot="false" :percentage="videoUploadPercent" />
              </div>
              <span class="link" v-if="formData[idx]" @click="href(idx)">{{videoName[idx]}}</span>
              <van-uploader :before-read="uploadVideo" accept="*" v-if="!showUpload" :name="idx">
                <van-button icon="plus" type="primary">上传视频</van-button>
              </van-uploader>
            </div>
          </template>
        </van-field>
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 6">
        <div class="van-form_card">
          <div class="field__label">{{item.topicName}}</div>
          <div class="field__desc">支持“文件”扩展名:doc、docx、txt、pdf、pptx、ppt、xlsx、xls</div>
        </div>
        <van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请选择',index:idx }]" v-model="formData[idx]">
          <template #input>
            <div class="flex-col">
              <span class="link" v-if="formData[idx]" @click="href(idx)">{{formData[idx]}}</span>
              <van-uploader :before-read="uploadFile" accept=".doc, .docx, .txt, .pdf, .pptx, .ppt, .xlsx, .xls" :name="idx">
                <van-button icon="plus" type="primary">上传文档</van-button>
              </van-uploader>
            </div>
          </template>
        </van-field>
      </div>
      <div class="sign-form" :key="idx" v-if="item.inputType == 7">
        <van-field readonly @click="clickDatePicker(idx)" name="name" :rules="[{ validator: valiFormData, message: '请选择日期',index:idx }]"
              :label="item.topicName" v-model="formData[idx]" :placeholder="'请选择'+item.topicName" />
        <van-popup v-model="showDatePicker" position="bottom">
          <van-picker
                show-toolbar
                title="选择日期"
                :columns="dateCol"
                @cancel="showDatePicker = false"
                @confirm="dateConfirm" />
        </van-popup>
        <!-- van-popup默认在兄级加蒙层van-overlay,若没有出现,可能有人在项目的全局给清除了 -->
      </div>
    </template>
    <div class="vt-foot ">
      <van-button :loading="loading" loading-text="保存中..." native-type="submit" block color="#fdb235">提交</van-button>
      <!-- native-type,原生button标签的type属性,表单内点击此按钮,自动触发表单的onsubmit事件 -->
      <!-- loading,设置按钮为加载状态,加载状态下默认会隐藏按钮文字 -->
      <!-- loading-text,设置加载状态下的文字 -->
    </div>
  </van-form>
(2)js部分
  A、上传图片
    uploadFile(file, detail) {
      let formData = new FormData();
      formData.append('file', file);
      axios.post(this.$upload, formData, {
        'Content-Type': 'multipart/form-data'
      })
      .then(res => {
        if (res.data && res.data.code === 10000) {
          this.formData[detail.name] = res.data.data;
          let temp = res.data.data;
          this.formData.splice(detail.name, 1, temp);
        } else {
          this.$dialog.alert({
            title: '提示',
            message: '上传文件失败',
            theme: 'round-button',
          })
          .then(() => {
            //on close
          });
        }
      })
    },
  B、上传视频
  来源,https://help.aliyun.com/document_detail/383952.html
    import OSS from "ali-oss";
    //开放存储服务(OpenStorageService,简称OSS),是阿里云对外提供的海量,安全,低成本,高可靠的云存储服务。
    //用户可以通过简单的API(REST方式的接口),在任何时间、任何地点、任何互联网设备上进行数据上传和下载。 
    <van-uploader accept="*" v-if="!showUpload"  :name="idx"  :before-read="uploadVideo">
      <van-button icon="plus" type="primary">上传视频</van-button>
    </van-uploader>
    uploadVideo(file, detail) {
      let type = file.name.substring(file.name.lastIndexOf(".") + 1)
        .toLowerCase()
      if (this.videoType.indexOf(type) < 0 || file.type.indexOf("video") < 0) {
        this.$dialog.alert({
          title: '提示',
          message: '仅支持 .avi, .mp4, .rmvb, .mov 格式的视频',
          theme: 'round-button',
        });
        return;
      }
      let max = 1024 * 1024 * 1024;
      if (file.size > max) {
        this.$dialog.alert({
          title: '提示',
          message: '上传视频大小不能超过 1G!',
          theme: 'round-button',
        })
        .then(() => {});
        return;
      }
      myRequest({
          tokenName: "ios",
        })
        .then(data => {
          let client = new OSS({
            region: "oss-cn-beijing",
            accessKeyId: data.data.data.accessKeyId,
            accessKeySecret: data.data.data.accessKeySecret,
            bucket: data.data.data.bucketName,
            stsToken: data.data.data.securityToken,
          });
          const suffix = file.name.substr(file.name.indexOf("."));
          let fileUrl = `test/${new Date().getTime()}${suffix}`;
          client
            .multipartUpload(fileUrl, file, {
              progress: (p) => {
                this.videoUploadPercent = Math.round(p * 100);
                this.showUpload = true;
              },
              partSize: 102400,
            })
            .then((res) => {
              this.showUpload = false;
              this.videoName[detail.name] = res.name;
              let url = res.res.requestUrls[0];
              this.formData[detail.name] = url.substring(0, url.indexOf("?"));
              let temp = this.formData[detail.name];
              this.formData.splice(detail.name, 1, temp);

            })
            .catch((err) => {
              this.showUpload = false;
              console.log(err);
              this.$dialog.alert({
                title: '提示',
                message: '上传失败',
                theme: 'round-button',
              })
            });
        })
    }
    
三、微信!!!
1、概览
  (1)相关网址
    A、微信官方文档(包含所有产品),https://developers.weixin.qq.com/doc/
    B、微信开发者代码管理平台,https://git.weixin.qq.com/ 
  (2)账号分类(需要单独注册的主账号,注册地址也是登录地址)
    A、开放平台
    B、公众平台(服务号、公众号(原订阅号)、小程序、小游戏)
    C、其它(企业微信、小商店、腾讯小微)
    D、依附于其他账号的功能(微信支付、智能对话、视频号)
  (3)账号身份管理(解绑)
    A、在微信搜索公众号“公众平台安全助手”,点击进入
    B、在底部菜单找到“绑定查询”菜单,点击进入列表
    C、在列表中找到要解绑的账号,点击该账号,弹出“解除绑定”选项,点击确定
    (注:此操作仅为解除个人微信号与管理员/运营者身份的绑定,并非注销账号。账号注销需在各平台后台进行。)
2、产品账号
  (1)需要单独注册的主账号-开放平台(1项)
    附、注册地址,https://open.weixin.qq.com
    A、用途
      a、微信开放平台为开发者提供接入服务,可将两类产品接入同一开放平台
      b、一类是微信生态内的产品(如小程序、小游戏、公众号、服务号等)
      c、一类是微信生态外的产品(如第三方移动应用、第三方网站、第三方硬件设备)
      d、接入后,开发者可通过UnionID关联不同产品中的同一用户身份
      e、实现基于微信账号的统一身份识别
    B、身份标记
      a、unionid,是微信用户对于一个开放平台的身份标记,微信用户+开放平台账号=unionid
      b、openid,是微信用户对于一个应用的身份标记,微信用户+AppID=openid
      c、一个unionid可以对应多个openid
    C、代码
      a、无需代码:以平台配置、信息维护、基础功能开通为主
      b、需代码:涉及接口调用、用户交互、业务逻辑实现的功能(如登录、支付、消息处理等)
      c、代码入口
        c1、移动应用,可在项目配置文件、应用入口文件、业务逻辑文件添加代码
        c2、Web应用,可在HTML页面、后端服务器添加
        c3、公众号/小程序,可在小程序代码文件、公众号后端服务器添加
  (2)需要单独注册的主账号-公众平台(4项)
    附、注册地址,https://mp.weixin.qq.com/
    A、服务号,账号分类为“服务号”,1个月可发送4条群发消息,服务交互
      a、无需代码:基础图文、用户消息回复
      b、需代码:几乎所有核心功能,如模板消息、微信支付、高级客服、核心API接口调用、网页授权、复杂业务逻辑实现
      c、代码入口:在开发者中心的服务器配置中填入自有服务器API地址
      d、强依赖开发
    B、公众号(原订阅号),账号分类为“公众号”,1天可群发1次消息,传达资讯
      a、无需代码:图文编辑、群发、自动回复
      b、需代码:个性化自定义菜单、网页授权、高级接口(如用户管理)、与第三方系统集成
      c、代码入口:在开发者中心的服务器配置中填入自有服务器API地址
    C、小程序,账号分类为“小程序”
      a、无需代码:后台版本管理、数据查看、客服回复
      b、需代码:所有前端界面、交互逻辑、API调用、后台业务(或使用云开发)
      c、代码入口:使用微信开发者工具创建、编写、调试并上传项目代码
      d、功能完全由代码实现,仅运营管理免代码
      e、官方文档-小程序-开发-工具-开发辅助-微信开发者·代码管理(代码调试)
    D、小游戏,账号分类为“小程序”,并在开发时选择游戏类目
      a、无需代码:后台版本提审、数据监控
      b、需代码:游戏引擎、所有核心玩法、图形渲染、音效、逻辑、广告接入等全部内容
      c、代码入口:使用开发者工具编写游戏代码并上传
      d、是代码依赖度最高的产品
  (3)需要单独注册的主账号-其它(3项)
    A、企业微信
      a、无需代码:基础组织架构管理、内部通讯、考勤审批等OA应用使用,客户联系基础功能配置
      b、需代码:自定义工作台应用开发、API系统集成、会话存档二次开发、个性化机器人创建
      c、代码入口:企业微信管理后台-应用与小程序-自建应用中进行应用创建和API配置管理
      d、注册地址:企业微信官网,https://work.weixin.qq.com/
      e、备注:注册一个组织/公司账号
    B、小商店
      a、无需代码:商品上下架管理、订单处理售后、物流跟踪、基础营销活动创建、直播带货功能
      b、需代码:自定义UI组件开发、外部ERP系统对接、个性化业务逻辑实现、数据报表定制
      c、代码入口:小商店后台-店铺-自定义组件中进行组件开发和上传,使用API接口对接
      d、注册地址:小商店后台,https://shop.weixin.qq.com/
      e、备注:个人或企业均可注册(由微信小店升级而来)
    C、腾讯小微
      a、无需代码:硬件设备注册绑定、基础语音指令配置、设备状态监控、基础技能启用管理
      b、需代码:硬件SDK集成开发、自定义语音技能创建、设备端语音交互逻辑、服务端API对接
      c、代码入口:腾讯小微开放平台-开发者中心创建技能项目,下载SDK进行硬件集成开发
      d、注册地址:腾讯小微,https://xiaowei.weixin.qq.com/
      e、备注:针对硬件厂商,需要注册账号以将智能语音助手接入硬件设备
  (4)依附于其他账号的功能(3项)
    A、微信支付
      a、无需代码:商户资质申请入驻、交易流水查询下载、退款申请操作、账单统计分析查看
      b、需代码:支付接口调用签名、支付结果回调处理、退款接口对接、分账功能开发实现
      c、代码入口:微信支付商户平台-开发配置中获取API密钥,在业务系统中调用支付API接口
      d、依赖于:已认证资质的企业或个体工商户的服务号、公众号(即原订阅号)、小程序、企业微信、APP
    B、智能对话
      a、无需代码:意图词槽配置、对话流程设计、问答知识库维护、基础对话机器人训练调试
      b、需代码:API接口集成调用、业务系统对接、个性化对话逻辑开发、数据统计分析处理
      c、代码入口:微信对话开放平台创建机器人,获取API密钥后在自己的服务器调用对话接口
      d、依赖于:公众号、小程序、企业微信
    C、视频号
      a、无需代码:视频拍摄上传发布、直播开播管理、评论互动回复、带货橱窗商品管理
      b、需代码:视频内容API管理、直播数据接口调用、自动化运营功能开发、数据分析对接
      c、代码入口:视频号助手后台-开发管理申请接口权限,获取access_token调用相关API
      d、身份依赖:必须由已实名认证的个人微信用户创建,或关联到已认证的公众号(企业/机构主体)
      e、注册地址:无需单独注册,通过微信个人账号的“发现”->“视频号”入口创建
3、小程序从0到上线的主要步骤
  (1)注册账号:访问微信公众平台,点击“立即注册”,选择“小程序”类型,填写企业或个人的相关信息完成注册,获取AppID
  (2)安装开发工具:下载并安装微信开发者工具,使用注册好的账号登录
  (3)创建项目:在开发者工具中点击“新建项目”,填写项目名称、目录和AppID,可选择空白模板或官方示例模板
  (4)页面开发:
    A、使用WXML编写页面结构,WXSS编写页面样式,JS编写页面逻辑
    B、通过微信提供的API实现功能,如获取用户信息、支付等
    C、使用wx.request发起网络请求与后端服务交互
  (5)调试与测试:利用开发者工具的调试功能,检查页面渲染、逻辑执行和网络请求
    A、确保开发电脑和手机(如电脑网线+手机WiFi)处于同一局域网下
    B、点击开发者工具的“预览”或“真机调试”生成二维码
    C、用手机微信扫一扫二维码,进入小程序
    D、手机微信实时加载项目的代码、资源(如图片、样式文件)并运行
  (6)代码上传:在开发者工具中点击“上传”,填写版本号和项目备注,将代码上传至微信公众平台
  (7)提交审核:
    A、登录微信公众平台,在左侧导航栏进入“管理”->“版本管理”页面
    B、选择上传的版本,填写审核信息并提交
    C、微信团队会对小程序的功能、内容和安全性进行审核
  (8)发布上线:审核通过后,在微信公众平台点击“发布”,小程序将正式上线,用户可通过搜索或扫码访问

四、微信小程序-目录说明
  来源,https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
  来源,https://developers.weixin.qq.com/miniprogram/dev/framework/structure.html
  附、目录结构
    ├── app.js
    ├── app.json
    ├── app.wxss
    ├── pages
    │   │── index
    │   │   ├── index.wxml
    │   │   ├── index.js
    │   │   ├── index.json
    │   │   └── index.wxss
    │   └── logs
    │       ├── logs.wxml
    │       └── logs.js
    └── utils
1、全局配置,一个小程序主体由三个文件组成,必须放在项目的根目录
  (1)app.js,小程序全局逻辑
    App({ //注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等
      onLaunch: function () {},//生命周期回调——监听小程序初始化
      onShow: function () {},//生命周期回调——监听小程序启动或切前台
      onHide: function () {},//生命周期回调——监听小程序切后台。
      onError: function () {},//错误监听函数。
      onPageNotFound: function () {},//页面不存在监听函数。
      onUnhandledRejection: function () {},//未处理的 Promise 拒绝事件监听函数。
      onThemeChange: function () {},//监听系统主题变化
      其他: 任意类型,开发者可以添加任意的函数或数据变量到参数中,用this可以访问
    })
    附、其它页面,获取实例
    // other.js
      var appInstance = getApp()
      console.log(appInstance.globalData) // I am global data
  (2)app.json,小程序全局配置
    {
      "pages": ["pages/index/index", "pages/logs/index"], //小程序的所有页面
      "requiredBackgroundModes": ["audio", "location"],
      "requiredPrivateInfos": [ 
        "getLocation",
        "onLocationChange",
        "startLocationUpdateBackground"
        "chooseAddress"
      ],
      "permission": {
        "scope.userLocation": {
          "desc": "你的位置信息将用于小程序位置接口的效果展示" //高速公路行驶持续后台定位
        }
      },
      "window": {
        "navigationBarTitleText": "Demo"
      },
      "tabBar": {
        "height": "50px",
        "fontSize": "10px",
        "list": [
          {
            "pagePath": "pages/index/index",
            "text": "首页"
          },
          {
            "pagePath": "pages/logs/logs",
            "text": "日志"
          }
        ]
      },
      "networkTimeout": {
        "request": 10000,
        "downloadFile": 10000
      },
      "plugins": { //在app.json中声明需要使用的插件
        "myPlugin": {
          "version": "1.0.0",
          "provider": "wxidxxxxxxxxxxxxxxxx"
        }
      },
      "debug": true,
      "resizable": true, //iPad上启用屏幕旋转支持
    }
  (3)app.wxss,小程序全局样式表
2、页面配置,一个小程序页面由四个文件组成
  (1)index.js,页面逻辑
    page({//组件逻辑用Component({})
      data: {},
      options: {},
      behaviors: String/Array,类似于mixins和traits的组件间代码复用机制,
      onLoad: function () {},
      onShow: function () {},
      onReady: function () {},
      onHide: function () {},
      onUnload: function () {},
      onRouteDone: function () {},
      onPullDownRefresh: function () {},
      onReachBottom: function () {},
      onShareAppMessage: function () {},
      onShareTimeline: function () {},
      onAddToFavorites: function () {},
      onPageScroll: function () {},
      onResize(res) { //屏幕旋转事件
        res.size.windowWidth //新的显示区域宽度
        res.size.windowHeight //新的显示区域高度
      },
      onTabItemTap: function () {},
      onSaweexitState: function () {},
      onShow: function () {},
      //其他: 任意类型,开发者可以添加任意的函数或数据变量到参数中,
      //在页面的函数中用this可以访问,这部分属性会在页面实例创建时进行一次深拷贝
      tapName: function(event) {
        console.log(event)
      }
    })
    附、getCurrentPages,获取当前页面栈,数组中第一个元素为首页,最后一个元素为当前页面
    附、Router,页面路由器对象,可以通过this.pageRouter或this.router获得当前页面或自定义组件的路由器对象
  (2)index.json,页面配置
    {
      "navigationBarBackgroundColor": "#ffffff",
      "navigationBarTextStyle": "black",
      "navigationBarTitleText": "微信接口功能演示",
      "backgroundColor": "#eeeeee",
      "backgroundTextStyle": "light"
      "pageOrientation": "auto", //手机单页面启用屏幕旋转支持
    }
  (3)index.wxss,页面样式表
  (4)index.wxml,页面结构

五、微信小程序-WXML语法参考
 附、是一套标签语言,结合基础组件、事件系统,可以构建出页面的结构
1、数据绑定,使用Mustache语法(双大括号)将变量包起来
  (1)简单绑定
    <view> {{ message }} </view>
  (2)组件属性
    <view id="item-{{id}}"> </view>
  (3)控制属性
    <view wx:if="{{condition}}"> </view>
  (4)关键字
    <checkbox checked="{{false}}"> </checkbox>
  (5)三元运算
    <view hidden="{{flag ? true : false}}"> Hidden </view>
  (6)算数运算
    <view> {{a + b}} + {{c}} + d </view>
  (7)逻辑判断
    <view wx:if="{{length > 5}}"> </view> 
  (8)字符串运算
    <view>{{"hello" + name}}</view>
  (9)数据路径运算
    <view>{{object.key}} {{array[0]}}</view>
  (10)组合数组
    <view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
  (11)组合对象
    <template is="objectCombine" data="{{for: a, bar: b}}"></template>
  (12)组合对象,后面覆盖前面
    <template is="objectCombine" data="{{...obj1, ...obj2, a: 5}}"></template>
  (13)组合对象
    <template is="objectCombine" data="{{foo, bar}}"></template>
  (14)花括号和引号之间如果有空格,将最终被解析成为字符串
    <view wx:for="{{[1,2,3]}} ">
      {{item}}
    </view> //等同于
    <view wx:for="{{[1,2,3] + ' '}}">
      {{item}}
    </view>
  (15)当wx:for的值为字符串时,会将字符串解析成字符串数组
    <view wx:for="array">
      {{item}}
    </view> //等同于
    <view wx:for="{{['a','r','r','a','y']}}">
      {{item}}
    </view>
  (16)data
    Page({
      data: {
        message: 'Hello MINA!',
        id: 0,
        condition: true,
        a: 1,
        b: 2,
        c: 3,
        name: 'MINA',
        object: {
          key: 'Hello '
        },
        array: ['MINA'],
        zero: 0,
        obj1: {
          a: 1,
          b: 2
        },
        obj2: {
          c: 3,
          d: 4
        },
        foo: 'my-foo',
        bar: 'my-bar',
      }
    })
2、列表渲染,
  (1)默认数组的当前项的下标变量名默认为index,数组当前项的变量名默认为item
    <view wx:for="{{array}}">
      {{index}}: {{item.message}}
    </view>
  (2)使用wx:for-item可以指定数组当前元素的变量名,使用wx:for-index可以指定数组当前下标的变量名:
    <view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
      {{idx}}: {{itemName.message}}
    </view>
  (3)wx:for也可以嵌套,下边是一个九九乘法表 
    <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
      <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
        <view wx:if="{{i <= j}}">
          {{i}} * {{j}} = {{i * j}}
        </view>
      </view>
    </view> 
  (4)block wx:for,渲染一个包含多节点的结构块
    <block wx:for="{{[1, 2, 3]}}">
      <view> {{index}}: </view>
      <view> {{item}} </view>
    </block>
  (5)数据
    Page({
      data: {
        array: [{
          message: 'foo',
        }, {
          message: 'bar'
        }]
      }
    })
  (6)wx:key的值以两种形式提供
    A、字符串,代表在for循环的array中item的某个property,该property的值需要是列表中唯一的字符串或数字,且不能动态改变
    B、保留关键字*this,代表在for循环中的item本身,这种表示需要item本身是一个唯一的字符串或者数字
      <switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;"> {{item.id}} </switch>
      <button bindtap="switch"> Switch </button>
      <button bindtap="addToFront"> Add to the front </button>
      <switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;"> {{item}} </switch>
      <button bindtap="addNumberToFront"> Add to the front </button>
      Page({
        data: {
          objectArray: [
            {id: 5, unique: 'unique_5'},
            {id: 4, unique: 'unique_4'},
            {id: 3, unique: 'unique_3'},
            {id: 2, unique: 'unique_2'},
            {id: 1, unique: 'unique_1'},
            {id: 0, unique: 'unique_0'},
          ],
          numberArray: [1, 2, 3, 4]
        },
        switch: function(e) {
          const length = this.data.objectArray.length
          for (let i = 0; i < length; ++i) {
            const x = Math.floor(Math.random() * length)
            const y = Math.floor(Math.random() * length)
            const temp = this.data.objectArray[x]
            this.data.objectArray[x] = this.data.objectArray[y]
            this.data.objectArray[y] = temp
          }
          this.setData({
            objectArray: this.data.objectArray
          })
        },
        addToFront: function(e) {
          const length = this.data.objectArray.length
          this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
          this.setData({
            objectArray: this.data.objectArray
          })
        },
        addNumberToFront: function(e){
          this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
          this.setData({
            numberArray: this.data.numberArray
          })
        }
      })
3、条件渲染
  (1)使用 wx:if="" 来判断是否需要渲染该代码块:
    <view wx:if="{{condition}}"> True </view>
  (2)用 wx:elif 和 wx:else 来添加一个 else 块:
    <view wx:if="{{length > 5}}"> 1 </view>
    <view wx:elif="{{length > 2}}"> 2 </view>
    <view wx:else> 3 </view>
  (3)用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性
    <block wx:if="{{true}}">
      <view> view1 </view>
      <view> view2 </view>
    </block>
    <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性
  (4)if与hidden,wx:if有更高的切换消耗;hidden有更高的初始渲染消耗
4、模板,
  WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用
(1)定义模板
  使用name属性,作为模板的名字。然后在<template/>内定义代码片段,如:
    <!--
      index: int
      msg: string
      time: string
    -->
    <template name="msgItem">
      <view>
        <text> {{index}}: {{msg}} </text>
        <text> Time: {{time}} </text>
      </view>
    </template>
(2)使用模板
  使用is属性,声明需要的使用的模板,然后将模板所需要的data传入
    <template is="msgItem" data="{{...item}}"/>
    Page({
      data: {
        item: {
          index: 0,
          msg: 'this is a template',
          time: '2016-09-15'
        }
      }
    })
(3)is属性可以使用Mustache语法,来动态决定具体需要渲染哪个模板
  <template name="odd">
    <view> odd </view>
  </template>
  <template name="even">
    <view> even </view>
  </template>
  <block wx:for="{{[1, 2, 3, 4, 5]}}">
    <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/>
  </block>
(4)模板的作用域
  模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的<wxs/>模块
5、引用
  WXML提供两种文件引用方式import和include
  (1)在 item.wxml 中定义了一个叫item的template
    <template name="item">
      <text>{{text}}</text>
    </template>
  (2)在 index.wxml 中引用了 item.wxml,就可以使用item模板
    <import src="item.wxml"/>
    <template is="item" data="{{text: 'forbar'}}"/>
  (3)import的作用域
    import有作用域的概念,只会import目标文件中定义的template,而不会import目标文件import的template
    <!-- A.wxml -->
      <template name="A">
        <text> A template </text>
      </template>
    <!-- B.wxml -->
      <import src="a.wxml"/>
      <template name="B">
        <text> B template </text>
      </template>
    <!-- C.wxml -->
      <import src="b.wxml"/>
      <template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
      <template is="B"/>
  (4)include
    include,可以将目标文件除了<template/><wxs/>外的整个代码引入,相当于是拷贝到include位置
      <!-- index.wxml -->
        <include src="header.wxml"/>
        <view> body </view>
        <include src="footer.wxml"/>
      <!-- header.wxml -->
        <view> header </view>
      <!-- footer.wxml -->
        <view> footer </view>

六、微信小程序-WXS语法参考
1、说明
  (1)WXS是小程序的一套脚本语言,与JavaScript是不同的语言,有自己的语法
  (2)WXS代码可以编写在wxml文件中的<wxs>标签内,或以.wxs为后缀名的文件内
  (3)变量、注释、运算符、语句、数据类型、基础类库,与js类似
2、模块
  (1)每一个.wxs文件和<wxs>标签都是一个单独的模块
  (2)每个模块都有自己独立的作用域,即在一个模块里面定义的变量与函数,默认为私有的,对其他模块不可见
  (3)一个模块要想对外暴露其内部的私有变量与函数,只能通过module.exports实现
  (4)在微信开发者工具里面,右键可以直接创建.wxs文件,在其中直接编写WXS脚本
3、.wxs文件
  (1)module模块
    A、module对象,每个wxs模块均有一个内置的module对象
    B、exports属性,通过该属性,可以对外共享本模块的私有变量与函数
    /pages/tools.wxs
      var foo = "'hello world' from tools.wxs";
      var bar = function (d) {
        return d;
      }
      module.exports = {
        FOO: foo,
        bar: bar,
      };
      module.exports.msg = "some msg";
    page/index/index.wxml
      <wxs src="./../tools.wxs" module="tools" />
      <view> {{tools.msg}} </view>
      <view> {{tools.bar(tools.FOO)}} </view>
    页面输出
      some msg
      'hello world' from tools.wxs
  (2)require函数
    在.wxs模块中引用其他wxs文件模块,可以使用require函数,引用的时候,要注意如下几点
    A、只能引用.wxs文件模块,且必须使用相对路径
    B、wxs模块均为单例,wxs模块在第一次被引用时,会自动初始化为单例对象;多个页面,多个地方,多次引用,使用的都是同一个wxs模块对象
    C、如果一个wxs模块在定义之后,一直没有被引用,则该模块不会被解析与运行
      /pages/tools.wxs
        var foo = "'hello world' from tools.wxs";
        var bar = function (d) {
          return d;
        }
        module.exports = {
          FOO: foo,
          bar: bar,
        };
        module.exports.msg = "some msg";
      /pages/logic.wxs
        var tools = require("./tools.wxs");
        console.log(tools.FOO);
        console.log(tools.bar("logic.wxs"));
        console.log(tools.msg);
      /page/index/index.wxml -->
        <wxs src="./../logic.wxs" module="logic" />
  (3)<wxs>标签的属性
    A、module属性,是当前<wxs>标签的模块名,   
    B、src属性,可以用来引用其他的wxs文件模块,引用的时候,要注意如下几点
      只能引用.wxs文件模块,且必须使用相对路径
      wxs模块均为单例,wxs模块在第一次被引用时,会自动初始化为单例对象;多个页面,多个地方,多次引用,使用的都是同一个wxs模块对象
      /pages/index/index.js
        Page({
          data: {
            msg: "'hello wrold' from js",
          }
        })
      /pages/index/index.wxml -->
        <wxs src="./../comm.wxs" module="some_comms"></wxs>
      也可以直接使用单标签闭合的写法
        <wxs src="./../comm.wxs" module="some_comms" />
      调用some_comms模块里面的bar函数,且参数为some_comms模块里面的 foo   
        <view>{{some_comms.bar(some_comms.foo)}}</view>
      调用 some_comms 模块里面的bar函数,且参数为page/index/index.js里面的msg  
        <view>{{some_comms.bar(msg)}}</view>
      页面输出:
        'hello world' from comm.wxs
        'hello wrold' from js
      上述例子在文件/page/index/index.wxml中通过<wxs>标签引用了/page/comm.wxs 模块
  (4)注意事项
    A、<wxs>模块只能在定义模块的WXML文件中被访问到,使用<include>或<import>时,<wxs>模块不会被引入到对应的WXML文件中
    B、<template>标签中,只能使用定义该<template>的WXML文件中定义的<wxs>模块
4、引入插件
  var myPluginInterface = requirePlugin('myPlugin');
  myPluginInterface.hello();

七、微信小程序-事件系统
  来源,https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html
1、什么是事件
  (1)事件是视图层到逻辑层的通讯方式,(由用户决定执行逻辑和执行时机)
  (2)事件可以将用户的行为反馈到逻辑层进行处理
  (3)事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数
  (4)事件对象可以携带额外信息,如id,dataset,touches
2、事件的使用方式,在组件中绑定一个事件处理函数
  (1)JS绑定
    以下,在index.wxml中,触发点击事件
      <view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
    以下,在index.js中,定义点击函数
      Page({
        tapName: function(event) {
          console.log(event)
        }
      })
  (2)WXS绑定
    以下,在index.wxml中,引入test.wxs脚本,触发点击事件
      <wxs module="wxs" src="./test.wxs"></wxs>
      <view id="tapTest" data-hi="Weixin" bindtap="{{wxs.tapName}}">绑定的WXS函数必须用{{Mustache语法,Mustache胡子}}括起来</view>
    以下,在test.wxs中,定义点击函数
      function tapName(event, ownerInstance) {
        console.log('tap Weixin', JSON.stringify(event))
      }
      module.exports = {
        tapName: tapName
      }
3、事件详解
  (1)冒泡从里向外,先捕获后冒泡
    bind,绑定事件,向上冒泡,后可以紧跟一个冒号,其含义不变,如bind:tap
    mut-bind,绑定事件,向上冒泡,与上级mut-bind“互斥”的,上级mut-bind不会被触发,上级的其它绑定依然会触发
    catch,绑定事件,阻止向上冒泡
  (2)冒泡与阻止冒泡,bindtap与catchtap,点击“inner view”会先后调用handleTap3和handleTap2
    <view id="outer" bindtap="handleTap1">
      “outer view”
      <view id="middle" catchtap="handleTap2">
        “middle view”
        <view id="inner" bindtap="handleTap3">
          “inner view”
        </view>
      </view>
    </view>
  (3)互斥事件,mut-bind,点击“inner view”会先后调用handleTap3和handleTap2,点击“middle view”会调用handleTap2和handleTap1
    <view id="outer" mut-bind:tap="handleTap1">
      “outer view”
      <view id="middle" bindtap="handleTap2">
        “middle view”
        <view id="inner" mut-bind:tap="handleTap3">
          “inner view”
        </view>
      </view>
    </view>
  (4)捕获从外向里,先捕获后冒泡
    capture-bind,捕获
    capture-catch,中断捕获、取消冒泡
  (5)事件的捕获阶段,点击“inner view”会先后调用handleTap2、handleTap4、handleTap3、handleTap1
    <view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
      “outer view”
      <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
        “inner view”
      </view>
    </view>
    如果将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2
  (6)事件对象,如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象
    A、BaseEvent 基础事件对象属性列表
      type,代表事件的类型
      timeStamp,页面打开到触发事件所经过的毫秒数
      target,触发事件的源组件
        id,事件源组件的id
        dataset,事件源组件上由data-开头的自定义属性组成的集合
      currentTarget,事件绑定的当前组件
        id,当前组件的id
        dataset,当前组件上由data-开头的自定义属性组成的集合
      mark,识别具体触发事件的target节点,承载一些自定义数据,类似于dataset
        <view mark:myMark="last" bindtap="bindViewTap">
          <button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
        </view>
        在上述WXML中,如果按钮被点击,将触发bindViewTap和bindButtonTap两个事件,事件携带的event.mark将包含myMark和anotherMark两项
        Page({
          bindViewTap: function(e) {
            e.mark.myMark === "last" //true
            e.mark.anotherMark === "leaf" //true
          }
        })
      //mark,会包含从触发事件的节点到根节点上所有的mark属性值
      //dataset,仅包含一个节点的data-属性值
    B、CustomEvent 自定义事件对象属性列表(继承 BaseEvent)
      detail,额外的信息
    C、TouchEvent 触摸事件对象属性列表(继承 BaseEvent)
      touches,触摸事件,当前停留在屏幕中的触摸点信息的数组,每个元素为一个Touch对象,表示当前停留在屏幕上的触摸点
        identifier,触摸点的标识符
        pageX/pageY,距离文档左上角的距离,文档的左上角为原点 ,横向为X轴,纵向为Y轴
        clientX/clientY,距离页面可显示区域(屏幕除去导航条)左上角距离,横向为X轴,纵向为Y轴
      changedTouches,触摸事件,当前变化的触摸点信息的数组
4、WXS响应事件
  (1)以下test.wxml
    <wxs module="test" src="./test.wxs"></wxs>
    <view bindtouchmove="{{test.touchmove}}" change:prop="{{test.propObserver}}" prop="{{propValue}}" class="movable"></view>
    //以上,WXS函数必须用{{}}括起来
    //以上,change:prop(标签上的监听--自悟),属性前面带“change:”前缀,是在prop属性被设置的时候触发WXS函数,类似Component定义的properties里面的observer属性
    //以上,在setData({propValue: newValue})调用之后会触发,而不只是值发生改变,所以在页面初始化的时候会调用一次WxsPropObserver的函数
    //以上,在JS里,可以自动调用WXS,因page()而执行
  (2)以下test.wxs
    module.exports = {
      touchmove: function(event, ownerInstance) {
        //event.instance,表示触发事件的组件的ComponentDescriptor实例
        //ownerInstance,表示的是触发事件的组件所在的组件的ComponentDescriptor实例,如果触发事件的组件是在页面内的,ownerInstance表示的是页面实例
      },
      propObserver: function(newValue, oldValue, ownerInstance, instance) {  
        //ownerInstance,表示的是触发事件的组件所在的组件的ComponentDescriptor实例,如果触发事件的组件是在页面内的,ownerInstance表示的是页面实例
        //instance,表示触发事件的组件的ComponentDescriptor实例
      }
    }
    //以上,instance.callMethod(funcName:string, args:object),WXS调用当前-组件-在逻辑层(App Service)定义的函数
    //以上,ownerInstance.callMethod(funcName:string, args:object),WXS调用当前-页面-在逻辑层(App Service)定义的函数
    //以上,在WXS里,可以手动调用JS

八、微信小程序-组件,基础组件
 来源,https://developers.weixin.qq.com/miniprogram/dev/component/
 附、<block><block/>,不是组件,只是一个包装元素,不会在页面中做任何的渲染,它能够设置if、for这些控制属性,设置class、id不会生效
1、视图容器
(1)cover-view,覆盖在原生组件之上的文本视图
  <view class="page-section page-section-gap">
    <map
      style="width: 100%; height: 300px;"
      latitude="{{latitude}}"
      longitude="{{longitude}}"
    >
      <cover-view class="cover-view">
        <cover-view class="container">
          <cover-view class="flex-wrp" style="flex-direction:row;">
            <cover-view class="flex-item demo-text-1"></cover-view>
            <cover-view class="flex-item demo-text-2"></cover-view>
            <cover-view class="flex-item demo-text-3"></cover-view>
          </cover-view>
        </cover-view>
      </cover-view>
    </map>
  </view>
(2)match-media,匹配检测节点
  <match-media min-width="300" max-width="600">
    <view>当页面宽度在 300 ~ 500 px 之间时展示这里</view>
  </match-media>
(3)movable-area,可移动的视图容器,在页面中可以拖拽滑动
  <movable-area>
    <movable-view x="{{x}}" y="{{y}}" direction="all">text</movable-view>
  </movable-area>
(4)scroll-view,可滚动视图区域。类似于轮播图,像素级滑动
  <scroll-view scroll-y="true" style="height: 300rpx;" bindscrolltoupper="upper" bindscrolltolower="lower" 
    bindscroll="scroll" scroll-into-view="{{toView}}" scroll-top="{{scrollTop}}">
    <view id="demo1" class="scroll-view-item demo-text-1"></view>
    <view id="demo2"  class="scroll-view-item demo-text-2"></view>
    <view id="demo3" class="scroll-view-item demo-text-3"></view>
  </scroll-view>
(5)swiper,滑块视图容器,swiper-item,仅可放置在swiper组件中,宽高自动设置为100%。类似于轮播图,块级滑动
  <swiper indicator-dots="{{indicatorDots}}"  autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
    <block wx:for="{{background}}" wx:key="*this">
      <swiper-item>
        <view class="swiper-item {{item}}"></view>
      </swiper-item>
    </block>
  </swiper>
(6)view,视图容器
  <view class="container">
    <view class="page-body">
      <view class="page-section">
        <view class="page-section-title">
          <text>flex-direction: row\n横向布局</text>
        </view>
        <view class="page-section-spacing">
          <view class="flex-wrp" style="flex-direction:row;">
            <view class="flex-item demo-text-1"></view>
            <view class="flex-item demo-text-2"></view>
            <view class="flex-item demo-text-3"></view>
          </view>
        </view>
      </view>
      <view class="page-section">
        <view class="page-section-title">
          <text>flex-direction: column\n纵向布局</text>
        </view>
        <view class="flex-wrp" style="flex-direction:column;">
          <view class="flex-item flex-item-V demo-text-1"></view>
          <view class="flex-item flex-item-V demo-text-2"></view>
          <view class="flex-item flex-item-V demo-text-3"></view>
        </view>
      </view>
    </view>
  </view>
2、基础内容
(1)icon,图标组件
  <view class="icon-box">
    <icon class="icon-box-img" type="success" size="93"></icon>
    <view class="icon-box-ctn">
      <view class="icon-box-title">成功</view>
      <view class="icon-box-desc">用于表示操作顺利完成</view>
    </view>
  </view>
(2)progress,进度条
  <view class="progress-box">
    <progress percent="20" show-info stroke-width="3"/>
  </view>
(3)rich-text,富文本
  <block wx:if="{{renderedByHtml}}">
    <rich-text nodes="{{htmlSnip}}"></rich-text>
  </block>
  const htmlSnip =
    `<div class="div_class">
      <h1>Title</h1>
      <p class="p">
        Life is <i>like</i> a box of
        <b> chocolates</b>.
      </p>
    </div>
    `
(4)text,文本
  <view class="page-section page-section-spacing">
    <view class="text-box" scroll-y="true" scroll-top="{{scrollTop}}">
      <text>{{text}}</text>
    </view>
    <button disabled="{{!canAdd}}" bindtap="add">add line</button>
    <button disabled="{{!canRemove}}" bindtap="remove">remove line</button>
  </view>
  const texts = [
    '2011年1月,微信1.0发布',
    '同年5月,微信2.0语音对讲发布',
    '10月,微信3.0新增摇一摇功能',
    '2012年3月,微信用户突破1亿',
    '4月份,微信4.0朋友圈发布',
    '同年7月,微信4.2发布公众平台',
    '2013年8月,微信5.0发布微信支付',
    '2014年9月,企业号发布',
    '同月,发布微信卡包',
    '2015年1月,微信第一条朋友圈广告',
    '2016年1月,企业微信发布',
    '2017年1月,小程序发布',
    '......'
  ]
3、表单组件
(1)button,按钮
  <view class="button-sp-area">
    <button type="primary" plain="true">按钮</button>
    <button type="primary" disabled="true" plain="true">不可点击的按钮</button>
  </view>
(2)checkbox,多选项目
  <label class="checkbox">
    <checkbox value="cb" />未选中
  </label>
  <checkbox-group bindchange="checkboxChange">
    <label class="weui-cell weui-check__label" wx:for="{{items}}" wx:key="{{item.value}}">
      <view class="weui-cell__hd">
        <checkbox value="{{item.value}}" checked="{{item.checked}}"/>
      </view>
      <view class="weui-cell__bd">{{item.name}}</view>
    </label>
  </checkbox-group>
(3)checkbox-group,多项选择器,内部由多个checkbox组成,示例见(2)
(4)editor,富文本编辑器,可以对图片、文字进行编辑
  <editor id="editor" read-only="{{false}}" /> //自写
(5)form,表单
  <form catchsubmit="formSubmit" catchreset="formReset">
    <view class="page-section page-section-gap">
      <view class="page-section-title">switch</view>
      <switch name="switch"/>
    </view>
    <view class="btn-area">
      <button style="margin: 30rpx 0" type="primary" formType="submit">Submit</button>
      <button style="margin: 30rpx 0" formType="reset">Reset</button>
    </view>
  </form>
(6)input,输入框
  <view class="weui-cell weui-cell_input">
    <input class="weui-input" auto-focus placeholder="将会获取焦点"/>
  </view>
(7)keyboard-accessory,设置input/textarea聚焦时,键盘上方cover-view/cover-image工具栏视图
  <textarea hold-keyboard="{{true}}">
    <keyboard-accessory class="container" style="height: 50px;">
      <cover-view bindtap="tap" style="flex: 1; background: green;">1</cover-view>
      <cover-view bindtap="tap" style="flex: 1; background: red;">2</cover-view>
    </keyboard-accessory>
  </textarea>
(8)label,用来改进表单组件的可用性
  <checkbox-group class="group" bindchange="checkboxChange">
    <view class="label-1" wx:for="{{checkboxItems}}">
      <label>
        <checkbox value="{{item.name}}" checked="{{item.checked}}"></checkbox>
        <text class="label-1-text">{{item.value}}</text>
      </label>
    </view>
  </checkbox-group>
(9)picker,从底部弹起的滚动选择器
  <picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">
    <view class="picker">
      当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}
    </view>
  </picker>
(10)picker-view,嵌入页面的滚动选择器
  <picker-view indicator-style="height: 50px;" style="width: 100%; height: 300px;" value="{{value}}" bindchange="bindChange">
    <picker-view-column>
      <view wx:for="{{years}}" wx:key="{{years}}" style="line-height: 50px; text-align: center;">{{item}}年</view>
    </picker-view-column>
    <picker-view-column>
      <view wx:for="{{months}}" wx:key="{{months}}" style="line-height: 50px; text-align: center;">{{item}}月</view>
    </picker-view-column>
  </picker-view>
(11)picker-view-column,滚动选择器子项
(12)radio,单选项目
  <radio-group bindchange="radioChange">
    <label class="weui-cell weui-check__label" wx:for="{{items}}" wx:key="{{item.value}}">
      <view class="weui-cell__hd">
        <radio value="{{item.value}}" checked="true"/>
      </view>
      <view class="weui-cell__bd">{{item.name}}</view>
    </label>
  </radio-group>
(13)radio-group,单项选择器,内部由多个radio组成,示例见(12)
(14)slider,滑动选择器
  <view class="body-view">
    <slider bindchange="slider1change" left-icon="cancel" right-icon="success_no_circle"/>
  </view>
(15)switch,开关选择器
  <view class="body-view">
    <switch checked="{{switch1Checked}}" bindchange="switch1Change"/>
  </view>
(16)textarea,多行输入框
  <view class="section">
    <textarea bindblur="bindTextAreaBlur" auto-height placeholder="自动变高" />
  </view>
4、导航
(1)functional-page-navigator,仅在插件中有效,用于跳转到插件功能页
  <functional-page-navigator name="loginAndGetUserInfo" bind:success="loginSuccess">
    <button>登录到插件</button>
  </functional-page-navigator>
(2)navigator,页面链接
  <view class="btn-area">
    <navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>
    <navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>
    <navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>
    <navigator target="miniProgram" open-type="navigate" app-id="" path="" extra-data="" version="release">打开绑定的小程序</navigator>
  </view>
5、媒体组件
(1)camera,系统相机。点击相机图标,调出下面页面。出现人像-拍照人像-预览人像-存储人像
  <camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera>
  <button type="primary" bindtap="takePhoto">拍照</button>
  <view>预览</view>
  <image mode="widthFix" src="{{src}}"></image>
  Page({
    takePhoto() {
      const ctx = wx.createCameraContext()
      ctx.takePhoto({
        quality: 'high',
        success: (res) => {
          this.setData({
            src: res.tempImagePath
          })
        }
      })
    },
    error(e) {
      console.log(e.detail)
    }
  })
(2)image,图片。mode,图片裁剪、缩放的模式
  <view class="section section_gap" wx:for="{{array}}" wx:for-item="item">
    <view class="section__title">{{item.text}}</view>
    <view class="section__ctn">
      <image style="width: 200px; height: 200px; background-color: #eeeeee;" mode="{{item.mode}}" src="{{src}}"></image>
    </view>
  </view>
(3)live-player,实时音视频播放
  <live-player src="https://domain/pull_stream" mode="RTC" autoplay bindstatechange="statechange" binderror="error" style="width: 300px; height: 225px;" />
(4)live-pusher,实时音视频录制
  <live-pusher url="https://domain/push_stream" mode="RTC" autopush bindstatechange="statechange" style="width: 300px; height: 225px;" />
(5)video,视频
  <video 
    id="myVideo" 
    src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400" 
    binderror="videoErrorCallback" 
    danmu-list="{{danmuList}}" 
    enable-danmu 
    danmu-btn 
    show-center-play-btn='{{false}}' 
    show-play-btn="{{true}}" 
    controls
    picture-in-picture-mode="{{['push', 'pop']}}"
    bindenterpictureinpicture='bindVideoEnterPictureInPicture'
    bindleavepictureinpicture='bindVideoLeavePictureInPicture'
  ></video>
(6)voip-room,多人音视频对话
  <block wx:for="{{openIdList}}" wx:key="*this">
    <voip-room
      openid="{{item}}"
      mode="{{selfOpenId === item ? 'camera' : 'video'}}">
    </voip-room>
  </block>
6、地图,https://developers.weixin.qq.com/miniprogram/dev/component/map.html

九、微信小程序-自定义组件
  来源,开发-指南-自定义组件
  来源,https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/
1、一个小程序自定义组件由四个文件组成,类似于页面Page
(1).js,自定义组件逻辑
  // page-common-behavior.js
  // module.exports = Behavior({ //https://developers.weixin.qq.com/miniprogram/dev/reference/api/Behavior.html
  //   properties: {}, //组件的对外属性
  //   data: {}, //组件的内部数据
  // })
  var pageCommonBehavior = require('./page-common-behavior')
  Component({
    //来源,https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html
    //properties,组件的对外属性,是属性名到属性设置的映射表
    properties: { //properties,组件的对外属性,是属性名到属性设置的映射表
      innerText: {
        type: String,
        value: 'default value',
      }
    },
    //data,组件的内部数据,和properties一同用于组件的模板渲染
    data: { 
      someData: {}
    },
    //observers,组件数据字段监听器,用于监听properties和data的变化
    observers: { //组件数据字段监听器,用于监听properties和data的变化;普通监听
      'numberA, numberB': function(numberA, numberB) {//在 numberA或者numberB被设置时,执行这个函数
        this.setData({
          sum: numberA + numberB
        })
      }
    },
    observers: { //监听子数据字段
      'some.subfield': function(subfield) {
        //使用 setData 设置 this.data.some.subfield 时触发
        //(除此以外,使用 setData 设置 this.data.some 也会触发)
        subfield === this.data.some.subfield
      },
      'arr[12]': function(arr12) {
        //使用 setData 设置 this.data.arr[12] 时触发
        //(除此以外,使用 setData 设置 this.data.arr 也会触发)
        arr12 === this.data.arr[12]
      },
    },
    observers: { //监听所有子数据字段的变化,可以使用通配符 **
      'some.field.**': function(field) {
        //使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发
        //(除此以外,使用 setData 设置 this.data.some 也会触发)
        field === this.data.some.field
      },
    },
    observers: { //仅使用通配符 ** 可以监听全部 setData
      '**': function() {
        //每次 setData 都触发
      },
    },
    //methods,组件的方法,包括事件响应函数和任意的自定义方法
    methods: { 
      customMethod: function(){
        this.setData({ //设置data并执行视图层渲染
          s: false
        })
        this.hasBehavior() //检查组件是否具有behavior(检查时会递归检查被直接或间接引入的所有behavior)
        this.triggerEvent() //触发事件
      }
    },
    //behaviors,组件间可共享的部分,组件间代码复用机制,类似于mixins和traits
    //behaviors,组件引用它时,它的properties、data、method会被合并到组件对应的配置中,生命周期函数也会在对应时机被调用
    behaviors: [pageCommonBehavior], 
    behaviors: ['wx://form-field'], //它使得这个自定义组件有类似于表单控件的行为
    //lifetimes,组件生命周期-声明对象,里面的生命周期可以写到外面,但写在这里优先级更高
    lifetimes: {//生命周期函数,可以为函数,或一个在methods段中定义的方法名
      //created,在组件实例刚刚被创建时执行,此时不能调用setData
      created: function() {},
      //attached,在组件实例进入页面节点树时执行,声明的字段会被lifetimes声明的字段覆盖
      attached: function() { 
        this.setData({
          numberA: 1,
          numberB: 2,
        })
        this.setData({ //这样会触发上面的 observer
          'some.field': { /* ... */ }
        })
        this.setData({ //这样也会触发上面的 observer
          'some.field.xxx': { /* ... */ }
        })
        this.setData({ //这样还是会触发上面的 observer
          'some': { /* ... */ }
        })
      },
      //ready,组件布局完成后执行
      ready: function() { },
      //moved,组件实例被移动到节点树另一个位置时执行
      moved: function() { },
      //detached,组件实例被从页面节点树移除时执行
      detached: function() { },
    },
    pageLifetimes: {// 组件所在页面的生命周期函数
      show: function () { }, //组件所在的页面被展示时执行
      hide: function () { }, //组件所在的页面被隐藏时执行
      resize(res) { //屏幕旋转事件
        res.size.windowWidth //新的显示区域宽度
        res.size.windowHeight //新的显示区域高度
      }
      routeDone: function () { }, //组件所在页面路由动画完成时执行
    },
    // 组件实例的属性和方法可以在组件的方法、生命周期函数和属性observer中通过this访问
    // 属性名
    //   is,组件的文件路径
    //   id,节点id
    //   dataset,节点dataset
    //   data,组件数据,包括内部数据和属性值
    //   properties,组件数据,包括内部数据和属性值(与data一致)
    //   router,相对于当前自定义组件的Router对象
    //   pageRouter,相对于当前自定义组件所在页面的Router对象
    //   renderer,渲染当前组件的渲染后端
    // 方法名
    //   setData,设置data并执行视图层渲染	
    //   hasBehavior,检查组件是否具有behavior(检查时会递归检查被直接或间接引入的所有behavior)	
    //   triggerEvent,触发事件,参见,组件间通信与事件,
    //     https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
  })
(2).json,自定义组件配置
  {
    "component": true//这一组文件为自定义组件
  }
(3).wxss,自定义组件样式表
  .inner {
    color: red;
  }
(4).wxml,自定义组件结构
  <view class="inner">
    <view>这里是组件的内部节点</view>
    <slot></slot>
  </view>
(5)自定义组件使用
  以下,在页面的.json文件中声明引用
    {
      "usingComponents": {
        "component-tag-name": "path/to/the/custom/component"
      }
    }
  以下,在页面的.wxml文件中使用
    <view>
      <component-tag-name inner-text="Some text">这是插槽内容</component-tag-name>
    </view>
(6)自定义组件扩展
  //提供了修改自定义组件定义段的能力,下面例子就是修改了自定义组件中的data定义段里的内容
  //behavior.js
  module.exports = Behavior({
    definitionFilter(defFields) {
      defFields.data.from = 'behavior'
    },
  })
  //component.js
  Component({
    data: {
      from: 'component'
    },
    behaviors: [require('behavior.js')],
    ready() {
      console.log(this.data.from) //此处会发现输出 behavior 而不是 component
    }
  })
(7)简易双向绑定,自定义组件将自身的myValue属性双向绑定到了组件内输入框的value属性上
  custom-component定义,<input model:value="{{myValue}}" />
  custom-component使用,<custom-component model:my-value="{{pageValue}}" />  
2、抽象节点,
  有默认渲染内容,使用者决定渲染内容,与插槽相似
(1)自定义组件
  <!-- selectable-group.json -->
  {
    "componentGenerics": {//generic,通用
      "selectable": true
    }
  }
  {
    "componentGenerics": {
      "selectable": {  /* 默认抽象节点 */
        "default": "path/to/default/component"
      }
    }
  }
  <!-- selectable-group.wxml 渲染抽象节点 -->
  <view wx:for="{{labels}}">
    <label>
      <selectable disabled="{{false}}"></selectable>
      {{item}}
    </label>
  </view>
(2)父组件
  <!-- parent.json 定义抽象节点 -->
  {
    "usingComponents": {
      "custom-radio": "path/to/custom/radio",
      "custom-checkbox": "path/to/custom/checkbox"
    }
  }
  <!-- parent.wxml 选择抽象节点 -->
  <selectable-group generic:selectable="custom-radio" />
3、组件间通信与事件
(1)父级
  <!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 -->
  <component-tag-name bindmyevent="onMyEvent" />
  <!-- 或者可以写成 -->
  <component-tag-name bind:myevent="onMyEvent" />
  Page({
    onMyEvent: function(e){
      e.detail //自定义组件触发事件时提供的detail对象
    }
  })
(2)自身
  <button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>
  Component({
    properties: {},
    methods: {
      onTap: function(){
        var myEventDetail = {} //detail对象,提供给事件监听函数
        var myEventOption = {} //触发事件的选项
        this.triggerEvent('myevent', myEventDetail, myEventOption) //trigger,触发
      }
    }
  })

十、微信小程序-API
1、基础
(1)系统
  A、wx.getWindowInfo() //获取窗口信息
  B、wx.getSystemInfo(Object object) // 获取系统信息。是异步的调用格式,但是是同步返回
  C、wx.getDeviceInfo() //获取设备基础信息
  D、wx.getAppBaseInfo() //获取微信APP基础信息
(2)更新
  A、wx.updateWeChatApp(Object object) //更新客户端版本
  B、wx.getUpdateManager() //获取全局唯一的版本更新管理器,用于管理小程序更新
(3)小程序 
  A、wx.getApiCategory() //获取当前API类别
  B、wx.onThemeChange(function listener) //监听系统主题改变事件。该事件与App.onThemeChange的回调时机一致
  C、wx.onAppShow(function listener) //监听小程序切前台事件。该事件与App.onShow的回调参数一致
(4)调试
  A、console.log() //向调试面板中打印log日志
  B、console.warn() //向调试面板中打印warn日志
(5)性能
  A、wx.preloadWebview(Object object) //预加载下个页面的WebView
  B、wx.getPerformance() //获取当前小程序性能相关的信息
  C、wx.preloadAssets(Object object) //为视图层预加载媒体资源文件
2、路由
  (1)wx.switchTab(Object object) //跳转到tabBar页面,并关闭其他所有非tabBar页面
  (2)wx.reLaunch(Object object) //关闭所有页面,打开到应用内的某个页面
  (3)wx.redirectTo(Object object) //关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到tabbar页面
  (4)wx.navigateTo(Object object) //保留当前页面,跳转到应用内的某个页面,但是不能跳到tabbar页面,使用wx.navigateBack可以返回到原页面
  (5)wx.navigateBack(Object object) //关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages获取当前的页面栈,决定需要返回几层。
  (6)自定义路由
    A、wx.router //获取router对象
    B、router.addRouteBuilder(string routeType, function routeBuilder) //添加自定义路由配置
      wx.router.addRouteBuilder('slide', slideRouteBuilder) //路由类型,路由动画定义函数
    C、router.getRouteContext(Object this) //获取页面对应的自定义路由上下文对象
    D、router.removeRouteBuilder(string routeType) //移除自定义路由配置
      wx.router.removeRouteBuilder('slide')
3、跳转
  (1)wx.restartMiniProgram(Object object) //重启当前小程序
  (2)wx.openEmbeddedMiniProgram(Object object) //打开半屏小程序
  (3)wx.navigateToMiniProgram(Object object) //打开另一个小程序
  (4)wx.exitMiniProgram(Object object) //退出当前小程序。必须有点击行为才能调用成功
4、转发
  (1)wx.updateShareMenu(Object object) //更新转发属性
  (2)wx.showShareMenu(Object object) //显示当前页面的转发按钮
  (3)wx.shareVideoMessage(Object object) //转发视频到聊天
  (4)wx.shareFileMessage(Object object) //转发文件到聊天
5、界面
(1)交互
  A、wx.showToast(Object object) //显示消息提示框
  B、wx.showModal(Object object) //显示模态对话框
  C、wx.showLoading(Object object) //显示loading提示框。需主动调用wx.hideLoading才能关闭提示框
  D、wx.showActionSheet(Object object) //显示操作菜单
  E、wx.hideToast(Object object) //隐藏消息提示框
  F、wx.hideLoading(Object object) //隐藏loading提示框
(2)导航栏
  A、wx.showNavigationBarLoading(Object object) //在当前页面显示导航条加载动画
  B、wx.setNavigationBarTitle(Object object) //动态设置当前页面的标题
  C、wx.setNavigationBarColor(Object object) //设置页面导航条颜色
  D、wx.hideNavigationBarLoading(Object object) //在当前页面隐藏导航条加载动画
  E、wx.hideHomeButton(Object object) //隐藏返回首页按钮
(3)背景
  A、wx.setBackgroundTextStyle(Object object) //动态设置下拉背景字体、loading图的样式
  B、wx.setBackgroundColor(Object object) //动态设置窗口的背景色
(4)Tab Bar,位于小程序底部的导航栏菜单
  A、wx.showTabBarRedDot(Object object) //显示tabBar某一项的右上角的红点
  B、wx.showTabBar(Object object) //显示tabBar
  C、wx.setTabBarStyle(Object object) //动态设置tabBar的整体样式
  D、wx.setTabBarItem(Object object) //动态设置tabBar某一项的内容
  E、wx.setTabBarBadge(Object object) //为tabBar某一项的右上角添加文本
  F、wx.removeTabBarBadge(Object object) //移除tabBar某一项右上角的文本
  G、wx.hideTabBarRedDot(Object object) //隐藏tabBar某一项的右上角的红点
  H、wx.hideTabBar(Object object) //隐藏tabBar
(5)字体
  A、wx.loadFontFace(Object object) //动态加载网络字体,文件地址需为下载类型。需在app.js中调用
(6)下拉刷新
  A、wx.stopPullDownRefresh(Object object) //停止当前页面下拉刷新
  B、wx.startPullDownRefresh(Object object) //开始下拉刷新。调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。
(7)滚动
  A、wx.pageScrollTo(Object object) //将页面滚动到目标位置,支持选择器和滚动距离两种方式定位
(8)动画
  A、wx.createAnimation(Object object) //创建一个动画实例animation
(9)置顶
  A、wx.setTopBarText(Object object) //动态设置置顶栏文字内容
(10)自定义组件
  A、wx.nextTick(Object object) //延迟一部分操作到下一个时间片再执行。(类似于setTimeout)
(11)菜单
  A、wx.getMenuButtonBoundingClientRect(Object object) //获取菜单按钮(右上角胶囊按钮)的布局位置信息。坐标信息以屏幕左上角为原点。
(12)窗口
  A、wx.setWindowSize(Object object) //设置窗口大小,该接口仅适用于PC平台
  B、wx.onWindowResize(Object object) //窗口尺寸变化事件的监听函数
  C、wx.offWindowResize(Object object) //移除窗口尺寸变化事件的监听函数
  D、wx.checkIsPictureInPictureActive(Object object) //返回当前是否存在小窗播放(小窗在video/live-player/live-pusher下可用)
(13)worklet 动画
  A、wx.worklet(Object object) //获取worklet对象
6、网络
(1)发起请求,wx.request(Object object) //发起HTTPS网络请求
  wx.request({
    url: 'example.php', 
    data: {
      x: '',
      y: ''
    },
    header: {
      'content-type': 'application/json' // 默认值
    },
    timeout: 60000, //默认值
    method: 'get', //默认值
    dataType: 'text', //默认值。返回的数据格式,即res.data,返回的数据为JSON时,返回后会对返回的数据进行一次 JSON.parse
    responseType: 'text', //默认值。响应的数据类型,即res,
    enableProfil: true, //是否开启profile,默认开启
    success (res) {
      console.log(res.profile) //网络连接过程中关键时间点的耗时信息
    },
    false (res) {
      console.log(res.errMsg)
    }
  })
(2)wx.downloadFile(Object object) //下载文件资源到本地
  const DownloadTask = wx.downloadFile({
    url: 'https://example.com/audio/123', 
    filePath: '指定文件下载后存储的路径(本地路径)',
    enableProfil: true, //是否开启profile,默认开启
    success (res) {
      //只要服务器有响应数据,就会把响应内容写入文件并进入success回调,业务需要自行判断是否下载到了想要的内容
      console.log(res.profile) //网络连接过程中关键时间点的耗时信息
      if(res.statusCode === 200) {
        wx.playVoice({
          filePath: res.tempFilePath
        })
      }
    },
  })
  //DownloadTask.onProgressUpdate(function listener),监听下载进度变化事件
(3)wx.uploadFile(Object object) //将本地资源上传到服务器
  wx.chooseImage({
    success (res) {
      const tempFilePaths = res.tempFilePaths
      const UploadTask = wx.uploadFile({
        url: 'https://example.weixin.qq.com/upload', //仅为示例,非真实的接口地址
        filePath: tempFilePaths[0],
        name: 'file',
        formData: {
          'user': 'test'
        },
        enableProfil: true, //是否开启profile,默认开启
        success (res){
          const data = res.data
          //do something
        }
      })
      //UploadTask.onProgressUpdate(function listener),监听上传进度变化事件
    }
  })
(4)WebSocket //通过WebSocket连接发送数据
  let socketOpen = false
  let socketMsgQueue = []
  wx.connectSocket({
    url: 'test.php'
  })
  wx.onSocketOpen(function(res) {
    socketOpen = true
    for (let i = 0; i < socketMsgQueue.length; i++){
      sendSocketMessage(socketMsgQueue[i])
    }
    socketMsgQueue = []
  })
  function sendSocketMessage(msg) {
    if (socketOpen) {
      wx.sendSocketMessage({
        data:msg
      })
    } else {
      socketMsgQueue.push(msg)
    }
  }
7、支付
  (1)wx.requestVirtualPayment(Object object),发起米大师虚拟支付
  (2)wx.requestPluginPayment(Object object),插件中发起支付
  (3)wx.requestPayment(Object object),发起微信支付
  (4)wx.requestMerchantTransfer(Object object),商家转账用户确认模式下,在微信客户端通过小程序拉起页面请求用户确认收款
    //调用前需在微信支付商户平台/合作伙伴平台-产品中心,申请开通商家转账
  (5)wx.requestCommonPayment(Object object),发起通用支付
8、数据缓存
  (1)wx.setStorageSync(string key, any data) //将数据存储在本地缓存中指定的key中,数据都一直可用
    wx.setStorageSync('key', 'value')
  (2)wx.setStorage(Object object) //将数据存储在本地缓存中指定的key中,数据都一直可用
    wx.setStorage({
      key:"key",
      data:"value"
    })
    wx.setStorage({ //开启加密存储
      key: "key",
      data: "value",
      encrypt: true, //若开启加密存储,setStorage和getStorage需要同时声明encrypt的值为true
      success() {
        wx.getStorage({
          key: "key",
          encrypt: true, //若开启加密存储,setStorage和getStorage需要同时声明encrypt的值为true
          success(res) {
            console.log(res.data)
          }
        })
      }
    })
  (3)wx.getStorageSync(string key) //从本地缓存中同步获取指定key的内容
  (4)wx.getStorage(Object object) //从本地缓存中异步获取指定key的内容
9、数据分析
10、XR-FRAME
11、画布
12、媒体
  (1)地图
    A、wx.createMapContext(string mapId, Object this) //创建map上下文MapContext对象。
      //建议使用“wx.createSelectorQuery”-1,获取context对象。
  (2)图片
    A、wx.saveImageToPhotosAlbum(Object object) //保存图片到系统相册
    B、wx.previewMedia(Object object) //预览图片和视频
    C、wx.previewImage(Object object) //在新页面中全屏预览图片。预览的过程中用户可以进行保存图片、发送给朋友等操作
    D、wx.getImageInfo(Object object) //获取图片信息。网络图片需先配置download域名才能生效
    E、wx.editImage(Object object) //编辑图片接口
    F、wx.cropImage(Object object) //裁剪图片接口
    G、wx.compressImage(Object object) //压缩图片接口,可选压缩质量
    H、wx.chooseMessageFile(Object object) //从客户端会话选择文件
    I、wx.chooseImage(Object object) //从本地相册选择图片或使用相机拍照,请使用wx.chooseMedia代替
  (3)视频
    A、wx.saveVideoToPhotosAlbum(Object object) //保存视频到系统相册。支持mp4视频格式
    B、wx.openVideoEditor(Object object) //打开视频编辑器
    C、wx.getVideoInfo(Object object) //获取视频详细信息
    D、wx.createVideoContext(string id, Object this) //创建video上下文VideoContext对象。
      //建议使用“wx.createSelectorQuery”-1,获取context对象。
    E、wx.compressVideo(Object object) //压缩视频接口
    F、wx.chooseVideo(Object object) //拍摄视频或从手机相册中选视频,请使用wx.chooseMedia代替
    G、wx.chooseMedia(Object object) //拍摄或从手机相册中选择图片或视频
  (4)音频
    A、wx.stopVoice(Object object) //结束播放语音,请使用wx.createInnerAudioContext代替
    B、wx.setInnerAudioOption(Object object) //设置InnerAudioContext的播放选项。设置之后对当前小程序全局生效。
    C、wx.playVoice(Object object) //开始播放语音,请使用wx.createInnerAudioContext代替
    D、wx.pauseVoice(Object object) //暂停正在播放的语音,请使用wx.createInnerAudioContext代替
    E、wx.getAvailableAudioSources(Object object) //获取当前支持的音频输入源
    F、wx.createWebAudioContext() //创建WebAudio上下文
    G、wx.createMediaAudioPlayer() //创建媒体音频播放器对象MediaAudioPlayer对象,可用于播放视频解码器VideoDecoder输出的音频
    H、wx.createInnerAudioContext(Object object) //创建内部audio上下文InnerAudioContext对象
    I、wx.createAudioContext(string id, Object this) //创建audio上下文AudioContext对象,请使用wx.createInnerAudioContext代替
  (5)背景音频
    A、wx.stopBackgroundAudio(Object object) //停止播放音乐,请使用wx.getBackgroundAudioManager代替
    B、wx.seekBackgroundAudio(Object object) //控制音乐播放进度,请使用wx.getBackgroundAudioManager代替
    C、wx.playBackgroundAudio(Object object) //使用后台播放器播放音乐,请使用wx.getBackgroundAudioManager代替
    D、wx.pauseBackgroundAudio(Object object) //暂停播放音乐,请使用wx.getBackgroundAudioManager代替
    E、wx.onBackgroundAudioStop(function listener) //监听音乐停止事件,请使用wx.getBackgroundAudioManager代替
    F、wx.onBackgroundAudioPlay(function listener) //监听音乐播放事件,请使用wx.getBackgroundAudioManager代替
    G、wx.onBackgroundAudioPause(function listener) //监听音乐暂停事件,请使用wx.getBackgroundAudioManager代替
    H、wx.getBackgroundAudioPlayerState(Object object) //获取后台音乐播放状态,请使用wx.getBackgroundAudioManager代替
    I、wx.getBackgroundAudioManager() //获取全局唯一的背景音频管理器
  (6)实时音视频
    A、wx.createLivePusherContext() //创建live-pusher上下文LivePusherContext对象
    B、wx.createLivePlayerContext(string id, Object this) //创建live-player上下文LivePlayerContext对象
      //建议使用“wx.createSelectorQuery”-1,获取context对象
  (7)录音
    A、wx.stopRecord(Object object) //停止录音,请使用wx.getRecorderManager代替
    B、wx.startRecord(Object object) //开始录音,请使用wx.getRecorderManager代替
    C、wx.getRecorderManager() //获取全局唯一的录音管理器RecorderManager
  (8)相机
    A、wx.createCameraContext() //创建camera上下文CameraContext对象
  (9)富文本
      <editor id="editor" /> //自写
      const EditorContext = wx.createSelectorQuery()
      EditorContext.select('#editor')
    A、EditorContext.blur(Object object) //编辑器失焦,同时收起键盘
    B、EditorContext.clear(Object object) //清空编辑器内容
    C、EditorContext.format(string name, string value) //修改样式
    D、EditorContext.getContents(Object object) //获取编辑器内容
    E、EditorContext.getSelectionText(Object object) //获取编辑器已选区域内的纯文本内容
    F、EditorContext.insertDivider(Object object) //插入分割线
    G、EditorContext.insertImage(Object object) //插入图片
    H、EditorContext.insertText(Object object) //覆盖当前选区,设置一段文本
    I、EditorContext.redo(Object object) //恢复
    J、EditorContext.removeFormat(Object object) //清除当前选区的样式
    K、EditorContext.scrollIntoView() //使得编辑器光标处滚动到窗口可视区域内
    L、EditorContext.setContents(Object object) //初始化编辑器内容,html和delta同时存在时仅delta生效
    M、EditorContext.undo(Object object) //撤销
    N、
  (10)音视频合成
    A、wx.createMediaContainer() //创建音视频处理容器,最终可将容器中的轨道合成一个视频
  (11)实时语音,下面接口不完整
    A、wx.updateVoIPChatMuteConfig(Object object) //更新实时语音静音设置
    B、wx.subscribeVoIPVideoMembers(Object object) //订阅视频画面成员
  (12)画面录制器
    A、wx.createMediaRecorder(Object canvas, Object options) //创建WebGL画面录制器,可逐帧录制在WebGL上渲染的画面并导出视频文件
  (13)视频解码器
    A、wx.createVideoDecoder() //创建视频解码器,可逐帧获取解码后的数据
13、位置,下面接口不完整
  (1)wx.stopLocationUpdate(Object object) //关闭监听实时位置变化,前后台都停止消息接收
  (2)wx.startLocationUpdate(Object object) //开启小程序进入前台时接收位置消息
  (3)wx.openLocation(Object object) //使用微信内置地图查看位置
  (4)wx.getLocation(Object object) //获取当前的地理位置、速度
14、文件
  (1)wx.saveFileToDisk(Object object) //保存文件系统的文件到用户磁盘,仅在PC端支持
  (2)wx.openDocument(Object object) //新开页面打开文档
  (3)wx.getFileSystemManager() //获取全局唯一的文件管理器
15、开放接口
  (1)登录
    A、wx.pluginLogin(Object args) //该接口仅在小程序插件中可调用,调用接口获得插件用户标志凭证(code)
    B、wx.login(Object object) //调用接口获取登录凭证(code)
    C、wx.checkSession(Object object) //检查登录态是否过期
  (2)账号信息
    A、wx.getAccountInfoSync() //获取当前账号信息
  (3)用户信息
    A、wx.getUserProfile(Object object) //获取用户信息
    B、wx.getUserInfo(Object object) //获取用户信息
  (4)授权
    A、wx.authorizeForMiniProgram //仅小程序插件中能调用该接口,用法同wx.authorize。目前仅支持三种scope
      wx.authorizeForMiniProgram({
        scope: 'scope.record', //scope.record、scope.writePhotosAlbum、scope.camera
        success () {
          //用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
          wx.startRecord()
        }
      })
    B、wx.authorize //提前向用户发起授权请求
      //可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope
      wx.getSetting({
        success(res) {
          if (!res.authSetting['scope.record']) {
            wx.authorize({
              scope: 'scope.record',
              success () {
                // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
                wx.startRecord()
              }
            })
          }
        }
      })
  (5)设置
    A、wx.openSetting //功能描述,调起客户端小程序设置界面,返回用户设置的操作结果
      wx.openSetting({
        success (res) {
          console.log(res.authSetting)
          // res.authSetting = {
          //   "scope.userInfo": true,
          //   "scope.userLocation": true
          // }
        }
      })
    B、wx.getSetting //获取用户的当前设置
  (6)收货地址
    A、wx.chooseAddress(Object object) //获取用户收货地址
  (7)卡券
    A、wx.openCard(Object object) //查看微信卡包中的卡券
    B、wx.addCard(Object object) //
  (8)发票
    A、wx.chooseInvoiceTitle(Object object) //选择用户的发票抬头
    B、wx.chooseInvoice(Object object) //选择用户已有的发票
  (9)生物认证
    A、wx.startSoterAuthentication(Object object) //开始SOTER生物认证
    B、wx.checkIsSupportSoterAuthentication(Object object) //获取本机支持的SOTER生物认证方式
    C、wx.checkIsSoterEnrolledInDevice(Object object) //获取设备内是否录入如指纹等生物信息的接口
  (10)微信运动
    A、wx.shareToWeRun(Object object) //分享数据到微信运动
    B、wx.getWeRunData(Object object) //获取用户过去三十一天微信运动步数
  (11)订阅消息
    A、wx.requestSubscribeMessage(Object object) //调起客户端小程序订阅消息界面,返回用户订阅消息的操作结果
    B、wx.requestSubscribeDeviceMessage(Object object) //订阅设备消息接口,调用后弹出授权框,用户同意后会允许开发者给用户发送订阅模版消息
  (12)微信红包
    A、wx.showRedPackage(Object object) //拉取h5领取红包封面页
  (13)收藏
    A、wx.addVideoToFavorites(Object object) //收藏视频
    B、wx.addFileToFavorites(Object object) //收藏文件
  (14)我的小程序
    A、wx.checkIsAddedToMyMiniProgram(Object object) //检查小程序是否被添加至 「我的小程序」
  (15)车牌
    A、wx.chooseLicensePlate(Object object) //选择车牌号
  (16)视频号
    A、wx.reserveChannelsLive(Object object) //预约视频号直播
    B、wx.openChannelsUserProfile(Object object) //打开视频号主页
    C、wx.openChannelsLive(Object object) //打开视频号直播
    D、wx.openChannelsEvent(Object object) //打开视频号活动页
    E、wx.openChannelsActivity(Object object) //打开视频号视频
    F、wx.getChannelsShareKey(Object object) //获取视频号直播卡片/视频卡片的分享来源
    G、wx.getChannelsLiveNoticeInfo(Object object) //获取视频号直播预告信息
    H、wx.getChannelsLiveInfo(Object object) //获取视频号直播信息
  (17)音视频通话
    A、wx.requestDeviceVoIP(Object object) //请求用户授权与设备(组)间进行音视频通话
    B、wx.getDeviceVoIPList(Object object) //查询当前用户授权的音视频通话设备(组)信息
  (18)微信群
    A、wx.getGroupEnterInfo(Object object) //获取微信群聊场景下的小程序启动信息
  (19)隐私信息授权
    A、wx.requirePrivacyAuthorize(Object object) //模拟隐私接口调用,并触发隐私弹窗逻辑
    B、wx.openPrivacyContract(Object object) //跳转至隐私协议页面
    C、wx.onNeedPrivacyAuthorization(function listener) //监听隐私接口需要用户授权事件
    D、wx.getPrivacySetting(Object object) //查询隐私授权情况
  (20)微信客服
    A、wx.openCustomerServiceChat(Object object) //打开微信客服,页面产生点击事件(例如 button 上 bindtap 的回调中)后才可调用
  (21)微信表情
    A、wx.openStickerSetView(Object object) //打开表情专辑
    B、wx.openStickerIPView(Object object) //打开表情IP合辑
    C、wx.openSingleStickerView(Object object) //打开单个表情
16、设备
  (1)蓝牙-通用 
  (2)蓝牙-低功耗中心设备
  (3)蓝牙-低功耗外围设备
  (4)蓝牙-信标(Beacon) 
  (5)NFC读写
    A、wx.getNFCAdapter() //获取NFC实例
  (6)Wi-Fi(wifi),无线保真(Wireless Fidelity),下面接口不完整
    A、wx.stopWifi(Object object) //关闭wifi模块
    B、wx.startWifi(Object object) //初始化wifi模块
    C、wx.setWifiList(Object object) //设置wifiList中 AP 的相关信息,iOS特有接口
    D、wx.getWifiList(Object object) //请求获取wifi列表
    E、wx.getConnectedWifi(Object object) //获取已连接中的wifi信息
  (7)日历
  (8)联系人
    A、wx.chooseContact(Object object) //拉起手机通讯录,选择联系人
    B、wx.addPhoneContact(Object object) //添加手机通讯录联系人
  (9)无障碍
    A、wx.checkIsOpenAccessibility(Object object) //检测是否开启视觉无障碍功能
  (10)电量
    A、wx.getBatteryInfoSync() //同步获取设备电量
    B、wx.getBatteryInfo(Object object) //获取设备电量
  (11)剪贴板
    A、wx.setClipboardData(Object object) //设置系统剪贴板的内容
    B、wx.getClipboardData(Object object) //获取系统剪贴板的内容
  (12)NFC主机卡模拟
  (13)网络 
    A、wx.onNetworkWeakChange(function listener) //监听弱网状态变化事件
    B、wx.onNetworkStatusChange(function listener) //监听网络状态变化事件
    C、wx.offNetworkWeakChange(function listener) //移除弱网状态变化事件的监听函数
    D、wx.offNetworkStatusChange(function listener) //移除网络状态变化事件的监听函数
    E、wx.getNetworkType(Object object) //获取网络类型
    F、wx.getLocalIPAddress(Object object) //
  (14)加密 
    A、wx.getRandomValues(Object object) //获取密码学安全随机数
  (15)屏幕
    A、wx.setVisualEffectOnCapture(Object object) //wx.setVisualEffectOnCapture(Object object)
    B、wx.setScreenBrightness(Object object) //设置屏幕亮度
    C、wx.setKeepScreenOn(Object object) //设置是否保持常亮状态
    D、wx.onUserCaptureScreen(function listener) //监听用户主动截屏事件
    E、wx.onScreenRecordingStateChanged(function listener) //监听用户录屏事件
    F、wx.offUserCaptureScreen(function callback) //取消监听用户主动截屏事件
    G、wx.offScreenRecordingStateChanged(function listener) //移除用户录屏事件的监听函数
    H、wx.getScreenRecordingState(Object object) //查询用户是否在录屏
    I、wx.getScreenBrightness(Object object) //获取屏幕亮度
  (16)键盘 
    A、wx.onKeyboardHeightChange(function listener) //监听键盘高度变化事件
    B、wx.offKeyboardHeightChange(function listener) //移除键盘高度变化事件的监听函数
    C、wx.hideKeyboard(Object object) //在input、textarea等focus拉起键盘之后,手动调用此接口收起键盘
    D、wx.getSelectedTextRange(Object object) //在input、textarea等focus之后,获取输入框的光标位置
  (17)电话 
    A、wx.makePhoneCall(Object object) //拨打电话
  (18)加速计,例如摇一摇
    A、wx.stopAccelerometer(Object object) //停止监听加速度数据
    B、wx.startAccelerometer(Object object) //开始监听加速度数据
    C、wx.onAccelerometerChange(function listener) //监听加速度数据事件
    D、wx.offAccelerometerChange(function listener) //移除加速度数据事件的监听函数
  (19)罗盘
    A、wx.stopCompass(Object object) //停止监听罗盘数据
    B、wx.startCompass(Object object) //开始监听罗盘数据
    C、wx.onCompassChange(function listener) //罗盘数据变化事件的监听函数
    D、wx.offCompassChange(function listener) //移除罗盘数据变化事件的监听函数
  (20)设备方向 
    A、wx.stopDeviceMotionListening(Object object) //停止监听设备方向的变化
    B、wx.startDeviceMotionListening(Object object) //开始监听设备方向的变化
    C、wx.onDeviceMotionChange(function listener) //监听设备方向变化事件
    D、wx.offDeviceMotionChange(function listener) //移除设备方向变化事件的监听函数
  (21)陀螺仪,测量偏转、倾斜时的转动角速度 
    A、wx.stopGyroscope(Object object) //停止监听陀螺仪数据
    B、wx.startGyroscope(Object object) //开始监听陀螺仪数据 
    C、wx.onGyroscopeChange(function listener) //监听陀螺仪数据变化事件
    D、wx.offGyroscopeChange(function listener) //移除陀螺仪数据变化事件的监听函数
  (22)内存 
    A、wx.onMemoryWarning(function listener) //监听内存不足告警事件
    B、wx.offMemoryWarning(function listener) //移除内存不足告警事件的监听函数
  (23)扫码 
    A、wx.scanCode(Object object) //调起客户端扫码界面进行扫码
  (24)短信
    A、wx.sendSms(Object object) //拉起手机发送短信界面
  (25)振动
    A、wx.vibrateShort(Object object) //使手机发生较短时间的振动(15 ms)
    B、wx.vibrateLong(Object object) //使手机发生较长时间的振动(400 ms)
17、AI
  (1)AI推理 
    A、wx.getInferenceEnvInfo(Object object) //获取通用AI推理引擎版本
    B、wx.createInferenceSession(Object object) //创建AI推理Session
  (2)视觉算法
    A、wx.isVKSupport(string version) //判断支持版本
    B、wx.createVKSession(Object object) //创建vision kit会话对象
  (3)人脸检测 
    A、wx.stopFaceDetect(Object object) //停止人脸检测。本接口不再维护,请使用wx.createVKSession接口代替
    B、wx.initFaceDetect(Object object) //初始化人脸检测。本接口不再维护,请使用wx.createVKSession接口代替
    C、wx.faceDetect(Object object) //人脸检测。本接口不再维护,请使用wx.createVKSession接口代替
18、Worker
  function createNewWorker() {
    const worker = wx.createWorker('workers/index.js', { //创建一个Worker线程
      useExperimentalWorker: true
    })
    //监听worker被系统回收事件
    worker.onProcessKilled(() => {
      //重新创建一个worker
      createNewWorker()
    })
  }
  //创建实验worker
  createNewWorker()
19、WXML
  const query = wx.createSelectorQuery() 
    //返回一个SelectorQuery对象实例。在自定义组件或包含自定义组件的页面中,应使用this.createSelectorQuery()来代替
  query.select('#the-id').boundingClientRect()
  query.selectViewport().scrollOffset()
  query.exec(function(res){
    res[0].top //#the-id节点的上边界坐标
    res[1].scrollTop //显示区域的竖直滚动位置
  })
20、第三方平台
21、广告
22、Skyline

十一、uniapp跨端框架-简介 
来源,https://gitee.com/dcloud/uni-app/tags?page=60
来源,https://uniapp.dcloud.net.cn/history.html
来源,https://uniapp.dcloud.net.cn/tutorial/
1、uni-app的产生背景
  注、Dcloud一般指数字天堂(北京)网络技术有限公司,2012年04月登记成立,法定代表人王安;来源,百度百科“Dcloud”
  (1)2011年1月,微信1.0发布
  (2)2012年,DCloud开始研发小程序技术,推出了HBuilder开发工具
  (3)2015年,DCloud商用了小程序技术,命名“流应用”,并将该技术标准捐给工信部,同时推进各家流量巨头接入该标准,
    众多开发者用该标准为“流应用”平台提供应用,供用户下载
  (4)2015年9月,DCloud推进微信团队开展小程序业务
  (5)2016年初,微信团队决定上线微信小程序业务,订制了自己的标准,随后包括手机厂商在内的各大流量巨头,
    陆续上线类似微信小程序的业务
  (6)2017年1月,微信小程序发布,微信开发工具开发的微信小程序只能在微信上运行,
    来源,https://developers.weixin.qq.com/miniprogram/dev/component/text.html
  (7)2018年8月,DCloud发布uni-app1.0,为开发者抹平各平台的差异,让一套代码在各APP(应用)上运行,
    开发工具为HBuilder,js框架为vue,DCloud由标准的提供者转为标准的汇总者
2、uni-app的版本发布
  (1)2018年8月,uni-app1.0.0 版本发布,
  (2)2021年9月,uni-app2.0.0 版本发布,
  (3)2023年1月,uni-app3.0.0 版本发布,
3、uni-app与weex[wi:ks]、mpvue功能类似,通过单一代码库构建iOS、Android、Web(H5)和小程序等多个平台的应用
  (1)2016年4月,阿里巴巴发布跨平台移动开发工具weex,开发者将Weex的SDK嵌入自己的APP,解决了频繁发版和多端研发两大问题
  (2)uni-app的vue页面,用“WebKit的”webview渲染,WebKit是一个开源的浏览器引擎
  (3)uni-app的nvue页面,用“基于weex改进的”原生渲染引擎渲染,nvue是“native vue”的缩写,意为“原生vue”
  (4)原生渲染引擎,不是封装别人现成的渲染引擎,而是自己开发的渲染引擎
  (5)自悟,浏览器与APP,前者可以浏览所有网站,后者只能浏览一个网站
4、Hybrid,混合开发
  (1)Native App,一般指原生应用,是一个完整的应用,依托于操作系统,交互性强,拓展性强,需要用户下载安装使用
  (2)Web App,一般指H5应用,使用Web技术(如HTML、CSS和JavaScript)来创建应用程序的用户界面
  (3)Hybrid App,一般是指混合型App,带有原生应用外壳的Web应用,是最多的移动端开发方式
    A、底层依赖于Native提供的容器(WebView),上层使用html&css&JS做业务开发
    B、既有前者良好用户体验的优势,又有后者使用HTML5跨平台开发低成本的优势
5、uni-app与hybrid的区别
  (1)uni-app,用于移动端,后出现,负责-前后端分离-的前端
  (2)hybrid,用于移动端,先出现,负责-前后端混合-的全部
  (3)electron
    A、electron是什么?
      a、用Web技术构建跨平台的桌面应用,electron = Chromium + Node.js + Native API。2016年5月Electron发布了v1.0.0版本
      b、Chromium,为Electron提供了强大的UI能力,可以不考虑兼容性的情况下,利用强大的Web生态来开发界面
      c、Node.js,让Electron有了底层的操作能力,比如文件的读写,甚至是集成C++等等操作,并可以使用大量开源的npm包来完成开发需求
      d、Native API,Native API让Electron有了跨平台和桌面端的原生能力,比如说它有统一的原生界面,窗口、托盘这些。
    B、什么时候使用Electron?
      a、公司没有专门的桌面应用开发者,而需要前端兼顾来进行开发时,用Electron就是一个不错的选择。
      b、一个应用需要同时开发Web端和桌面端的时候,那使用Electron来进行开发就对了。
      c、开发一些效率工具,比如API类的工具。

十二、uniapp跨端框架-教程 
1、uni-app的开发规范
  (1)页面文件遵循Vue单文件组件(SFC)规范,即每个页面是一个.vue文件
  (2)组件标签靠近小程序规范,
  (3)接口能力靠近小程序规范,但需将前缀wx、my等替换为uni
  (4)数据绑定及事件处理同Vue.js规范,同时补充了应用生命周期及页面的生命周期
  (5)如需兼容app-nvue平台,建议使用flex布局进行开发
  (6)uni-app分编译器和运行时(runtime),都内置在HBuilderX中,编译器在开发环境编译代码并生成输出物,
    在各终端上,各运行时(runtime)解析各输出物
2、uni-app的编译器
  (1)编译器,运行在电脑开发环境
  (2)开发者按uni-app规范编写代码,由编译器将开发者的统一代码编译生成每个平台支持的特有代码
    在web平台,将.vue文件编译为js代码
    在微信小程序平台,编译器将.vue文件拆分生成wxml、wxss、js等代码
    在app平台,将.vue文件编译为js代码
    在Android平台,将.uts文件编译为kotlin代码
    在iOS平台,将.uts文件编译为swift代码
  (3)uni-app项目根据所依赖的Vue版本不同,编译器的实现也不同
    vue2版,基于webpack实现
    vue3版,基于Vite实现,性能更快
  (4)支持条件编译
  (5)编译到任意平台时,
    static目录下满足编译条件的文件,会直接复制到最终的打包目录
    非static目录下的文件(vue、js、css等)只有被引用时,才会被打包编译
  (6).uts,意为统一类型脚本,全称为“uni type script”
3、uni-app的运行时(runtime)
  (1)runtime,运行在终端上,动态处理数据绑定、事件代理,保证Vue和平台宿主数据的一致性,这是一个比较庞大的工程
    在小程序端,uni-app的runtime,主要是一个小程序版的vue runtime,页面路由、组件、api等方面基本都是转义
    在web端,uni-app的runtime相比普通的vue项目,多了一套ui库、页面路由框架、和uni对象(即常见API封装)
    在App端,uni-app的runtime更复杂,DCloud也有一套小程序引擎,打包app时将开发者的代码和DCloud的小程序打包成了apk或ipa
  (2)uni-app的runtime包括3部分,基础框架、组件、API
4、uni-app处理各终端的逻辑层和视图层分离
  (1)在web平台,逻辑层(js)和视图层(html、css),都运行在统一的webview里
  (2)在小程序和app端,逻辑层和视图层被分离了,原因为基于webview的app因js运算和界面渲染抢资源导致卡顿而性能不佳
    逻辑层都独立成单独的js引擎,不支持浏览器专用的window、dom等API,
    视图层仍然是webview,能视图层操作window、dom
5、uni-app的工程,一个uni-app工程,就是一个“Vue项目”(非常重要),可以通过HBuilderX或cli快速创建  
6、页面
  (1)一个页面就是一个符合Vue SFC规范的.vue文件或.nvue文件
  (2)页面保存在工程根目录下的pages目录下
  (3)每次新建页面,均需在工程根目录下的pages.json中配置pages列表
  (4)删除页面时,需删除.vue文件或.nvue文件,删除pages.json中pages列表项中的配置
  (5)uni-app会将pages.json中pages配置项中的第一个页面,作为当前工程的首页(启动页)
  (6)uni-app页面除支持Vue组件生命周期外还支持页面生命周期函数onInit、onLoad、onShow、onReady、onHide、onUnload、onResize、.........
  (7)uni-app组件支持的生命周期,与vue标准组件的生命周期相同beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
  (8)页面调用接口
    getApp,函数用于获取当前应用实例,一般用于获取全局数据
      const app = getApp()
      console.log(app.globalData)
    getCurrentPages,函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面
      const page = getCurrentPages()
      page.$getAppWebview(),获取当前页面的webview对象实例
      page.route,获取当前页面的路由	
    $getAppWebview(),在getCurrentPages()获得的页面里内置了一个方法$getAppWebview(),获取当前页面的webview对象实例
      var pages = getCurrentPages();
      var page = pages[pages.length - 1];
      var currentWebview = page.$getAppWebview();
      console.log(currentWebview.id);//获得当前webview的id
      console.log(currentWebview.isVisible());//查询当前webview是否可见
  (9)页面通讯
    uni.$emit(eventName,OBJECT),触发全局的自定义事件,附加参数都会传给监听器回调
    uni.$on(eventName,callback),监听全局的自定义事件,事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数
    uni.$once(eventName,callback),监听全局的自定义事件,事件可以由 uni.$emit 触发,但是只触发一次,在第一次触发之后移除监听器
    uni.$off([eventName,callback]),移除全局自定义事件监听器
  (10)uni-app框架统一管理页面路由,开发者需要在pages.json里配置每个路由页面的路径及页面样式,使用navigator组件跳转、调用API跳转
  (11)框架以栈的形式管理当前所有页面
  (12)支持在template模板中嵌套<template/>和<block/>
  (13)nvue开发与vue开发的常见区别
7、组件 
  (1)uni-app组件
    视图容器 
    基础内容 
    表单组件 
    路由与页面跳转 
    媒体组件 
  (2)Vue组件
  (3)NVUE组件
  (4)小程序组件
  (5)扩展组件(uni-ui)
8、引用组件
  (1)传统vue项目开发,引用组件需要导入-注册-使用三个步骤,如下:
    <template>
      <view>
        <!-- 3.使用组件 -->
        <uni-rate text="1"></uni-rate>
      </view>
    </template>
    <script>
      //1. 导入组件
      import uniRate from '@/components/uni-rate/uni-rate.vue';
      export default {
        components: { uniRate } //2. 注册组件
      }
    </script>
  (2)Vue 3.x增加了script setup特性,将三步优化为两步,无需注册步骤,更为简洁:
    <template>
      <view>
        <!-- 2.使用组件 -->
        <uni-rate text="1"></uni-rate>
      </view>
    </template>
    <script setup>
      //1. 导入组件
      import uniRate from '@/components/uni-rate/uni-rate.vue';
    </script>
  (3)uni-app的easycom机制,将组件引用进一步优化,开发者只管使用,无需考虑导入和注册,更为高效:
    在 uni-app 项目中,页面引用组件和组件引用组件的方式都是一样的(可以理解为:页面是一种特殊的组件),均支持通过 easycom 方式直接引用。
    <template>
      <view>
        <!-- 1.使用组件 -->
        <uni-rate text="1"></uni-rate>
      </view>
    </template>
    <script>
    </script>
9、引用JS
  (1)绝对路径,@指向项目根目录,在cli项目中@指向src目录
    import add from '@/common/add.js';
  (2)相对路径
    import add from '../../common/add.js';
  (3)js中引入npm包
    import package from 'packageName'
    const package = require('packageName') 
10、引用CSS
  <style>
    @import "../../common/uni.css";
    .uni-card {
        box-shadow: none;
    }
  </style>  
11、引用静态资源,https://uniapp.dcloud.net.cn/tutorial/page-static-assets.html
  (1)#模板内引入静态资源
    template内引入静态资源,如image、video等标签的src属性时,可以使用相对路径或者绝对路径
    <!-- 绝对路径,/static指根目录下的static目录,在cli项目中/static指src目录下的static目录 -->
    <image class="logo" src="/static/logo.png"></image>
    <image class="logo" src="@/static/logo.png"></image>
    <!-- 相对路径 -->
    <image class="logo" src="../../static/logo.png"></image>
  (2)css 引入静态资源
    css文件或style标签内引入css文件时(scss、less 文件同理),可以使用相对路径或绝对路径
    /* 绝对路径 */
    @import url('/common/uni.css');
    @import url('@/common/uni.css');
    /* 相对路径 */
    @import url('../../common/uni.css');
12、JS语法
  (1)uni-app的js API由标准ES的js API和uni扩展API这两部分组成
    标准ECMAScript的js仅是最基础的js
    浏览器扩展了window、document、navigator等对象
    小程序扩展了各种wx.xx、my.xx、swan.xx的API
    node扩展了fs等模块
    uni-app扩展了uni对象,并且API命名与小程序保持兼容
  (2)标准js和浏览器js的区别
    h5端,JS运行于浏览器中
    非h5端(包含小程序和App),Android平台JS运行在v8引擎中,iOS平台JS运行在iOS自带的jscore引擎中,都没有运行在浏览器或webview里
    非H5端,虽然不支持window、document、navigator等浏览器的js API,但也支持标准ECMAScript
  (3)支持绝大部分ES6 API的同时,也支持了ES7的 await/async
13、CSS语法
  (1)uni-app的css与web的css基本一致
  (2)nvue页面里的样式比web页面里的样式限制更多
  (3)支持的通用css单位包括px、rpx
14、vue语法,仅以“组合式API”为例
  (1)目前uni-app基于Vue2.6,组合式API由@vue/composition-api支持,setup由unplugin-vue2-script-setup支持
  (2)在main.js或main.ts文件内增加安装@vue/composition-api插件
  (3)在每个nvue页面安装@vue/composition-api插件
15、vue3组合式API(Composition API),按照逻辑关注点,对部分代码进行分组
    注,来自于文章10,https://www.cnblogs.com/gushixianqiancheng/p/13392540.html
  (1)vue2中,通过配置项(data、computed、methods、watch)将相关逻辑拆散,不便于组件阅读和维护
  (2)vue3中,通过组合式API(setup、ref、reactive、watchEffect、watch、computed、toRefs、生命周期的hooks),将相关逻辑写在一起,便于组件阅读和维护
  (3)setup函数接受两个参数:(props、context)
    props参数是响应式数据,解构props的操作必须通过toRefs完成,toRefs将对象的多个属性变成响应式数据,如var {title} = toRefs(props)
    context参数不是响应式数据,可以直接解构,包含attrs、slots、emit,其中attrs是组件的props配置中没有声明的属性
  (4)setup执行时,组件实例尚未被创建,this修改成undefined
  (5)setup之ref和reactive的区别
    ref定义基本类型数据,使用Object.defineProperty实现数据代理,使用refData.value.name获取数据(template模板中不需要.value)
    reactive定义引用类型数据,使用Proxy实现数据代理,使用reactData.username获取数据
  (6)setup通过ref、reactive、computed生成组件所需值;通过方法,改变组件值、全局值
    以下,组合式API之computed,来源,https://blog.csdn.net/ct5211314/article/details/125874348
    <template>
      <div>
        <div>姓:<input type="text" v-model="per.surname"></div>
        <div>名:<input type="text" v-model="per.name"></div>
        <div>姓名:<input type="text" v-model="per.fullName"></div>
      </div>
    </template>
    <script>
    import { computed, reactive } from 'vue'
    export default {
      setup(){
        let per = reactive({
          surname:'勇敢',
          name:'小陈'
        })
        per.fullName = computed(()=>{
          return per.surname+'~'+per.name
        })
        return{
          per
        }
      }
    }
    </script>
    以下,组合式API之watchEffect,来源,https://blog.csdn.net/ZYS10000/article/details/124535467
    watchEffect,是一个帧听器,是一个副作用函数,它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听。
    import { reactive, watchEffect } from 'vue';
    export default {
      setup(){
        let obj = reactive({
          name:'zs'
        });
        watchEffect(() => {
          console.log('name:',obj.name)
        })
        return {
          obj
        }
      }
    }
16、ts语法
  <script lang="ts">
    //这里编写ts代码
    let s:string = "123"
    console.log(s)
  </script>
17、uts语法
  (1)uts,全称uni type script,是一门跨平台的、高性能的、强类型的现代编程语言,
  (2)uts采用了与ts基本一致的语法规范,支持绝大部分ES6 API
  (3)uts可以被编译为不同平台的编程语言
    web平台,编译为JavaScript
    Android平台,编译为Kotlin
    iOS平台,编译Swift

十三、uniapp跨端框架-全局文件 
1、pages.json 页面路由,对uni-app进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等
  {
    "pages": [{
      "path": "pages/component/index",
      "style": {
        "navigationBarTitleText": "组件"
      }
    }, {
      "path": "pages/API/index",
      "style": {
        "navigationBarTitleText": "接口"
      }
    }, {
      "path": "pages/component/view/index",
      "style": {
        "navigationBarTitleText": "view"
      }
    }],
    "condition": { //模式配置,仅开发期间生效
      "current": 0, //当前激活的模式(list 的索引项)
      "list": [{
        "name": "test", //模式名称
        "path": "pages/component/view/index" //启动页面,必选
      }]
    },
    "globalStyle": {
      "navigationBarTextStyle": "black",
      "navigationBarTitleText": "演示",
      "navigationBarBackgroundColor": "#F8F8F8",
      "backgroundColor": "#F8F8F8",
      "usingComponents":{
        "collapse-tree-item":"/components/collapse-tree-item"
      },
      "renderingMode": "seperated", //仅微信小程序,webrtc 无法正常时尝试强制关闭同层渲染
      "pageOrientation": "portrait", //横屏配置,全局屏幕旋转设置(仅 APP/微信/QQ小程序),支持 auto / portrait / landscape
      "rpxCalcMaxDeviceWidth": 960,
      "rpxCalcBaseDeviceWidth": 375,
      "rpxCalcIncludeWidth": 750
    },
    "tabBar": {
      "color": "#7A7E83",
      "selectedColor": "#3cc51f",
      "borderStyle": "black",
      "backgroundColor": "#ffffff",
      "height": "50px",
      "fontSize": "10px",
      "iconWidth": "24px",
      "spacing": "3px",
        "iconfontSrc":"static/iconfont.ttf", //app tabbar 字体.ttf文件路径 app 3.4.4+
      "list": [{
        "pagePath": "pages/component/index",
        "iconPath": "static/image/icon_component.png",
        "selectedIconPath": "static/image/icon_component_HL.png",
        "text": "组件",
            "iconfont": { //优先级高于 iconPath,该属性依赖 tabbar 根节点的 iconfontSrc
              "text": "\ue102",
              "selectedText": "\ue103",
              "fontSize": "17px",
              "color": "#000000",
              "selectedColor": "#0000ff"
            }
      }, {
        "pagePath": "pages/API/index",
        "iconPath": "static/image/icon_API.png",
        "selectedIconPath": "static/image/icon_API_HL.png",
        "text": "接口"
      }],
      "midButton": {
        "width": "80px",
        "height": "50px",
        "text": "文字",
        "iconPath": "static/image/midButton_iconPath.png",
        "iconWidth": "24px",
        "backgroundImage": "static/image/midButton_backgroundImage.png"
      }
    },
    "easycom": {
      "autoscan": true, //是否自动扫描组件
      "custom": {//自定义扫描规则
        "^uni-(.*)": "@/components/uni-$1.vue"
      }
    },
    "topWindow": {
      "path": "responsive/top-window.vue",
      "style": {
        "height": "44px"
      }
    },
    "leftWindow": {
      "path": "responsive/left-window.vue",
      "style": {
        "width": "300px"
      }
    },
    "rightWindow": {
      "path": "responsive/right-window.vue",
      "style": {
        "width": "300px"
      },
      "matchMedia": {
        "minWidth": 768
      }
    }
  }
2、manifest.json配置,文件是应用的配置文件,用于指定应用的名称、图标、权限等
  {
    "quickapp-webview": {//快应用通用配置
      "icon": "/static/logo.png",
      "package": "com.example.demo",
      "versionName": "1.0.0",
      "versionCode": 100
    },
    "quickapp-webview-union": {//快应用联盟,目前仅支持 vivo、oppo
      "minPlatformVersion": 1063 //最小平台支持
    },
    "quickapp-webview-huawei": {//快应用华为
      "minPlatformVersion": 1070 //最小平台支持
    }
  }
3、package.json扩展配置
  {
    /**
     * package.json其它原有配置 
     * 拷贝代码后请去掉注释!
     */
    "uni-app": {//扩展配置
      "scripts": {
        "custom-platform": { //自定义编译平台配置,可通过cli方式调用
          "title":"自定义扩展名称", //在HBuilderX中会显示在 运行/发行 菜单中
          "browser":"",  //运行到的目标浏览器,仅当UNI_PLATFORM为h5时有效
          "env": {//环境变量
            "UNI_PLATFORM": "",  //基准平台
            "MY_TEST": "", //... 其他自定义环境变量
          },
          "define": { //自定义条件编译
            "CUSTOM-CONST": true //自定义条件编译常量,建议为大写
          }
        }
      }    
    }
  }
4、vue.config.js,是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置webpack等编译选项
  const path = require('path')
  const webpack = require('webpack')
  const CopyWebpackPlugin = require('copy-webpack-plugin') //最新版本copy-webpack-plugin插件暂不兼容,推荐v5.0.0
  module.exports = {
    configureWebpack: {
      plugins: [
        new CopyWebpackPlugin([
          {
            from: path.join(__dirname, 'src/images'),
            to: path.join(__dirname, 'dist', process.env.NODE_ENV === 'production' ? 'build' : 'dev', process.env.UNI_PLATFORM, 'images')
          }
        ])
      ]
    },
    chainWebpack: config => {
      config
        .plugin('define')
        .tap(args => {
          args[0]['process.env'].VUE_APP_TEST = '"test"'
          return args
        })
    }
  }
5、vite.config.js,是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置vite的编译选项,
  必须引用 '@dcloudio/vite-plugin-uni' 并且添加到 plugins 中
  示例一、自定义静态资源目录
  import path from 'path';
  import fs from 'fs-extra';
  import { defineConfig } from 'vite';
  import uni from '@dcloudio/vite-plugin-uni';
  function copyFile() {
    return {
      enforce: 'post',
      async writeBundle() {
        await fs.copy(
          path.resolve(__dirname, 'images'),
          path.join(
            __dirname,
            'unpackage/dist',
            process.env.NODE_ENV === 'production' ? 'build' : 'dev',
            process.env.UNI_PLATFORM,
            'images'
          )
        );
      },
    };
  }
  export default defineConfig({
    plugins: [uni(), copyFile()],
  });
  示例二、注入全局依赖
  //示例从插件市场下载 mp-storage
  import path from 'path';
  import { defineConfig } from 'vite';
  import uni from '@dcloudio/vite-plugin-uni';
  import inject from '@rollup/plugin-inject';
  const mpStoragePath = path.resolve(__dirname, './js_sdk/mp-storage/mp-storage');
  export default defineConfig({
    plugins: [
      uni(),
      inject({
        localStorage: [mpStoragePath, 'localStorage'],
        'window.localStorage': [mpStoragePath, 'localStorage'],
      }),
    ],
    define: {
      'process.env.VUE_APP_TEST': JSON.stringify('test'),
    },
  });
  示例三、配置环境变量
  import { defineConfig } from 'vite';
  import uni from '@dcloudio/vite-plugin-uni';
  export default defineConfig({
    plugins: [uni()],
    define: {
      'process.env.VUE_APP_TEST': JSON.stringify('test'),
    },
  });
6、uni.scss文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss文件里预置了一批scss变量预置 
  <style lang="scss">
  </style>
  以下是uni.scss的相关变量
  $uni-color-primary: #007aff;
  $uni-color-success: #4cd964;
  $uni-color-warning: #f0ad4e;
  $uni-color-error: #dd524d;
7、App.vue是uni-app的主组件,所有页面都是在App.vue下进行切换的,是页面的入口文件
  (1)App.vue本身不是页面,这里不能编写视图元素,也就是没有<template>
  (2)这个文件的作用包括:调用应用生命周期函数、配置全局样式、配置全局的存储globalData
  (3)应用生命周期仅可在App.vue中监听,在页面监听无效
  (4)js中操作globalData的方式如下:getApp().globalData.text = 'test'
  (5)示例代码
    <script>
      //只能在App.vue里监听应用的生命周期
      export default {
        globalData: {  
          text: 'text'  
        }
        onLaunch: function() {
          console.log('App Launch')
        },
        onShow: function() {
          console.log('App Show')
        },
        onHide: function() {
          console.log('App Hide')
        }
      }
    </script>
8、main.js是uni-app的入口文件,主要作用是初始化vue实例、定义全局组件、使用需要的插件如vuex
  (1)vue2代码示例
    import Vue from 'vue'
    import App from './App'
    import pageHead from './components/page-head.vue' //全局引用 page-head 组件
    Vue.config.productionTip = false
    Vue.component('page-head', pageHead) //全局注册 page-head 组件,每个页面将可以直接使用该组件
    App.mpType = 'app'
    const app = new Vue({
      ...App
    })
    app.$mount() //挂载 Vue 实例
  (2)vue3代码示例
    import App from './App'
    import { createSSRApp } from 'vue'
    export function createApp() {
      const app = createSSRApp(App)
      return {
        app
      }
    }

十四、postcss.config.js
来源,https://www.fke6.com/html/110797.html
来源,https://blog.csdn.net/Jensen_Yao/article/details/103203490
注、通过gulp、webpack、grunt等构建工具来使用,用于配置PostCSS的插件和选项,处理CSS
1、示例一,插件排序与插件参数
  module.exports = {
    plugins: [//插件排序
      require('postcss-import'),
      require('postcss-url')({//插件参数
        url: 'inline'
      }),
      require('precss'),
      require('autoprefixer')({//插件参数
        browsers: ['last 2 versions']
      })
    ]
  }
2、示例二,px转rem,
  (1)根字体的3种定义 //任选其一
    <style>
      html { font-size: 20px; } //第1种定义
      :root { font-size: 20px; } //第2种定义
    </style>
    <script>
      document.documentElement.style.fontSize = document.documentElement.clientWidth/10+'px'; //第3种定义
    </script>
  (2)rootValue //postcss.config.js
    module.exports = () => ({
      plugins: [
        require('autoprefixer')(), //自动添加浏览器前缀
        //下面是不同开发者开发的、旨在解决同一个问题的2个竞争产品,
        //在实际开发中,只能二选一,第1个是更现代、更主流的选择
        require('postcss-pxtorem')({
          rootValue: 37.5, //转换比例
          //定义rem:在flexible.js里,自动用js获取屏幕宽并将其固定比例,如1/10定义为1rem。在main.js里引入这个文件
          //转为rem:在postcss.config.js里,插件把项目中如375px,用(375÷37.5=)10rem换掉。插件不知1rem是多少px!!!
          //渲染rem:浏览器根据flexible.js为html标签设置的font-size值,来计算rem对应的像素值,把10rem渲染为屏幕宽
          propList: ['*'], //将哪些属性的px值转换,
          //['*']将所有属性的px值转换,
          //['*', '!border*']含border的属性的px值不转换,其余的都转换
          selectorBlackList: ['.norem'] //忽略所有包含'.norem'类名的选择器
        }),
        // require('postcss-px2rem')({ 
        //   remUnit: 75 //转换比例
        //   //这里没有propList、selectorBlackList配置项
        // }) 
      ]
    });
3、示例三,px转vw
  module.exports = {
    plugins: {
      autoprefixer: {},
      'postcss-px-to-viewport': {
        exclude: undefined, //忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
        fontViewportUnit: 'vw', //字体使用的视口单位
        include: undefined, //如果设置了include,那将只有匹配到的文件才会被转换
        landscape: false, //是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
        landscapeUnit: 'vw', //横屏时使用的单位
        landscapeWidth: 1920, //横屏时使用的视口宽度
        mediaQuery: false, //媒体查询里的单位`px`是否需要转换,false为默认值
        minPixelValue: 1, //设置最小的转换数值,只有大于`1px`的值转换为视窗单位
        propList: ['*'], //能转化为vw的属性列表
        replace: true, //是否直接更换属性值,而不添加备用属性
        selectorBlackList: ['p', '.hairlines'], //需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位
        unitPrecision: 5, //转换后保留的小数位数,precision精确
        unitToConvert: 'px', //需要转换的单位,默认为"px"
        viewportWidth: 375, //设计稿的视口宽度,375px=100vw
        viewportHeight: 1334, //视窗的高度,1334px=100vh
        viewportUnit: 'vw', //指定转换后的视窗单位,建议使用vw
      },
      'postcss-viewport-units': {
        //排除会产生警告的部份
        filterRule: rule => rule.nodes.findIndex(i => i.prop === 'content') === -1
      },
      cssnano: {//集成了部分PostCSS插件,用false禁用其中的某个插件,nano毫微
        preset: 'advanced', //预设: 高级(转换)
        autoprefixer: false, //禁用autoprefixer,和上面的autoprefixer: {}具有相同效果
        'postcss-zindex': false
      }
    }
  }
  

  

posted @ 2020-10-27 00:25  WEB前端工程师_钱成  阅读(6928)  评论(0)    收藏  举报