Load JS modules conditionally (SystemJS) - javascript

I am currently using jspm and SystemJS to load ES6 modules. However, I would like to be able to
Scan the page for certain selectors (e.g., id, data-plugin)
Map these selectors to their module dependencies
Load only those modules
My thought was to handle these imports through the single entry point, System.import('src/main'), that has access to the document. I could then find the relevant selectors, map those selectors to modules, and then import those modules.
src/main would look something like this:
['d3', 'jquery'].forEach(function(dependency) {
import dependency;
});
This is not a viable solution as it is invalid syntax. Is there a better way to achieve dynamic module loading in this sense?

Normal import syntax can't be used to conditionally load modules, as you've already seen. To get around this, we can use the programmatic API provided by System. You're already familiar with this API, since you use it to System.import('src/main');.
To conditionally load modules, instead of using the import keyword, you just have to continue using the System.import method.
An example of this:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
</head>
<body>
<div id='one'>One</div>
<div>Two</div>
<script>
System.import('main');
</script>
</body>
</html>
main.js
const map = {
'#one': 'one',
'#two': 'two'
};
for (let selector in map) {
if (document.querySelector(selector)) {
System.import(map[selector]);
}
}
one.js
window.HAS_ONE = true;
two.js
window.HAS_TWO = true;
In this example, window.HAS_ONE would be defined, but window.HAS_TWO would remain undefined.

Related

Javascript library without using an import in the script tag

I try to create a library using typescript and I would like to use a static method that allows me to render some HTML content that is returned to me by an api rest call based on the parameters I pass to that function now there is a way to create it using a architecture eg pattern repository, compile it and then use it as in the code below (in js format of course)?
<div id="test"></div>
<script scr="../myLybrary.js"></script>
<script type="module">
MyLibrary.render({
param: "test",
param1: "test",
param2: "test"
}, "test")
</script>
at the moment to create this situation I have to use an import in the script where the call to the static method resides in this way:
import MyLibrary from './myLybrary.js'
is there any way to avoid this last step?
Thanks to anyone who wants to help me.
at the moment to create this situation I have to use an import in the script where the call to the static method resides in this way:
import MyLibrary from './myLybrary.js'
That would generally be the right thing to do. You'd just remove the first script tag, since with it you'll end up loading your library twice. So that leaves us with:
<div id="test"></div>
<!-- Note: No `script` tag for `myLybrary.js` here. -->
<script type="module">
import MyLibrary from "./myLybrary.js";
MyLibrary.render({
param: "test",
param1: "test",
param2: "test"
}, "test");
</script>
Loading a module runs the top-level code in the module, so if myLybrary.js has top-level code that loads some resource, that code will be run when the import declaration is processed.

Getting 'ReferenceError: not defined' when using import/export modules

I've got and exportUserList.js, importUserList.js and the main.js files. The main.js file contains already defined 2 variables which I want to log to the console and I also want to log the imported user variable from the main.js file. But I keep on receiving 'user not defined'. I've used the suggested window option, but it is not passing on the variable. Can this be corrected?
this is the html:
<html lang="en">
<head>
<meta charset="utf-8" />
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" />
<title></title>
<style>
</style>
<head>
</head>
<body>
<script type="module" src="/importUserList.js"></script>
<script type="text/javascript" src="/main.js"></script>
</body>
this is the export exportUserList.js :
export const user = 'first user'
this is the import importUserList.js :
import {user} from './exportList.js'
console.log(user)
window.user = user
and this is the main.js:
let names = 'mark'
let numbers = 10
console.log(names)
console.log(numbers)
console.log(user)
Option 1: defer your main script, because modules defer by default, and so because your main script doesn't it gets runs before the module has.
However, that's the bad option, and you shouldn't put that into practice because that keeps you using modules in completely the wrong way. Never do that.
The much better solution is to make your main.js a module and then tell it to import what it needs in order to run. You don't load modules only to bind their data onto globalThis (window in this case), the whole point of modules is to keep code contained, so that whatever needs a module's exports, can just import that as needed. You use modules to not pollute the global scope =)
So: remove that importUserList.js and instead put the import in your main script:
import {user} from './exportList.js'
let names = 'mark'
let numbers = 10
console.log(names)
console.log(numbers)
console.log(user)
And then load your main script as a module with:
<script type="module" src="/main.js"></script>
And then of course remember that modules always load deferred.
Basically: if you're using modules, use modules "all the way down".

Is it possible to remove inactive code with Webpack?

I am importing lodash into my project, but the main script doesn't use more than 5 lodash functions, I don't want to fill my project with unused/inactive code, is there a way to check the whole project structure and delete the inactive code on bundle/on build?
Example:
index.html
<body>
<script src="lodash.js"></script>
<script src="app.js"></script>
</body>
lodash.js
function a() {
console.log('Hi')
}
function b() {
console.log('I am not used anywhere')
}
app.js
...
document.getElementById('main').addEventListener('click', a)
...
Function b() is not used in the project.
Question:
How to get rid of inactive code function b() automatically?
What you want is called Tree Shaking, a pattern that decides which code is dead code and should not be included in the bundle. One of the requirements to use this with Webpack is to use ES Modules. If you're using already webpack to bundle app.js, you should import to import lodash inside your project, instead of adding it as a dependency in your html file.
Keep in mind that this will only work if lodash is using ES Modules, if that's not the case there's always lodash-es

