WePY

介绍

是什么

WePY (发音: /‘wepi/)是一款让小程序支持组件化开发的框架,通过预编译的手段让开发者可以选择自己喜欢的开发风格去开发小程序。框架的细节优化,Promise,Async Functions的引入都是为了能让开发小程序项目变得更加简单,高效。

同时WePY也是一款成长中的框架,大量吸收借鉴了一些优化前端工具以及框架的设计理念和思想。如果WePY有不足地方,或者你有更好的想法,欢迎提交ISSUE或者PR。

特性

  • 类Vue开发风格
  • 支持自定义组件开发
  • 支持引入NPM包
  • 支持Promise
  • 支持ES2015+特性,如Async Functions
  • 支持多种编译器,Less/Sass/Stylus/PostCSS、Babel/Typescript、Pug
  • 支持多种插件处理,文件压缩,图片压缩,内容替换等
  • 支持 Sourcemap,ESLint等
  • 小程序细节优化,如请求列队,事件优化等

相关链接

快速入门

安装

  1. # 全局安装 Wepy 命令行工具
  2. npm i -g wepy-cli
  3. # 查看安装的 Wepy 确实 Wepy 是否安装好
  4. wepy --version

初始化

  1. # 查看可用模板
  2. wepy list
  1. # 使用 Wepy 基于 standard 模板初始化项目
  2. wepy init standard wepy-demo
  1. # 安装依赖
  2. cd wepy-demo
  3. npm install

编译构建

  1. # 执行打包
  2. # wepy 会把 src 中的源码编程成小程序代码文件,输出到 dist 目录中
  3. wepy build
  4. # 监视打包
  5. # 建议在开发的时候使用监视代码,Wepy 会监视 src 源码的改变,然后实时的编译打包
  6. wepy build -w

项目目录结构

  1. ├── dist 小程序运行代码目录(该目录由WePYbuild指令自动编译生成,请不要直接修改该目录下的文件)
  2. ├── node_modules
  3. ├── src 代码编写的目录(该目录为使用WePY后的开发目录)
  4. | ├── components WePY组件目录(组件不属于完整页面,仅供完整页面或其他组件引用)
  5. | | ├── com_a.wpy 可复用的WePY组件a
  6. | | └── com_b.wpy 可复用的WePY组件b
  7. | ├── pages WePY页面目录(属于完整页面)
  8. | | ├── index.wpy index页面(经build后,会在dist目录下的pages目录生成index.jsindex.jsonindex.wxmlindex.wxss文件)
  9. | | └── other.wpy other页面(经build后,会在dist目录下的pages目录生成other.jsother.jsonother.wxmlother.wxss文件)
  10. | └── app.wpy 小程序配置项(全局数据、样式、声明钩子等;经build后,会在dist目录下生成app.jsapp.jsonapp.wxss文件)
  11. └── package.json 项目的package配置

app.wpy

1527737843592

页面.wpy

1527739143610

添加项目

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

如果存在,使用微信开发者工具—>添加项目项目目录请选择项目根目录即可根据配置完成项目信息自动配置。

如果不存在,建议手动创建该文件后再添加项目。project.config.json文件内容如下:

  1. {
  2. "description": "project description",
  3. "setting": {
  4. "urlCheck": true,
  5. "es6": false,
  6. "postcss": false,
  7. "minified": false
  8. },
  9. "compileType": "miniprogram",
  10. "appid": "touristappid",
  11. "projectname": "Project name",
  12. "miniprogramRoot": "./dist"
  13. }

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

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

minified: 对应关闭代码压缩上传选项,关闭。重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替,详见后文相关介绍以及Demo项目根目录中的wepy.config.jspackage.json文件。)

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

参考建议

  1. WePY借鉴了Vue.js(后文简称Vue)的语法风格和功能特性,如果你之前从未接触过Vue,建议先阅读Vue的官方文档,以熟悉相关概念,否则在阅读WePY文档以及使用WePY进行开发的过程中,将会遇到比较多的障碍。

  2. 开发建议使用第三方成熟IDE或编辑器(具体请参看后文的代码高亮部分),微信开发者工具仅用于实时预览和调试。

代码高亮

