Ste*_*ert 13 javascript side-effects rollupjs es6-modules tree-shaking
我一直在努力学习如何编写树摇动友好的代码,但遇到了一个不可避免的副作用的问题,我不知道如何处理.
在我的一个模块中,我访问全局Audio
构造函数并使用它来确定浏览器可以播放哪些音频文件(类似于Modernizr的工作方式).每当我尝试树摇动我的代码时,Audio
即使我不在我的文件中导入模块,元素及其所有引用也不会被消除.
let audio = new Audio(); // or document.createElement('audio')
let canPlay = {
ogg: audio.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '');
mp3: audio.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/, '');
// ...
};
Run Code Online (Sandbox Code Playgroud)
我知道包含副作用的代码无法消除,但我找不到的是如何处理不可避免的副作用.我不能只是访问全局对象来创建audio
检测功能支持所需的元素.那么我如何处理访问全局浏览器函数/对象(我在这个库中做了很多)的方式,树摇动友好,仍然允许我消除代码?
您可以从Haskell / PureScript的书中抽出一页,并简单地限制自己在导入模块时不会出现任何副作用。取而代之的是,导出一个thunk,该thunk表示例如副作用,例如,可以访问Audio
用户浏览器中的global 元素,并对该thunk产生的值进行参数化其他功能/值。
这是您的代码段的外观:
// :: type IO a = () -!-> a
// :: IO Audio
let getAudio = () => new Audio();
// :: Audio -> { [MimeType]: Boolean }
let canPlay = audio => {
ogg: audio.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '');
mp3: audio.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/, '');
// ...
};
Run Code Online (Sandbox Code Playgroud)
然后,在主模块中,您可以使用适当的thunk实例化实际需要的全局变量,并将它们插入使用它们的参数化函数/值中。
显而易见,如何手动插入所有这些新参数,但这会很乏味。有几种技术可以减轻这种情况。您可以再次从Haskell / PureScript中窃取的一种方法是使用阅读器monad,这有助于对由简单函数组成的程序进行依赖注入。
对读者monad以及如何使用它在整个程序中穿插某些上下文的更详细的解释超出了此答案的范围,但是在一些链接中,您可以了解这些内容:
(免责声明:我没有彻底阅读或审查所有这些链接,我只是用关键字搜索并复制了一些看起来引人入胜的链接)