WePY 2 组件化小程序框架指南:类 Vue 开发体验、编译原理与上手指南
【免费下载链接】wepy小程序组件化开发框架 - 已归档项目地址: https://gitcode.com/gh_mirrors/we/wepy
WePY(读音 /'wepi/)是腾讯开源的一款微信小程序组件化开发框架,核心思路是通过预编译把.wpy单文件组件转换为微信小程序原生代码,让开发者用类 Vue 的语法、ES2015+ 的能力和自定义组件体系来编写小程序。本文以 README_EN.md 为骨架,结合本仓库 packages/ 下的源码实现,完整介绍 WePY 2(beta)的核心特性、单文件组件写法、CLI 使用流程,以及框架"编译 + 运行时"的底层原理,读完即可上手初始化项目并理解其工作方式。
归档状态提示:项目仓库首页顶部已明确标注"This project has been archived"(已归档)。由于微信小程序生态的演进,该项目已停止活跃维护,WePY 的代码不再作为新项目的推荐参考。本文仅面向历史技术研究、已有项目维护与源码学习场景,新项目建议采用更现代的小程序解决方案。
一、WePY 是什么:预编译与组件化的核心理念
WePY 的口号是 "componentization of small programs"(小程序组件化)。与当时直接手写原生app.js / page.js / component.js / wxml / wxss / json的常规做法不同,WePY 选择了编译期转换路线:开发者以 Vue 风格的 SFC(Single File Component)形式编写.wpy文件,框架在构建阶段将其预编译为微信开发者工具能直接运行的wxml / wxss / js / json产物。
从源码可以印证这一"预编译"定位。CLI 的入口 packages/cli/index.js 直接导出./core/compile.js,而 编译核心实现 中的run()方法串起了完整流程:
run() { return this.init().then(() => this.start()); }其中 start() 会先解析 App 入口的wpy文件,读取其config中声明的pages、subPackages、usingComponents、自定义tabBar等配置,然后逐一编译页面与组件;output() 则把每个 SFC 块的产物按映射写出:
const outputMap = { script: 'js', styles: 'wxss', config: 'json', template: 'wxml' };也就是说,一个.wpy文件会被拆成.js、.wxss、.json、.wxml四个原生文件输出到weapp/目录,这正是 WePY "pre-compiling" 理念的源码级证据。
同时,WePY 也在持续吸收前端优化工具与框架的设计思想(README 原文表述为 "drawing heavily on the design concepts and ideas of some front-end optimization tools and frameworks"),例如响应式观察者、指令系统、编译器插件化等,都属于这一思路的产物。
二、核心特性总览
README 中明确列出了 WePY 2 的 8 项核心特性,逐条展开如下:
| 特性 | 说明 | 仓库佐证 |
|---|---|---|
| 类 Vue 的开发风格 | wepy.page/wepy.component/wepy.app注册 API,data/computed/watch/methods等选项式写法 | packages/core/weapp/apis/index.js |
| 自定义组件开发 | SFC 组件 +config.usingComponents声明引用 | packages/cli/core/compile.js |
| 支持引入 NPM 包 | 编译期通过 enhanced-resolve 解析依赖,vendor 独立打包 | packages/cli/core/compile.js#L59-L126 |
| 支持 Promise | 运行时内置响应式与异步调度(next-tick、scheduler) | packages/core/weapp/util/next-tick.js |
| 支持 ES2015+(如 Async Functions) | 通过 Babel 编译器处理,配套compiler-babel包 | packages/compiler-babel/index.js |
| 多编译器支持 | Less / Sass / Stylus / PostCSS / Babel / TypeScript / Pug,按需加载@wepy/compiler-* | packages/compiler-less、packages/compiler-sass、packages/compiler-stylus、packages/compiler-postcss、packages/compiler-typescript |
| 多种插件处理 | 文件/图片压缩、内容替换等,插件即函数 | packages/cli/core/init/plugin.js、packages/plugin-uglifyjs、packages/plugin-define |
| 支持 Sourcemap、ESLint 等 | eslint、cliLogs、noCache等 CLI 配置项 | packages/cli/core/parseOptions.js#L4-L27 |
| 小程序细节优化 | 事件优化($emit/$trigger)、请求队列、渲染 nextTick 调度 | packages/core/weapp/class/WepyComponent.js |
2.1 类 Vue 开发风格:注册 API 与响应式系统
WePY 的全局对象由 packages/core/weapp/wepy.js 构建,通过initGlobalAPI挂载了全套注册与工具方法。查看 packages/core/weapp/apis/index.js 可见:
wepy.use = use; // 安装插件 wepy.mixin = mixin; // 全局混入 wepy.set / wepy.delete // 响应式新增/删除属性 wepy.observe // 响应式观察 wepy.nextTick // 渲染调度 wepy.app / wepy.page / wepy.component // 三类组件注册入口其中wepy.page与wepy.component的实现位于 packages/core/weapp/native/index.js。以 page.js 为例,注册时会依次执行patchMixins(混入合并)、patchProps(属性声明)、patchMethods(方法)、patchData(数据)、patchLifecycle(生命周期),最终调用微信原生的Component(pageConfig)生成小程序组件;而 app.js 则通过patchMixins + patchAppLifecycle后调用原生App(appConfig)。
组件的类实现 WepyComponent 提供了$watch(可传数组或带handler的对象,支持immediate)、$forceUpdate、$emit、$trigger(转发到$wx.triggerEvent与父组件通信)以及$nextTick;WepyConstructor 则完成data初始化、watch初始化与computed初始化——这三者构成了类 Vue 响应式开发的运行时基础。
2.2 自定义组件与 NPM 依赖解析
在编译期,compile.js 使用 webpack 同源的enhanced-resolve创建 normal / context 两套 resolver,并将.js、.ts、.json、.node、.wxs、.wpy全部纳入扩展名解析范围。组件间依赖会被递归收集:buildComps会遍历每个组件config.parsed.components,区分 WePY 组件(.wpy)与原生组件(.js),逐个编译后统一打包 vendor 与静态资源(compile.js#L289-L349)。README 特性中的 "Support for introducing NPM packages" 正是由这套解析 + vendor 打包机制支撑。
2.3 多编译器与多插件生态
编译器:WePY 2 把样式、脚本编译器做成独立 npm 包,编译时按需加载。查看 packages/cli/core/init/compiler.js 可知其加载约定:
let moduleName = `@wepy/compiler-${c}`; // 通过 resolver 解析后 require,再以 compilers[c] 作为配置调用即compilers配置中的每个键(如less、sass、babel、typescript)都会映射到@wepy/compiler-xxx包。本仓库中即包含 compiler-less、compiler-sass、compiler-stylus、compiler-postcss、compiler-babel、compiler-typescript 六个编译器包,每个都自带完整的 fixtures 与测试用例。
插件:插件初始化见 packages/cli/core/init/plugin.js。框架会先挂载一批内置系统插件(scriptDepFix、scriptInjection、template/parse、template/attrs、template/directives、errorHandler等),再校验并执行用户在wepy.config.js中配置的自定义插件(要求插件必须是一个函数)。仓库中配套了 plugin-define(编译期内容替换/宏定义)、plugin-eslint(代码检查)、plugin-uglifyjs(压缩)等示例插件,以及 use-promisify、use-intercept 两个运行时增强插件。
三、单文件组件 Demo 逐段解析
README 给出了一个完整的.wpy示例,这里原样继承并逐块拆解。这是理解 WePY 开发风格最直接的入口:
<style lang="less"> @color: #4D926F; .num { color: @color; } </style> <template> <div class="container"> <div class="num" @tap="num++"> {{num}} </div> <custom-component></custom-component> <vendor-component></vendor-component> <div>{{text}}</div> <input v-model="text"/> </div> </template> <config> { usingComponents: { customComponent: '@/components/customComponent', vendorComponent: 'module:vendorComponent' } } </config> <script> import wepy from '@wepy/core'; wepy.page({ data: { num: 0, text: 'Hello World', }, }); </script>一个.wpy文件由四个块组成,顺序与语义如下:
| 块 | 作用 | 说明 |
|---|---|---|
<style> | 样式 | 支持lang属性切换编译器(如less),对应输出为.wxss |
<template> | 模板 | 类 HTML 的 WXML 语法,支持@tap事件绑定、{{}}插值、v-model指令,对应输出.wxml |
<config> | 组件配置 | JSON 格式,声明usingComponents等,对应输出.json |
<script> | 脚本 | 通过import wepy from '@wepy/core'引入运行时,用wepy.page()注册页面,对应输出.js |
3.1 style 块:Less 编译
<style lang="less">声明后,编译期会走wepy-compiler-less钩子(对应 packages/compiler-less/index.js)。仓库中该包的 test/fixtures/less 下提供了变量、导入、守卫、懒求值、选择器、列表等一整套 Less 特性测试样例,验证其编译能力。同理,lang换成sass/stylus/postcss即可切换对应编译器。
3.2 template 块:事件、插值与指令
@tap="num++":事件绑定,@前缀对应原生bindtap,在 packages/cli/core/plugins/template/directives 中由v-on指令转换实现(v-on.js),对应的转换断言用例可在 packages/cli/test/core/fixtures/template/assert/v-on 中查看;{{num}}、{{text}}:双花括号插值,编译为 WXML 文本节点;v-model="text":双向绑定指令,由 model.js 实现;- 自定义标签
<custom-component>、<vendor-component>:在config.usingComponents中声明后即可在模板中直接使用。
3.3 config 块:组件引用与路径别名
{ usingComponents: { customComponent: '@/components/customComponent', vendorComponent: 'module:vendorComponent' } }这里的路径规则值得注意:
@/components/customComponent:@为项目src目录别名,指向本地自定义组件;module:vendorComponent:module:前缀表示该组件来自 node_modules(NPM 包),编译时会经 vendor 解析与打包机制处理(对应 compile.js 的getModuleTarget将 node_modules 依赖输出到weapp/vendor/目录)。
3.4 script 块:页面注册
import wepy from '@wepy/core'; wepy.page({ data: { num: 0, text: 'Hello World', }, });@wepy/core即本仓库的 packages/core 包(入口为 packages/core/index.js)。wepy.page内部会做 mixins / props / methods / data / lifecycle 的合并与补丁,最终调用原生Component()(详见上文 2.1 节)。在选项对象中还可以继续补充computed、watch、methods、onLoad等生命周期字段,运行时分别由 init/computed.js、init/watch.js、init/lifecycle.js 处理。
四、快速上手:从初始化到真机预览
README 的 Usage 章节给出了一套完整的命令行流程,这里完整继承并补充说明:
4.1 安装(升级)WePY 命令行工具
npm install @wepy/cli@next -g@next标签对应 WePY 2(beta)版本线。本仓库中的 CLI 源码位于 packages/cli,其入口 packages/cli/index.js 最终导出编译核心模块。
4.2 使用模板初始化项目
wepy init standard myprojectwepy init会基于standard模板在当前目录生成名为myproject的项目骨架(模板拉取与交互逻辑可参见 packages/cli/core/init 下的 compiler / parser 实现)。
4.3 安装依赖
cd myproject npm install项目依赖中应包含@wepy/core运行时、所需@wepy/compiler-*编译器与@wepy/cli本地版本。
4.4 监听模式构建
wepy build --watch--watch开启监听模式:构建完成后 CLI 会持续监听src目录(见 compile.js 的 watch()),底层使用chokidar监听文件变化,并以300ms 防抖合并连续改动;单文件变化时走partialBuild/weappBuild增量编译,多文件同时变化则触发全量start()重建。构建产物默认输出到weapp/目录(见下文的output配置项)。
4.5 导入微信开发者工具
在微信开发者工具中新建项目,选择本地项目根目录(即myproject),工具会自动识别并导入weapp/下的编译产物与project.config.json配置,随后即可在模拟器/真机中预览调试。
需要说明:由于仓库已归档,
@wepy/cli@next及其配套包在 npm 上的可安装性以实际发布状态为准,安装失败时建议参考本仓库 packages 目录下的源码与package.json自行构建使用。
4.6 工程配置:wepy.config.js
虽然 README 未展开wepy.config.js,但它承载了上面所有 CLI 行为的具体配置。从 packages/cli/core/parseOptions.js#L4-L27 可以看到全部默认配置项:
const DEFAULT_OPTIONS = { entry: { type: String, default: 'app' }, // 应用入口名(不含扩展名) src: { type: String, default: 'src' }, // 源码目录 target: { type: String, default: 'weapp' }, // 编译目标(weapp) static: { type: [String, Array], default: 'static' }, // 静态资源目录(可多个) output: { type: String, default: 'weapp' }, // 输出目录 platform: { type: String }, // 目标平台 wpyExt: { type: String, default: '.wpy' }, // SFC 文件扩展名 eslint: { type: Boolean, default: true }, // 是否启用 ESLint cliLogs: { type: Boolean, default: false }, // 是否输出 CLI 日志 watch: { type: Boolean, default: false }, // 是否监听 watchOption: { type: Object }, // chokidar 监听选项 noCache: { type: Boolean, default: false }, // 是否禁用编译缓存 resolve: { type: Object, default: {} }, // 模块解析配置 compilers: { type: Object }, // 编译器配置(如 less、babel 等) plugins: { type: Array, default: [] }, // 插件函数列表 appConfig: { type: Object }, // 应用级配置 'appConfig.noPromiseAPI': { type: Array, default: [] } };一个真实的配置示例可参考仓库内 packages/plugin-define/test/fixtures/wepy.config.js:
module.exports = { plugins: [ DefinePlugin({ BASE_URL: JSON.stringify('http://www.bar.com') }), DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('development'), 'typeof window': JSON.stringify('undefined'), }) ], }可以看到插件以函数数组形式传入,编译期对源码做内容替换,这正是 README 特性中"内容替换"类插件处理的落地示例。
五、从源码理解编译流水线
结合前面的分析,WePY 2 的完整构建流水线可以归纳为如下阶段(对应 compile.js 的 hook 设计):
- 解析入口:
wepy-parser-wpy解析app.wpy,读取config中的pages/subPackages/usingComponents/tabBar.custom; - 收集任务:为每个页面与组件创建解析任务,
.wpy走 SFC 解析,原生.js组件走wepy-parser-component;subPackages中暂不支持independent独立分包(源码中有显式告警与EXIT处理,见 compile.js#L213-L227); - 递归构建组件:
buildComps递归收集并编译全部组件依赖,随后按output-app、output-pages、output-components、output-vendor、output-assets、output-static顺序输出产物; - 块级编译:
applyCompiler依据<style>的lang找到wepy-compiler-{lang}钩子执行编译(compile.js#L493-L520),且带依赖的样式文件会跳过缓存(避免依赖变更未生效); - 产物写出:SFC 四块内容分别映射为
.js / .wxss / .json / .wxml写出到output目录(compile.js#L563-L590)。
整个编译过程构建在Hook事件系统之上(packages/cli/core/hook.js),框架内置插件与用户插件均通过注册钩子参与各阶段,这也是 WePY 可扩展性的根基。
六、谁在用 WePY:历史案例
README 中列出了一批基于 WePY 开发的微信小程序案例(其中标注"(开源)"的项目在原始 README 中附有仓库链接,此处仅保留名称):
腾讯疫苗查询小程序、腾讯翻译君小程序、腾讯地图小程序、玩转故宫小程序、手机充值+、手机余额查询、手机流量充值优惠、友福图书馆(开源)、素洁商城(开源)、NewsLite(开源)、西安找拼车(开源)、深大的树洞(开源)、求知微阅读(开源)、给你的 iPhone X 换个发型、天天跟我买、坚橙、群脱单、米淘联盟、帮助圈、众安保险福利、阅邻二手书、趣店招聘、满熊阅读(开源,含微信/支付宝双端)、育儿柚道、平行进口报价内参、GitHub 掘金版、班级群管、鲜花说小店、逛人备忘、英语助手君、花花百科、独角兽公司、爱羽客羽毛球、斑马小店、小小羽球、培恩医学、农资优选、公务员朝夕刷题、七弦琴小助手、七弦琴大数据、爽到家小程序、应用全球排行(开源)、we 川大(开源)、聊会儿、诗词墨客(开源)、南京邮电大学(开源)……
这批案例覆盖工具、内容、电商、社交、校园等多种场景,一定程度上反映了 WePY 在 2017—2020 年前后的小程序生态中的普及度。
七、贡献与交流(历史信息)
- 微信交流群:README 说明 WePY 交流群当时已达 500 人上限,需添加
gcaufy_helper好友并回复验证语 "wepy" 后按指引入群(原文档附有群二维码,此处不展开)。 - 贡献方式:欢迎通过提交 Issue 或 Pull Request 的方式参与改进,详见 CONTRIBUTING.md;同时 README 提到腾讯开源激励计划鼓励开发者参与贡献。
- 文档与更新记录:README 原文链接指向在线文档站与 Changelog 页面(外部站点,此处不展开);仓库内的更新记录见根目录 CHANGELOG.md 及 packages/cli/CHANGELOG.md、packages/core/CHANGELOG.md 等各子包更新日志。
- 开源协议:项目基于 MIT 协议开源,见 LICENSE。
结语:如何看待已归档的 WePY
作为小程序早期组件化方案的代表,WePY 2 的价值主要体现在三方面:其一,它以"预编译 + SFC"路线证明了类 Vue 语法在小程序领域的可行性;其二,它的编译器/插件/运行时分层设计(本仓库 packages/ 下的cli、core、compiler-*、plugin-*、redux、router、use-*包即是这套架构的完整呈现)对理解前端编译工具链很有参考意义;其三,它为后来者提供了"如何把工程化开发体验引入封闭平台"的实践样本。对于新项目,请遵循归档声明选择更现代的方案;对于源码研究与历史项目维护,本仓库仍是一份结构清晰、测试完备的参考资料(CLI 与模板相关的断言用例可见 packages/cli/test、packages/compiler-less/test 等目录)。
【免费下载链接】wepy小程序组件化开发框架 - 已归档项目地址: https://gitcode.com/gh_mirrors/we/wepy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考