go-app: Generated wasm too large

Thank you for your contribution. This looks pretty awesome to work with. I just started playing around with it.

I started with minimal example:

package main

import (
	"log"
	"net/http"

	"github.com/maxence-charriere/go-app/v8/pkg/app"
)

type home struct {
	app.Compo
}

func (h *home) Render() app.UI {
	return app.H1().Text("Hello World!")
}

func main() {
	app.Route("/", &home{})
	app.RunWhenOnBrowser()
	http.Handle("/", &app.Handler{
		Name:        "Hello",
		Description: "An Hello World! example",
	})

	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

Generated wasm with:

GOARCH=wasm GOOS=js go build -o web/app.wasm

But the generated wasm is a whooping 11.5 MB. Any suggestion to reduce the file size?

Go version: 1.16.4

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 65 (32 by maintainers)

Most upvoted comments

@mar1n3r0 i don’t give up. I got that a lot of you folks want this to happen.

I also made a try for tinygo support. Here is the code: https://github.com/maxence-charriere/go-app/pull/682

I was able to compile but look like there is still some issues to figure out.

note that this is pure hacking and is far from being stable.

I found something else but can not dig up the link right now.

Here’s a lighter weight http client for wasm : https://github.com/marwan-at-work/wasm-fetch

Some important links / milestones, to save time the next person trying to read through the whole thread –

To get the sizes down I made two targets for the HTTP server and one is a stub. In addition, I experimenting in using the chi router and with that, I have a compression middleware for the wasm. This brings the size to 1.3 MB (from >16 MB) and lets me add the backend code using chi. I also experiment with “live reloading” (through a javascript HEAD checking and using reflex for recompilation). This is a bit rough about the edges but I am actually pretty happy with how everything works so far. You may want to look at https://github.com/oderwat/go-guess-the-number-app (this is not meant to be public for real, but I thought I want to share this as example and maybe get comments)

Here are the results and the example code based on the hello example: https://github.com/mar1n3r0/hello-wasm

  1. Original size - 12 MB After multi-stage and omitting net/http in wasm
  2. Go compiler - 5 MB
  3. TinyGo compiler - 1.8 MB
  4. TinyGo + gzipped - 574KB

I also made a try for tinygo support. Here is the code: #682

I was able to compile but look like there is still some issues to figure out.

note that this is pure hacking and is far from being stable.

We might not be able to achieve the best in one shot, IMHO, even things half done is better than the current situation, as my Original app.wasm size is 14 MB, gziped size is 3.2M.

I already have a wasm module loader in my go-app tests which can load additional wasm modules generated by the original or the TinyGo compiler. This is why I know that 😃

A common pattern is to have a server configurable from environment variables.

Things like api endpoint can be configured and be automatically relayed to a client.

I don’t think that “setenv” has anything to do with the error (besides that I already wondered what “setenv” in WASM may even mean). If you compile with TinyGo you also need to use the TinyGo WASM framework variant. You can not just use this WASM with the WASM of the standard Go compiler.

SetEnv has nothing to do with WASM but it has to be implemented in TinyGo in order to be used.

I don’t think that “setenv” has anything to do with the error (besides that I already wondered what “setenv” in WASM may even mean). If you compile with TinyGo you also need to use the TinyGo WASM framework variant. You can not just use this WASM with the WASM of the standard Go compiler.

Thanks, that worked like a charm. Here are the results and the example code based on the hello example: https://github.com/mar1n3r0/hello-wasm

  1. Original size - 12 MB After multi-stage and omitting net/http in wasm
  2. Go compiler - 5 MB
  3. TinyGo compiler - 1.8 MB
  4. TinyGo + gzipped - 574KB

Still the reduction from TinyGo is not on par with what vecty has achieved but at least we are under the 1 MB threshold. For comparison hellovecty.wasm with TinyGo + gzip is 5 times smaller sitting at 97K. An error occurred of course after removing the env variables but it shows what would be possible once SetEnv is included in the next TinyGo release.

Screenshot from 2022-01-10 15-53-27

main.go

func main() {
	// Frontend routing
	app.RouteWithRegexp("/.*", &appControl{})

	// this concludes the part which goes into the front-end
	app.RunWhenOnBrowser()

	// this will depend on the target (wasm or not wasm) and
	// it starts the servers if it is not the wasm target.
	AppServer()
}

wasmstub.go

// Our empty version of the httpServer for usage with the wasm target
// this way we will not include any of the related code
//go:build wasm

package main

func AppServer() {
}

server.go

// Our empty version of the httpServer for usage with the wasm target
// this way we will not include any of the related code
//go:build !wasm

package main

import (
....
)

func AppServer() {
	// the actuall server code
......

@mar1n3r0 you simply build two times. One for the WASM not containing the server and one for the server itself (which simply uses net/http).

And btw. gRPC uses HTTP/2. I would rather use something like https://github.com/twitchtv/twirp

That is as a wasm server, not an HTTP client library to use in the frontend? The Server is fine in go-app as it is just the server binary where size matters less.

Yes. Still it will be around 3 MB. I will experiment with tinygo to see if that helps.