 
  
   简体中文 
 uni-app在支持 vue 组件之外,也实现了对小程序自定义组件的兼容。
小程序组件不是 vue 组件,并且每家小程序都有自己的组件规范,比如微信小程序的组件是 wxml 格式。
小程序组件不是全端可用,支持度最广的微信小程序自定义组件,也只能支持微信小程序、app-vue、web,其他平台无法兼容。
如果需求上只需兼容有限平台,也可以使用小程序组件。否则仍然推荐使用 vue 组件。
平台差异说明
| 平台 | 支持情况 | 小程序组件存放目录 | 
|---|---|---|
| H5 | vue2 项目支持微信小程序组件(2.4.7+),vue3 不支持 | wxcomponents | 
| App(不含 nvue) | vue2 项目支持微信小程序组件,vue3 不支持 | wxcomponents | 
| 微信小程序 | 支持微信小程序组件 | wxcomponents | 
| 支付宝小程序 | 支持支付宝小程序组件 | mycomponents | 
| 百度小程序 | 支持百度小程序组件 | swancomponents | 
| 抖音小程序、飞书小程序 | 支持抖音小程序、飞书小程序组件 | ttcomponents | 
| QQ 小程序 | 支持 QQ 小程序组件 | wxcomponents | 
| 快手小程序 | 支持快手小程序组件 | kscomponents | 
| 京东小程序 | 支持京东小程序组件 | jdcomponents | 
| 鸿蒙元服务 | 支持鸿蒙元服务组件 | hascomponents | 
| 小红书小程序 | 支持小红书小程序组件 | xhscomponents | 
此文档要求开发者对各端小程序的自定义组件有一定了解,没接触过小程序自定义组件的可以参考:
目录结构
	
┌─wxcomponents                  微信小程序自定义组件存放目录
│   └──custom                   微信小程序自定义组件
│		├─index.js
│		├─index.wxml
│		├─index.json
│		└─index.wxss
├─mycomponents                  支付宝小程序自定义组件存放目录
│   └──custom                   支付宝小程序自定义组件
│		├─index.js
│		├─index.axml
│		├─index.json
│		└─index.acss
├─swancomponents                百度小程序自定义组件存放目录
│   └──custom                   百度小程序自定义组件
│		├─index.js
│		├─index.swan
│		├─index.json
│		└─index.css
├─pages
│  └─index
│		└─index.vue
│
├─static
├─main.js
├─App.vue
├─manifest.json
└─pages.json
	
 使用方式
在 pages.json 对应页面的 style -> usingComponents 引入组件:
{
	"pages": [{
		"path": "index/index",
		"style": {
			// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-QQ
			"usingComponents": {
				"custom": "/wxcomponents/custom/index"
			},
			// #endif
			// #ifdef MP-BAIDU
			"usingComponents": {
				"custom": "/swancomponents/custom/index"
			},
			// #endif
			// #ifdef MP-ALIPAY
			"usingComponents": {
				"custom": "/mycomponents/custom/index"
			},
			// #endif
			"navigationBarTitleText": "uni-app"
		}
	}]
}
在页面中使用
<!-- 页面模板 (index.vue) -->
<view>
  <!-- 在页面中对自定义组件进行引用 -->
  <custom name="uni-app"></custom>
</view>
代码示例
下面以微信小程序官方自定义组件示例 miniprogram-slide-view 为例演示小程序自定义组件的使用方式。
其他组件使用示例见 GitHub:wxcomponents-template。
插件市场有一个完整的vant weapp 引用好的示例工程,详见https://ext.dcloud.net.cn/plugin?id=302。
目录结构
	
┌─components
├─wxcomponents
│   └──miniprogram-slide-view
│		├─index.js
│		├─index.wxml
│		├─index.json
│		└─index.wxss
│
├─pages
│  └─slide-view
│		└─slide-view.vue
│
├─static
├─main.js
├─App.vue
├─manifest.json
└─pages.json
	
 pages.json
{
  "pages": [
    {
      "path": "slide-view/slide-view",
      "style": {
        "navigationBarTitleText": "slide-view",
        "usingComponents": {
          "slide-view": "/wxcomponents/miniprogram-slide-view/index"
        }
      }
    }
  ]
}
slide-view.vue
<template>
  <view class="slide">
    <slide-view width="750" height="110" slide-width="500">
      <view slot="left" class="l">
        <image src="/static/file_transfer.jpg" class="img"></image>
        <view class="text">
          <view class="title">文件传输助手</view>
          <view class="time">7:00 PM</view>
        </view>
      </view>
      <view slot="right" class="r">
        <view class="read">标为已读</view>
        <view class="delete">删除</view>
      </view>
    </slide-view>
  </view>
