小程序 wxs

标签(空格分隔): 小程序


小程序的一套脚本语言,结合 WXML,可以构建出页面的结构

WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API

页面渲染

<!--wxml-->
<wxs module="m1">
var msg = "hello world";

module.exports.message = msg;
</wxs>

<view> {{m1.message}} </view>

数据处理

// page.js
Page({
  data: {
    array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
  }
})
<!--wxml-->
<!-- 下面的 getMax 函数,接受一个数组,且返回数组中最大的元素的值 -->
<wxs module="m1">
var getMax = function(array) {
  var max = undefined;
  for (var i = 0; i < array.length; ++i) {
    max = max === undefined ?
      array[i] :
      (max >= array[i] ? max : array[i]);
  }
  return max;
}

module.exports.getMax = getMax;
</wxs>

<!-- 调用 wxs 里面的 getMax 函数,参数为 page.js 里面的 array -->
<view> {{m1.getMax(array)}} </view>

WXS响应事件

背景:一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。为了减少通信的次数,让事件在视图层(Webview)响应。

使用 WXS 函数用来响应小程序事件,目前只能响应内置组件的事件,不支持自定义组件事件

WXS 函数的除了纯逻辑的运算,还可以通过封装好的ComponentDescriptor 实例来访问以及设置组件的 class 和样式,对于交互动画,设置 style 和 class 足够了

var wxsFunction = function(event, ownerInstance) {
    var instance = ownerInstance.selectComponent('.classSelector') // 返回组件的实例
    instance.setStyle({
        "font-size": "14px" // 支持rpx
    })
    instance.getDataset()
    instance.setClass(className)
    // ...
    return false // 不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault
}

其中入参 event 是小程序事件对象基础上多了 event.instance 来表示触发事件的组件的 ComponentDescriptor 实例。ownerInstance 表示的是触发事件的组件所在的组件的 ComponentDescriptor 实例,如果触发事件的组件是在页面内的,ownerInstance 表示的是页面实例。

方法 参数 描述
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一致。

WXS 运行在视图层(Webview),里面的逻辑毕竟能做的事件比较少,需要有一个机制和逻辑层(App Service)开发者的代码通信,上面的 callMethod 是 WXS 里面调用逻辑层(App Service)开发者的代码的方法

使用方法
WXML定义事件:

<wxs module="test" src="./test.wxs"></wxs>
<view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>

上面的change:prop(属性前面带change:前缀)是在 prop 属性被设置的时候触发 WXS 函数,值必须用{{}}括起来。类似 Component 定义的 properties 里面的 observer 属性,在setData({propValue: newValue})调用之后会触发。

注意:WXS函数必须用{{}}括起来。当 prop 的值被设置 WXS 函数就会触发,而不只是值发生改变,所以在页面初始化的时候会调用一次WxsPropObserver的函数。

WXS文件test.wxs里面定义并导出事件处理函数和属性改变触发的函数:

module.exports = {
    touchmove: function(event, instance) {
        console.log('log event', JSON.stringify(event))
    },
    propObserver: function(newValue, oldValue, ownerInstance, instance) {
        console.log('prop observer', newValue, oldValue)
    }
}

示例:

/* wxs文件 */
swipeDirection = 0; //是否触发水平滑动 0:未触发 1:触发水平滑动 2:触发垂直滑动
var touchstart = function(event, ownerInstance) {

  var ins = event.instance
  var st = ins.getState()
  st.isMoving = true

  st.startX = event.touches[0].clientX;
  st.startY = event.touches[0].clientY;

  var _data = event.currentTarget.dataset,
    _loc = _data.loc,
    _twoIndex = _data.index;

  st.index = _loc;
  st.twoIndex = _twoIndex;
}


var touchmove = function(event, ownerInstance) {
  var ins = event.instance
  var st = ins.getState()
  if (!st.isMoving) return

  if (event.touches.length == 1) {


    if (swipeDirection === 2) {
      return;
    }



    var pagex = event.touches[0].pageX - st.startX
    var movex = pagex > 0 ? Math.min(st.delBtnWidth, pagex) : Math.max(-st.delBtnWidth, pagex)


    // //已触发垂直滑动,由scroll-view处理滑动操作;
    // //手指起始点位置与移动期间的差值 
    var pagey = event.touches[0].pageY - st.startY;
    // //未触发滑动方向
    if (swipeDirection === 0) {

      //触发垂直操作
      if (Math.abs(pagey) > 20) {
        swipeDirection = 2;

        return;
      }
      //触发水平操作
      if (Math.abs(movex) > 4) {

        swipeDirection = 1;
        ownerInstance.callMethod('scrollYFn', {
          scrollY: false
        })
      } else {
        // return;
      }

    }
    // 往回滑动的情况
    var goods = ownerInstance.selectAllComponents('.good-con')
    goods.map(function(item) {
      var itemOut = item.getState().out
      if (itemOut) {
        item.setStyle({
          'transform': 'translateX(0px)',
          'transition': 'transform 0.4s'
        })
      }
    })

    if (st.out) {
      // 已经是划出来了,还要往左滑动,忽略
      if (movex < 0) return
      ins.setStyle({
        'transform': 'translateX(' + (st.transformx + movex) + 'px)',
        'transition': ''
      })

      return
    }

    if (movex > 0) movex = 0

    st.transformx = movex

    ins.setStyle({
      'transform': 'translateX(' + movex + 'px)',
      'transition': ''
    })

    ownerInstance.callMethod('wxsTouchmove')

    return
  }

}


