To make my aurelia app a proper PWA, that I have created using aurelia cli, I need to register a service worker.
There is already a similar unanswered question or few discussions and forum posts on the web available regarding the subject matter but I couldn't find anything concrete to help me get started.
I am thinking of three possible ways to handle this
option[1] - use index.html page and register a service worer inside a script tag outside the scope of aurelia app.
// service worker in index.html file
<!DOCTYPE html>
<html>
<head>
</head>
<body aurelia-app="main">
<script type="text/javascript">
... my service worker code here ...
</script>
</body>
</html>
option[2] - inside aurelia main.js/main.ts
// inside aurelia main.js/main.ts file
export function configure(aurelia) {
... rest of the conf ...
registerServiceWorker();
return aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app')));
}
option [3] - app.js file
// inside app.js file constructor
export class App {
constructor() {
this.registerServiceWorker();
}
What would be the proper/right way to handle this task? As I am using webpack, would it need more configuration to make the service worker play nice with webpack and aurelia?
If using WorkBox is the only way forward, I can live with that too but to keep things simple I would prefer to use vanilla service workers directly in aurelia.
This is how I made it to work.
1- insatll workbox plugin
yarn add workbox-webpack-plugin
2- configure webpack
//webpack.config.js
// add plugin somewhere in the top section
const workboxPlugin = require('workbox-webpack-plugin');
//inside plugins section
plugins: [
// somewhere in the bottom is OK
new workboxPlugin.GenerateSW({
swDest: 'sw.js', // name of your service worker file
clientsClaim: true,
skipWaiting: true
}),
3- put your sw.js file in static dir. It will be moved to the dist folder on build.
4- if it is a complete PWA, add manifest.json file too in the static dir. you can get a sample manifest.json file from google developr site.
5- add service worker init script into the index file
// index.html or index.ejs file
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) { return registration.update(); })
.catch(error => console.log(error));
}
</script>
run and hopefully you should get the serveiceworker installed and running.
P.S. is this the proper way to do it? I have no idea, but for now it is working for me.
Related
here's my situation. I just started learning about node.js server with express and I find it to be an amazing technology, based on JavaScript that I am already a bit familiar with.
Now, so far, whenever I want to change something on the page, I have to do it with JS on the server-side with node.js, and thus, I have to refresh the page with new information. But in several cases this seems to be not the best way to handle things, like changing simple text contents or click events. I would like to use browser-side JS for that reason, but I have not found out a way to do that yet.
I tried to call js-files and import them, but I couldn't make it work.
<script src="../../public/js/index.js"></script>
I also put the index.js and functional.js in the public folder, which I have made available to node.js at all times, but the imported JS still cannot be found when the project is run on the server.
app.use(express.static('public'));
app.use(express.static(path.join(__dirname, '/public')));
The strange thing is, I have been looking all over the internet on trying to find an explanation to it for several days already, but I couldn't find anything that made it clear to me on how to use browser-JS with a node.js server.
My folder structure is this:
functional.js
exports.functional = () => {
alert("Do something...");
}
index.js
const { functional } = require('./functional');
document.addEventListener('DOMContentLoaded', () => {
const init = (() => {
functional();
})();
});
So now I have the following questions:
Can browser-side JS even be used with a node.js server?
If it can be used, how do I implement those js-files, so they actually work?
Every help is greatly appreciated!
Thanks :)
Static Folder
You're defining a static folder in Express, here.
app.use(express.static('public'));
This tells Express that every static file (css, js, images, .etc) are found in the folder public. Now Express handles this smart and now knows that when you're looking for a static file, it is in that folder you've specified.
<script src="js/index.js"></script>
This will look into the folder public for the folder js and the file index.js. public/js/index.js.
Modules
The script itself needs some modification. Node.js uses a module system called CommonJS. And the syntax works works like in your file exports.functional, require('./functional'). But that is a technology that only works on the Node.js environment. Not in the browser.
JavaScript does also have a native module system, which is also available in Node.js, which works with the import and export keywords.
export const functional = () => {
alert("Do something...");
};
import { functional } from './functional' ;
document.addEventListener('DOMContentLoaded', () => {
functional();
});
But wait
By using the native module system on the client side, you'll need to load all the scripts that have exported and imported values in them, otherwise you'd be missing pieces.
<script type="module" src="js/functional.js"></script>
<script type="module" src="js/index.js"></script>
Bundler
The mainstream use of import and export modules is in combination with a module bundler like Webpack, which combines all the modules into a single file, which you can serve to the browser. This is very useful for development to keep the files you work in small.
It can transform the following files from this:
public
-- dist <-- bundled files ready for distribution
-- src <-- raw source files to work with
---- js
------ index.js
------ functional.js
to this:
public
-- dist <-- bundled files ready for distribution
---- js
------ bundle.js <-- Single file ready for the browser
-- src <-- raw source files to work with
---- js
------ index.js
------ functional.js
The bundler program also works on the Node.js environment, just like Express.js, so you can include it in your project.
Alternative
If the bundlers seem like a huge hassle, then you could always choose to classically use a single file to serve your JavaScript in.
on what you did:
<script src="../../public/js/index.js"></script
you are trying to call index.js relative to your node file (correct me if i'm wrong)
its a mistake because this is what express.static does
just try:
<script src="js/index.js"></script>
beacuse you already told node to take all files from that location
so instead calling files relative to your node file you need to call it from public folder
hope i helped you
I want to bundle a vue app with the styles and everything into a single UMD javascript module using vue-cli-service so that I can import it into another Vue app via my component distribution server. I am able to do this with one component on the serve, but I don't know how I'll be able to bundle an entire app and load it remotely into a separate app. I use this article as a guide https://markus.oberlehner.net/blog/distributed-vue-applications-loading-components-via-http/
This is where I am importing it:
{
path: '/games',
component: GamesHome,
children: [
{
path: 'fun',
component: () =>
externalComponent(
'http://localhost:8200/game/Game.cd590421a6d6835e7ae2.umd.min.js'
),
name: 'Fun Game'
}
] }
So basically how do I create a Vue app then bundle it entirely with CSS and all using vue-cli-service
This is the problem which I have been trying to solve from day 1 ever since I started using VueJS. I will not consider a client side JS framework if it does not provide a solution for this problem.
I recently did a PoC in this and able to consume a VueJS application as module in another VueJS application. In my case I have a suite of VueJs applications where each of these applications is running in its own dedicated docker container. These applications have a lot of functionality which is common across all the applications. So I decided to move this common code (page layout, css frameworks etc) to a separate VueJS application and consume all existing VueJS applications as modules in this global application. I call this micro-app based architecture to differentiate it from micro-frontends based architecture because it does not use multiple client side JS frameworks and does not require another framework to achieve this. This is how the deployment architecture looks like in my case (you can ignore kubernetes specific stuff if your are not aware about it) -
Coming back to implementation part, you need to take a step wise approach to convert a VueJS application to a micro-app.
Lets say you project structure look as following (it shows only few files which require changes and NOT all the files) -
app-1
public
index.html
src
main.js
App.vue
router
index.js
store
index.js
Split your vuex state and routes files into global and application specific files -
app-1
public
index.html
src
main.js
App.vue
router
app1
index.js
index.js
store
app1
index.js
index.js
Make a copy of this project (global-app), remove global-app specific files from app-1 and app-1 from specific files from global-app. Also remove index.html and App.vue from app-1 project -
Add ROUTES and STORE_MODULES variables to index.html file of global-app -
<head>
....
....
<script type="text/javascript">
const ROUTES = []
const STORE_MODULES = {}
</script>
</head>
<body>
....
....
<div id="app"></div>
<script type="text/javascript" src="/app1/micro-app.umd.min.js"></script>
<!-- built files will be auto injected -->
</body>
Update router\index.js file of global-app for ROUTES variable -
const routes = [
....
....
]
routes.push(...ROUTES)
const router = new VueRouter({
Update store\index.js file of global-app for STORE_MODULES variable -
export default new Vuex.Store({
....
....
modules: STORE_MODULES
})
Clear content of app-1\src\main.js file and replace it with following content -
import routes from '#/router/app1'
import app1 from '#/store/app1'
ROUTES.push(...routes)
STORE_MODULES['app1'] = app1
Define build-app command under scripts block of package.json file of app-1 -
....
"scripts": {
"build-app": "vue-cli-service build --target lib --formats umd-min --no-clean --name micro-app src/main.js"
},
....
Now build and deploy these two applications in their dedicated containers and update nginx conf file of proxy container to forward requests to these containers as following -
location / {
proxy_pass http://global-app:80;
}
location /app1/ {
proxy_pass http://app1:80/;
}
You can access global app by using IP address and port of nginx container.
I hope I have included all the steps which are required to implement micro-app based architecture. You can refer following git repositories which were created as part of this PoC -
https://github.com/mechcloud/large-app-docker
https://github.com/mechcloud/large-app
https://github.com/mechcloud/large-app-plugin1
While I am not an expert in the internals of client side JS frameworks, I believe this approach will work for other JS frameworks (Angular, React etc) as well.
i had build react app web page
with custom environment variables
the problem is when i build the script
and change the .env variables no thing change in the website !
.env file :
REACT_APP_SITENAME=TheSiteName App
After building a react app all code is static and can't be changed. I think the only solution to send some dynamic data to your react app is to either create a special file per system you running your app on and load this directly inside the index.html or create the content of this file on the fly.
So when you're using create-react-app in public/index.html add something like this:
<head>
...
<script src="https://www.example.com/envconfig.js" type="text/javascript">
</script>
...
</head>
This file should contain the environmental config, e.g.:
window.globalConfig = {
siteName: "The Sitename App"
}
The file can also be created on the fly by PHP, Java or any other backend service. Just make sure to answer as valid Javascript.
And in your React app at index.js, App.js or anywhere you can access this variable as it is global:
const appConfig = window.globalConfig || { siteName: process.env.REACT_APP_SITENAME}
With this you've got an fallback to the static env config if globalConfig is not available, likely in development.
Now you can use appConfig in any way and provide it via redux, context or property to your components.
Last thing to mention, you have to make sure that the config-file is loaded before executing all the React code. So the file should be loaded before all the created React files.
Quote from the docs:
The environment variables are embedded during the build time.
It isn't possible to apply environment changes as runtime.
Reference
Here is an example of how to use the environment at runtime:
CodeSandbox
here is an idea:
add a json file (e.a. config.json) with your configuration to the "public" folder. That file will be in the root of the build:
{
"name": "value" }
in your React code, create a static class with the variable you want to configure:
class Config {
static name= "[default value overwritten by the config]"; }
somewhere high in the startup of your React application, read the json and set the static variable:
fetch("config.json") .then((r) => r.json()) .then((data) =>{
Config.name=data.name; })
now you can use that config anywhere you need it :
Config.name
Note that any configuration you make will be vulnerable for public eyes, since the file can be opened directly with a URL. Also note that when deleting that json file, everything will still work with the default value. You could implement some check that the file must exist.
What options are there to bundle an external javascript sdk into a React Component?
I have tried including the javascript in the index.html and referring to it through window.xyz . It works well but I can't do a production build since the javascript is not packaged in this way.
Is there a way to simply import a javascript file into the React Component definition?
PS: React Newbie here !
If you want the script to be bundled in the build, you have 2 options:
1. If the external file is a module, I would approach it as follows:
Download the external JS file and save it somewhere in the project. For example, save it to /utils folder.
Just reference it and use it in the components: import { test } from '/utils/external'
2. If it's not a module:
The same as above - save the file to your project.
The difference is that you have to configure your module bundler to export the globals. This process is called Shimming and here's how to do it with Webpack.
The same as step 2 - import { test } from '/utils/external'
* In both scenarios I assume it's a standalone external file, not being hosted somewhere as a package (npm / bower / etc.). If it's a package, instead of downloading it manually, you should use a package manager.
If you want to load it async (but not bundled):
Follow the #Paras answer, where he suggests for using a library for script async lazy loading.
To load external scripts from a CDN, a good option is to use the react-async-script-loader library. Not only can it load external JS files asynchronously, but it also loads on demand, i.e., lazy loading and supports both parallel and sequential loading.
It allows you to decorate your component using an HOC like so:
export default scriptLoader(
[
'https://cdnjs.cloudflare.com/somelibrary1.min.js',
'https://cdnjs.cloudflare.com/somelibrary2.min.js'
]
)(YourComponent)
Actually you should know about the entire approach then see the codes.
You must make a separate folder for your alternative cdn JavaScript files which they are out of files that webpack build them. Then paste these files into this folder and after all import them as externals into webpack configuration.
Then config them as vendor files, and absolutely output file name should make dynamically, so the webpack build its bundle and then copy your JavaScript files into dist folder. follow below:
// webpack.configuration.js
~~~
module.exports = {
~~~
externals: {
cdnFileOne: `${srcRoot}/outFiles/cdnFile1.js`,
cdnFileTwo: `${srcRoot}/outFiles/cdnFile2.js`,
},
~~~
};
Sounds good, now you have external names for JavaScript files and import it into webpack configuration as a externals config.
Now you should put them in entry to import them as separate files:
// webpack.configuration.js
~~~
module.exports = {
~~~
entry: {
cdnFiles: ['cdnFileOne', 'cdnFileTwo'], <-- cdn files
app: `${srcRoot}/app/index.js`, // <-- its your own codes
},
output: {
path: '/dist',
filename: '[name].js' // <== dynamically make your JavaScript files,
// so, in dist folder you can see app.js and
// cdnFiles.js file
}
~~~
};
Surly, you must add bundles to your HTML template:
~~~
res.status(200).send(`
<!doctype html>
<html lang="en">
<head>
<meta charSet="utf-8" />
${styles}
${title}
</head>
<body>
<div id="root">${ssrHTML}</div>
<script src="app.js" defer></script>
<script src="cdnFiles.js" defer></script>
</body>
</html>
`);
~~~
try something like this:
componentDidMount () {
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/somelibrary1.min.js";
script.async = true;
document.body.appendChild(script2);
}
I am new to Angular2 and have a bit of confusion with the node.js and the angular2 framework functioning and relationship.
I can run my app with the lite-server on localhost, but my problem is uploading the app to the hosting service.
There are not any tutorials or guides of what to do when the app is ready, so I have been trying to make a bundle with webpack, but I am not successful.
I know it is a BAD practice to upload all node_modules installed by npm but am I correct trying to make such bundle?
Another clarification would be if my app can run my app just by uploading the html, css and js files (including those in the node_modules)? or do I need to configure a host that allows Node.js to run my application?
In Angular2 if you use Typescript you need transpile the webapp, this transpile put the files in /dist folder.
If you use ES6, you use the app in the root folder of you develop.
I you open the "index.html" in your browser of you /dist folder, the app in angular2 work.
In the index.html you have this code
System.import('system-config.js').then(function () {
System.import('main');
}).catch(console.error.bind(console));
In your main.js of the /dist you have this code
var _1 = require('./app/');
In this folder require you have this (for example)
var ng_fire_component_1 = require('./ng-fire.component');
this require call to your principal component of the webbapp... In this logic your app run with only open the index.html when ng-fire.component is your root component.
In node you only need create a web-server, this webserver (if use express js ) you need call the index.html
router.get('/', function(req, res){
res.sendfile('yourAPPfolder/index.html');
});
and your webApp its run again when you open the www.yourweb.com/ or localhost:yourPort/
For the last question, if use the server, you have import the folder /dist in this folder you have all file who need.
I recomend the angular ci (https://cli.angular.io) for work with angular2 ... if you need other vendor file or vendor folder you can add in the file angular-cli-build.js
for example:
/* global require, module */
var Angular2App = require('angular-cli/lib/broccoli/angular2-app');
module.exports = function(defaults) {
return new Angular2App(defaults, {
vendorNpmFiles: [
'systemjs/dist/system-polyfills.js',
'systemjs/dist/system.src.js',
'zone.js/dist/*.js',
'es6-shim/es6-shim.js',
'reflect-metadata/*.js',
'rxjs/**/*.js',
'#angular/**/*.js'
]
});
};