惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Fast, extensible, configurable, and beautiful linter for Go Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Using Stripe with Angular (Deprecated) Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Even Faster AngularJS Data Structures Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Asynchronous calls with ES6 generators Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman The magic of $resource (or simply a client-side Active Record) AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
Dynamic imports solve all the problems, right?
2019-05-11 · via Minko Gechev's blog

Since Angular version 8, we support dynamic imports in loadChildren in the route declaration. In this article, I want to give more information about why dynamic imports could be tricky to handle from tooling perspective and why you should be careful with them.

As engineers, we often have the perception that dynamic == good. With statically typed languages, such as TypeScript, this has shifted over the years. Because of compile-time checking, more folks started appreciating what tooling can give us if we provide statically analyzable information at build time.

In the past, I’ve heard a lot of complaints around the static imports in JavaScript:

Why does JavaScript restricts us to use only string literals when specifying the module in ES2015 imports?

To see what are the benefits of this, let’s look at an example:

import { foo } from './bar';

Above we have the import keyword, import specifier ({ foo }), from keyword, followed by the ./bar string literal. There are two great things about this construct:

  • Statically we can determine what symbols we import
  • Statically we can determine from which module we import

Static vs. Dynamic Analysis

It’s essential to understand what I mean by saying statically. In practice, any correct program can be partially executed at build time (for example, by webpack, prepack, etc.) and, potentially, fully executed at runtime (if we have sufficient information, and of course, this does not imply that the program will ever terminate). Look at this example:

const fib = n => n === 1 || n === 2 ? 1 : fib(n - 1) + fib(n - 2);

console.log(fib(5));

If we add a prepack plugin to our webpack build, the final bundle will look something like this:

Which means that our users will directly see 5 in the console, without having to execute the fib function. This is only possible because our program does not use data which is only available at runtime. For example:

const fib = n => n === 1 || n === 2 ? 1 : fib(n - 1) + fib(n - 2);

console.log(fib(window.num));

The evaluation of the snippet above would be impossible at build time because we don’t know what’s the value of window.num (imagine we rely on a Chrome extension to set the window.num property).

How does this relate to ES2015 imports? It connects in at least two different ways. With ES2015 imports we can:

  • Statically analyze the import specifier when the imported symbols are explicitly listed
  • Statically analyze the path because it’s a string literal

Let’s suppose for a second that this is a valid syntax:

import { foo } from getPath();

The implementation of getPath could involve information which is only available at runtime, for example, we can read a property from localStorage, send a sync XHR, etc. The ECMAScript standard allows only string literals so that the bundler or the browser, can know the exact location of the module we’re importing from.

Now, let’s look at another example:

import * as foo from './foo';

We specify the path as a string literal but we use a wildcard import. In this scenario, in the general case the bundler will be unable to determine which symbols from ./foo we use in the current module. Imagine we have the following snippet:

// foo.js
export const a = 42;
export const b = 1.618;
export const c = a + b;

// bar.js
import * as foo from './foo';

console.log(foo.a);
console.log(foo[localStorage.get('bar')]);

If we invoke the bundler by specifying bar.js as an entry point, statically we can determine that bar.js uses a but we have no idea if b and c are going to be needed at runtime, we don’t know what the value of localStorage.get('bar') is. This way we won’t be able to get rid of the unused symbols since we don’t know what symbols from foo.js its consumers use.

That’s why I often discourage people to use wildcard imports since they could block the bundler from tree-shaking effectively.

What about dynamic imports

Originally, I started writing this article to explain why dynamic imports could be tricky from a tooling perspective. Let’s suppose that we have a lazy-loaded module:

// dynamic.js
export const a = 42;

// foo.js
import('./dynamic.js').then(m => console.log(m.a));

If we try to bundle this with rollup by specifying foo.js as an entry point we’ll get something like:

$ rollup foo.js --output.format esm

foo.js → stdout...

//→ foo.js:
import('./chunk-75df839a.js').then(m => console.log(m.a));

//→ chunk-75df839a.js:
const a = 42;

export { a };
created stdout in 59ms

Here’s what rollup did:

  • Created a chunk from foo.js, bundling it with all of its static imports (in this case there are none)
  • Statically analyzed foo.js and found that it dynamically imports dynamic.js
  • Created another chunk called chunk-75df839a.js by bundling together dynamic.js with all its static imports (in this case there are none)

Now let’s change something in foo.js:

// foo.js
import('./dynamic.js' + '').then(m => console.log(m.a));

In this case, we get:

$ rollup foo.js --output.format esm

foo.js → stdout...
import('./dynamic.js' + '').then(m => console.log(m.a));
created stdout in 39ms

This means that rollup was not able to correctly figure out the entry point of the dynamically loaded chunk. Why? We changed the argument of the import from a string literal to an expression. Same, will happen if we use an expression which could be only evaluated at runtime (for example, import(localStorage.getItem('foo'))). Webpack would handle this case because it’ll try to evaluate the expression statically, at build time:

$ webpack foo.js

Hash: 2a1d2bcb0c5277c3bf29
Version: webpack 4.30.0
Time: 396ms
Built at: 05/11/2019 3:44:22 PM
  Asset       Size  Chunks             Chunk Names
   1.js  141 bytes       1  [emitted]
main.js      2 KiB       0  [emitted]  main
Entrypoint main = main.js
[0] ./foo.js 58 bytes {0} [built]
[1] ./dynamic.js 21 bytes {1} [built]

Dynamic Imports and TypeScript

Now, let’s look at another example using TypeScript. Let’s suppose we have the following snippet:

// foo.ts
export const foo = 42;

// bar.ts
import('./foo').then(m => m.foo);

If we open bar.ts in a text editor and we point over m.foo, we’ll see that it’s of type number. This means that TypeScript’s type inference has tracked the reference and figured out its type. Now, change bar.ts to:

// bar.ts
import('./foo' + '').then(m => m.foo);

If we point over m.foo again, we’ll find out that it has type any (TypeScript version 3.1.3). Why both differ? When TypeScript finds out that we’ve passed a string literal to the dynamic import it follows the module reference and performs type inference; if it finds an expression, it fallbacks to type any.

Let’s go through a quick recap of our observations:

  • In the general case, dynamic imports cannot be tree-shaken because we can access the exported symbols with index signature with an expression that contains data only available at runtime (i.e. import(...).then(m => m[localStorage.getItem('foo')]))
  • Modern bundlers and TypeScript can resolve dynamic imports only when we have specified the module with a string literal (an exception is webpack, which statically performs partial evaluation)

This is one of the reasons why in the general case Guess.js, cannot handle dynamic imports and map them directly to Google Analytics URLs so that it can reuse the predictive model at runtime.

Conclusions

Dynamic imports are fantastic for code-splitting on a granular level. They allow us to provide lazy-loading boundaries in our application. In the same time, because of their dynamical nature, they often will enable us to sneak in code that requires runtime data for resolution of the imported module, or for accessing its exports.

In such cases, we should be extremely cautious because we limit the capabilities of the tools that we’re using. We sacrifice automatic bundling of lazy-loaded chunks, type inference, and much more.