微信小程序wepy框架笔记小结


Posted in Javascript onAugust 08, 2018

该框架是腾讯内部基于小程序的开发框架,设计思路基本参考VUE,开发模式和编码风 格上80%以上接近VUE

优势

组件化开发

小程序虽然有标签可以实现组件复用,但仅限于模板片段层面的复用,业务代码与交互事件 仍需在页面处理。无法实现组件化的松耦合与复用的效果。

wepy组件示例

// index.wpy

 <template>

  <view>

   <panel>

    <h1 slot="title"></h1>

   </panel>

   <counter1 :num="myNum"></counter1>

   <counter2 :num.sync="syncNum"></counter2>

   <list :item="items"></list>

  </view>

 </template>

 <script>
 import wepy from 'wepy';

 import List from '../components/list';

 import Panel from '../components/panel';

 import Counter from '../components/counter';

 export default class Index extends wepy.page {

  config = {

   "navigationBarTitleText": "test"

  };

  components = {

   panel: Panel,

   counter1: Counter,

   counter2: Counter,

   list: List

  };

  data = {

   myNum: 50,

   syncNum: 100,

   items: [1, 2, 3, 4]

  }

 }
 </script>

支持加载外部NPM包

小程序较大的缺陷是不支持NPM包,导致无法直接使用大量优秀的开源内容,wepy在编译过程当中,会递归 遍历代码中的require然后将对应依赖文件从node_modules当中拷贝出来,并且修改require为相对路径, 从而实现对外部NPM包的支持

单文件模式,使得目录结构更加清晰

小程序官方目录结构要求app必须有三个文件app.json,app.js,app.wxss,页面有4个文件 index.json,index.js,index.wxml,index.wxss。而且文 件必须同名。 所以使用wepy开发前后开发目录对比如下:

官方DEMO:

project

├── pages

| ├── index

| | ├── index.json index 页面配置

| | ├── index.js index 页面逻辑

| | ├── index.wxml index 页面结构

| | └── index.wxss index 页面样式表

| └── log

|  ├── log.json log 页面配置

|  ├── log.wxml log 页面逻辑

|  ├── log.js  log 页面结构

|  └── log.wxss log 页面样式表

├── app.js    小程序逻辑

├── app.json   小程序公共设置

└── app.wxss   小程序公共样式表

使用wepy框架后目录结构:

project

└── src

 ├── pages

 | ├── index.wpy index 页面配置、结构、样式、逻辑

 | └── log.wpy  log 页面配置、结构、样式、逻辑

 └──app.wpy   小程序配置项(全局样式配置、声明钩子等)

如何开发

快速起步

安装

npm install wepy-cli -g

小程序框架wepy命令行工具

创建项目

wepy new myproject

切换至项目目录

cd myproject

实时编译

wepy build ?watch

目录结构 ├── dist 微信开发者工具指定的目录

├── node_modules

├── src     代码编写的目录

| ├── components   组件文件夹(非完整页面)

| | ├── com_a.wpy  可复用组件 a

| | └── com_b.wpy  可复用组件 b

| ├── pages    页面文件夹(完整页面)

| | ├── index.wpy  页面 index

| | └── page.wpy  页面 page

| └── app.wpy   小程序配置项(全局样式配置、声明钩子等)

└── package.json   package 配置

wepy和VUE主要区别

1.二者均支持props、data、computed、components、methods、watch(wepy中是watcher), 但wepy中的methods仅可用于页面事件绑定,其他自定义方法都要放在外层,而VUE中所有方法均放在 methods下

2.wepy中props传递需要加上.sync修饰符(类似VUE1.x)才能实现props动态更新,并且父组件再 变更传递给子组件props后要执行this.$apply()方法才能更新

3.wepy支持数据双向绑定,子组件在定义props时加上twoway:true属性值即可实现子组件修改父组 件数据

4.VUE2.x推荐使用eventBus方式进行组件通信,而在wepy中是通过broadcast,broadcast,emit,$invoke 三种方法实现通信

· 首先事件监听需要写在events属性下:

``` bash

import wepy from 'wepy';

export default class Com extends wepy.component {

 components = {};

 data = {};

 methods = {};

 events = {

  'some-event': (p1, p2, p3, $event) => {

    console.log(`${this.name} receive ${$event.name} from ${$event.source.name}`);

  }

 };

 // Other properties

}

```
· $broadcast:父组件触发所有子组件事件
· $emit:子组件触发父组件事件
· $invoke:子组件触发子组件事件

 5.VUE的生命周期包括created、mounted等,wepy仅支持小程序的生命周期:onLoad、onReady等