var touchend = function(event, ownerInstance) {
  var ins = event.instance
  var st = ins.getState()
  if (!st.isMoving) return
  st.isMoving = false
 

  swipeDirection = 0;
  if (event.changedTouches.length == 1) {
    //手指移动结束后水平位置
    var endX = event.changedTouches[0].clientX;
    var endY = event.changedTouches[0].clientY;
    //触摸开始与结束,手指移动的距离
    var disX = st.startX - endX;
    var disY = st.startY - endY;
    var delBtnWidth = st.delBtnWidth;
    // if ((Math.abs(disY) > 10 && disX < delBtnWidth) || disX<0) {
    if (Math.abs(event.changedTouches[0].pageX - st.startX) < st.throttle || event.changedTouches[0].pageX - st.startX > 0) {
      st.out = false
      ins.setStyle({
        'transform': 'translateX(0px)',
        'transition': 'transform 0.4s'
      })
      ownerInstance.callMethod('scrollYFn', {
        scrollY: true
      })
      return;
    }


    //如果距离小于删除按钮的1/2,不显示删除按钮
    var txtStyle = disX > delBtnWidth / 2 ? "-" + delBtnWidth : "0";

    ins.setStyle({
      'transform': 'translateX(' + (txtStyle) + 'px)',
      'transition': 'transform 0.4s'
    })
    st.out = true;
    st.transformx = -Math.abs(txtStyle)
    if (disX <= delBtnWidth / 2) {
      ownerInstance.callMethod('scrollYFn', {
        scrollY: true
      })
    }
  }
  ownerInstance.callMethod('show')
}


var hideButton = function(event, ownerInstance) {
  var goods = ownerInstance.selectAllComponents('.good-con')

  goods.map(function(item) {
    if (item.hasClass('touchmove')) {
      item.setStyle({
        'transform': 'translateX(0px)',
        'transition': 'transform 500ms'
      })
    }
  })

  st.transformx = 0
  ownerInstance.callMethod('buttonTapByWxs', {
    id: event.currentTarget.dataset.id
  })
  return false
}

var delBtnWidthFn = function(newVal, oldVal, ownerInstance, ins) {
  var st = ins.getState()
  st.transformx = 0
  st.delBtnWidth = newVal;
  st.throttle = 40 // 固定值
}

module.exports = {
  touchstart: touchstart,
  touchmove: touchmove,
  touchend: touchend,
  hideButton: hideButton,
  delBtnWidthFn: delBtnWidthFn

}
//wxml
<wxs src="./../common/slideview.wxs" module="slideHander"></wxs>

//...
<view class="good-con flex"  bindtouchstart="{{slideHander.touchstart}}" bindtouchmove="{{slideHander.touchmove}}" bindtouchend="{{slideHander.touchend}}" delbtnwidth="{{delBtnWidth}}" change:delbtnwidth="{{slideHander.delBtnWidthFn}}"></view>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 因新工作主要负责微信小程序这一块,最近的重心就移到这一块,该博客是对微信小程序整体的整理归纳以及标明一些细节点,初...
    majun00阅读 7,341评论 0 9
  • 引言 WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。 注意 w...
    OzanShareing阅读 1,387评论 0 3
  • 1官方解读 wxs 与 javascript 是不同的语言,有自己的语法,并不和 javascript 一致。其实...
    嘻洋洋阅读 7,440评论 0 4
  • 在我们人生的各个阶段里好像永远绕不过去一个字,就是“怕”。幼儿园时怕黑,小学时怕写作业,中学时怕考不上重点高中、重...
    circle_local阅读 311评论 0 2
  • 坚持分享26天 俗话说万事开头难,有些事,如果不行动,我们永远发现不了这些事其实我们是可以做得到的。...
    一祉微笑阅读 112评论 0 0