前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小程序工程化系列(一):文件依赖分析

小程序工程化系列(一):文件依赖分析

作者头像
WecTeam
发布2020-08-04 10:44:36
1.9K0
发布2020-08-04 10:44:36
举报
文章被收录于专栏:WecTeamWecTeam

一、前言

去年(19年10月)在某技术沙龙上分享了《小程序工程化探索》后,陆续有网友联系到我询问一些实现方面的细节,虽然常年顶着黑眼圈修着“福报”,但还是决定抽出时间写一个小程序工程化系列,一是希望能帮到部分同学,二是希望能提升自己的总结与表达能力,由于是一个系列,所以每篇文章会尽量聚焦一个点,篇幅不会很长。闲话少述,本篇是小程序工程化系列第一篇,我将会详细介绍如何利用 Webpack 实现对小程序代码的文件依赖分析。

二、小程序早期的打包方式

我们知道,小程序开发者工具自带了 ES6 转 ES5 及压缩混淆能力,所以 js 这块,可以很方便地使用 ES6 进行开发,不需要额外使用什么工具来进行编译。但 css 这块缺少了 Sass/PostCSS 的支持,所以前端同学一般还会补充 Sass/PostCSS 的支持,打包上传时则只需要对源码中的 *.sass 文件进行转换并将其他源文件直接提取出来即可,Gulp 其实就很适合做这些,事实上,我们一开始也是使用的 Gulp,但随着项目的迭代,久而久之,仅依靠 Gulp 的 glob 规则,已经很难识别项目中哪些文件是真正需要的,从而导致漏传代码、多传代码等问题。这个时候就需要做文件的依赖分析,轮到 Webpack 上场了(并不是只有 Webpack 才能做依赖分析,我们选用它因为还需要做别的事情)。

三、小程序的依赖资源有哪些

以微信小程序举例,小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录:app.js、app.json、app.wxss。一个小程序页面由四个文件组成,分别是:js、json、wxss、wxml。除此之外,还有一些图片字体等文件以及脚本文件wxs。如何识别小程序项目中哪些文件是真正需要的?也就是如何做依赖分析,我们知道小程序是所有页面必须要在 app.json 里进行注册,通过这个信息就可以拿到所有页面的文件依赖及组件的文件依赖。想到这点并不难,接下来看看如何实现。

四、依赖分析如何实现

Webpack 有一个很重要的概念就是入口,你在编译时必须要指定一个入口,Webpack 会从入口开始分析它的所有依赖,在 Web 页面构建中,入口一般对应到页面的主 js。但小程序并没有这样一个 js 入口,所以我们需要根据小程序特性来动态生成一个入口,并从这个入口开始递归获取小程序的所有依赖资源,如下图。

注,阅读本系列文章需要有一定的 Webpack 基础知识。

文件依赖分析

入口的生成

假设小程序的目录结构如下

|-- pages           
    |-- index
        |-- index.js
        |-- index.wxss
        |-- index.wxml
        |-- index.json
|-- components  
    |-- nav
        |-- nav.js  
        |-- nav.wxss
        |-- nav.wxml
        |-- nav.json
|-- common
    |-- color.wxss  
|-- app.js
|-- app.json
|-- app.wxss

app.json文件文件内容如下

{
  "pages": [
    "pages/index/index"
  ]
}

前面提到组成小程序主体所必须的三个文件:app.js、app.json、app.wxss,由于app.json仅注册了一个页面,由此我们可以生成这样一个入口文件 entry.js

require('./app.js');
require('./app.wxss');
require('./pages/index/index.js');
require('./pages/index/index.json');
require('./pages/index/index.wxss');
require('./pages/index/index.wxml');

我们知道 Webpack 默认只能对 js 文件进行识别,不管你的模块加载使用的是 CommonJS 还是 AMD 还是 ESM,它都能进行识别,并以此生成一个依赖树,依赖关系保存在编译实例对象中。但针对这样一个入口文件,还有 wxss、wxml、json 文件无法处理(json文件可以识别,但无法获得依赖),需要编写相应的 loader。

