Skip to content

Commit 155bf65

Browse files
zthryyppy
andauthored
Dynamic imports v11 blog post (#683)
* initial work on dynamic imports blog post * Tweak intro section (making the ReScript challenges part of the intro itself) * Simplifying / restructuring * Rename blog post file to reflect date / title. Tweaked some text * Add introduction header * Conclusion * More wording fixes * Change date to actual release date --------- Co-authored-by: Patrick Ecker <[email protected]>
1 parent e1d699e commit 155bf65

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
author: rescript-team
3+
date: "2023-06-05"
4+
title: First-class Dynamic Import Support
5+
badge: roadmap
6+
description: |
7+
A tour of new capabilities coming to ReScript v11
8+
---
9+
10+
> This is the third post covering new capabilities that'll ship in ReScript v11. You can check out the first post on [Better Interop with Customizable Variants](/blog/improving-interop) and the second post on [Enhanced Ergonomics for Record Types](/blog/enhanced-ergonomics-for-record-types).
11+
12+
## Introduction
13+
14+
When developing apps in JavaScript, every line of code eventually needs to be bundled up and shipped to the browser. As the app grows, it's usually a good idea to split up and load parts of the app code on demand as separate JS modules to prevent bundle bloat.
15+
16+
To accomplish this, browsers provide support for dynamic loading via the globally available [`import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) function to allow code splitting and lazy loading and ultimately reducing initial load times for our applications.
17+
18+
Even though ReScript has been able to bind to `import` calls via `external` bindings, doing so was quite hard to maintain for to the following reasons:
19+
20+
1. An `import` call requires a path to a JS file. The ReScript compiler doesn't directly expose file paths for compiled modules, so the user has to manually find and rely on compiled file paths.
21+
2. The return type of an `import` call needs to be defined manually; a quite repetitive task with lots of potential bugs when the imported module has changed.
22+
23+
Arguably, these kind of problems should ideally be tackled on the compiler level, since the ReScript compiler knows best about module structures and compiled JS file locations — so we finally decided to fix this.
24+
25+
Today we're happy to announce that ReScript v11 will ship with first-class support for dynamic imports as part of the language.
26+
27+
Let's have a look!
28+
29+
## Import Parts of a Module
30+
31+
We can now use the `Js.import` function to dynamically import a value or function from a ReScript module. The import call will return a promise, resolving to the dynamically loaded value.
32+
33+
For example, imagine the following file `MathUtils.res`:
34+
35+
```rescript
36+
// MathUtils.res
37+
let add = (a, b) => a + b
38+
let sub = (a, b) => a - b
39+
```
40+
41+
Now let's dynamically import the `add` function in another module, e.g. `App.res`:
42+
43+
```rescript
44+
// App.res
45+
let main = async () => {
46+
let add = await Js.import(MathUtils.add)
47+
let onePlusOne = add(1, 1)
48+
49+
RescriptCore.Console.log(onePlusOne)
50+
}
51+
```
52+
53+
This compiles to:
54+
55+
```javascript
56+
async function main() {
57+
var add = await import("./MathUtils.mjs").then(function(m) {
58+
return m.add;
59+
});
60+
61+
var onePlusOne = add(1, 1);
62+
console.log(onePlusOne);
63+
}
64+
```
65+
66+
Notice how the compiler keeps track of the relative path to the module you're importing, as well as plucking out the value you want to use from the imported module.
67+
68+
Quite a difference compared to doing both of those things manually, right? Now let's have a look at a more concrete use-case with React components.
69+
70+
### Use-case: Importing a React component
71+
72+
> **Note:** This section requires the latest [@rescript/react](https://github.com/rescript-lang/rescript-react) bindings to be installed (_0.12.0-alpha.2 and above_).
73+
74+
Our dynamic import makes tasks like [lazy loading React components](https://react.dev/reference/react/lazy#lazy) a simple one-liner. First let's define a simple component as an example:
75+
76+
```rescript
77+
// Title.res
78+
@react.component
79+
let make = (~text) => {
80+
<div className="title">{text->React.string}</div>
81+
}
82+
```
83+
84+
Now let's dynamically import the `<Title/>` component by passing the result of our dynamic import to `React.lazy_`:
85+
86+
```rescript
87+
module LazyTitle = {
88+
let make = React.lazy_(() => Js.import(Title.make))
89+
}
90+
91+
let titleJsx = <LazyTitle text="Hello!" />
92+
```
93+
94+
That's all the code we need! The new `<LazyTitle />` component behaves exactly the same as the wrapped `<Title />` component, but will be lazy loaded via React's built in lazy mechanism.
95+
96+
Needless to say, all the code examples you've seen so far are fully type-safe.
97+
98+
## Import a Whole Module
99+
100+
Sometimes it is useful to dynamically import the whole module instead. For example, you might have a collection of utility functions in a dedicated module that tend to be used together.
101+
102+
The syntax for importing a whole module looks a little different, since we are operating on the module syntax level; instead of using `Js.import`, you may simply `await` the module itself:
103+
104+
```rescript
105+
// App.res
106+
let main = async () => {
107+
module Utils = await MathUtils
108+
109+
let twoPlusTwo = Utils.add(2, 2)
110+
RescriptCore.Console.log(twoPlusTwo)
111+
}
112+
```
113+
114+
And, the generated JavaScript will look like this:
115+
116+
```js
117+
async function main() {
118+
var Utils = await import("./MathUtils.mjs");
119+
120+
var twoPlusTwo = Utils.add(2, 2);
121+
console.log(twoPlusTwo);
122+
}
123+
```
124+
125+
The compiler correctly inserts the module's import path and stores the result in a `Utils` variable.
126+
127+
## Try it out!
128+
129+
Feel free to try out our new dynamic import feature with the latest beta release:
130+
131+
`npm install [email protected]`
132+
133+
Please note that this release is only intended for experiments and feedback purposes.
134+
135+
136+
## Conclusion
137+
138+
The most important take away of the new dynamic imports functionality in ReScript is that you'll never need to care about _where_ what you're importing is located on the file system - the compiler already does it for you.
139+
140+
We hope that it will help shipping software with better end-user experience with faster load times and quicker app interaction, especially on slower network connections.
141+
142+
As always, we're eager to hear about your experiences with our new features. Feel free to share your thoughts and feedback with us on our [issue tracker](https://github.com/rescript-lang/rescript-compiler/issues) or on the [forum](https://forum.rescript-lang.org).
143+
144+
Happy hacking!

0 commit comments

Comments
 (0)