</template>
<script>
  export default {};
</script>
<style>
  .slide {
    border-bottom: 1px solid #dedede;
  }
  .l {
    background-color: white;
    height: 110rpx;
    width: 750rpx;
    display: flex;
    flex-direction: row;
  }
  .r {
    height: 110rpx;
    display: flex;
    direction: row;
    text-align: center;
    vertical-align: middle;
    line-height: 110rpx;
  }
  .read {
    background-color: #ccc;
    color: #fff;
    width: 350rpx;
  }
  .delete {
    background-color: red;
    color: #fff;
    width: 150rpx;
  }
  .img {
    width: 90rpx;
    height: 90rpx;
    border-radius: 10rpx;
    margin: 10rpx 15rpx;
  }
  .text {
    display: flex;
    flex-direction: row;
  }
  .title {
    margin-top: 15rpx;
    font-size: 33rpx;
  }
  .time {
    margin-top: 15rpx;
    color: #ccc;
    font-size: 25rpx;
    margin-left: 330rpx;
  }
</style>
注意事项
wxcomponents(或 mycomponents、swancomponents)下的 js 文件,否则这些文件会被编译。wxcomponents(或 mycomponents、swancomponents)。HBuilderX 建立的工程 wxcomponents 文件夹在 项目根目录下。vue-cli 建立的工程 wxcomponents 文件夹在 src 目录下。可以在 vue.config.js 中自定义其他目录vue 组件中使用小程序组件时,注意在 pages.json 的 globalStyle 中配置 usingComponents,而不是页面级配置。vue 的数据和事件绑定方式
attr="",改为 :attr="a";从 title="复选框" 改为 :title="'复选框' + item"bind:click="toggleActionSheet1" 改为 @click="toggleActionSheet1",目前支付宝小程序不支持 vue 的事件绑定方式,具体参考:支付宝小程序组件事件监听示例catch:tap="xx" 改为 @tap.native.stop="xx"wx:if 改为 v-ifwx:for="" wx:key="" 改为v-for="(item,index) in list"详细的小程序转 uni-app 语法差异可参考文档https://ask.dcloud.net.cn/article/35786。
WXS 是一套运行在视图层的脚本语言,微信端的规范详见。
它的特点是运行在视图层。当需要避免逻辑层和渲染层交互通信折损时,可采用 wxs。
uni-app 可以将 wxs 代码编译到微信小程序、QQ 小程序、app-vue、H5 上(uni-app 2.2.5及以上版本)
与 wxs 类似,百度小程序提供了 Filter,阿里小程序和抖音小程序提供了 SJS,uni-app 也支持使用这些功能,并将它们编译到百度和阿里的小程序端。不过它们的功能还不如 wxs 强大。
平台差异说明
| App | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 抖音小程序、飞书小程序 | QQ 小程序 | 小红书小程序 | 京东小程序 | 
|---|---|---|---|---|---|---|---|---|
| √(不支持 nvue) | √ | √ | SJS | Filter | SJS | √ | SJS | JDS | 
App 端 nvue 解决此类需求,不应该使用 wxs,而是使用 bindingx。
wxs 示例
以下是一些使用 WXS 的简单示例,要完整了解 WXS 语法,请参考WXS 语法参考。本示例使用 wxs 响应touchmove事件,减少视图层与逻辑层通信,使滑动更加丝滑。
<template>
  <view>
    <view class="area">
      <view
        @touchstart="test.touchstart"
        @touchmove="test.touchmove"
        class="movable"
        >{{test.msg}}</view
      >
    </view>
  </view>
</template>
<script module="test" lang="wxs">
  var startX = 0
  var startY = 0
  var lastLeft = 50; var lastTop = 50
  function touchstart(event, ins) {
  	console.log("touchstart")
    var touch = event.touches[0] || event.changedTouches[0]
    startX = touch.pageX
    startY = touch.pageY
  }
  function touchmove(event, ins) {
    var touch = event.touches[0] || event.changedTouches[0]
    var pageX = touch.pageX
    var pageY = touch.pageY
    var left = pageX - startX + lastLeft
    var top = pageY - startY + lastTop
    startX = pageX
    startY = pageY
    lastLeft = left
    lastTop = top
    ins.selectComponent('.movable').setStyle({
      left: left + 'px',
      top: top + 'px'
    })
  	return false
  }
  module.exports = {
  	msg: 'Hello',
    touchstart: touchstart,
    touchmove: touchmove
  }
</script>
<script>
  export default {
    data() {
      return {};
    },
    methods: {},
  };