6.wepy不支持过滤器、keep-alive、ref、transition、全局插件、路由管理、服务端渲染等VUE特性技术

进阶介绍

.wpy文件说明

一个.wpy文件可分为三大部分,各自对应于一个标签:

脚本部分,即标签中的内容,又可分为两个部分:
    逻辑部分,除了config对象之外的部分,对应于原生的.js文件;
    配置部分,即config对象,对应于原生的.json文件。

结构部分,即模板部分,对应于原生的.wxml文件。

样式部分,即样式部分,对应于原生的.wxss文件。

.wpy文件中的script、template、style这三个标签都支持lang和src属性,lang决定了其代码编译过程,src决定是否外联代码,存在src属性且有效时,会忽略内联代码。

标签 lang默认值 lang支持值
style css css、less、sass、stylus
template wxml wxml、xml、pug(原jade)
script babel babel、TypeScript

普通组件引用

当页面需要引入组件或组件需要引入子组件时,必须在.wpy文件的

<template>
 <!-- 以`<script>`脚本部分中所声明的组件ID为名命名自定义标签,从而在`<template>`模板部分中插入组件 -->
 <child></child>
</template>

<script>
 import wepy from 'wepy';
 //引入组件文件
 import Child from '../components/child';

 export default class Index extends wepy.component {
  //声明组件,分配组件id为child
  components = {
   child: Child
  };
 }
</script>

需要注意的是,WePY中的组件都是静态组件,是以组件ID作为唯一标识的,每一个ID都对应一个组件实例,当页面引入两个相同ID的组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。
如果需要避免这个问题,则需要分配多个组件ID和实例。

组件的循环渲染

1.4.6新增

当需要循环渲染WePY组件时(类似于通过wx:for循环渲染原生的wxml标签),必须使用WePY定义的辅助标签

<template>
 <!-- 注意,使用for属性,而不是使用wx:for属性 -->
 <repeat for="{{list}}" key="index" index="index" item="item">
  <!-- 插入<script>脚本部分所声明的child组件,同时传入item -->
  <child :item="item"></child>
 </repeat>
</template>

computed 计算属性

computed计算属性,是一个有返回值的函数,可直接被当作绑定数据来使用。因此类似于data属性,代码中可通过this.计算属性名来引用,模板中也可通过{{ 计算属性名 }}来绑定数据。

需要注意的是,只要是组件中有任何数据发生了改变,那么所有计算属性就都会被重新计算。

data = {
  a: 1
 }

 // 计算属性aPlus,在脚本中可通过this.aPlus来引用,在模板中可通过{{ aPlus }}来插值
 computed = {
  aPlus () {
   return this.a + 1
  }
 }

watcher 监听器

通过监听器watcher能够监听到任何数值属性的数值更新。监听器在watch对象中声明,类型为函数,函数名与需要被监听的data对象中的数值属性同名,每当被监听的数值属性改变一次,监听器函数就会被自动调用执行一次。
监听器适用于当数值属性改变时需要进行某些额外处理的情形。

data = {
  num: 1
 }

 // 监听器函数名必须跟需要被监听的data对象中的数值属性num同名,
 // 其参数中的newValue为数值属性改变后的新值,oldValue为改变前的旧值
 watch = {
  num (newValue, oldValue) {
   console.log(`num value: ${oldValue} -> ${newValue}`)
  }
 }

 // 每当被监听的数值属性num改变一次,对应的同名监听器函数num()就被自动调用执行一次
 onLoad () {
  setInterval(() => {
   this.num++;
   this.$apply();
  }, 1000)
 }

props 传值

静态传值

静态传值为父组件向子组件传递常量数据,因此只能传递String字符串类型。

在父组件template模板部分的组件标签中,使用子组件props对象中所声明的属性名作为其属性名来接收父组件传递的值。

<child title="mytitle"></child>

// child.wpy
props = {
 title: String
};

onLoad () {
 console.log(this.title); // mytitle
}

动态传值

