Js模块化, 摆脱玩具语言的定位的阅读笔记
竟然2个月没写文章了, 我堕落了………
这是一篇阅读笔记, 原文: https://www.airpair.com/javascript/posts/the-mind-boggling-universe-of-javascript-modules
很久没看到这么通透的文章了. 我要记笔记(不是翻译是因为我总想扩展, 并且水平渣渣, 信达雅, 连信都做不到).
-
js看上去是一种玩具语言, 本身的全局变量导致根本无法构建大型应用(本身只有var)
-
leverage encapsulation, 代码的外延要能够被封闭, node之前js木有这个能力.
-
最原始的 ad hoc方式:
//这个方式主要目的是实现链式调用. var Zoo = (function() { var getBarkStyle = function(isHowler) { return isHowler? 'woooooow!': 'woof, woof!'; }; var Dog = function(name, breed) { this.bark = function() { return name + ': ' + getBarkStyle(breed === 'husky'); }; }; var Wolf = function(name) { this.bark = function() { return name + ': ' + getBarkStyle(true); }; }; return { Dog: Dog, Wolf: Wolf }; })(); //这里是调用方式, 丑陋的new方式. var myDog = new Zoo.Dog('Sherlock', 'beagle'); console.log(myDog.bark()); // Sherlock: woof, woof! var myWolf = new Zoo.Wolf('Werewolf'); console.log(myWolf.bark()); // Werewolf: woooooow!
-
全局代码 vs ad-hoc
- 全局代码副作用强烈, 性能也很容易出问题, 阅读困难, 代码组织地狱级难度.
- 全局代码暴露实现细节, 会引起各种错综复杂的问题, 不利于分治调优
- 互相之间的依赖, 传递依赖, 用全局都不知道咋搞了.
- 全局代码很容易出现全局依赖.
- 区分模块之后, 也比较容易做出代码的内聚.
-
ad - hoc的问题
- 依旧定义了zoo这个全局变量, 类似jquery的$.
- 代码脆弱fragile, zoo依旧可以被任意的代码影响到. 很容易这里正常, 但是, 那里就不正常了. 因为, 某段代码对zoo做了扩展.
- 不可扩展scalable, 如果你有100个module, 地狱降临, 你被迫逐个引入, 如果不想麻烦就是所有模块一次性, 同步引入, 这个必然有爆表的性能问题.
- 适得其反, 其实比不用模块更糟糕, counter-productive, 你必须手工解决依赖问题, 换个场景用这100个模块时, 你必须记忆这100个模块的内在逻辑, 以及使用逻辑(甚至于这个使用还是有顺序的).
-
那么一个包加载器应该咋样呢?
- 模块的注册机制, 基于aliases
- 解除依赖, 基于依赖分析
- 实例化模块, 基于工厂模式
- 其实简单的说就是读入一个js, 然后, 做好自己该处理的, 再把剩下的交给js解释器.
-
大魔王出现了 commonJS or AMD?
- commonJS是
- module require语法
- node官方的加载器.
- 同步加载的.
- 语法简单
- 可以保证执行顺序
- 本身不能在浏览器执行, 不过broweserify和webpack可以帮他.
- 同步加载导致加载时间比较长.
- 依赖膨胀的很厉害, 会导致你的代码很容易引入非常巨大数目的依赖包.
- 此时我又重新去了解了一下闭包, 建议还是能不用就不用.
- AMD
- define require方案
- 可以异步加载
- 自然适用于浏览器
- 懒加载很方便
- 异步加载如果思考不清楚会出现冲突
- 不保证加载顺序
- 语法会难于理解
- commonJS是
-
ES6自带了
- export import语法
- 其实我们可以认为这些内容属于framework, 并不是语言本身的核心语法, 但是, js和java的区别就在于, 语言层面就包含了这些framework, js并不允许framework竞争. js允许竞争的只有包lib, 没有framework
作者的个人网站, 严重推荐: https://tiagorg.com
有用的参考链接
- https://stackoverflow.com/questions/37050932/commonjs-imports-vs-es6-imports
如果在浏览器环境使用 import和export
js:
//文件尾部
export default out;
//或者
export {out1, out2, out3};
浏览器端也是一堆坑, 坑死人了.
//引入端这么写:
<script type="module">
import {images} from '/show/images.js'
//输出端这么写:
//export default out;
export {
out as images
};
</script>
不要相信其他写法, 比如下面这种就怎么搞都不可以:
<script type="module" src="/show/images.js"></script>
If you export function xyz, you must import { xyz }
If you export default function xyz, you must import xyz or import { default as xyz }
type=module之后, 会导致html里面直接注册事件失败.
<input type="file" id="input" onchange="handleFiles(this.files)">
<script type="module">
function handleFiles() {
alert('success') //这里不会被执行.
}
</script>
我猜测原因是: 因为type=module之后就是异步加载的了, 所以input里面写的onchange就失效了, 因为函数定义并没有被提升上去.
据说是scope问题, 我再试试.
window.handlefiles=handlefiles; //这样就解决了.
正解再这里: https://stackoverflow.com/questions/44590393/es6-modules-undefined-onclick-function-after-import
然后这个讲的很清楚: https://www.contentful.com/blog/2017/04/04/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/