</script>
<style>
  .area {
    position: absolute;
    width: 100%;
    height: 100%;
  }
  .movable {
    position: absolute;
    width: 100px;
    height: 100px;
    left: 50px;
    top: 50px;
    color: white;
    text-align: center;
    line-height: 100px;
    background-color: red;
  }
</style>
支付宝小程序,百度小程序官方暂未支持事件响应,不过也可以使用对应的 SJS、Filter 过滤器实现一些数据处理的操作,以下代码展示了一个时间格式化的小功能
index.vue
<template>
  <view>
    <view> {{timestr}} 是 </view>
    <view> {{utils.friendlyDate(timestamp)}} </view>
  </view>
</template>
<script module="utils" lang="filter" src="./utils.filter.js"></script>
<script module="utils" lang="sjs" src="./utils.sjs"></script>
<script>
  export default {
    data() {
      return {
        timestr: "2019/08/22 10:10:10",
        timestamp: 0,
      };
    },
    created() {
      this.timestamp = new Date(this.timestr).getTime();
    },
    methods: {},
  };
</script>
utils.sjs 与 utils.filter.js 内容一致
export default {
  friendlyDate: (timestamp) => {
    var formats = {
      year: "%n% 年前",
      month: "%n% 月前",
      day: "%n% 天前",
      hour: "%n% 小时前",
      minute: "%n% 分钟前",
      second: "%n% 秒前",
    };
    var now = Date.now();
    var seconds = Math.floor((now - parseInt(timestamp)) / 1000);
    var minutes = Math.floor(seconds / 60);
    var hours = Math.floor(minutes / 60);
    var days = Math.floor(hours / 24);
    var months = Math.floor(days / 30);
    var years = Math.floor(months / 12);
    var diffType = "";
    var diffValue = 0;
    if (years > 0) {
      diffType = "year";
      diffValue = years;
    } else {
      if (months > 0) {
        diffType = "month";
        diffValue = months;
      } else {
        if (days > 0) {
          diffType = "day";
          diffValue = days;
        } else {
          if (hours > 0) {
            diffType = "hour";
            diffValue = hours;
          } else {
            if (minutes > 0) {
              diffType = "minute";
              diffValue = minutes;
            } else {
              diffType = "second";
              diffValue = seconds === 0 ? (seconds = 1) : seconds;
            }
          }
        }
      }
    }
    return formats[diffType].replace("%n%", diffValue);
  },
};
注意
引入方式
<!-- 内联 -->
<script module="test" lang="wxs">
  //...code
</script>
<script module="utils" lang="filter">
  //...code
</script>
<!-- 外部引入 -->
<script module="utils" lang="wxs" src="./utils.wxs"></script>
<script module="utils" lang="filter" src="./utils.filter.js"></script>
<script module="utils" lang="sjs" src="./utils.sjs"></script>
module所指定的模块名不可与data、methods、computed内的属性重名<script setup> 用法<script>标签引入script的标签属性name、from被统一为了module、src以便后续实现多平台统一写法function函数src 不能为空,必须设置 静态资源地址wxcomponents也可以使用 wxsnvue页面暂不支持 wxs、sjs、filter.jsscript标签会分别被打包至对应支持平台,不需要额外写条件编译HBuilderX 2.2.5开始,不推荐使用各个小程序自有的引入方式,推荐使用script标签引入此特性依赖配置 componentPlaceholder,目前仅支持在 pages.json 中添加页面级别的配置,如果需要在某个组件中配置,可以使用 插件
小程序端默认支持跨分包 JS 代码引用,需要写小程序原生支持的语法,不能使用静态引入或者动态引入。示例如下:
sub分包 定义 utils.js 文件
// sub/utils.js
export function add(a, b) {
    return a + b
}
sub分包 正常使用 utils.js 文件
// sub/index.vue
<template>
    <view>
        {{ count }}
        <button @tap="handleClick">add one</button>
    </view>
</template>
<script>
    import {
        add
    } from "./utils.js";
    export default {
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleClick() {
                this.count = add(this.count, 1)
            }
        }
    }
</script>
其他分包使用 sub分包 的 utils.js 文件
// sub2/index.vue
<template>
    <view>
       {{ count }}
        <button @tap="handleClick">add two</button>
    </view>
</template>
<script>
    export default {
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleClick() {
                require('../sub/utils.js', sub_utils => {
                    this.count = sub_utils.add(this.count, 2);
                }, ({
                    mod,
                    errMsg
                }) => {
                    console.error(`path: ${mod}, ${errMsg}`)
                })
            }
        }
    }
</script>
注意: