天天看点

wepy公共样式_我的wepy使用小册

学习途中总是忘心大,看了官方文档,自己也总结写一遍吧,写一遍记得印象更深刻~

目录

1、wepy的安装与使用

2、vscode 使用 wepy的小技巧

3、微信开发者工具使用

4、项目配置

5、功能特性

6、进阶学习

复制代码

一、wepy的安装与使用

wepy公共样式_我的wepy使用小册

全局安装或更新WePY命令行工具

npm install wepy-cli -g

复制代码

初始化项目

wepy init standard test_wepy // 标准模式,包含了react等测试

wepy init empty test_wepy // 空白模式,适合初始化的空项目

复制代码

安装依赖包

yarn install / npm install

复制代码

项目生成后是这样的

// 以下是空白模式下的目录结构

├── dist 小程序运行代码目录(该目录由WePY的build指令自动编译生成,请不要直接修改该目录下的文件)

├── node_modules

├── src 代码编写的目录(该目录为使用WePY后的开发目录)

| ├── components WePY组件目录(组件不属于完整页面,仅供完整页面或其他组件引用)

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

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

| ├── pages WePY页面目录(属于完整页面)

| | ├── index.wpy index页面(经build后,会在dist目录下的pages目录生成index.js、index.json、index.wxml和index.wxss文件)

| | └── other.wpy other页面(经build后,会在dist目录下的pages目录生成other.js、other.json、other.wxml和other.wxss文件)

| └── app.wpy 小程序配置项(全局数据、样式、声明钩子等;经build后,会在dist目录下生成app.js、app.json和app.wxss文件)

└── package.json 项目的package配置

复制代码

开启实时编译功能

npm run dev

复制代码

至此我们的第一个wepy小程序项目构建完成。

二、vscode 使用 wepy的小技巧

首选项-应用程序-setting.json加上如下代码,重启就ok

"files.associations": {

"*.vue": "vue",

"*.wpy": "vue",

"*.wxml": "html",

"*.wxss": "css"

},

"emmet.syntaxProfiles": {

"vue-html": "html",

"vue": "html"

}

复制代码关于wepy的一些插件

wepy公共样式_我的wepy使用小册

三、微信开发者工具使用

注意事项

1.使用微信开发者工具-->添加项目,项目目录请选择dist目录。

2.微信开发者工具-->项目-->关闭ES6转ES5。 【重要】:漏掉此项会运行报错。

3.微信开发者工具-->项目-->关闭上传代码时样式自动补全。 【重要】:某些情况下漏掉此项也会运行报错。

4.微信开发者工具-->项目-->关闭代码压缩上传。 【重要】:开启后,会导致真机computed, props.sync 等等属性失效。

复制代码

四、项目配置

1.7.0之后的版本init新生成的代码包会在根目录包含project.config.json文件,之前生成的代码包可能不存在project.config.json文件。

{

"description": "A WePY project",

"setting": {

"urlCheck": true, // 对应不检查安全域名选项,开启。 如果已配置好安全域名则建议关闭。

"es6": false, // 对应关闭ES6转ES5选项,关闭。 重要:未关闭会运行报错。

"postcss": false, // 对应关闭上传代码时样式自动补全选项,关闭。 重要:某些情况下漏掉此项也会运行报错。

"minified": false // 对应关闭代码压缩上传选项,关闭。重要:开启后,会导致真机computed, props.sync 等等属性失效。

},

"compileType": "miniprogram",

"appid": "touristappid",

"projectname": "empty_wepy",

"miniprogramRoot": "./dist" // 自己创建一个wepy项目,竟然不用在dist目录下也可以在开发者工具中打开,原因在此

}

复制代码

五、功能特性

1、单文件模式

原生小程序要求app实例必须有3个文件:app.js、app.json、app.wxss,而page页面则一般有4个文件:page.js、page.json、page.wxml、page.wxss,并且还要求app实例的3个文件以及page页面的4个文件除后缀名外必须同名

而在WePY中则使用了单文件模式,将原生小程序app实例的3个文件统一为app.wpy,page页面的4个文件统一为page.wpy。使用WePY开发前后的开发目录结构对比如下:

// 原生小程序的目录结构:

project

├── pages

| ├── index

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

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

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

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

| └── log

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

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

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

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

├── app.js 小程序逻辑

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

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

复制代码// wepy小程序

project

└── src

├── pages

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

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

└──app.wpy 小程序逻辑、公共配置、公共样式

复制代码

2、默认使用babel编译,支持ES6/7的一些新特性

用户可以通过修改wepy.config.js(老版本使用.wepyrc)配置文件,配置自己熟悉的babel环境进行开发。默认开启使用了一些新的特性如promise、async/await。

import wepy from 'wepy';

export default class Index extends wepy.page{

getData() {

return new Promise((resolve, reject) => {

setTimeout(() => {

resolve({data: 123});

}, 3000);

});

};

async onLoad() {

let data = await this.getData();

console.log(data.data);

};

}

复制代码

3、针对原生API进行优化

对小程序原生API进行promise处理,同时修复了一些原生API的缺陷,比如:wx.request的并发问题等。

// 原生

onLoad = function () {

var self = this;

wx.login({

success: function (data) {

wx.getUserInfo({

success: function (userinfo) {

self.setData({userInfo: userinfo});

}

});

}

});

}

复制代码// wepy

import wepy from 'wepy';

async onLoad() {

await wepy.login();

this.userInfo = await wepy.getUserInfo();

}

复制代码在同时并发10个request请求测试时:

wepy公共样式_我的wepy使用小册
wepy公共样式_我的wepy使用小册

使用WePY后:

wepy公共样式_我的wepy使用小册

4、开发模式转换

WePY框架在开发过程中参考了Vue等现有框架的一些语法风格和功能特性,对原生小程序的开发模式进行了再次封装,更贴近于MVVM架构模式。

//index.wpy中的

import wepy from 'wepy';

//通过继承自wepy.page的类创建页面逻辑

export default class Index extends wepy.page {

//可用于页面模板绑定的数据

data = {

motto: 'Hello World',

userInfo: {}

};

//事件处理函数(集中保存在methods对象中)

methods = {

bindViewTap () {

console.log('button clicked');

}

};

//页面的生命周期函数

onLoad() {

console.log('onLoad');

};

}

复制代码

5、支持组件化开发

具体详见下面。

六、进阶学习

1、wepy.config.js配置文件说明

执行wepy init standard demo后,会生成类似下面这样的配置文件。

const path = require('path');

var prod = process.env.NODE_ENV === 'production';

module.exports = {

wpyExt: '.wpy',

eslint: true,

cliLogs: !prod,

build: {

web: {

htmlTemplate: path.join('src', 'index.template.html'),

htmlOutput: path.join('web', 'index.html'),

jsOutput: path.join('web', 'index.js')

}

},

resolve: {

alias: {

counter: path.join(__dirname, 'src/components/counter'),

'@': path.join(__dirname, 'src')

},

aliasFields: ['wepy', 'weapp'],

modules: ['node_modules']

},

compilers: {

less: {

compress: prod

},

babel: {

sourceMap: true,

presets: [

'env'

],

plugins: [

'transform-class-properties',

'transform-decorators-legacy',

'transform-object-rest-spread',

'transform-export-extensions',

]

}

},

plugins: {

},

appConfig: {

noPromiseAPI: ['createSelectorQuery']

}

}

if (prod) {

// 压缩sass

// module.exports.compilers['sass'] = {outputStyle: 'compressed'}

// 压缩js

module.exports.plugins = {

uglifyjs: {

filter: /\.js$/,

config: {

}

},

// 图片压缩

imagemin: {

filter: /\.(jpg|png|jpeg)$/,

config: {

jpg: {

quality: 80

},

png: {

quality: 80

}

}

}

}

}

复制代码

2、.wpy文件说明

.wpy文件的编译过程过下:

wepy公共样式_我的wepy使用小册

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

脚本部分: 即标签中的内容,又可分为两个部分:

逻辑部分: 除了config对象之外的部分,对应于原生的.js文件;

配置部分: 即config对象,对应于原生的.json文件。

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

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

复制代码

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

// some code

复制代码标签

lang默认值

lang支持值

style

css

css、less、scss、stylus、postcss

template

wxml

wxml、xml、pug(原jade)

script

babel

babel、TypeScript

3、脚本解释

小程序入口app.wpy

import wepy from 'wepy'

export default class extends wepy.app {

// 全局配置

config = {

pages: [

'pages/index', // 首页

'pages/find', // 发现页

'pages/my' // 个人中心

],

window: {

backgroundTextStyle: 'light',

navigationBarBackgroundColor: '#fff',

navigationBarTitleText: 'meils',

navigationBarTextStyle: 'black'

}

}

onLaunch() {

console.log('on launch')

}

}

复制代码

入口文件app.wpy中所声明的小程序实例继承自wepy.app类,包含一个config属性和其它全局属性、方法、事件。其中config属性对应原生的app.json文件,build编译时会根据config属性自动生成app.json文件,如果需要修改config中的内容,请使用微信提供的相关API。

页面page.wpy

import wepy from 'wepy';

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

export default class Page extends wepy.page {

config = {};

components = {counter1: Counter};

data = {};

methods = {};

events = {};

onLoad() {};

// Other properties

}

复制代码页面文件page.wpy中所声明的页面实例继承自wepy.page类,该类的主要属性介绍如下:

属性

说明

config

页面配置对象,对应于原生的page.json文件,类似于app.wpy中的config

components

页面组件列表对象,声明页面所引入的组件列表

data

页面渲染数据对象,存放可用于页面模板绑定的渲染数据

methods

wxml事件处理函数对象,存放响应wxml中所捕获到的事件的函数,如bindtap、bindchange

events

WePY组件事件处理函数对象,存放响应组件之间通过 $broadcast、$emit、$invoke所传递的事件的函数

其它

小程序页面生命周期函数,如onLoad、onReady等,以及其它自定义的方法与属性

组件com.wpy

import wepy from 'wepy';

export default class Com extends wepy.component {

components = {};

data = {};

methods = {};

events = {};

// Other properties

}

复制代码

组件文件com.wpy中所声明的组件实例继承自wepy.component类,除了不需要config配置以及页面特有的一些生命周期函数之外,其属性与页面属性大致相同。

4、实例

在 WePY 中,小程序被分为三个实例:小程序实例App、页面实例Page、组件实例Component。其中Page实例继承自Component。

import wepy from 'wepy';

// 声明一个App小程序实例

export default class MyAPP extends wepy.app {

}

// 声明一个Page页面实例

export default class IndexPage extends wepy.page {

}

// 声明一个Component组件实例

export default class MyComponent extends wepy.component {

}

复制代码

App小程序实例

App小程序实例中主要包含小程序生命周期函数、config配置对象、globalData全局数据对象,以及其他自定义方法与属性。

import wepy from 'wepy';

export default class MyAPP extends wepy.app {

customData = {}; // 自定义数据对象

customFunction () { } // 自定义方法

onLaunch () {} // 生命周期,加载的时候

onShow () {} // 页面展示

config = {} // 对应 app.json 文件

globalData = {} // 全局对象

}

复制代码在Page页面实例中,可以通过this.$parent来访问App实例。

Page页面实例和Component组件实例

import wepy from 'wepy';

export default class MyPage extends wepy.page {

// export default class MyComponent extends wepy.component {

customData = {} // 自定义数据

customFunction () {} //自定义方法

onLoad () {} // 在Page和Component共用的生命周期函数

onShow () {} // 只在Page中存在的页面生命周期函数

config = {}; // 只在Page实例中存在的配置数据,对应于原生的page.json文件

data = {}; // 页面所需数据均需在这里声明,可用于模板数据绑定

components = {}; // 声明页面中所引用的组件,或声明组件中所引用的子组件

mixins = []; // 声明页面所引用的Mixin实例

computed = {}; // 声明计算属性(详见后文介绍)

watch = {}; // 声明数据watcher(详见后文介绍)

methods = {}; // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明

events = {}; // 声明组件之间的事件处理函数

}

复制代码这里需要特别强调一下:WePY中的methods属性只能声明页面wxml标签的bind、catch事件,不能声明自定义方法,这与Vue中的用法是不一致的。

5、组件

原生小程序支持js模块化,但彼此独立,业务代码与交互事件仍需在页面处理。无法实现组件化的松耦合与复用的效果。

例如模板A中绑定一个bindtap="myclick",模板B中同样绑定一样bindtap="myclick",那么就会影响同一个页面事件。对于数据同样如此。因此,只有通过改变变量或者事件方法,或者给其加不同前缀才能实现绑定不同事件或者不同数据。当页面复杂之后就十分不利于开发维护。

因此,在WePY中实现了小程序的组件化开发,组件的所有业务与功能在组件本身实现,组件与组件之间彼此隔离,上述例子在WePY的组件化开发过程中,A组件只会影响到A所绑定的myclick,B也如此。

wepy公共样式_我的wepy使用小册

普通组件引用

// index.wpy

import wepy from 'wepy';

//引入组件文件

import Child from '../components/child';

export default class Index extends wepy.page {

//声明组件,分配组件id为child

components = {

child: Child

};

}

复制代码需要注意的是,WePY中的组件都是静态组件,是以组件ID作为唯一标识的,每一个ID都对应一个组件实例,当页面引入两个相同ID的组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。

如果需要避免这个问题,则需要分配多个组件ID和实例。代码如下:

import wepy from 'wepy';

import Child from '../components/child';

export default class Index extends wepy.page {

components = {

//为两个相同组件的不同实例分配不同的组件ID,从而避免数据同步变化的问题

child: Child,

anotherchild: Child

};

}

复制代码

注意:WePY中,在父组件template模板部分插入驼峰式命名的子组件标签时,不能将驼峰式命名转换成短横杆式命名(比如将childCom转换成child-com),这与Vue中的习惯是不一致。

组件的循环渲染

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

// index.wpy

import wepy from 'wepy';

// 引入child组件文件

import Child from '../components/child';

export default class Index extends wepy.page {

components = {

// 声明页面中要使用到的Child组件的ID为child

child: Child

}

data = {

list: [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}]

}

}

复制代码

5、computed

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

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

data = {

a: 1

}

// 计算属性aPlus,在脚本中可通过this.aPlus来引用,在模板中可通过{{ aPlus }}来插值

computed = {

aPlus () {

return this.a + 1

}

}

复制代码

6、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)

}

复制代码

7、props 传值

props传值在WePY中属于父子组件之间传值的一种机制,包括静态传值与动态传值。

静态传值

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

// child.wpy

props = {

title: String

};

onLoad () {

console.log(this.title); // mytitle

}

复制代码动态传值

通过使用.sync修饰符来达到父组件数据绑定至子组件的效果,也可以通过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。那如果既使用.sync修饰符,同时子组件props中添加的twoWay: true时,就可以实现数据的双向绑定了。

// parent.wpy

data = {

parentTitle: 'p-title'

};

// child.wpy

props = {

// 静态传值

title: String,

// 父向子单向动态传值

syncTitle: {

type: String,

default: 'null'

},

twoWayTitle: {

type: String,

default: 'nothing',

twoWay: true

}

};

onLoad () {

console.log(this.title); // p-title

console.log(this.syncTitle); // p-title

console.log(this.twoWayTitle); // p-title

this.title = 'c-title';

console.log(this.$parent.parentTitle); // p-title.

this.twoWayTitle = 'two-way-title';

this.$apply();

console.log(this.$parent.parentTitle); // two-way-title. --- twoWay为true时,子组件props中的属性值改变时,会同时改变父组件对应的值

this.$parent.parentTitle = 'p-title-changed';

this.$parent.$apply();

console.log(this.title); // 'c-title';

console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修饰符的props属性值,当在父组件中改变时,会同时改变子组件对应的值。

}

