Code splitting 和 long term caching 是前端面对的老问题了.

Code splitting 是指将前端代码拆分成多个不同的文件,
主要的场景是 vendor.jsmain.js 进行的拆分,
以方便在业务代码更新时, 可以避免用户下载所有代码,
因为 vendor.js 当中的类库未必是更新的.

Long term caching 是将文件存储在 CDN 并设置永久不过期,
那样的话用户浏览器请求代码时可以直接命中本地的缓存, 从而减少流量, 并加快方案.
由于业务代码必然有更新的需求, 这里更新就需要更新资源路径了.
通常会用 MD5 插入文件名, 保证文件路径唯一. 经常用 MD5 的片段.

自从有了 Webpack 之后, 这些问题都是不值一提了, 几行配置就搞定了.
而且 code splitting 还发展出来按路由异步加载之类的玩法.. 高端..

但是, ClojureScript 社区就没那么好了, 过去对这两个特性支持一直不够.
而且由于 cljs 代码通常体积就是挺大的, 导致对加载优化一直不放心.
cljs 生成的 js 代码会经过 Closure Compiler 裁剪, 体积还算控制住了.
之前了解到的是通过 Closure Library 有相关方案的, 但是一直没人挖掘.

shadow-cljs 方案

最近折腾 shadow-cljs 有一个原因是作者 thheller 介绍过这类方案,
然后今天终于尝试了一下, 想不到瞬间就搞定了. 大致的配置是这样的:

{:source-paths ["src/"]
 :dependencies []
 :builds {:app {:output-dir "dist/"
                :target :browser
                :asset-path "."
                :modules {:main {:entries [app.main]}}
                :release {:module-hash-names true
                          :modules {:main {:entries [app.main]
                                           :depends-on #{:lib}}
                                    :lib {:entries [app.lib]}}}}}}

完整的项目可以到这里 clone 下来运行, 记得安装 yarn.
https://github.com/minimal-xy...

前面的配置跟普通的 cljs 编译没啥区别,

  • :output-dir 输出文件的位置

  • :target 编译到目标, 其中 :browser 代表浏览器

  • :asset-path 热替换时资源的相对路径

  • :modules 开发过程的只需要一个文件入口, 就只指定一个 :main

开发编译 cljs 的命令是 shadow-cljs compile app,
而发布用的编译命令是 shadow-cljs release app,
在 release 过程会去读取 :release 上的配置, 覆盖前面的部分配置,

  • :modules, 定义了两个模块, 而且配置了依赖关系,

  • :entries 当中是命名空间, 指定当前模块从哪些开始引入

  • :depends-on 依赖关系

  • :modules-hash-names 指定生成的文件名当中包含 MD5

运行命令之后, 最终在 :output-dir 对应位置将生成新的文件,
同时包含一个 manifest.json 文件, 其中包含了具体的文件信息, 比如文件名:

[{"name":"lib",
  "js-name":"lib.74FBE158EF12CABA0651D2512D372142.js",
  "entries":["app.lib"],
  "depends-on":[],
  "default":true,
  "sources":
  ["goog/debug/error.js", "goog/dom/nodetype.js",
   "goog/string/string.js", "goog/asserts/asserts.js",
   "goog/reflect/reflect.js", "goog/math/long.js",
   "goog/math/integer.js", "goog/object/object.js",
   "goog/array/array.js", "goog/string/stringbuffer.js",
   "cljs/core.cljs", "app/lib.cljs"],
  "js-modules":[]},
 {"name":"main",
  "js-name":"main.0A6DFA95007EECEFC381865B2A6E7E29.js",
  "entries":["app.main"],
  "depends-on":["lib"],
  "default":false,
  "sources":["app/main.cljs"],
  "js-modules":[]}]

拿到这些信息, 就跟 Webpack 里做 code splitting 和 long term caching 差不多了.

其他

另外关于模块的异步加载, 目前在 shadow-cljs 我还没有用到,
我记得 Closure Compiler 里是实现了的, 所以也暴露出 API,
大致翻了一下, 也是可以做的, 等以后用到再细看了:
https://github.com/thheller/s...


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者


引用和评论

0 条评论