How do you use GRPC-Web on the browser?
I mean, in pure browser code, without any NodeJS involved.
Official example from here: https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld are mainly NodeJS oriented.
Is there way to use GRPC-Web in pure Javascript form without:
const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');
Meaning, just standard <script>-way of adding Javascript dependencies?
And be able to do: var client = new GreeterClient('http://localhost:8080');
Use grpc-web, not #grpc/grpc-js npm package.
The example provided in Write Client Code uses #grpc/grpc-js, which only works on NodeJS.
To use your protobufs and gRPC services defined in your .proto files, you need to generate your code using grpc-web. Then, import those generated files and use them.
Some things I learnt along the way:
You can't really use protobufjs with gprc-web in browsers, you need to use grpc-web and google-protobuf. If someone has an alternative solution, let me know please.
The generated code in grpc-web is not very nice - it is difficult to read and doesn't have all the features of protobufs. You need to have a lot of Javascript and bundling experience to use it.
grpc-web is more limited than gRPC. NodeJS runs gRPC, but in the browser, you have to use grpc-web, and must run a gRPC proxy to translate gRPC-web into/from gRPC.
Yes. You need to bundle your sources with webpack. This step is also described in the documentation you mentioned. At the bottom of the readme:
Just config your webpack to expose variable:
client.js
...
export function instantiateGreeterClient(...) {
return new GreeterClient(...)
};
webpack.config.js
module.exports = {
...
entry: './path/to/client.js',
output: {
path: './bundle/js/',
filename: 'grpc.js',
library: {
name: 'grpc',
type: 'umd',
},
...
}
And after that import your bundle as usual. Now you be able to use all defined variables in your script tag code as
<script src="path/to/grpc.js"></script>
<script>
const client = grpc.instantiateGreeterClient(...)
...
</script>
More information can be found in webpack documentation
Related
I’m building an application that uses Vue for our front-end, specifically Vue 3 Composition API + Typescript. I need the ability to use web workers for long running processes in the background. I was able to get a vanillajs web worker to run alongside the Vue application no problem. My issue is that I need my web worker to have access to classes and functions that are written inside the Vue app. Both my web worker and my Vue app need to use the same classes and functions and I don’t want to have to write them in two places. Is there a way to share them? Any help is appreciated.
The answer is yes:
There is a way to let the web worker having access to the Vue.js framework through one single JavaScript file.
The library that will help you with this in your Vue.js project is vue-worker. This library uses the simple-web-worker package. (Inline Web Workers approach)
As you may know, a standard web worker requires an extra js file that will contain the code of the worker. For inline web workers it isn't necessary to use a helper method. That will inject a property into Vue (and pass it to every child component), with a default name of $worker. In total you can say it is behaving as an JavaScript promise but without really being a second JavaScript file.
Now your turn:
There is a really good documentation to follow along with this approach I described and additional a second "old-fashion" way. Try it out :)
I was able to figure out a solution using esbuild. I added a process to my npm run serve and npm run build commands so it runs node ./esbuild.js before the vue-cli commands. This compiles my typescript web worker to javascript and puts it in the public folder before compiling the Vue app. Now I can create a web worker like normal using:
const worker = new Worker('/worker.js', { type: 'module' })
And here is my esbuild.js file:
const esbuild = require('esbuild')
const { nodeExternalsPlugin } = require('esbuild-node-externals')
const config = {
entryPoints: ['./src/workers/worker.ts'],
outfile: 'public/worker.js',
bundle: true,
minify: false,
plugins: [nodeExternalsPlugin()]
}
esbuild.build(config).catch(() => process.exit(1))
I am using vanilla javascript as modules and directly loading them in the browser like this
<script type="module" src="app.js"></script>
obviously the app.js uses imported modules from other files as well.
What I see in the browser [latest(March 2020) chrome & firefox] dev tools is waterfall of network requests to fetch all the nested modules.
Some of the modules are referenced in the import but they are used only when a user performs a specific action.
Is downloading all modules with import reference the standard spec by browsers and it is going to stay this way? I suspect www specs are always evolving and I am sure there are smarter folks out there handling it, so they must have thought and solved this issue way before me reaching this point. right?
Is there a workaround to tell the browser to fetch modules only when the app actually needs them and no need to pull a deluge of modules at the first load?
I am using pure vanilla JS without any bundlers, webpack, babel etc.
Sure, there is an import() function, and it doesn't even need to be called from a module script.
const script_url = URL.createObjectURL(new Blob([
`export const foo = "hello, world";`
], { type: 'application/javascript' }));
document.getElementById('btn').onclick = async(evt) => {
const mymodule = await import(script_url);
console.log(mymodule.foo);
};
<button id="btn">fetch my module</button>
Sharing modules between Node JS and standard web application.
Hi,
I have a library of classes which I want to use both in a NodeJS and a simple Web page. To that purpose, I created module to export my functions (mode.export ...) and I can call it in my NodeJS code (var func = require("module.js"). However, when I want to use the same module in a Web javascript with the syntax:
<script type="module">
import { Function1 } from './libmain.js';
</script>
I have an error: Import not found (Function1).
Is this something doable or client and server side can't have commonalities?
Many thanks - Christian
Try
exports.Function1 = function() {
return "something";
};
you can use it in the same way as node var func = require("module.js"),
but you need "compile the code" you can use browserify, it's similar to how to use node modules in HTML
see Hello World for JavaScript with npm modules in the browser
I want to use a custom require() function in my application.
Namely I have node's standard require() and a custom one I wrote to require files starting from the root called rootRequire() which internally all it does is:
// rootRequire.js
var path = require('path');
var rootPath = __dirname;
global.rootRequire = function (modulePath) {
var filepath = path.join(rootPath, modulePath);
return require(filepath);
};
module.exports = rootRequire;
But even though rootRequire() internally uses node's require(), it does not pick up any files required through that method
Example:
require('rootRequire.js');
rootRequire('/A.js'); // server side it works, in the browser I get an error saying can't find module A.js
Perhaps, this will answer your question on why it is not working on browser.
Quoting the owner's comment: "Browserify can only analyze static requires. It is not in the scope of browserify to handle dynamic requires".
Technically, you are trying to load files dynamically from a variable with your custom require. Obviously, node.js can handle that.
In case of browserify, all requires are statically loaded and packaged when you run the CLI to build the bundle.
HTH.
What i recommend doing here is this:
First create a bundle to be required of anything you might use in rootRequire().
Something like browserify -r CURRENT_PATH/A.js -r CURRENT_PATH/B.js > rootRequirePackages.js
Then, in your webpage, you can include both rootRequirePackages.js and your regular browserified file.
Perhaps just use RequireJS ? It's simple to set up and relatively easy to use.
I'd like to use the musicjson.js package that helps to convert musicXML files into json notation, looking if it's a good way to import for example exported musicXML Finale scores into a browser playing with the Fermata/VexFlow class.
https://github.com/saebekassebil/musicjson
The thing is that this module works with require (calling for
nodes packages like fs) and I'm just a newbee in requirejs...Even if I spent few time in understanding the tutorial in the website, I don't still get how to solve this kind of basic problem when the dependencies of my musicjson.js need to be called like :
var xmldom = require('flat-xmldom'),
fs = require('fs'),
path = require('path'),
util = require('util');
My index.php page does the classic require call:
<!DOCTYPE html>
<head>
<!-- javascript head -->
<!-- REQUIRE -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js"></script>
</head>
<body>
</body>
</html>
In my scripts/main.js, I'd like to do simply what it is told from musicjon :
var music = require('musicjson');
music.musicJSON(xml, function(err, json) {
// Do something with the MusicJSON data
});
I putted also, in the same directory scripts/, the flat-xmldom folder, fs.js, path.js, util.js
When I do this, I've just obtain this classic error of :
*Error: Module name "musicjson" has not been loaded yet for context: _. Use require([])*
...That looks like a common error referenced in the requirejs website,
but if I try things that I guess it should be written, I get a bit lost to determine where is the fundamental conceptual mistake here :
require.config({
baseUrl: '/scripts/',
paths: {
flatxmldom:'./flat-xmldom/__package__',
fs: 'fs',
path:'path',
util:'util',
musicjson: 'musicjson'
}
});
require(['flatxmldom','fs','path','util','musicjson'],function(flatxmldom,fs,path,util,musicjson){})
Error returned in this case for example :
*Module name "fs" has not been loaded yet for context: _. Use require([])*
Thanks a lot for your attention.
So, this is not a RequireJS problem per-se. The package you want to use is a Node.js package. It is intended to run in node (a server/desktop execution environment for JavaScript). It cannot/will not run in web page in a browser.
The packages it is trying to use (fs in particular) provide access to system resources such as the file system. Node provides these packages as part of its core libraries to any package that runs in node. A browser is specifically designed for security reasons never to allow direct access to such resources to any code that run in the browser because who knows where it came from or might try to do.
I haven't really tried to do this myself, but browserify (alternative to requirejs) claims that it will allow you to use any node package in your application.
If musicjson is at the heart of what your app should achieve and requirejs is a small step on the way to getting there, you could try your luck with browserify instead.