文件后缀为.wpy,可共用Vue的高亮规则,但需要手动设置。下面提供一些常见IDE或编辑器中实现代码高亮的相关设置步骤以供参考(也可通过更改文件后缀名的方式来实现高亮,详见后文相关介绍)。

  • Sublime

  1. 打开Sublime->Preferences->Browse Packages..进入用户包文件夹。

  2. 在此文件夹下打开cmd,运行git clone git@github.com:vuejs/vue-syntax-highlight.git,无GIT用户可以直接下载zip包解压至当前文件夹。

  3. 关闭.wpy文件重新打开即可高亮。

  • WebStorm/PhpStorm

  1. 打开Settings,搜索Plugins,搜索Vue.js插件并安装。

  2. 打开Settings,搜索File Types,找到Vue.js Template,在Registered Patterns添加*.wpy,即可高亮。

  • Atom

  1. 在Atom里先安装Vue的语法高亮 - language-vue,如果装过了就忽略这一步。

  2. 打开Atom -> Config菜单。在core键下添加:

  1. customFileTypes:
  2. "text.html.vue": [
  3. "wpy"
  4. ]
  • VS Code

  1. 在 Code 里先安装 Vue 的语法高亮插件 Vetur

  2. 打开任意 .wpy 文件。

  3. 点击右下角的选择语言模式,默认为纯文本

  4. 在弹出的窗口中选择 .wpy 的配置文件关联...

  5. 在选择要与 .wpy 关联的语言模式 中选择 Vue

  • VIM

  1. 安装 Vue 的 VIM 高亮插件,例如 posva/vim-vue

  2. 配置 .wpy 后缀名的文件使用 Vue 语法高亮。

  1. au BufRead,BufNewFile *.wpy setlocal filetype=vue.html.javascript.css

代码编写规则

  1. 变量与方法尽量使用驼峰式命名,并且注意避免使用$开头。 以$开头的标识符为WePY框架的内建属性和方法,可在JavaScript脚本中以this.的方式直接使用,具体请参考API文档
  2. 小程序入口、页面、组件文件名的后缀为.wpy;外链的文件可以是其它后缀。 具体请参考wpy文件说明
  3. 使用ES6语法开发。 框架在ES6(ECMAScript 6)下开发,因此也需要使用ES6开发小程序,ES6中有大量的语法糖可以让我们的代码更加简洁高效。
  4. 使用Promise。 框架默认对小程序提供的API全都进行了 Promise 处理,甚至可以直接使用async/await等新特性进行开发。启用Promise方法
  5. 事件绑定语法使用优化语法代替。
    • bindtap="click" 替换为 @tap="click",原catchtap="click"替换为@tap.stop="click"
    • capture-bind:tap="click" 替换为 @tap.capture="click",原capture-catch:tap="click"替换为@tap.capture.stop="click"
    • 更多@符用法,参见组件自定义事件
  6. 事件传参使用优化后语法代替。 原bindtap="click" data-index={{index}}替换为@tap="click({{index}})"
  7. 自定义组件命名应避开微信原生组件名称以及功能标签<repeat>。 不可以使用input、button、view、repeat等微信小程序原生组件名称命名自定义组件;另外也不要使用WePY框架定义的辅助标签repeat命名。有关repeat的详细信息,请参见循环列表组件引用

主要功能特性

开发模式转换

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

原生代码:

  1. //index.js
  2. //获取应用实例
  3. var app = getApp()
  4. //通过Page构造函数创建页面逻辑
  5. Page({
  6. //可用于页面模板绑定的数据
  7. data: {
  8. motto: 'Hello World',
  9. userInfo: {}
  10. },
  11. //事件处理函数
  12. bindViewTap: function() {
  13. console.log('button clicked')
  14. },
  15. //页面的生命周期函数
  16. onLoad: function () {
  17. console.log('onLoad')
  18. }
  19. })

基于WePY的代码:

  1. //index.wpy中的<script>部分
  2. import wepy from 'wepy';
  3. //通过继承自wepy.page的类创建页面逻辑
  4. export default class Index extends wepy.page {
  5. //可用于页面模板绑定的数据
  6. data = {
  7. motto: 'Hello World',
  8. userInfo: {}
  9. };
  10. //事件处理函数(集中保存在methods对象中)
  11. methods = {
  12. bindViewTap () {
  13. console.log('button clicked');
  14. }
  15. };
  16. //页面的生命周期函数
  17. onLoad() {
  18. console.log('onLoad');
  19. };
  20. }

