Migrating from requirejs to es6 imports - javascript

I have a javascript app that uses requireJS to load its components. This is simple example of how it works:
require(['eventHandler'], function(eventHandler)) {
... // app code that uses the eventHandler in some way
}
It uses several components, but let's simplify it and pretend its only this one.
I have two different "versions" of my miniapp in 2 parts of the bigger web application we develop in my company. In order to improve reusability I came up with the following idea:
I have 2 files of require configuration that define the location of the modules the app needs to run and I can define the set of components it should use for each case.
Standard config:
require.config({
paths : {
// components
eventHandler : 'lib/eventHandlers/standardEventHandler',
...
});
Especial config:
require.config({
paths : {
// components
eventHandler : 'lib/eventHandlers/specialEventHandler',
...
});
When I load the app I do the following:
<script data-main="#standardConfigFile" src="/lib/require/require.js"
type="text/javascript" charset="UTF-8"></script>
And this is how I can load different components for each configuration. Currently I have 2 different ones but I expect to increase that number soon.
Now we are switching to es6 modules with native import and export and I wonder how can I migrate my current architecture to native js imports and exports and the documentation does not cover this particular problem.
Any ideas?

I found the answer, it's actually really simple. I only need to make an "importer" module.
In my main component I'll do:
import * as components from '#standardComponentsLoader.js';
And then do all the imports in standardComponentsLoader:
import * as eventHandler from '#standardEventHandler.js';
export { eventHandler };

Related

Tree-shakeable strategy pattern in Typescript

I have been trying to build a library that requires its consumers to use a specific strategy per target. My current architecture follows:
[Application] -> contains -> [player] -> contains -> [renderer]
Currently, Renderer is an interface which needs to be replaced for different platforms:
Mobile -> uses MobileRenderer
Web -> uses WebRenderer
I have the freedom to use any bundlers - currently using Webpack for the app and Rollup for the lib - and what I could achieve follows:
My lib exports a Player interface and a createPlayer function which returns a PlayerInterface. Then, on the application side I have a Webpack alias that resolves the correct platform library based on a build input.
e.g:
import { createPlayer } from "my-lib";
const player = createPlayer()
Then we build the application with
npm run build --platform=web
to which webpack will transform the my-lib import into my-lib/platforms/web, which also contains an exported createPlayer function which uses the correct renderer.
My question is the, from the application's point of view, how can we make sure that we import the correct renderer per platform on build time while allowing tree-shaking (so only including the correct sources)? I find that using the build system to do that is quite obscure, as it doesn't leave a clear trace of what's going on.
Is there a better way of doing this?
Best,
You have a couple options. I'd recommend against having a compile-time switch, since that requires you to distribute multiple copies of your library (which isn't idiomatic). However, if you really wanted to do this, I'd suggest using webpack's ProvidePlugin or resolve.alias config field to dynamically link your code to the appropriate renderer at build time.
A more pragmatic approach, in my opinion, would be to have two entry points to your application, and allow the implementor to choose which entry point to use. This is similar to how react-dom switches between browser rendering and server rendering (i.e., react-dom versus react-dom/server):
// index.js
export * from './shared';
export {default as renderer} from './renderers/web';
// mobile/index.js
export * from '../shared';
export {default as renderer} from '../renderers/mobile';
Then, someone using your library could import {renderer, foo} from 'your-lib' or import {renderer, foo} from 'your-lib/mobile'.
Both of the above approaches work at build time.
While the latter approach normally forces you to choose which version of the library to use in your code, you can use webpack's resolve.alias configuration field to force imports to your-lib be redirected to your-lib/mobile.

ES6 Modules - 3 ways of importing files

Hello guys i have a little question about importing files into a single .js file.
Which way is better (best practice), what's the scenario that is used for:
import './file;'
import { something } from './file'
import * as evertything from './file'
Because i see that 2 and 3 are the same thing but different syntax(maybe Syntactic Sugar).
All three do different things.
import './file;'
That loads the file, and does not import anything. This is useful if you want to initialize that module (or add some external dependency, e.g. a css file if you use Webpack).
import { something } from './file'
That just imports something from the file, therefore a bundler could optimize all other dependencies away. I'd always try to go with that instead of
import * as evertything from './file'
That imports everything from that module under a namespace, and therefore makes treeshaking more difficult (the bundler cannot optimize it well). I'd only use that if you need everything from that dependency, or if that dependency is loaded externally nevertheless (e.g. import * as React from "react").
I guess the following MDN documentation will make you clear about those things:
import - JavaScript|MDN
As far as I know, 1st method is used when you have only one default export. 2nd is used when you have multiple default exports but you don't want all of them to load and want only few of them. 3rd is the case when you want everything under a single object (which can be used similar to namespace in other programming languages).

Using Vue without bundlers?

I am creating some experimental web sites that are using JavaScript without bundling. For dependency management I have used RequireJS until now, but I have started to use SystemJS recently since it has some very nice support for HTTP2. I have done some experiments so far on my custom set web server and results are great for the web sites I am creating. For example, first page render happens around 400ms, full page load at 800ms etc.
I am doing this because I want to take full advantage of HTTP2 and I want to lazy load only scripts that I am using at a certain moment. Code like that is easier to maintain, it’s cached better etc. At the moment there is a total craze about bundlers like Webpack, but that is not something I want to use.
Here is the question: is there a way to compile single Vue file / components by using Gulp and then load them with SystemJS as AMD or CommonJS modules?
edit:
This is what I want to achieve with SystemJS and Vue:
entry point JS file:
// SystemJS config
SystemJS.config({
/* ... */
baseURL: './',
map: {
// App
'app': 'scripts/app.min.js',
// Utils
'axios': 'scripts/vendor/axios/axios.min.js',
'modernizr': 'scripts/vendor/modernizr/modernizr.min.js',
// Framework
'vue': 'scripts/vendor/vue/vue.min.js',
// Components
'vueHelloWorld': 'scripts/components/hello/vueHelloWorld.js', // <- Compiled VUE component
'vueMenu': 'scripts/components/menu/vueMenu.js'
},
depcache: {
'vueHelloWorld': ['vue'],
'vueMenu': ['vue', 'vueHelloWorld']
}
/* ... */
)};
// Initially Load default scripts
require(['modernizr', 'axios', 'app']);
Vue components vueHelloWorld.js and vueMenu.js are end result, compiled into pure JS from single file templates vueHelloWorld.vue and vueMenu.vue.
After that initial file, app.min.js is loaded and it will have declarations to load rendered vue components.
This is what I don't know how to do - how to render separate files for each Vue component that I want to load in this manner?
If I understand the question correctly, all you are looking for is something that takes a single *.vue file and returns a single compiled *.js file. You could either try and write your own thing using https://github.com/vuejs/vue-component-compiler, or what I ended up doing is misusing rollup as my vue compiler, configuring it, so that it ignores all dependencies and therefore takes one vue component in and only compiles that one component. Here you can see the config that achieves that: https://github.com/ecosia/bazel_rules_nodejs_contrib/blob/master/internal/vue_component/rollup.config.js
It seems that Async components and webpacks code splitting are what you are looking for.
Here you find an article about using them:
https://vuejsdevelopers.com/2017/07/03/vue-js-code-splitting-webpack/
No, because single Vue file (*.vue) can only be recognized by vue-loader through webpack, SystemJS or AMD or CommonJS are totally unrelated to it, these three are modularity standards or ways to make your javascipts files organized.
Also u can write in this way.
a.component.js
var ComponentA = {
template: '#view-a',
}
b.component.js
var ComponentB = {
template: '#view-b',
}
then in your html file
<script type="text/x-template" id="view-a">
<div> .... </div>
</script>
<script type="text/x-template" id="view-b">
<div> .... </div>
</script>

How to asynchronously import components in VueJS by full URL

I need to import a Vue component asynchronously by the full URL opposed to relative URL. The following example taken from the VueJS documentation works just fine for components within the same project
Vue.component(
'app-component-one',
() => import('./component-from-app-one')
)
However, my goal is to import components from a separate project that's deployed on the same server. I was hoping I could use the full URL and do something like...
Vue.component(
'app-component-two',
() => import ('http://sample-domain.com/project-2/components/component-from-app-two.vue')
)
but it results in and error:
This dependency was not found:
* http://sample-domain.com/app-2/components/component-from-app-two.vue in ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0&bustCache!./src/components/SampleComponent.vue
Is importing components by full URL possible? Thanks in advance!
The example you referenced from the Vue website is leveraging the code-splitting functionality from WebPack, so it will NOT work to load files that are not part of the same project.
What you are trying to do is typically called "dynamic/asynchronous ES6 module loading". Not to get too deep in to it.. but the current import blah from X only support static imports. If you care more about the nitty-gritty details of this.. there is a TC39 proposal for dynamic imports in JS
In the mean time... us mortals have to depend on tools like SystemJS which will do exactly what you are asking for.
But like #divine mentioned... you need some type of build-process that generates the stand-alone Vue component. You can use either WebPack or RollUp to export it as a UMD and the use SystemJS to import that component by referencing the full URL (you could even import it from a different domain! assuming that domain supports CORS)

using angular 2 CLI, how can I use absolute paths so I don't have to use import { .. } from '../../../shared/thing'

I created my project using angular 2 CLI. However I am wondering how I can stop using the crazy imports like
import { SomeSharedComponent } from '../../../shared/some-shared-component';
I am using what the angular cli generated for me. So, is it possible to use something like
import { SomeComponent } from 'app/shared/components/some-component'
Thanks
The best approach is to use TypeScript v2.0 or newer (still in beta). The reason is that it gives you the ability to use path mappings.
This would allow you to define a path map named app-shared and then use that to point to the desired shared component: app-shared/some-shared-component
I had the same problem and resolve it by using a symbolic link referencing the shared folder. You should also add it to system-config.js.
import { SomeSharedComponent } from './shared/';
with the following declaration in system-config.js.
const barrels: string[] = [
(...)
// App specific barrels.
'app',
'app/shared',
/** #cli-barrel */
];
I would be interested by a cleaner solution.
Edit:
You should also include an index.ts in the shared folder with the following content.
export * from './some-shared-component';
Then you can use the import statement without the name of the component.
import SomeSharedComponent from ''../../../shared/'
However, it still requires the relative path part.
I have made a Plunker to clarify the usage from the Angular2 hero tutorial. See in particular the following files.
app/relative/hero-relative.component.ts
app/shared/my-shared.component.ts
app/hero-search.component.ts
You should have a look at this issue about the Angular2 style guide. This question could also be interesting.
I think for a complete solution (without relative path) we will have to wait until webpack module manager is adopted by Angular CLI as stated in the GitHub issue. A webpack preview is already available.

Categories

Resources