资讯专栏INFORMATION COLUMN

shadow-cljs: JavaScript 依赖的实践

张汉庆 / 2467人阅读

摘要:原文原作者是作者是一个面向开发者友好的编译器之前关于依赖的文章问题前景里面我解释了为什么当中采用了和默认的方案不同的做法简单回顾下或者的写法难以扩张自定义的打包实际当中使用繁琐目前对大部分的模块的处理不够可靠自定义了一个而移除了

原文 https://code.thheller.com/blo...
原作者是 shadow-cljs 作者, shadow-cljs 是一个面向 JavaScript 开发者友好的 ClojureScript 编译器.

之前关于 js 依赖的文章(问题, 前景)里面, 我解释了为什么 shadow-cljs 当中采用了和 ClojureScript 默认的方案不同的做法. 简单回顾下:

cljsjs 或者 :foreign-libs 的写法难以扩张

自定义的打包实际当中使用繁琐

Closure Compiler 目前对大部分的 npm 模块的处理不够可靠

shadow-cljs 自定义了一个 js bundler, 而移除了 :foreign-libs 的支持

安装 js 依赖

几乎所有的 npm 模块都会写一遍如何安装. 现在对于 shadow-cljs 来说也是适用的. 比如有个类库要你运行:

npm install the-thing

你照做就好. 不需要其他步骤了. 当然你喜欢的话可以用 yarn. 然后依赖就会被写进 package.json 文件用于管理. 如果没有 package.json 那就运行 npm init.

上面说到这些东西, 你可以用这个 QuickStart 模板 来试用.

试用 js 依赖

大部分的 npm 模块也会写一下具体的代码表示怎样使用模块. "旧的" CommonJS 的写法是用 require 调用. 翻译到 ClojureScript 就是:

var react = require("react");
(ns my.app
  (:require ["react" :as react]))

不管 "string" 参数的地方用了什么然后被 require 调用, 我们都是这样换成 ns :require. :as 的 alias 部分就随你定义. 有了这个之后, 它就像是其他的 cljs 的命名空间那样可以调用了:

(react/createElement "div" nil "helle world")

这跟以前 :foreign-libs 或者 CLJSJS 当中做的不一样, 以前比如引入了 thingns 然后要用 js/Thing(或者其他全局导出的变量)来使用代码. 现在可以用 ns 格式以及 :as 后面提供的名称. 需要的话还可以写 :refer:rename.

一些模块会暴露一个函数, 那你可以写 (:require ["thing" as thing]) 然后调用 (thing).

最近一些模块开始用 ES6 的 import 语法作为例子了. 这些代码除了一个 default 写法以外, 基本上在 ClojureScript 能做到一一对应. 比如说翻译下面的例子:

import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";

到(包裹在 ns 里面的):

(:require ["module-name" :default defaultExport])
(:require ["module-name" :as name])
(:require ["module-name" :refer (export)])
(:require ["module-name" :rename {export alias}])
(:require ["module-name" :refer (export1) :rename {export2 alias2}])
(:require ["module-name" :refer (export) :default defaultExport])
(:require ["module-name" :as name :default defaultExport])
(:require ["module-name"])

其中 :default 参数目前只在 shadow-cljs 里面支持, 但是你也可以在这里投票帮助它进入到规范当中. 或者你也可以一直用 :as alias 然后调用 alias/default, 这样你觉得能个标准的 cljs 始终保持兼容的话. 我觉得吧, 对于某些模块来说啰嗦了点.

新的可能性

之前我们的使用打包之后的代码, 可能会包含我们用不到的代码. 某些模块也说明了一些办法可以只引入部分的模块, 这样最终构建的代码体积会小一些.

react-virtualized 有个这样的例子:

// You can import any component you want as a named export from "react-virtualized", eg
import { Column, Table } from "react-virtualized"

// But if you only use a few react-virtualized components,
// And you"re concerned about increasing your application"s bundle size,
// You can directly import only the components you need, like so:
import AutoSizer from "react-virtualized/dist/commonjs/AutoSizer"
import List from "react-virtualized/dist/commonjs/List"

那么很容易翻译过去:

;; all
(:require ["react-virtualized" :refer (Column Table)])
;; one by one
(:require ["react-virtualized/dist/commonjs/AutoSizer" :default virtual-auto-sizer])
(:require ["react-virtualized/dist/commonjs/List" :default virtual-list])
查找 js 依赖

默认情况下 shadow-cljs 通过 npm 的方式引用查找所有 (:require ["thing" :as x]). 也就是说会查找 /node_modules/thing/... 当中的代码. 为了对这个行为进行自定义, shadow-cljs 暴露了一个 :resolve 配置项, 你可以自己定义某些模块如何查找.

使用 CDN

比如说页面里的 React 从 CDN 上引用了. 这时候按说你可以用 js/React 了, 但是最好还是不要这样. 你应该是继续用 (:require ["react" :as react]), 同时在 shadow-cljs 里定义 react 怎样查找. 这个配置在 shadow-cljs.edn 文件里配置:

