React-router and import router are used to fragment code
The original link
-
Code sharding allows you to split your application into multiple packages, making it popular for your users to gradually load the application. In this article, we’ll take a look at what code sharding is and how to do it, and learn how to implement it with the React Router.
-
It’s 2018. Your users don’t need to download an entire app just for a small piece of content. It makes no sense for a user to download all the code just to request a registration page. And users don’t need to download the huge rich text editor code of the user Settings page to sign up. If you have to download that much content, it’s wasteful. And for some users, they will complain that they are not respected for not having particularly good bandwidth. Not only has the idea caught on in recent years, but it’s gotten exponentially harder to implement. It even has a cool name, code sharding.
-
The idea is simple: load on demand. In practice, it can be a little complicated. The reason for the complexity is not code sharding per se, but the fact that there are now a variety of tools available to do this. And everyone has an opinion about which approach is best. When you first start out, it can be difficult to analyze what is what.
-
The two most common ways to do this are to use Webpack and its bundle-loader, or the dynamic import() of ECMAScript’s STAGE3 proposal. Any chance I don’t use Webpack, I won’t, so I’ll use dynamic import() in this article.
-
If you are familiar with ES modules, you should know that they are static. This means that you must determine what you want to import and export at compile time, not run time. This also means that you cannot dynamically import a module based on conditions. The imported content must be declared at the beginning of the file or an error will be thrown.
if(! user) {import * as api from './api' "Import" and "export" can only appear at the top of the file } Copy the code
-
Now, what if the import doesn’t need to be static? Does that mean the code above works? What benefits will it bring us? First, it means I can load a module as needed. This is very powerful and brings us closer to the imagination of downloading code on demand.
if (editPost === true) { import * as edit from './editpost' edit.showEditor() } Copy the code
-
Given that __Editpost__ contains a very large rich text editor, we need to make sure that users don’t download it when they’re not using it.
-
Another cool example is for legacy support. You can download the code only when the browser is sure it doesn’t exist.
-
Good news (as I alluded to above). This type of method does exist, it is supported by the Create React App (an official creation method for React projects), and it is a proposal for ECMAScript StagE3. The difference is that it replaces the way you used import before. It acts like a method and returns a Promise to resolve once the module is fully loaded.
if (editPost === true) { import('./editpost') .then(module= > module.showEditor()) .catch(e= >)}Copy the code
-
Pretty good, right?
-
Now that we know how to introduce the module dynamically, the next step is to figure out how to use it in combination with React and React Router.
-
The first (and probably biggest) question, when we shard React code, where should we shard it? There are two typical responses
- Sharding at the routing level
- Shard at the component level
-
Sharding is more common at the routing level. You have split your application into different routes, so it is natural to code sharding based on this.
-
Let me start with a simple React Router example. We will have three routes: /, /topics, / Settings.
import React, { Component } from 'react' import { BrowserRouter as Router, Route, Link, } from 'react-router-dom' import Home from './Home' import Topics from './Topics' import Settings from './Settings' class App extends Component { render() { return ( <Router> <div> <ul> <li><Link to='/'>Home</Link></li> <li><Link to='/topics'>Topics</Link></li> <li><Link to='/settings'>Settings</Link></li> </ul> <hr /> <Route exact path='/' component={Home} /> <Route exact path='/topics' component={Topics} /> <Route exact path='/settings' component={Settings} /> </div> </Router> ) } } export default App Copy the code
-
Now, suppose our __/settings__ route has a lot of content. It contains a rich text editor, a copy of the original Super Mario Bros., and high-definition pictures of Guy Farley. We don’t want users to download all of this when they’re not on the __/settings__ route. Let’s fragment __/settings__ routes using what we know about React and dynamic import(import()).
-
Just like we solve any problem in React, we’ll start by writing a component. We’ll call it __DynamicImport__. The purpose of this component is to dynamically load a module and pass it to its children as soon as the module is loaded.
const Settings = (props) = > ( <DynamicImport load={()= > import('./Settings')}> {(Component) => Component === null ? <Loading /> : <Component {. props} / >} </DynamicImport> ) Copy the code
-
The code above tells us two important things. First, the component takes an attribute __load__ when executed, which dynamically imports a module using the syntax we mentioned earlier. Second, the component accepts as its child a function that needs to be called along with the imported module.
-
Before we dive into the implementation of __DynamicImport__, let’s think about how we would implement it. The first thing we need to make sure is to call props. Load. This lets us return a Promise that should return the module when it resolves. Then, once we have the module, we need a way to trigger the re-render, so we need to pass the module to props. Children and call it. How to trigger rerender in React? SetState (setState). By adding the dynamically introduced module to __DynamicImport__ state, as we used it before, we follow the same process as React – get data -> set to state -> re-render. This time we just replaced the fetch with the import module.
-
Okay, first, let’s add the initial state to the component.
class DynamicImport extends Component { state = { component: null}}Copy the code
-
Now we need to call the props. Load method. This returns a Promise and a module after resolve
class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then(component= > { this.setState((a)= >{component)}})}}Copy the code
-
Here’s a puzzle. If we mix the ES module with the CommonJS module, the ES module will have a.default attribute, and the CommonJS module does not. Let’s change the code to accommodate this.
this.props.load() .then(component= > { this.setState((a)= > { component: component.default ? component.default : component }) }) }) Copy the code
-
Now that we’ve introduced the module dynamically and added it to state, the last thing we need to do is see what the render method looks like. If you remember, __DynamicImport__ looks like this when used
const Settings = (props) = > ( <DynamicImport load={()= >import('./Settings')}> {(Component) => Component === null ? < Loading / > : <Component {. props} / >} </DynamicImport> ) Copy the code
-
Notice that we passed a function to the component as a child node. This means that we need to execute this function, passing in the component introduced in state.
class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then((component) = > { this.setState({ component: component.default ? component.default : component }) }) } render() { return this.props.children(this.state.component) } } Copy the code
-
Oh, now any time we dynamically introduce a module, we can wrap it around __DynamicImport__. If we had tried this approach to our route, our code would have looked something like this
import React, { Component } from 'react' import { BrowserRouter as Router, Route, Link } from 'react-router-dom' class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then((component) = >  {this.setState({ component: component.default ? component.default : component }) }) } render() { return this.props.children(this.state.component) } } const Home = (props) = >( <DynamicImport load={() => import('./Home')}> {(Component) => Component === null ? <p>Loading</p> : <Component {... props} /> } </DynamicImport> ) const Topics = (props) => ( <DynamicImport load={() => import('./Settings')}> {(Component) => Component === null ? <p>Loading</p> : <Component {... props}/> } </DynamicImport> ) class App extends Component { render() { return ( <Router> <div> <ul> <li><Link to='/'>Home</Link></li> <li><Link to='/topics'>Topics</Link></li> <li><Link to='/settings'>Settings</Link></li> </ul> <hr /> <Route exact path='/' component={Home} /> <Route path='/topics' component={Topics} /> <Route path='/settings' component={Settings} /> </div> </Router> ) } } export default AppCopy the code
How do we know this is actually working and sharding our route? If you Create an App with the React official Create React App and run __npm run build__, you’ll see the App shard.
-
Each package is introduced into our application one by one
-
You’re at this point, you can relax with a dance
-
Remember when I said there were two levels of code sharding? The guide we had at hand
- Sharding at the routing level
- To form hierarchical shards
-
So far, we have only talked about code sharding at the routing level. That’s where a lot of people stop. Sharding code at the routing level, it’s like brushing your teeth, you brush them every day, most of your teeth are clean, but you still have cavities.
-
Instead of thinking about sharding with routes, you should think about sharding with components. If you have a lot of content in the shell, the shard will still download the shell code regardless of whether the shell is displayed.
-
From this point of view, it’s more a change in your brain than a new knowledge. Now that you know how to use dynamic import, you need to figure out which components to download when you need them.
-
I’d be dumb if I didn’t say React Loadable. It is a “high-level component that loads components through dynamic import”. The important thing is that it handles all the things we mentioned and makes it a nice API. It even dealt with a lot of marginal things, like we didn’t consider server-side rendering and error handling. Check it out if you want a simple, out-of-the-box solution.
Welcome to the DCG front end team. Resume please send to [email protected] [16 salary per year] [communication allowance] [transportation allowance] [Holiday benefits] [paid annual leave] [performance bonus] [regular medical examination] [birthday benefits]…