动态传值是指父组件向子组件传递动态数据内容,父子组件数据完全独立互不干扰。但可以通过使用.sync修饰符来达到父组件数据绑定至子组件的效果,也可以通过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。那如果即使用.sync修饰符,同时子组件props中添加的twoWay: true时,就可以实现数据的双向绑定了。

注意:下文示例中的twoWay为true时,表示子组件向父组件单向动态传值,而twoWay为false(默认值,可不写)时,则表示子组件不向父组件传值。这是与Vue不一致的地方,而这里之所以仍然使用twoWay,只是为了尽可能保持与Vue在标识符命名上的一致性。

在父组件template模板部分所插入的子组件标签中,使用:prop属性(等价于Vue中的v-bind:prop属性)来进行动态传值。

// parent.wpy

<child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child>

data = {
 parentTitle: 'p-title'
};


// child.wpy

props = {
 // 静态传值
 title: String,

 // 父向子单向动态传值
 syncTitle: {
  type: String,
  default: 'null'
 },

 twoWayTitle: {
  type: Number,
  default: 50,
  twoWay: true
 }
};

onLoad () {
 console.log(this.title); // p-title
 console.log(this.syncTitle); // p-title
 console.log(this.twoWayTitle); // 50

 this.title = 'c-title';
 console.log(this.$parent.parentTitle); // p-title.
 this.twoWayTitle = 60;
 this.$apply();
 console.log(this.$parent.parentTitle); // 60. --- twoWay为true时,子组件props中的属性值改变时,会同时改变父组件对应的值
 this.$parent.parentTitle = 'p-title-changed';
 this.$parent.$apply();
 console.log(this.title); // 'p-title';
 console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修饰符的props属性值,当在父组件中改变时,会同时改变子组件对应的值。
}

 组件通信与交互

用于监听组件之间的通信与交互事件的事件处理函数需要写在组件和页面的events对象中

- broadcastbroadcastbroadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序

- emitemitemit与broadcast正好相反,事件发起组件的所有祖先组件会依次接收到broadcast正好相反,事件发起组件的所有祖先组件会依次接收到emit事件。

- invokeinvokeinvoke是一个页面或组件对另一个组件中的方法的直接调用,通过传入组件路径找到相应的组件,然后再调用其方法。

比如,想在页面Page_Index中调用组件ComA的某个方法:

this.$invoke('ComA', 'someMethod', 'someArgs');

如果想在组件ComA中调用组件ComG的某个方法:

this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');

组件自定义事件处理函数

可以通过使用.user修饰符为自定义组件绑定事件,如:@customEvent.user=”myFn”

其中,@表示事件修饰符,customEvent 表示事件名称,.user表示事件后缀。

目前总共有三种事件后缀:

.default: 绑定小程序冒泡型事件,如bindtap,.default后缀可省略不写;

.stop: 绑定小程序捕获型事,如catchtap;

.user: 绑定用户自定义组件事件,通过$emit触发。

// index.wpy

<template>
 <child @childFn.user="parentFn"></child>
</template>

<script>
 import wepy from 'wepy'
 import Child from '../components/child'

 export default class Index extends wepy.page {
  components = {
   child: Child
  }

  methods = {
   parentFn (num, evt) {
    console.log('parent received emit event, number is: ' + num)
   }
  }
 }
</script>


// child.wpy

<template>
 <view @tap="tap">Click me</view>
</template>

<script>
 import wepy from 'wepy'

 export default class Child extends wepy.component {
  methods = {
   tap () {
    console.log('child is clicked')
    this.$emit('childFn', 100)
   }
  }
 }
</script>

slot 组件内容分发插槽

WePY中的slot插槽作为内容分发标签的空间占位标签,便于在父组件中通过对相当于扩展板卡的内容分发标签的“插拔”,更为灵活、方便地对子组件进行内容分发。

具体使用方法是,首先在子组件template模板部分中声明slot标签作为内容插槽,同时必须在其name属性中指定插槽名称,还可设置默认的标签内容;然后在引入了该带有插槽的子组件的父组件template模板部分中声明用于“插拔”的内容分发标签。
注意,这些父组件中的内容分发标签必须具有slot属性,并且其值为子组件中对应的插槽名称,这样父组件内容分发标签中的内容会覆盖掉子组件对应插槽中的默认内容。

在Panel组件中有以下模板:

<view class="panel">
 <slot name="title">默认标题</slot>
 <slot name="content">默认内容</slot>
</view>

在父组件中使用Pannel子组件时,可以这样使用:

<panel>
 <view slot="title">新的标题</view>
 <view slot="content">
  <text>新的内容</text>
 </view>
</panel>

混合

默认式混合

对于组件data数据,components组件,events事件以及其它自定义方法采用默认式混合,即如果组件未声明该数据,组件,事件,自定义方法等,那么将混合对象中的选项将注入组件这中。对于组件已声明的选项将不受影响。

// mixins/test.js
import wepy from 'wepy';

export default class TestMixin extends wepy.mixin {
 data = {
  foo: 'foo defined by page',
  bar: 'bar defined by testMix'
 };
 methods: {
 tap () {
  console.log('mix tap');
 }
 }
}

// pages/index.wpy
import wepy from 'wepy';
import TestMixin from './mixins/test';

export default class Index extends wepy.page {
 data = {
  foo: 'foo defined by index'
 };
 mixins = [TestMixin ];
 onShow() {
  console.log(this.foo); // foo defined by index.
  console.log(this.bar); // foo defined by testMix.
 }
}

兼容式混合

对于组件methods响应事件,以及小程序页面事件将采用兼容式混合,即先响应组件本身响应事件,然后再响应混合对象中响应事件。

// mixins/test.js
import wepy from 'wepy';

export default class TestMixin extends wepy.mixin {
 methods = {
  tap () {
   console.log('mix tap');
  }
 };
 onShow() {
  console.log('mix onshow');
 }
}

// pages/index.wpy
import wepy from 'wepy';
import TestMixin from './mixins/test';

export default class Index extends wepy.page {

 mixins = [TestMixin];
 methods = {
  tap () {
   console.log('index tap');
  }
 };
 onShow() {
  console.log('index onshow');
 }
}


// index onshow
// mix onshow
// ----- when tap
// index tap
// mix tap

拦截器

可以使用全域拦截器配置API的config、fail、success、complete方法,参考示例:

import wepy from 'wepy';

export default class extends wepy.app {

 constructor () {
  this.intercept('request', {
   config (p) {
    p.timestamp = +new Date();
    return p;
   },
   success (p) {
    console.log('request success');
    return p;
   },
   fail (p) {
    console.log('request error');
    return p;
   }
  });
 }
}

WePY数据绑定方式

WePY使用脏数据检查对setData进行封装,在函数运行周期结束时执行脏数据检查,一来可以不用关心页面多次setData是否会有性能上的问题,二来可以更加简洁去修改数据实现绑定,不用重复去写setData方法。

this.title = 'this is title';

在函数运行周期之外的函数里去修改数据需要手动调用$apply方法

setTimeout(() => {
 this.title = 'this is title';
 this.$apply();
}, 3000);

优化事件参数传递

// 官方
<view data-id="{{index}}" data-title="wepy" data-other="otherparams" bindtap="tapName"> Click me! </view>
Page({
 tapName: function(event) {
 console.log(event.currentTarget.dataset.id)// output: 1
 console.log(event.currentTarget.dataset.title)// output: wepy
 console.log(event.currentTarget.dataset.other)// output: otherparams
 }
});

// WePY 1.1.8以后的版本,只允许传string。
<view bindtap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view>

methods: {
 tapName (id, title, other, event) {
  console.log(id, title, other)// output: 1, wepy, otherparams
 }
}

改变数据绑定方式

保留setData方法,但不建议使用setData执行绑定,修复传入undefined的bug,并且修改入参支持: this.setData(target, value) this.setData(object)

// 官方
<view> {{ message }} </view>

onLoad: function () {
 this.setData({message: 'hello world'});
}


// WePY
<view> {{ message }} </view>

onLoad () {
 this.message = 'hello world';
}

重要提醒

  1. 使用微信开发者工具?>添加项目,项目目录请选择dist目录。
  2. 微信开发者工具?>项目?>关闭ES6转ES5。 重要:漏掉此项会运行报错。
  3. 微信开发者工具?>项目?>关闭上传代码时样式自动补全。 重要:某些情况下漏掉此项也会运行报错。
  4. 微信开发者工具?>项目?>关闭代码压缩上传。 重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替,详见后文相关介绍以及Demo项目根目录中的wepy.config.js和package.json文件。)
  5. 本地项目根目录运行wepy build ?watch,开启实时编译。(注:如果同时在微信开发者工具?>设置?>编辑器中勾选了文件保存时自动编译小程序,将可以实时预览,非常方便。)