{:builds
 {:app
  {:target :browser
   ...
   :js-options
   {:resolve {"react" {:target :global
                       :global "React"}}}}

  :server
  {:target :node-script
   ...}}}

现在 :app 这个构建会使用全局的 React 实例, 而在 :server 这个构建当中会继续使用 react 的 npm 模块. 不需要额外折腾代码去完成需求.

重定向 require

某些模块提供多个 "dist" 文件, 然后可能默认的那个刚好是在 shadow-cljs 里有问题的. 一个明显的例子就是 d3. 他们默认的 "main" 指向 build/d3.node.js, 这个不是在浏览器里面用的版本. 他们的 ES6 代码还触发了 Closure Compiler 里的一个 bug, 所以我们不能用. 这样的话我们就重定向到其他的引用去:

{:resolve {"d3" {:target :npm
                 :require "d3/build/d3.js"}}}

你也可以直接就写 (:require ["d3/build/d3.js" :as d3]), 如果你只关心浏览器当中的使用的话.

使用本地文件

你还可以用 :resolve 来直接映射到一个项目中的本地文件:

{:resolve {"my-thing" {:target :file
                       :file "path/to/file.js"}}}

这里的 :file 总是相对于项目根路径. 这个文件里可以用 require 或者 import/export, 这些随后都会被处理好的.

迁移 cljsjs.*

很多 cljs 类库还在用 CLJSJS 包, 它们在 shadow-cljs 里不能正常使用了, 因为 :foreign-libs 不再支持. 我提供了一个清晰的迁移路线, 只需要增加一个 shim 文件把 cljsjs.thing 映射回到原始的 npm 模块, 然后把全局变量暴露出去.

比如 react 需要一个这样的文件src/cljsjs/react.cljs:

(ns cljsjs.react
  (:require ["react" :as react]
            ["create-react-class" :as crc]))

(js/goog.object.set react "createClass" crc)
(js/goog.exportSymbol "React" react)

因为这样的话每个人手动处理会麻烦, 所以我提供了 shadow-cljsjs 这个类库来提供这个功能. 虽然不会包含每一个模块, 但是我会持续添加. 欢迎来帮忙贡献模块.

不过它仅仅提供 shim 文件. 你还是需要用 npm install 安装真实的模块.

其他功能不能用怎么办?

JavaScript 社区变化很快, 并不是每个人都一样地写代码, 都一样地分发代码, 有些东西是 shadow-cljs 不能自动处理或者需要自定义 :resolve 配置的. 可能是会遇到 bug, 毕竟都是新东西.

遇到任何模块不能按照预期地使用, 请报告. 在 #shadow-cljs 很容易找到我.

关于这篇文章的讨论请移步 :clojurevese.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89693.html

相关文章

  • Luxon 初步介绍(Moment 团队日期另一个类库)

    摘要:我不是的深度用户只是日常会遇到一些时间格式化的需求之前用的都是不过对的感到有些不舒服同事介绍过里边用不可变数据的风格设计的所以就试试上星星很多请放心食用是项目的某个维护者开发的按照他个人想法进行了改进除了不可变性使用的工具做了更新也尝试到时 我不是 Moment 的深度用户, 只是日常会遇到一些时间格式化的需求,之前用的都是 Moment, 不过对 Moment 的 mutable A...

    stormzhang 评论0 收藏0
  • 前端每周清单第 40 期: JS Core 与 Cost,Node 内存溢出调试,Softwar

    摘要:已被所有主流浏览器支持在过去几周苹果的浏览器与微软的浏览器分别发布新版本,支持了,再加上早已支持的和,已得到所有主流浏览器支持。 showImg(https://segmentfault.com/img/remote/1460000012086220?w=1240&h=823); 前端每周清单第 40 期: JS 的 Core 与 Cost,Node 内存溢出调试,Software 2...

    番茄西红柿 评论0 收藏0
  • 预告:JavaScript模块全览

    摘要:之前写的文章急速全栈教程得到了不错的阅读量,霸屏掘金头条天,点赞过千,阅读近万,甚至还有人在评论区打广告,可见也是一个小小的生态了。今天看到的霸屏的,也是讲全栈的,见参考文章接下来要写的是模块。全局命名污染和命名冲突依赖管理。 之前写的文章急速Js全栈教程得到了不错的阅读量,霸屏掘金头条3天,点赞过千,阅读近万,甚至还有人在评论区打广告,可见也是一个小小的生态了;)。看来和JS全栈有关...

    focusj 评论0 收藏0
  • RequireJS配置项笔记

    摘要:读不顺中文文档,对应中文文档,自行翻译的如果有问题错误,欢迎指点修改配置选项方法一在顶级页面或顶级脚本文件没有定义模块的脚本文件中配置方法二在主模块中配置缺点主模块异步加载,多入口的话,会随机报错方法三在调用之前,将配置定义为全局变量配置在 读不顺中文文档,对应中文文档,自行翻译的……如果有问题/错误,欢迎指点; 修改配置选项: 方法一、 requi...

    FWHeart 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<