编写 wxss-loader 获取 wxss 的依赖关系

wxss-loader 的作用就是让 Webpack 能识别并处理 wxss 文件,识别 wxss 文件后需要做什么呢?也就是 wxss-loader 接收到 wxss 文件的内容后该转换成什么?如果不做处理,直接返回 wxss 文件内容,那下一步 Webpack 会将 wxss 文件内容当成 js 代码去解析,得到的自然是解析失败。其实这里我们只需要知道当前 wxss 文件的依赖即可,wxss 的依赖即通过 @import 语法引入的其他 wxss 文件。通过正则/@import\s+['"]([^'"]* "'"")['"]/gi,我们可以很简单地解析出依赖路径。得到路径后我们如何告诉 Webpack 这个路径是我们所依赖的呢?刚刚提到本次 loader 返回后的内容会被 Webpack 当成 js 去解析,所以我们需要构造一个 js 模块返回,在 js 模块中将正则解析到的路径 require 进去即可,接着 Webpack 便会自动递归解析所有 wxss 文件。例如,app.wxss 文件内容如下:

@import './common/color.wxss';
.fixed{
    position: fixed;
}

经过 wxss-loader 后返回的内容如下,至于样式代码,则直接丢弃即可,至于如何获取内容后文会讲到。

require('./common/color.wxss');
module.exports = '';

本系列涉及的所有内容,都会提供源码供大家参考,wxss-loader 源码详见wecteam/dm[1]

编写 wxml-loader 获取 wxml 及 wxs 的依赖关系

同 wxss-loader,只不过依赖资源的解析稍微复杂一点,首先 wxml 的引入可以通过 importinclude 标签引入,另外 wxml 还可以 通过 wxs 标签引入 wxs 文件,跟 wxss-loader 相比,主要就是解析依赖的正则不同了:/<(import|include|wxs)\s+[^>]*src\s*=\s*['"]([^'"]* "^>]*src\s*=\s*['"")['"][^>]*>/gi

编写 wxs-loader 获取 wxs 的依赖关系

wxs-loader 的作用就简单了,让 Webpack 能识别 wxs 文件就行,由于 wxs 文件内容就是一个单独的 js 模块,loader 直接返回文件内容,交给 Webpack 做递归解析即可。

编写 wxjson-loader 获取组件的依赖关系

wxjson-loader 是针对页面配置的 json 文件,我们知道 Webpack4 默认是可以识别 json 文件的,但对于 json 文件的识别,获取到的是序列化的 json 对象,这个不是我们想要的,我们要的是 json 文件中 usingComponents 字段记录的当前页面对自定义组件的依赖,比如 index.json 中记录了对 nav 组件的依赖:

{
    "navigationBarTitleText": "首页",
    "usingComponents" : {
        "nav" : "../../nav/nav",
    }
}

由此我们通过路径边可以解析到 nav 组件的依赖:nav.wxml、nav.js、nav.json、nav.wxss 这个 4 个文件,跟前面的 loader 一样,仍然是构造一个 js 模块,将这个 4 个文件的依赖 require 进去,下一步交给 Webpack 去解析这个构造好的 js 模块,进一步递归获取组件各文件的依赖。

  1. 需要注意的是,Webpack4 对 json 文件的最终处理默认会去做 json 解析,而我们在 wxjson-loader 里已经将内容转成了一个 js 模块,因此我们需要将 wxjson-loader 的 type 设置为javascript/auto来告诉 Webpack:你得把我的 json 文件当 js 模块来解析。

  1. 组件的依赖可以是插件形式,路径会以plugin://开头,插件的代码在外部,不需要做依赖分析,直接略过即可。

如何处理图片字体等资源的依赖关系

图片资源,其实不太好处理,app.json 和 wxml 都可以使用相对路径的图片,app.json 中用于导航的图片路径可以直接解析,但用于 wxml 文件中的图片路径,则不太好处理了,因为这时候的路径往往是通过 setData 动态设置的,这种动态设置的图片只有在运行时才能确定其路径,没办法提前解析。所以针对图片字体等资源,一是建议除了用于导航的图片,其他页面的图片全部转到 CDN,尽量减少本地图片的使用,不管是对于减少小程序体积也好,提升启动速度也好,都有很大帮助。二是对于不关心小程序体积的那部分同学,图片及字体资源,直接按路径拷贝即可。

注意 app.json 中的其他依赖项

  1. 全局的 Component 配置。
  2. 用于小程序内搜索的 sitemap.json 文件。
  3. 用于插件功能页的 ./functional-pages 目录,注意此目录不能引用其他目录文件,也不可被其他目录引用。

不同操作系统的路径问题

前面提到我们在各 loader 中解析文件路径后,会转换成 require(/path/to/file),如果大家看了源码,应该有注意到在借助 Webpack 的 context 计算完路径后做了一个 replace(/\\/g, '/') 处理,这个主要是因为在 win 系统生成路径 d:\\path\\to\\file 在经过 Webpack 解析时,每解析一次就会消耗一次反斜杠,最终会导致前面的路径变成 d:path ofile,其中\t 被转换成了制表符,因此需要将其转成 POSIX 风格避免这个问题。

不规范的路径写法兼容

主要是在资源引用时,写法不规范,有以下几点:

  1. js文件的引用,比如 require('util'),就会有很大歧义,从规范 CommonJS 或者 AMD 来讲,这代表引用的是 util 包,但在小程序里,它代表引用当前目录的 util.js,而且它还有另外两种写法:require('./util')require('/util')、最后一个以/开头,一般是代表文件系统根目录,但小程序里它与./是等价的,这些都会导致 Webpack 解析依赖失败,所以这些都需要在解析时做兼容处理。
  2. wxml、wxss、组件的引用,如果以/开头,代表的是小程序根目录,比如上述小程序目录,不管你在哪个层级的目录文件下使用 @import /app.wxss 这个写法,都代表与 app.js 文件平级的 app.wxss 文件。字母开头等价于./,如@import app.wxss@import ./app.wxss 是等价的。
  3. 往上层级有多余,最终会定位到小程序根目录,如上述目录结构 index.wxss 文件中 @import ../../../../../app.wxss,最终会找到与app.js 文件平级的 app.wxss 文件。

解决路径问题的时机

  1. 各 loader 针对的 wxss、wxs、wxml、json 文件,直接在 loader 中处理好路径问题即可
  2. js 文件的路径问题,可以在 Webpack 构建 normalModule 时的 beforeResolve 钩子中处理,源码详见wecteam/dm[2]

五、结语

本篇主要是讲小程序代码如何做文件依赖分析,虽然通篇是拿微信小程序举例,但其他小程序同理,针对不同文件类型添加不同的 loader 即可。如支付宝小程序的 acss 文件,写个 acss-loader 来处理就好。下篇会讲如何获取依赖分析的结果,并将所有依赖资源打包成小程序需要的目录结构,同时也会讲一讲单页抽取。

参考资料

[1]wecteam/dm: https://github.com/wecteam/dm/blob/master/packages/dm-cli/src/plugins/plugin-build/webpack-loaders/wxss-loader.ts

[2]wecteam/dm: https://github.com/wecteam/dm/blob/master/packages/dm-cli/src/plugins/plugin-build/webpack-plugins/deps-plugin.ts

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WecTeam 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、小程序早期的打包方式
  • 三、小程序的依赖资源有哪些
  • 四、依赖分析如何实现
    • 入口的生成
      • 编写 wxss-loader 获取 wxss 的依赖关系
        • 编写 wxml-loader 获取 wxml 及 wxs 的依赖关系
          • 编写 wxs-loader 获取 wxs 的依赖关系
            • 编写 wxjson-loader 获取组件的依赖关系
              • 如何处理图片字体等资源的依赖关系
                • 注意 app.json 中的其他依赖项
                  • 不同操作系统的路径问题
                    • 不规范的路径写法兼容
                      • 解决路径问题的时机
                      • 五、结语
                        • 参考资料
                        相关产品与服务
                        云开发 CloudBase
                        云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档