In node, in order to determine if I'm in the entry file, the file referenced directly by node, I can use __file__ === process.argv[1]. process.argv is an array containing the arguments I used to call the node script. Therefore, the first argv is usually node, the second is the file I'm using. This is useful if I'm creating a library and I want to know whether I'm calling the library directly, for example for testing. This would typically get minified out in a production build.
How can I do the same thing in a browser? Before ES6 modules, all JavaScript files were effectively global, meaning the <script> tags were all called directly by the HTML. However, now, with ES6 modules, it is possible to import another JavaScript file. How do I determine whether I am in a file directly sourced by the browser, with <script src="..."></script>, or whether I am in a file that was imported by another script?
This could be useful for react components, especially in libraries. If I am in the "entry" file, I can render myself in a tag that I choose, perhaps <main>. If not, I can assume I'm being imported by another component, which will presumably render itself or be rendered in some other way. The same concept applies to other component-based libraries, like angular 2+, aurelia, etc. For example,
import React from 'react';
import {render} from 'react-dom';
if(isEntryPoint()) {
const main = document.querySelector('main');
render(<App />, main);
}
function App() {
return <div>Hello World!<div>;
}
So how do I find out if a file is directly referenced by the browser via <script src="..."></script>, or if it was imported by another JavaScript file? How should the isEntryPoint function look?
I don't believe there's any reasonable way for a browser-hosted module to know that it's been loaded directly rather than by another module. The unreasonable way I can think to do it is to compare the last segment of the URL in import.meta.url with the script elements present in the DOM and seeing if one matches.
I don't recommend it.
In fact, even then, if for some reason I have
<script type="module" src="./myfile.js"></script>
<script type="module" src="./your-library.js"></script>
and myfile.js does:
import /* stuff */ from "./your-library.js";
That would make your library think it was loaded directly when it fact it was loaded via myfile.js.
So I really don't recommend it. ;-)
Instead, you're probably better off providing your library (your-library.js) with a setup call the code using it can use, and an optional additional module (auto-your-library.js or whatever) that A) loads your library, and B) does the setup call. (If you want to provide the automatic setup feature at all.)
Note: import.meta is new in ES2020.
Related
In my workplace I've been told by my supervisor to use autoloaders instead of using many import statements since it's slow! First of all, based on my research there are no performance benefits of doing so and secondly(I'm using js modules, and we are not talking about bundles), I can't think of any good reasons for doing such thing.
The way my imports work now are:
Imagine we import a.mjs like so ->
import '/js/components/a.mjs'
The route /js/ is a rule that redirects to my root of static js files, so I don't even need to use relative paths.
Is it bad practice? If there is something that I'm missing anything please tell me; I've done my research and didn't find any reasons to use autoloaders with js modules.
An actual example for you to see:
Flask route rule:
#app.route('/js/<path:filename>')
def js(filename):
return send_from_directory(Path(app.root_path).parent.absolute().joinpath("static\\js"), filename)
and in my js files:
import {View} from "/js/common.mjs";
import Spinner from "/js/components/spinner.mjs";
import RecommendedTripsSlider from "/js/components/recommended-trips-slider.mjs";
import TripsSlider from "/js/components/trips-slider.mjs";
import Blogs from "/js/components/blogs.mjs";
Thank you for your time. I appreciate it.
From the JavaScript Modules Guide on MDN :
Modules are only executed once, even if they have been referenced in multiple <script> tags.
This can be verified in the spec under section 16.2.1.5.2 Evaluate.
Evaluate creates and returns a Promise which resolves when the module has finished evaluating. This Promise is stored in the [[TopLevelCapability]] field of the [[CycleRoot]] for the component. Future invocations of Evaluate on any module in the component return the same Promise.
The first time the browser needs a module, it is hoisted, loaded, cached and executed. Additional import statements in future scripts will reference the pre-evaluated module from memory. No additional HTTP requests need to be made. No additional execution needs to happen.
On subsequent page views, the locally cached version of the module will be used, assuming your server's caching policy is configured appropriately, or you've implemented caching with a service-worker. In fact, you can even preload any specific modules that you might need ahead of time.
Performance-wise, as long as your app isn't importing hundreds of modules you should be fine. At that point you should probably look into bundling related components together with tree shaking. However, monolithic app bundles should be avoided as they obliterate the benefits gained from using modules since one small change to a single component means your users now have to re-fetch the entire app. Keeping your components split up means that user agents only download the parts needed to display the content on the page they're on and nothing more.
One reason you might want to use a module loader/bundler would be if you were installing dependencies through a package manager like npm. Then you would need a way to resolve the named imports to their actual file paths. But that process can easily be handled without a build step by using import-maps.
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"yourlib": "/lib/yourlib.js"
}
}
</script>
Unfortunately, this feature is not available in all browsers yet but. This already works in Chromium based browsers, has recently been implemented in Firefox (slated for release in v102) and support for other browsers can be polyfilled with es-module-shims.
This is the future of native module resolution. Implementing an autoloader into your project now would be a step backwards.
I've to write few methods that will be used throughout my React Applications.
One way is to create a utility file and put all methods inside the file. And wherever the methods needed import the file and call the methods.
In this way I've to import it everywhere.
Another way is to bind the methods in window object. Which I don't think is a good way.
I was searching for some webpack plugins. But haven't found anything useful. It would be great if someone can point me in right direction. All I want is to just import a file full of method once, and use these methods anywhere without the need to importing the file again and again.
I'm trying to figure the most efficient way to structure modules in our website that contains a lot of JavaScript namespaced modules in a single file (app.js) already.
Currently things look a bit like this:
app.js
OURAPP.mapsModule = (function() {
return ...
})();
...
OURAPP.searchModule = (function() {
return ...
})();
..and these are all contained within a single file. So, when we want to use any of these modules we:
search-page.js
...
OURAPP.searchModule.search(query);
...
search.html
...
<script src="js/main.js">
<script src="js/search-page.js">
...
We do have a lot of such modules. However, I'm wondering whether we should be doing something with import/export modules:
Method #2
searchModule.js
export default {
...
}
search-page.js
import searchModule from "./js/searchModule";
...
searchModule.search(query);
...
I'm guessing that a single file namespaced modules will be faster for the page to load(?) as it's only a single file to download. But when I'm looking at testing frameworks (e.g. Jest) they give many examples where module script files are loaded in this manner. Does it lend itself better to testing I'm wondering? Personally, I prefer this structure anyway but it's a bold change of direction from how the site has been built up until now and I'll need to good reason to suggest this. This website's is pretty much all generated server-side with JavaScript just doing the show/hide, query APIs, etc, with the addition on scrips and JavaScript libraries such as jQuery, Bootstrap, Isotope when required on each page.
I've read around but can't find anything comparing exactly both methods here and reasons for and against either. Would appreciate any suggestions or helpful advice, thanks.
Don't default-export objects, use named exports instead:
// searchModule.js
export function search(…) {
…
}
// search-page.js
import * as searchModule from "./js/searchModule";
…
searchModule.search(query);
…
I'm guessing that a single file namespaced modules will be faster for the page to load as it's only a single file to download.
Yes, that's true. However, do not let that affect your decision on how to structure your modules. You should always modularise your code so that it is the cleanest possible. Put the maps stuff in a different module than the search stuff.
You will then use a bundler or packer tool to create a single minified JS file to download that contains all the modules that the respective page needs.
In our previous node.js application(based on ES5), we struggled for having relative paths in require statements.
for eg: var conf = require('../../global/config');
The issue with the relative paths was bubbled up in code obfuscation. We had to spent considerable time in handling this issue.
With that experience, in our current node.js application(based on ES6), though I'm not sure whether we will obfuscate the code or not I don't want to use relative paths. Hence, I've created a const ROOTDIR in every file and assigned the project's absolute path to it. Then realized that import expects only static literals in it's fromClause. This makes life difficult by forcing to type(copy/paste) the full path for every import or go back using relative paths OR use require statement.
While I'm trying to understand the benefits of using import style, I need some help in understanding the following 3 points.
What are the advantages of using import apart from similar coding style of java/.net?
Why import is accepting only string literals? It seems it's by design, if so, why?
If I want to import a module based on some condition, I'll have to go for require because I can't use import in if/else or switch statements. Is there any solution/hack for it?
Appreciate all the help.
First, remember that import is not currently supported by any environment, including Node.js. So you will probably be using a transpiler, such as Babel, which will assume a CommonJS environment (such as Node) and will fill in using require.
import, unlike require is designed to be statically analysed. That is to say, your code could be analysed without running it and it would be possible to tell what modules need to be loaded.
This means that:
import declarations are hoisted. Wherever they are declared, they effectively exist at the very top of the file.
Putting them in blocks is therefore invalid.
Putting them in conditionals is therefore invalid.
Nothing can exist in the file before the import statement, so it is not possible to use any variables (including constants) because they will not exist when the import statement is run.
Because the code can be statically analysed, this will in the future ease the process of bundling code. It can be determined exactly what code needs to be included, which will mean that your code could be compiled for distribution more easily. For instance, if a browser requested a JS file that had an import statement, the server could see this and package both together. This is a long way off, but it is likely to be the future!
The main difference between import and require is that with import, you have static dependencies which are resolved by the browser when it parses your code, while with require, you have dynamic dependencies which are resolved when Node.js runs your code.
The advantage of static imports is that the code is easier for the JavaScript engine to optimize. webpack for example uses it to remove code from imported modules that are not needed to create a smaller bundle (known as tree shaking).
The downside of this is, as you have noted, that you cannot use a dynamic expression after "from", since this can only be resolved at runtime. So this is indeed by design.
There is no "hack" around it, if you must have a dynamic dependency that is resolved at runtime, you need to use "require".
I followed the documentation to put the constants in the lib/constants.js file.
Question:
How to access these constants in my client side html and js files?
Variables in Meteor are file-scoped.
Normally a var myVar would go in the global Node context, however in Meteor it stays enclosed in the file (which makes it really useful to write more transparent code). What happens is that Meteor will wrap all files in an IIFE, scoping the variables in that function and thus effectively in the file.
To define a global variable, simply remove the var/let/const keyword and Meteor will take care to export it. You have to create functions through the same mechanism (myFunc = function myFunc() {} or myFunc = () => {}). This export will either be client-side if the code is in the client directory, or server-side if it is in the server directory, or both if it is in some other not-so-special directories.
Don't forget to follow these rules:
HTML template files are always loaded before everything else
Files beginning with main. are loaded last
Files inside any lib/ directory are loaded next
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
Now you may run into an issue server-side if you try to access this global variable immediately, but Meteor hasn't yet instantiated it because it hasn't run over the file defining the variable. So you have to fight with files and folder names, or maybe try to trick Meteor.startup() (good luck with that). This means less readable, fragile location-dependant code. One of your colleague moves a file and your application breaks.
Or maybe you just don't want to have to go back to the documentation each time you add a file to run a five-step process to know where to place this file and how to name it.
There is two solutions to this problem as of Meteor 1.3:
1. ES6 modules
Meteor 1.3 (currently in beta) allows you to use modules in your application by using the modules package (meteor add modules or api.use('modules')).
Modules go a long way, here is a simple example taken directly from the link above:
File: a.js (loaded first with traditional load order rules):
import {bThing} from './b.js';
// bThing is now usable here
File: b.js (loaded second with traditional load order rules):
export const bThing = 'my constant';
Meteor 1.3 will take care of loading the b.js file before a.js since it's been explicitly told so.
2. Packages
The last option to declare global variables is to create a package.
meteor create --package global_constants
Each variable declared without the var keyword is exported to the whole package. It means that you can create your variables in their own files, finely grain the load order with api.addFiles, control if they should go to the client, the server, or both. It also allows you to api.use these variables in other packages.
This means clear, reusable code. Do you want to add a constant? Either do it in one of the already created file or create one and api.addFiles it.
You can read more about package management in the doc.
Here's a quote from "Structuring your application":
This [using packages] is the ultimate in code separation, modularity, and reusability. If you put the code for each feature in a separate package, the code for one feature won't be able to access the code for the other feature except through exports, making every dependency explicit. This also allows for the easiest independent testing of features. You can also publish the packages and use them in multiple apps with meteor add.
It's amazing to combine the two approaches with Meteor 1.3. Modules are way easier and lighter to write than packages since using them is one export line and as many imports as needed rather than the whole package creation procedure, but not as dumb-error-proof (forgot to write the import line at top of file) as packages.
A good bet would be to use modules first, then switch to a package as soon as they're tiring to write or if an error happened because of it (miswritten the import, ...).
Just make sure to avoid relying on traditional load order if you're doing anything bigger than a POC.
You will need to make them global variables in order for other files to see them.
JavaScript
/lib/constants.js
THE_ANSWER = 42; // note the lack of var
/client/some-other-file.js
console.log(THE_ANSWER);
CoffeeScript
/lib/constants.coffee
#THE_ANSWER = 42
/client/some-other-file.coffee
console.log THE_ANSWER