wepy 是腾讯开源的一款小程序框架,主要通过预编译的手段,让开发者采用类 Vue 风格开发。 让我们一起看看, wepy 是如何实现预编译的。先放上一张官网的流程图,后面的分析可以参考该图。

wepy-cli 主要负责 .wpy 文件的编译,目录结构如下:

编译的入口是 src/compile.js 中的 compile() 方法,该方法主要是根据文件类型,执行不同的 compiler ,比如 .wpy 文件会走 compile-wpy.js 下的 compile() 方法。

compile(opath) {...switch(opath.ext) {  case ext:  cWpy.compile(opath);  break;  case '.less':  cStyle.compile('less', opath);  break;  case '.sass':  cStyle.compile('sass', opath);  break;  case '.scss':  cStyle.compile('scss', opath);  break;  case '.js':  cScript.compile('babel', null, 'js', opath);  break;  case '.ts':  cScript.compile('typescript', null, 'ts', opath);  break;  default:  util.output('拷贝', path.join(opath.dir, opath.base)); ... }
}
复制代码

.wpy文件拆解

compile-wpy.js 下的 compile() 方法,核心调用了 resolveWpy() 方法。

resolveWpy() 方法,主要是将 .wpy 拆解成 rst 对象,并对其中的 template、script 做一些预处理,然后将 template、 script、 style 三部分移交给不同的 compiler 处理。

生成rst对象

通过 xmldom 获取 xml 对象,然后遍历节点,拆解为 rst对象。

import {DOMParser} from 'xmldom';
export default {createParser (opath) {return new DOMParser({...})},...resolveWpy () {let xml = this.createParser(opath).parseFromString(content);}
}
复制代码

rst对象结构如下:

let rst = {moduleId: moduleId,style: [],template: {code: '',src: '',type: ''},script: {code: '',src: '',type: ''}
};
复制代码

此外,还对 template 做了如下一些预处理:

  • pug 预编译
  • 获取文件中的 import ,放入 rst.template.components
  • 获取 propsevents ,放入 rst.script.code

compile-template

compile-template.js 中的 compile() 方法,根据 template 的 lang 值,执行不同的 compiler ,比如 wepy-compile-typescript 。编译完成后,执行 compileXML 方法,做了如下的操作:

  • updateSlot 方法: 替换 slot 内容
  • updateBind 方法: 在 {{}} 和 attr 上加入组件的前缀,例如:{{width}} -> {{$ComponentName$width}}
  • 把自定义的标签、指令转换为 wxml 语法,例如:
<repeat for="xxx" index="idx" item="xxx" key="xxx"></repeat>
<!-- 转换为 -->
<block wx:for="xxx" wx:for-index="xxx" wx:for-item="xxx" wx:key="xxxx"></block>
复制代码

compile-style

依旧先是根据 lang 值,先执行不同的 compiler ,比如 wepy-compile-less 。编译完成后,执行 src/style-compiler/scope.js 中的 scopedHandler() 方法,处理 scoped

import postcss from 'postcss';
import scopeId from './scope-id';export default function scopedHandler (id, content) {console.log('id is: ', id)console.log('css content is: ', content)return postcss([scopeId(id)]).process(content).then(function (result) {console.log('css result is: ', result.css)return result.css}).catch((e) => {return Promise.reject(e)})
}
复制代码

这里主要是利用 add-id 的 postcss 插件,插件源码可参考 src/style-compiler/scope-id.js。根据上面的代码,打印出来的log如下:

最后,会把 requires 由绝对路径替换为相对路径,并在 wxss 中引入,最终生成的 wxss 文件为:

