深入wepy源码:wpy文件编译过程

转载自 深入wepy源码:wpy文件编译过程

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 做了如下一些预处理:
获取文件中的 import ,放入 rst.template.components 中
获取 props 和 events ,放入 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 地址( https://github.com/yingye/wepy-plugin-px2units )。

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 分支,https://github.com/Tencent/wepy/tree/1.7.x )。另外,文中有任何表述不清或不当的地方,欢迎大家批评指正。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql事务、视图和索引

一、事务&#xff1a;1.概念&#xff1a;是单个逻辑单元执行的一系列操作&#xff08;一个事务中有多个sql语句&#xff09;&#xff0c;这个操作作为一个整体一起提交&#xff0c;要么执行&#xff0c;要么都不执行&#xff0c;多个事务操作是一个不可分割的逻辑单元。2.事务的…

qq空间说说服务器维护,如何解决QQ空间说说发表不了

如何解决QQ空间说说发表不了我的今天也是这种情况几个qq都发表不了&#xff0c;都不知道怎么办了&#xff0c;然后过一会儿又能发表了但是现在有发表不了了...是空间的系统问题吧&#xff01;还有啊就是不要加什么敏感词汇不然也发表不了一、如果您打开是提示“服务器忙&#x…

Win10上运行Docker

1. 前言 Docker最近推出了可以运行在Win10和Mac上的稳定版本&#xff0c;让我们赶紧来体验一下。 Docker发布Mac和Windows 的目标非常简单——开发者可以更加简单方便地在研发机器上使用Docker。下面是此次版本所改进的地方&#xff1a; 更快更可靠——在本地开发环境上&#x…

sql serve存储过程

存储过程一.概念:预先编译好的sql程序&#xff0c;可以包含&#xff1a;操作数据、变量、控制语句&#xff0c;增删改查操作都可以&#xff0c;存储过程是保存在数据库中 的一个对象。二.好处&#xff1a;1.安全性更高2.减少了网络流量3.提高速度&#xff0c;性能更高4.模…

JAVA数据库连接池实现

转载自 JAVA数据库连接池实现连接池的管理用了了享元模式&#xff0c;这里对连接池进行简单设计。 一、设计思路1.连接池配置属性DBbean&#xff1a;里面存放可以配置的一些属性2.连接池接口IConnectionPool&#xff1a;里面定义一些基本的获取连接的一些方法3.接口实现Conne…

在.NET开发面向Oracle数据库的应用程序

其实这个不是一个什么新的话题。但是之前在多次项目中&#xff0c;总是遇到大家针对Oracle数据库的访问时&#xff0c;会有各种各样的问题&#xff0c;最基本的就是要在客户端安装各种client&#xff0c;版本不一样的话还有各种问题。 静下心来看看&#xff0c;其实也没有那么难…

服务器ubuntu系统版本选型原则,系统集成 - 选择Ubuntu服务器版操作系统的六大理由_服务器应用_Linux公社-Linux系统门户网站...

二. 系统集成(1)集成现有的系统Ubuntu服务器版本用常用的身份认证方式和服务入口工具简单地集成企业现有的客户/服务器结构。我们都知道系统集成技术的重要性&#xff0c;这也是Ubuntu团队花费大量时间研究如何实现服务器与基础设施简单融合的原因。(2)简单的验证方式验证功能对…

sql serve基础

一、数据库登录名与数据库用户1.登录名登录服务器2.数据库用户访问具体数据库二者要建立映射关系二、数据库文件&#xff1a;1.主数据文件&#xff1a;*.mdf&#xff08;必须&#xff09;2.辅助数据文件&#xff1a;*.ndf(可选)3.日志文件&#xff1a;*.ldf&#xff08;必须&am…

sql基本操作语句

sql: 结构化查询语言T-SQL:sql server数据库中用的查询语言数据库对象操作&#xff1a;一、建库&#xff1a;二、建表&#xff1a;三、数据操作1.添加INSERT [INTO] 表名 (列列表) VALUES(值列表)a. 列列表和值列表一一对应&#xff08;顺序和个数&#xff09;b。可以为null的…

云服务器mqtt协议,云服务器mqtt协议

云服务器mqtt协议 内容精选换一换IPv6的使用&#xff0c;可以有效弥补IPv4网络地址资源有限的问题。如果当前云服务器使用IPv4&#xff0c;那么启用IPv6后&#xff0c;云服务器可在双栈模式下运行&#xff0c;即云服务器可以拥有两个不同版本的IP地址&#xff1a;IPv4地址和IPv…

常用的推荐算法解析

转载自 常用的推荐算法解析1. 前言随着互联网技术和社会化网络的发展&#xff0c;每天有大量包括博客&#xff0c;图片&#xff0c;视频&#xff0c;微博等等的信息发布到网上。传统的搜索技术已经不能满足用户对信息发现的需求&#xff0c;原因有多种&#xff0c;可能是用户…

一步一步封装自己的HtmlHelper组件:BootstrapHelper

前言&#xff1a;之前学习过很多的Bootstrap组件&#xff0c;博主就在脑海里构思&#xff1a;是否可以封装一套自己Bootstrap组件库呢。再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式&#xff0c;于是打算仿照HtmlHelper封装一套BootstrapHelper&#xff0c…

sql server简单查询

一、插入多行数据&#xff1a;1.insert into 。。。 select 从一个表中取出数据插入另一个已存在的表2.select into 从一个表中取出数据插入一个新表中3.insert into ()unionselect 常量列表 二、简单查询1. 查询所有行和列 SELECT * FROM 表名2.查询部分列 SELECT 列列…

推荐系统常用的推荐算法

转载自 推荐系统常用的推荐算法 一、推荐系统概述和常用评价指标1.1 推荐系统的特点 在知乎搜了一下推荐系统&#xff0c;果真结果比较少&#xff0c;显得小众一些&#xff0c;然后大家对推荐系统普遍的观点是&#xff1a; (1)重要性UI>数据>算法&#xff0c;就是推荐系…

拥抱.NET Core,学习.NET Core的基础知识补遗

前言 .NET Core的新特性之一就是跨平台&#xff0c;但由于对之前框架的兼容导致编写一个.NET Core类库变得相当复杂&#xff0c;主要体现为相当多的框架目标和支持平台&#xff0c;今天我们就对.NET Core的跨平台特性进行一次梳理。 在.NET Core之前 其实早在.NET Core之前微软…

sql server模糊查询、分组

一、系统函数1。convert&#xff08;类型[length] &#xff0c;表达式[&#xff0c;样式]&#xff09;2.isnull&#xff08;表达式&#xff0c;默认值&#xff09;字符函数&#xff1a;len() 长度ltrim&#xff08;&#xff09;|rtrim&#xff08;&#xff09;去除左右空格righ…

拥抱.NET Core,如何开发一个跨平台类库

在此前的文章中详细介绍了使用.NET Core的基本知识&#xff0c;如果还没有看&#xff0c;可以先去了解“拥抱.NET Core&#xff0c;学习.NET Core的基础知识补遗”&#xff0c;以便接下来的阅读。 在本文将介绍如何配置类库项目支持不同的平台&#xff0c;并为不同的平台进行兼…

常用的推荐算法小结

转载自 常用的推荐算法小结推荐系统的必然 互联网发展到现阶段&#xff0c;信息已经不是匮乏&#xff0c;而是爆炸。所以良好的用户体验就是把用户喜欢的&#xff0c;感兴趣的从大量的数据中筛选出来&#xff0c;再呈现给用户&#xff0c;实现千人千面的效果。 所以推荐系统的…

.NET 4.6.2正式发布带来众多特性

虽然大多数人的注意力都集中在.NET Core上&#xff0c;但与原来的.NET Framework相关的工作还在继续。.NET Framework 4.6.2正式版已于近日发布&#xff0c;其重点是安全和WinForms/WPF/ASP.NET/WCF相关的特性,英文博客文章https://blogs.msdn.microsoft.com/dotnet/2016/08/02…

推荐算法-关联分析(关联规则)

转载自 推荐算法-关联分析&#xff08;关联规则&#xff09;关联分析又称关联挖掘&#xff0c;就是在交易数据、关系数据或其他信息载体中&#xff0c;查找存在于项目集合或对象集合之间的频繁模式、关联、相关性或因果结构。或者说&#xff0c;关联分析是发现交易数据库中不…