注意

WePY中的methods属性只能声明页面wxml标签的bind、catch事件,不能声明自定义方法

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS模拟面向对象全解(二、类型与赋值)
Jul 13 Javascript
javascript不可用的问题探究
Oct 01 Javascript
使用jsonp完美解决跨域问题
Nov 27 Javascript
jQuery自定义组件(导入组件)
Nov 08 Javascript
Bootstrap基本样式学习笔记之表格(2)
Dec 07 Javascript
学习使用Bootstrap栅格系统
May 11 Javascript
vue.js加载新的内容(实例代码)
Jun 01 Javascript
Web技术实现移动监测的介绍
Sep 18 Javascript
Angular5.1新功能分享
Dec 21 Javascript
vue下拉列表功能实例代码
Apr 08 Javascript
Layui事件监听的实现(表单和数据表格)
Oct 17 Javascript
Vue触发input选取文件点击事件操作
Aug 07 Javascript
angularJs中$http获取后台数据的实例讲解
Aug 08 #Javascript
JavaScript常见JSON操作实例分析
Aug 08 #Javascript
vue.js与后台数据交互的实例讲解
Aug 08 #Javascript
JS实现自定义弹窗功能
Aug 08 #Javascript
vue.js提交按钮时进行简单的if判断表达式详解
Aug 08 #Javascript
解决jquery的ajax调取后端数据成功却渲染失败的问题
Aug 08 #jQuery
JS+HTML5实现获取手机验证码倒计时按钮
Aug 08 #Javascript
You might like
php二维数组排序与默认自然排序的方法介绍
2013/04/27 PHP
PHP设计模式之命令模式的深入解析
2013/06/13 PHP
LINUX下PHP程序实现WORD文件转化为PDF文件的方法
2016/05/13 PHP
laravel学习教程之存取器
2016/07/30 PHP
使用php自动备份数据库表的实现方法
2017/07/28 PHP
使用vs code编辑调试php配置的方法
2019/01/29 PHP
JScript内置对象Array中元素的删除方法
2007/03/08 Javascript
javascript 设置某DIV区域内的checkbox复选框
2009/11/30 Javascript
工作中常用到的JS表单验证代码(包括例子)
2010/11/11 Javascript
动态创建样式表在各浏览器中的差异测试代码
2011/09/13 Javascript
a标签的href和onclick 的事件的区别介绍
2013/07/26 Javascript
javascript中的self和this用法小结
2014/02/08 Javascript
js限制checkbox选中个数以限制六个为例
2014/07/15 Javascript
深入理解JavaScript中的对象
2015/06/04 Javascript
JavaScript中的this关键字使用详解
2015/08/14 Javascript
JS+CSS相对定位实现的下拉菜单
2015/10/06 Javascript
详解JavaScript中常用的函数类型
2015/11/18 Javascript
Bootstrap carousel轮转图的使用实例详解
2016/05/17 Javascript
javascript表单正则应用
2017/02/04 Javascript
JavaScript之Map和Set_动力节点Java学院整理
2017/06/29 Javascript
JavaScript屏蔽Backspace键的实现代码
2017/11/02 Javascript
微信小程序开发之左右分栏效果的实例代码
2019/05/20 Javascript
Vue + ts实现轮播插件的示例
2020/11/10 Javascript
Python 文件读写操作实例详解
2014/03/12 Python
apache部署python程序出现503错误的解决方法
2017/07/24 Python
儿童编程python入门
2018/05/08 Python
Python 字符串类型列表转换成真正列表类型过程解析
2019/08/26 Python
马来西亚航空官方网站:Malaysia Airlines
2017/07/28 全球购物
Sneaker Studio法国:购买运动鞋
2018/06/08 全球购物
财务总监管理职责范文
2014/03/09 职场文书
学校感恩教育活动总结
2014/07/07 职场文书
土地租赁意向书
2014/07/30 职场文书
2014入党积极分子批评与自我批评思想汇报
2014/09/20 职场文书
房屋过户委托书范本
2014/10/07 职场文书
2014年财政工作总结
2014/12/10 职场文书
vue本地构建热更新卡顿的问题“75 advanced module optimization”完美解决方案
2022/08/05 Vue.js