@import "./../components/demo.wxss";Page{background:#F4F5F7} ...
复制代码

compile-script

依旧先是根据 lang 值,执行不同的 compiler。compiler 执行完之后,判断是否是 npm 包,如果不是,依据不同的 type 类型,加入 wepy 初始化的代码。

if (type !== 'npm') {if (type === 'page' || type === 'app') {code = code.replace(/exports\.default\s*=\s*(\w+);/ig, function (m, defaultExport) {if (defaultExport === 'undefined') {return '';}if (type === 'page') {let pagePath = path.join(path.relative(appPath.dir, opath.dir), opath.name).replace(/\\/ig, '/');return `\nPage(require('wepy').default.$createPage(${defaultExport} , '${pagePath}'));\n`;} else {appPath = opath;let appConfig = JSON.stringify(config.appConfig || {});let appCode = `\nApp(require('wepy').default.$createApp(${defaultExport}, ${appConfig}));\n`;if (config.cliLogs) {appCode += 'require(\'./_wepylogs.js\')\n';}return appCode;}});}
}
复制代码

接下来会执行 resolveDeps() 方法,主要是处理 requires。根据 require 文件的类型,拷贝至对应的目录,再把 code 中的 require 代码替换为 相对路径。

处理好的 code 最终会写入 js 文件中,文件存储路径会判断类型是否为 npm。

let target;
if (type !== 'npm') {target = util.getDistPath(opath, 'js');
} else {code = this.npmHack(opath, code);target = path.join(npmPath, path.relative(opath.npm.modulePath, path.join(opath.dir, opath.base)));
}
复制代码

plugin

根据上面的流程图,可以看出所有的文件生成之前都会经过 Plugin 处理。先来看一下,compiler 中是如何载入 Plugin 的。

let plg = new loader.PluginHelper(config.plugins, {type: 'css',code: allContent,file: target,output (p) {util.output(p.action, p.file);},done (rst) {util.output('写入', rst.file);util.writeFile(target, rst.code);}
});
复制代码

其中,config.plugins 就是在 wepy.config.js 中定义的 plugins。让我们来看一下 PluginHelper 类是如何定义的。

class PluginHelper {constructor (plugins, op) {this.applyPlugin(0, op);return true;}applyPlugin (index, op) {let plg = loadedPlugins[index];if (!plg) {op.done && op.done(op);} else {op.next = () => {this.applyPlugin(index + 1, op);};op.catch = () => {op.error && op.error(op);};if (plg)plg.apply(op);}}
}
复制代码

在有多个插件的时候,不断的调用 next(),最后执行 done()

编写plugin

wxss 与 css 相比,拓展了尺寸单位,即引入了 rpx 单位。但是设计童鞋给到的设计稿单位一般为 px,那现在我们就一起来编写一个可以将 px 转换为 rpx 的 wepy plugin。

从 PluginHelper 类的定义可以看出,是调用了 plugin 中的 apply() 方法。另外,只有 .wxss 中的 rpx 才需要转换,所以会加一层判断,如果不是 wxss 文件,接着执行下一个 plugin。rpx 转换为 px 的核心是,使用了 postcss-px2units plugin。下面就是设计好的 wepy-plugin-px2units,更多源码可参考 github 地址。

import postcss from 'postcss';
import px2units from 'postcss-px2units';export default class {constructor(c = {}) {const def = {filter: new RegExp('\.(wxss)$'),config: {}};this.setting = Object.assign({}, def, c);}apply (op) {let setting = this.setting;if (!setting.filter.test(op.file)) {op.next();} else {op.output && op.output({action: '变更',file: op.file});let prefixer = postcss([ px2units(this.setting.config) ]);prefixer.process(op.code, { from: op.file }).then((result) => {op.code = result.css;op.next();}).catch(e => {op.err = e;op.catch();});}}
}
复制代码

最后

本文分析的源码以 wepy-cli@1.7.1 版本为准,更多信息可参考 wepy github (即 github 1.7.x 分支)。另外,文中有任何表述不清或不当的地方,欢迎大家批评指正。

本文首发于:github.com/yingye/Blog…

欢迎各位关注我的Blog,正文以issue形式呈现,喜欢请点star,订阅请点watch~

深入wepy源码:wpy文件编译过程相关推荐

  1. Ubuntu12.04下在Android4.0.4源码下载及其编译过程(转载)

    一.下载源码 1.下载工具repo:https://android.googlesource.com/tools/repo/ 在本地目录建个bin的文件夹,进入bin文件夹:通过git下载: [cpp ...

  2. golang源码分析:编译过程词法解析的流程

    golang编译 由于golang作为静态语言,当使用go build时就会生成对应的编译完成之后的文件,那这个编译过程大致会做什么事情呢,在golang中的编译大致有哪些流程. golang示例代码 ...

  3. WebRTC 开发(二)源码下载与编译

    转载:http://depthlove.github.io/2019/05/02/webrtc-development-2-source-code-download-and-build/ 在使用任何工 ...

  4. 树莓派Linux内核源码配置、编译、挂载(boot/kernal/根文件)、开启新内核

    目录 一.树莓派Linux源码配置(适合树莓派) 总体概述 配置的三种方式 1.照搬厂家的配置(使用这种方式) 2.参考厂家的配置(感受一下) 3.完全自主配置(需要一定工作经验) 二.树莓派Linu ...

  5. 【CyberSecurityLearning 37】网络安全常用Linux系统命令以及 源码包的安装过程

    目录 一.Linux系统中以下目录的作用 二.linux系统中以下颜色表示的文件类型 三.Linux系统基础命令 1.uname -a  内核版本号查询 2.fdisk  -l  查看硬盘以及分区 3 ...

  6. LwIP 之一 源码目录文件详解及移植说明

       lwIP 是 TCP/IP 协议套件的一个小型独立实现.lwIP TCP/IP 实现的重点是减少 RAM 使用同时仍然有一个完整的 TCP. 这使得 lwIP 适合使用在具有数 10 千字节的可 ...

  7. 探索比特币源码0-bitcoin的编译

    探索比特币源码0-bitcoin的编译 前言 区块链领域作为一个新兴的热点,迭代的实在是太快了,刚刚入门,必须要抓紧追赶. 在阅读了一些优秀的文档之后,已经对比特币及其背后的实现技术--区块链有了一定 ...

  8. android 源码开发 关于编译等小知识点总结

    fastboot flash boot out/target/product/generic/boot.img不管用,提示< waiting for device >    原因查找:   ...

  9. VLC for Android源码下载和编译

    Project Hosting on 点击打开链接 最受欢迎的开源项目 点击打开链接 由Android的核心开发团队提供了15个优秀的示例项 目,包含了游戏.图像处理.时间显示.开始菜单快捷方式等. ...

最新文章

  1. cNoteReadFile_C语言读取文件内容
  2. js for循环与for in循环的区别
  3. 迭代器自定义遍历对象
  4. python之路----验证客户端合法性
  5. 宝塔mysql data修改_宝塔一键修改mysql数据库存放路径,更改默认数据目录…
  6. 《小强与小明》——正在疯传的伟大的故事
  7. 站立会议03--个人总结
  8. 动图,用Python追踪NBA球员的运动轨迹
  9. Leetcode 24——Swap Nodes in Pairs
  10. php xml 怎么去掉头,PHP如何删除xml某条数据
  11. 190109每日一句
  12. iOS 各种项目源码集
  13. 猿创征文|工具在手,天下我有(初入职场必备的黑科技工具)
  14. sharedassets0_Unity3D研究院之mac上从.ipa中提取unity3D游戏资源(六十六)
  15. 论文解读:MOEA/D-TPN
  16. 2020cpu天梯图
  17. cidaemon.exe进程
  18. 高景一号01星遥感影像解译数据分辨率是多少
  19. vue项目使用vue-amap调用高德地图api详细步骤
  20. C++:符合RFC1321规范的MD5计算C++实现

热门文章

  1. 2020年最新编程语言排行榜出炉TIOBE
  2. 对于 docker-machine 的理解以及在 docker-swarm 中使用 python 针对于 docker 的 SDK
  3. linux命令行处理图片基础,linux下命令行图片处理工具ImageMagick
  4. 【深度学习 一】activation function以及loss function理解
  5. element-ui el-form-item 添加㎡单位的方法
  6. oracle执行update语句卡死
  7. 16瓶药水一瓶有毒,去小白鼠测试哪一瓶水有毒?
  8. java swing课程表设计_javaswing.Shedule 这是一个简单的课程表设计,数据连接为oracle 238万源代码下载- www.pudn.com...
  9. 服务器的相关注意事项
  10. 让未登录的用户跳转到登录页