复制代码

8、组件通信与交互

wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通信和交互,如:

$broadcast

$broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序,如上图,如果页面Page_Index发起一个$broadcast事件,那么按先后顺序依次接收到该事件的组件为:ComA、ComB、ComC、ComD、ComE、ComF、ComG、ComH。如下图:

wepy公共样式_我的wepy使用小册

$emit

$emit与$broadcast正好相反,事件发起组件的所有祖先组件会依次接收到$emit事件。如果组件ComE发起一个$emit事件,那么接收到事件的先后顺序为:组件ComA、页面Page_Index。如下图:

wepy公共样式_我的wepy使用小册

$invoke

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

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

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

复制代码

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

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

复制代码

9、组件自定义事件处理函数

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

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

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

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

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

.user: 绑定用户自定义组件事件,通过$emit触发。注意,如果用了自定义事件,则events中对应的监听函数不会再执行。

// index.wpy

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)

}

}

}

// child.wpy

Click me

import wepy from 'wepy'

export default class Child extends wepy.component {

methods = {

tap () {

console.log('child is clicked')

this.$emit('childFn', 100)

}

}

}

复制代码

10、slot 组件内容分发插槽

示例:

在Panel组件中有以下模板:

默认标题

默认内容

复制代码

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

新的标题

新的内容

复制代码

11、Mixin 混合

混合可以将组件之间的可复用部分抽离,从而在组件中使用混合时,可以将混合的数据,事件以及方法注入到组件之中。混合分为两种:

默认式混合

兼容式混合

默认混合

对于组件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); // bar defined by testMix 使用mixin中的

}

}

复制代码

兼容式混合

对于组件methods响应事件,以及小程序页面事件将采用兼容式混合,即先响应组件本身响应事件,然后再响应混合对象中响应事件。注意,这里事件的执行顺序跟Vue中相反,Vue中是先执行mixin中的函数, 再执行组件本身的函数。

// mixins/test.js

import wepy from 'wepy';

export default class TestMixin extends wepy.mixin {

methods = {

tap () {

console.log('mixin tap');

}

};

onShow() {

console.log('mixin 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

// mixin onshow

// ----- when tap

// index tap

// mixin tap

复制代码

12、数据绑定

原生小程序的数据绑定方式

this.setData({title: 'this is title'});

复制代码

因为小程序架构本身原因,页面渲染层和JS逻辑层分开的,setData操作实际就是JS逻辑层与页面渲染层之间的通信,那么如果在同一次运行周期内多次执行setData操作时,那么通信的次数是一次还是多次呢?这个取决于API本身的设计。

WePY数据绑定方式

this.title = 'this is title';

复制代码

需注意的是,在异步函数中更新数据的时候,必须手动调用$apply方法,才会触发脏数据检查流程的运行。如:

setTimeout(() => {

this.title = 'this is title';

this.$apply();

}, 3000);

复制代码

13、优化细节

请求

// 原生代码:

wx.request({

url: 'xxx',

success: function (data) {

console.log(data);

}

});

// WePY 使用方式, 需要开启 Promise 支持,参考开发规范章节

wepy.request('xxxx').then((d) => console.log(d));

// async/await 的使用方式, 需要开启 Promise 和 async/await 支持,参考 WIKI

async function request () {

let d = await wepy.request('xxxxx');

console.log(d);

}

复制代码

优化事件参数传递

// 原生的事件传参方式:

Click me!

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。

Click me!

methods: {

tapName (id, title, other, event) {

console.log(id, title, other)// output: 1, wepy, otherparams

}

}

复制代码

组件代替模板和模块

// 原生代码:

{{text}}

var item = require('item.js')

// WePY

{{text}}

import wepy from 'wepy';

import Item from '../components/item';

export default class Index extends wepy.page {

components = { com: Item }

}

复制代码先到这里了,以后再继续补充~~