微信的 WXS: https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxs/
    https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
    支付宝的 SJS: https://docs.alipay.com/mini/framework/sjs
    百度的 Filte: https://smartprogram.baidu.com/docs/develop/framework/view_filter/
    微信为何要创造 WXS
    WXS(WeiXin Script)是微信创造的一套脚本语言,它的官方说法是:“WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致”。
    小程序的运行环境分为逻辑层和视图层,分别由 2 个线程管理,其中:

    • WXML 模板和 WXSS 样式工作在视图层,界面使用 WebView 进行渲染。
    • JavaScript 代码工作在逻辑层,运行在 JsCore 或 v8 里。

    小程序在视图层与逻辑层两个线程间提供了数据传输和事件系统。这样的分离设计,带来了显而易见的好处:

    • 逻辑和视图分离,即使业务逻辑计算非常繁忙,也不会阻塞渲染和用户在视图层上的交互。

    但同时也带来了明显的坏处:

    • 视图层(webview)中不能运行 JS,而逻辑层 JS 又无法直接修改页面 DOM,数据更新及事件系统只能靠线程间通讯,但跨线程通信的成本极高,特别是需要频繁通信的场景。

    什么是需要频繁通讯的场景?最典型的例子就是用户持续交互的情况,比如触摸、滚动等。我们以侧滑菜单为例,假设在页面上滑动 A 元素,要求 B 元素跟随移动,一次滑动操作(touchmove)的响应过程如下:

    1. touchmove 事件从视图层(Webview)传递到逻辑层,中间会由微信客户端(Native)做中转。

    1646098902(1).png

    1. 逻辑层处理 touchmove 事件,计算需移动的位置,然后再通过 setData 传递到视图层,中间同样会由微信客户端(Native)做中转。

    1646098916(1).png
    一次 touchmove 的响应需要经过 视图层、Native、逻辑层三者之间 2 个完整来回的通信,通信的耗时开销较大,用户的交互就会出现延时卡顿的情况。
    对于小程序来讲,这类问题解决起来更容易。其实视图层的 webview,是有 js 环境的,只不过过去不给开发者开放。
    如果在视图层的 js 直接处理滚动或拖动交互、直接处理数据格式,就能避免大量通信损耗。
    但对于小程序平台而言,大量开放 webview 里的 js 编写,违反了它的初衷,比如开发者会直接操作 dom,影响性能体验。所以小程序平台提出一种新规范,限制 webview 里可运行的 js 的能力。这就是 wxs、sjs、filter 的由来。
    从本质来讲,wxs、sjs、filter 是一种被限制过的、运行在视图层 webview 里的 js。
    WXS 特征及适用场景
    WXS 具备如下特征:

    • WXS 是可以在视图层(webview)中运行的 JS。
    • WXS 无法修改业务数据,仅能设置当前组件的class和style。
    • WXS 是被限制过的 JavaScript,可以进行一些简单的逻辑运算。
    • WXS 可以监听 touch 事件,处理滚动、拖动交互。

    故可以得出 WXS 的适用场景,主要包括:

    • 用户交互频繁、仅需改动组件样式(比如布局位置),无需改动数据内容的场景,比如侧滑菜单、索引列表、滚动渐变等。
    • 纯粹的逻辑计算,比如文本、日期格式化,通过 WXS 可以模拟实现 Vue 框架的过滤器, 如下是一个通过 wxs 便捷实现首字母大写的示例:
      1. <wxs module="m1">
      2. // 首字母大写
      3. var capitalize = function(value) {
      4. if (!value) return ''
      5. value = value.toString()
      6. return value.charAt(0).toUpperCase() + value.slice(1)
      7. }
      8. module.exports = {
      9. capitalize: capitalize
      10. }
      11. </wxs>
      12. <view class="content">
      13. <view class="text-area">
      14. <!-- title 为当前页面 data 中定义的初始数据 -->
      15. <text class="title">{{m1.capitalize(title)}}</text>
      16. </view>
      17. </view>
      以下是拖拽的简单实例
      1. <view class="wrap">
      2. <image class="inner" data-id="1" src="1.jpg" @touchmove="drag.touchmove">
      3. </view>
      4. <wxs module="drag">
      5. var touchmove = function(e) {
      6. var instance = e.instance
      7. var x = e.touches[0].pageX
      8. var y = e.touches[0].pageY
      9. instance.setStyle({
      10. left: x+'px',
      11. top: y+'px'
      12. })
      13. }
      14. module.exports = {
      15. touchmove:touchmove
      16. }
      17. </wxs>
      18. <style>
      19. .wrap{
      20. position:relative
      21. }
      22. .inner{
      23. position:absolute;
      24. top:0;
      25. left:0
      26. }
      27. </style>
      其中入参 event 是小程序事件对象基础上多了 event.instance 来表示触发事件的组件的 ComponentDescriptor 实例。ownerInstance 表示的是触发事件的组件所在的组件的 ComponentDescriptor 实例,如果触发事件的组件是在页面内的,ownerInstance 表示的是页面实例。
      ComponentDescriptor的定义如下:
    方法 参数 描述 最低版本
    selectComponent selector对象 返回组件的 ComponentDescriptor 实例。
    selectAllComponents selector对象数组 返回组件的 ComponentDescriptor 实例数组。
    setStyle Object/string 设置组件样式,支持rpx。设置的样式优先级比组件 wxml 里面定义的样式高。不能设置最顶层页面的样式。
    addClass/removeClass/hasClass string 设置组件的 class。设置的 class 优先级比组件 wxml 里面定义的 class 高。不能设置最顶层页面的 class。
    getDataset 返回当前组件/页面的 dataset 对象
    callMethod (funcName:string, args:object) 调用当前组件/页面在逻辑层(App Service)定义的函数。funcName表示函数名称,args表示函数的参数。
    requestAnimationFrame Function 和原生 requestAnimationFrame 一样。用于设置动画。
    getState 返回一个object对象,当有局部变量需要存储起来后续使用的时候用这个方法。
    triggerEvent (eventName, detail) 和组件的triggerEvent
    一致。
    getComputedStyle Array. 参数与 SelectorQuery
    的 computedStyle 一致。
    2.11.2
    setTimeout (Function, Number) 与原生 setTimeout 一致。用于创建定时器。 2.14.2
    clearTimeout Number 与原生 clearTimeout 一致。用于清除定时器。 2.14.2
    getBoundingClientRect 返回值与 SelectorQuery
    的 boundingClientRect 一致。
    2.14.2