支持组件化开发

参见章节:组件

示例代码:

  1. // index.wpy
  2. <template>
  3. <view>
  4. <panel>
  5. <h1 slot="title"></h1>
  6. </panel>
  7. <counter1 :num="myNum"></counter1>
  8. <counter2 :num.sync="syncNum"></counter2>
  9. <list :item="items"></list>
  10. </view>
  11. </template>
  12. <script>
  13. import wepy from 'wepy';
  14. //引入List、Panel和Counter组件
  15. import List from '../components/list';
  16. import Panel from '../components/panel';
  17. import Counter from '../components/counter';
  18. export default class Index extends wepy.page {
  19. //页面配置
  20. config = {
  21. "navigationBarTitleText": "test"
  22. };
  23. //声明页面中将要使用到的组件
  24. components = {
  25. panel: Panel,
  26. counter1: Counter,
  27. counter2: Counter,
  28. list: List
  29. };
  30. //可用于页面模板中绑定的数据
  31. data = {
  32. myNum: 50,
  33. syncNum: 100,
  34. items: [1, 2, 3, 4]
  35. }
  36. }
  37. </script>

支持加载外部NPM包

在编译过程当中,会递归遍历代码中的require然后将对应依赖文件从node_modules当中拷贝出来,并且修改require为相对路径,从而实现对外部NPM包的支持。如下图:

img

单文件模式,目录结构更清晰,开发更方便

原生小程序要求app实例必须有3个文件:app.jsapp.jsonapp.wxss,而page页面则一般有4个文件:page.jspage.jsonpage.wxmlpage.wxss,并且还要求app实例的3个文件以及page页面的4个文件除后缀名外必须同名,具体可参看官方目录结构

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

原生小程序的目录结构:

  1. project
  2. ├── pages
  3. | ├── index
  4. | | ├── index.js index 页面逻辑
  5. | | ├── index.json index 页面配置
  6. | | ├── index.wxml index 页面结构
  7. | | └── index.wxss index 页面样式
  8. | └── log
  9. | ├── log.js log 页面逻辑
  10. | ├── log.json log 页面配置
  11. | ├── log.wxml log 页面结构
  12. | └── log.wxss log 页面样式
  13. ├── app.js 小程序逻辑
  14. ├── app.json 小程序公共配置
  15. └── app.wxss 小程序公共样式

使用WePY框架后的开发目录结构(主要为src目录的结构,dist目录除外):

注:dist目录为WePY通过build指令生成的目录,除额外增加的npm目录外,其目录结构与原生小程序的目录结构类似。

  1. project
  2. └── src
  3. ├── pages
  4. | ├── index.wpy index 页面逻辑、配置、结构、样式
  5. | └── log.wpy log 页面逻辑、配置、结构、样式
  6. └──app.wpy 小程序逻辑、公共配置、公共样式

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

用户可以通过修改wepy.config.js(老版本使用.wepyrc)配置文件,配置自己熟悉的babel环境进行开发。默认开启使用了一些新的特性如promiseasync/await(自WePY 1.4.1开始必须手动开启,原因参见前文代码规范一节中的介绍)等等。

示例代码:

  1. import wepy from 'wepy';
  2. export default class Index extends wepy.page {
  3. getData() {
  4. return new Promise((resolve, reject) => {
  5. setTimeout(() => {
  6. resolve({data: 123});
  7. }, 3000);
  8. });
  9. };
  10. async onLoad() {
  11. let data = await this.getData();
  12. console.log(data.data);
  13. };
  14. }

针对原生API进行优化

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

原生代码:

  1. onLoad = function () {
  2. var self = this;
  3. wx.login({
  4. success: function (data) {
  5. wx.getUserInfo({
  6. success: function (userinfo) {
  7. self.setData({userInfo: userinfo});
  8. }
  9. });
  10. }
  11. });
  12. }

基于WePY的代码:

  1. import wepy from 'wepy';
  2. async onLoad() {
  3. await wepy.login();
  4. this.userInfo = await wepy.getUserInfo();
  5. }

在同时并发10个request请求测试时:

不使用WePY:

2 small2 small

使用WePY后:

img

WePY中的数据绑定

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

原生小程序通过Page提供的setData方法来绑定数据,如:

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

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

WePY数据绑定方式

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

  1. this.title = 'this is title';

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

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