Tree shaking from remote import - reactjs

This question might seem unusual and a little anti-pattern but at this stage I am just trying to figure out what is possible.
The situation is that I have a few components which are available from a remote import (via Webpack 5's Module Federation). The caveat is that I don't want to lazily load them.
Once imported, the components are passed into a HOC to enrich some functionality and then exported:
import ComponentFooRemote from 'testRemote/Foo'
import ComponentBarRemote from 'testRemote/Bar'
const ComponentFoo = withEnrichedFunctionality(ComponentFooRemote)
const ComponentBar = withEnrichedFunctionality(ComponentBarRemote)
export {
ComponentFoo,
ComponentBar,
}
Functionally this works as expected. The components can be imported, rendered, and no components are loaded twice.
The issue is that the code imported from the remotes are always loaded. If I don't use ComponentBar - or even if I delete ComponentBar - the code from the remote will be downloaded as long as the original import is present. This is happening when using both development and production mode in the Webpack config.
Does anyone know if I can tree-shake these imports or restructure the code to better optimise the performance? Ideally I would like to import the components from the same path.

Related

Import svg as component using preact and vite

I'm currently working on an application using preact, tailwindcss and vite. Unfortunately, importing svgs seems to be a bit problematic.
There is a separate repository that only contains the svg resources.
My original plan was to just import them as components as I was used to do it in classic react with webpack applications using SVGR and the following syntax:
import { ReactComponent as Search } from 'assets/icons/search-lg.svg';
This won't work for (at least) two reasons. One being preact the other one being the lack of SVGR.
I then proceeded to give vite-plugin-svgr a try by importing it in my vite.config.js.
plugins: [svgr({ svgrOptions: { jsxRuntime: 'classic-preact' } }), preact(), tsconfigPaths()],
It kinda works using the following import syntax:
import Search from 'assets/icons/search-lg.svg?component';
Unfortunately this raises the following error which I couldn't manage to work around besides ignoring it which is not really an option:
TS2307: Cannot find module 'assets/icons/search-lg.svg?component' or its corresponding type declarations
The alternate plan would now be to create a wrapper component that just imports the svg from the given path and create a component from it myself. This would also simplify styling it with tailwind.
Unfortunately I fail to import the file in a way I can wrap it with <svg> tags. Using <img> is not really an option because I need to be able to manipulate the svg.
So, does anybody have an idea how to either
correctly use the component import in my setup
create a preact component that imports an svg from a file and still is customizable
Thanks in advance!
I finally got it to work by using vite-plugin-svgr. In the end adding /// <reference types="vite-plugin-svgr/client" /> to vite-env.d.ts made the compiler happy.
I can now use the good 'ol syntax:
import { ReactComponent as Search } from 'assets/icons/search-lg.svg';
Thanks to #user-28 for pointing me in the right direction!

ReactJS: Load .less only when component is used

I am probably missing a verry basic understanding of loading less files in ReactJS, but I am unable to solve the following issue.
I have created components and created less files for each of them, for example:
import * as React from 'react';
import Row from 'antd/lib/row';
import Col from 'antd/lib/col';
import { NavLink } from 'react-router-dom';
import '../styles/how-it-works-styles.less';
import Icon from 'antd/lib/icon';
class HowItWorksComponent extends React.PureComponent<Props> {
public constructor(props: Props) {
super(props);
}
public render() {
return (
<Row className={'steps-row'}>
Now when I load a page where this component is not at all used, the less file is stilling being loaded into the dom. Could someone explain why this is happening and how i can prevent this from happening?
Your problem is not really about less, but a general problem of how to bundle a web app optimally. Since you're not providing your main app component or your webpack config (assuming you're using webpack for bundling), I obviously don't know the details of your setup. In general however, the standard configuration is to bundle all the components and other imports reachable from the entry point file into one big file. The fact that you use react router or similar to split your app into "pages" doesn't change this, as react router only affects which components are rendered when, not the bundling.
If you need to split your app into multiple bundles (which IMO requires a relatively large app to consider) you can use dynamic imports to make some of your components "Loadable". This means they will be placed in a separate bundle which is only loaded as needed. There is a tutorial in the react router documentation on how to set this up: https://reacttraining.com/react-router/web/guides/code-splitting

How to import React from global variable?

I have a special scenario. One web application is built upon React. Another JavaScript utility is on React, but that utility is loaded by script tag. So, the application and the utility is built isolatedly. As a result, both the web application bundle file and utility bundle file have React built inside.
Now, we want to make them share one copy of React. It is preferred to export React as global variable in web application code(e.g. global.React) so that the utility can use it directly.
The code in utility is still like below. Hopefully, webpack can ignore it and import React from global.React.
import React from 'react';
import ReactDOM from 'react-dom';
The question is: how to config webpack to tell the utiltiy not to bundle React?
Tested with some non-React application:
in index.html import React from CDN, this will define global React.
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
somewhere in application doing as below, resolves to React instance:
declare var React;
console.log('React is ', React);
So if your first bundle, registers React globally, you may use it.
Just keep in mind, that it considered as bad practice to import from global like this. It beats whole concept of webpack's modularity. For instance, if your first module, upgrades to some backward incompatible version of React, your second module will break eventually as well.
For small projects, might be ok.

Can Vue import components by dependency inject(DI) like angular?

I used angularjs(1.x) and new to Vue.There has DI in angular and refer a service in controller or a directive in template is easy, wherever the service or directive is. Angular will help you inject it automatically.
But I realize Vue refer a component by import(ES6) the path where the component in.If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
I know there have a vue-injector like angular DI, but if it is easy to use?
Vue doesn't provide any formal dependency injection mechanism. It's completely up to you how you want to import dependencies.
Most Vue code samples do not use dependency injection at all and just use the ES6 module system via import and export.
If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
You probably shouldn't be changing the directory structure frequently. Check out the Vue webpack template for an example of how to correctly structure a Vue project.
That being said, it doesn't really answer your question. Are you using webpack (or something similar)? You can configure precisely how webpack should locate modules via the resolve configuration property, so that you don't have to use relative import paths.
Another way is to register each Vue component globally so you don't need to import them at all.
Another way is to abuse ES6 modules by creating, say, a components.js module which imports each Vue component (wherever they are) and exports them all from that one module. Now you only need to import from that central module instead of hunting down the specific module location of each thing you want to import.
// components.js
import Button from 'path/to/button.vue';
import Alert from 'path/to/alert.vue';
export {
Button,
Alert,
};
// myform.js
import { Button } from 'path/to/components.js';
If you change the location of button.vue, you only need to update the import from inside the components.js file.
If you think you are going to restructure a lot, you can have one file that everyone imports and it imports all the rest, that way you only need to change in one place.
I personally prefer the import over the Dependency Injection, way easier to understand where everything is coming from.

Feasible to adapt JSX 'import' to use http?

I'm currently brainstorming a http react component service (http://components.myCompany.com). It seems feasible, if the import directive could make an http request to grab a component. Then a consumer, likely compiling their JSX as a build step on the server side, could reference components coming from this service. E.g.
/**
* #jsx React.DOM
*/
module React from 'react';
import Timer from 'http://components.myCompany.com/Timer';
I've spent some time rooting into the react-tools nodejs module. Enough to realize that I can't quickly deduce exactly how the import statement transforms into actual component JavaScript inlined to the eventual JS file. So, asking for others who know the transformation process well enough to address the feasibility of this.
Thanks!
You can transform all your components in modules and publish them in GitHub (or in a HTTP server), and set them as dependency using URLs
On your package.json, you can set:
"dependencies": {
"Timer": "http://components.myCompany.com/Timer.tar.gz"
}
https://docs.npmjs.com/files/package.json#dependencies
Later, you can import it in React:
import Timer from 'Timer';

Resources