maxence-charriere / go-app

A package to build progressive web apps with Go programming language and WebAssembly.
https://go-app.dev
MIT License
7.78k stars 357 forks source link

Generated wasm too large #534

Closed iarkaroy closed 1 month ago

iarkaroy commented 3 years ago

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

oderwat commented 2 years ago

BTW: My main testing app still does not compile with TinyGo (after getting rid of the os.SetEnv()) because of /libexec/src/encoding/xml/typeinfo.go:114:29: f.Index undefined (type *reflect.StructField has no field or method Index) so yeah, more to do. But I "solved" the problem with the {{.Env }} in my latest PR #680 I believe. Maybe I am going to use a much smaller example later. But using TinyGo can not mean that we can't use a majority of modules.

mar1n3r0 commented 2 years ago

{{.Env }} is unchanged in my test so not taken from your template. I have no idea where the xml error comes from maybe send a full trace of the running code or a repo with it.

mlctrez commented 2 years ago

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

oderwat commented 2 years ago

@mlctrez yep this is exactly the one I had in mind

maxence-charriere commented 2 years ago

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.

maxence-charriere commented 2 years ago

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.

mar1n3r0 commented 2 years ago

@maxence-charriere Please keep the Tinygo issue open as it is quite close to being completed.

maxence-charriere commented 2 years ago

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

oderwat commented 2 years ago

If the compile times are longer but the result is (much) smaller it may just be good enough to use it for deployment. I also experienced quite long compile times while working on WASM "plugins" with TinyGo (and I am not sure if I wanna trust it to create stable code).

mar1n3r0 commented 2 years ago

@oderwat same here but I don't consider 1 min compile time to be a show stopper if the benefit is 50% + reduced binary size. To be honest I also expected 10 times reduction so a bit disappointed.

suntong commented 11 months ago

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.

g4bwy commented 10 months ago

Hi @mar1n3r0,

I'm reviving this thread a little bit, since you mention tinygo is working, but I'm still having a link-time issue with time.modTimer using tinygo 0.28.1:

tinygo:wasm-ld: error: /tmp/tinygo3209131542/main.o: undefined symbol: time.modTimer
failed to run tool: wasm-ld
error: failed to link /tmp/tinygo3209131542/main: exit status 1

how exactly did you manage to get past this one ?

cheers,

mar1n3r0 commented 10 months ago

Hi @mar1n3r0,

I'm reviving this thread a little bit, since you mention tinygo is working, but I'm still having a link-time issue with time.modTimer using tinygo 0.28.1:

tinygo:wasm-ld: error: /tmp/tinygo3209131542/main.o: undefined symbol: time.modTimer
failed to run tool: wasm-ld
error: failed to link /tmp/tinygo3209131542/main: exit status 1

how exactly did you manage to get past this one ?

cheers,

Sadly not there yet, see: https://github.com/maxence-charriere/go-app/pull/682

g4bwy commented 10 months ago

well that's too bad :cry:

in the meantime, did you try brotli or zstd compression instead of gzip ?

mar1n3r0 commented 10 months ago

Nope, gzip only so far.

maxence-charriere commented 1 month ago

That cannot really be handled on go-app side. A good practice is to separate the client code (wasm) and the server (part that use app.Handler).