import a library into a single file component of vue.js

I need to import a library in my vue component, in the documentation I explain how to install it using npm (already do this step) but not how to import it into a vue component, this is the way in which it explains how to use the files:
<link href="node_modules/webdatarocks/webdatarocks.min.css" rel="stylesheet"/>
<script src="node_modules/webdatarocks/webdatarocks.toolbar.min.js"></script>
<script src="node_modules/webdatarocks/webdatarocks.js"></script>
and this is the way to instantiate the library:
<script>
var pivot = new WebDataRocks({
container: "#wdr-component",
toolbar: true,
report: {
dataSource: {
filename: "https://cdn.webdatarocks.com/data/data.csv"
}
}
});
</script>
So what is the best way to call this in my component?
This is a bit heavy.
The library is is not develop in module-like system, so the solution is make the js file imported as global.
A good library would be like const WebDataRocks = require("WebDataRocks"); or with imports, but the library is made only for end-client.
First Part - Add the JS file to the global web client
To use WebDataRocks you have to get the global variable, to get the global variable you have to inyect, as common javascript on html but with webpack.
Here are a solution for this
Webpack - How to load non module scripts into global scope | window
You have to do this for webdatarocks.toolbar.min.js and webdatarocks.js
Second Part - Add the CSS
You have some options, the easy way i found to do this is use require in your <script> zone:
require('../node_modules/webdatarocks/webdatarocks.js')
Good luck! 😁
If something fails check the paths and let us know more information about it
Alternative solution (But worse)
If you are going to use this script in a internet system, you could insert the script and CSS in the HTML. For this do:
Open index.html
Add this code on the head section:
<link href="https://cdn.webdatarocks.com/latest/webdatarocks.min.css" rel="stylesheet"/>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.toolbar.min.js"></script>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.js"></script>
Rebuild
Extracted from WebDataRocks React Example
Important! this is unsafe ☣ ⚠
Make this only if you are sure about what this mean
If the webdatarocks CDN's fails, your app will also fails.
Hope it helps :)
I did this and it works:
import WebDataRocks from 'webdatarocks'
import '#/../node_modules/webdatarocks/webdatarocks.min.css' // # is resolved to src/ folder
I didn't import the toolbar as I don't need it:
WebDataRocks({
container: '#pivot',
toolbar: false,
...
})

How can I define a Webpack "global module" to hold my Knockout viewmodel?

I'm working on moving some legacy code into Webpack (to help me control some dependancy hell that I'm having. All's going well so far. The issue comes from the existing codes use of Knockout. I need a way to access the view model in various components. So I need something to hold this view model. This question seems to provide me a good solution:
Put your variables in a module.
Webpack evaluates modules only once, so your instance remains global
and carries changes through from module to module. So if you create
something like a globals.js and export an object of all your globals
then you can import './globals' and read/write to these globals.
I can't really figure out how to do this though. I'm pretty new to webpack/import/export statements so I'm not up to date on the fundamentals. I want a "Module". Great what does webpack have to say on this:
What is a webpack Module
In contrast to Node.js modules, webpack modules can express their
dependencies in a variety of ways
What? Really, that's it?! So I'm struggling to come to terms with what a module is and how I should use one?
Up till now I've defined exported functions and imported them (are these modules??!). So I would do something like this:
export default function koModule(){
var viewModel = {}
function setViewModel(vm){
viewModel = vm;
}
function getViewModel(){
return viewModel;
}
return {
setViewModel:setViewModel,
getViewModel : getViewModel
}
}
I'm think I can then kinda use this when I create my initial viewmodel:
import koModule from './koModule.js'
...
//obviously wrong....
var myKoModule = koModule();
myKoModule.setViewModel(vm);
...
But that's obviously wrong as the myKoModule will be instantiated every time I call the function... and any module trying to do read it is just going to get a blank object:
import koModule from './koModule.js'
...
//obviously wrong....
var myKoModule = koModule();
var vm = myKoModule.getViewModel();
//vm is undefined...
In the previous question it states "Webpack evaluates modules only once". So I'm obviously missing what a module is and how I should be using them then.
So given my requirements, can someone provide an example of a working Webpack "Module" and it's usage in holding,reading and writing a global variable while still allowing me to import it?
I'm obviously missing something fundamental here but I can't really figure out what it is.
This is about as close I can get for you without knowing exactly how you want to use your models and what not. This is often how I use viewModels in webpack, they are essentially just constructor functions with built in methods that I can call on when needed.
Main.js
import ko from 'knockout'
import koModule from './koModule.js'
const model = new koModule('Budhead2004 was here', 'More Stuff here');
ko.applyBindings(model);
KoModule.js
import ko from 'knockout'
// This is your viewModel
class koModule {
constructor(r,t) {
this.Test1 = ko.observable(t);
this.Something = ko.observable(r);
this.Click1 = function() {
alert('test')
}
}
}
export default koModule
HTML
<!-- language: html -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<h1 data-bind="text: Something"></h1>
<input type="text" data-bind="textInput: Test1" />
<span data-bind="text: 'Results of Test1: ' + (Test1() ? Test1() : '')"></span>
<br>
<button data-bind="click: Click1">Click Me</button>
<script src="main.js"></script>
</body>
</html>
Working example here

Categories