reitit 2024-02-26

Hello @ikitommi I am using Reitit frontend for my routing but I am seeing the build size goes very high when including reitit coercion which brings in the clojure analyzer.cljc file which is almost 200Kb size. Is there a way to minimise this or use Reitit without the coercion itself?

i was looking to similarly optimize my bundle size. after quite a bit of work i realized that it's kind of a fool's errand for a lot of applications. you cant depend on basically any javascript library; most of them are bloated with transient deps. you have to find ways to optimize your css with tailwinds-like stripping of unused classes, or some other strategy. you have to be particular about which parts of cljs core and other cljs you end up using. a lot of code has to be annotated for proper tree-shaking. then, after working really hard to cut a few kbs at every opportunity... one of the first xhr fetches to a backend service returns 500kb. all those gains in payload size and first render time are immediately lost. :x i think for most apps i've ever written it's a much better strategy to make my caching as efficient as possible, which is quite simple with something like shadowcljs and a static assets server

You can opt out of coercion

so if I opt out then I will also loose parsing of prams or body from routes, isn't it?

You should be able to hand-roll your own coercer that makes use of the same route data if that's a concernt

Not sure what is the dependency source for clojure analyzer.cljc

(def router
  (rf/router routes {:data {:coercion rcs/coercion}}))
@juhoteperi not sure, this line adds the analyzer to my build somehow. I removed the coercion and then analyzer got removed

@juhoteperi Here is the screenshot of my report file

aha you are using spec instead of malli

so what should we use then?

Depends on what you want. Due to Spec implementation spec-tools needs to use Cljs analyzer and there is probably no way around that.

I don't use spec anywhere except while defining the router which I showed above

Malli doesn't need the cljs analyzer but the Reitit coercion implementation isn't very optimized for frontend use, so I'm not sure if Malli coercion implementation is much lighter than Spec.

I'd say Malli is the preferred one, but examples are probably from time before Malli. And it depends a lot if you are already using one of the "schema libs" elsewhere.

So are you suggesting to use router without any coercion? how much will that affect my code. I haven't yet reached to very complex route definitions with query params and stuff, but will use then in later stage.

> I'd say Malli is the preferred one, but examples are probably from time before Malli. And it depends a lot if you are already using one of the "schema libs" elsewhere. I don't think I am using schema libs too.

I use Reitit frontend with Malli coercion

Query parameters could be inconvenient without coercion usually

ok. let me try with malli

I can add a new example quickly

sure, that would be helpful, but here is the gist of what I have done

...(:require [reitit.coercion.malli :as rcm])...

(def routes [....])

(def router
	(rf/router routes {:data {:coercion rcm/coercion}
										 ;:controllers [{:start (js/console.log "start" "root-controller")
											;							:stop (js/console.log "stop" "root controller")}]
										 }))

(rfe/start!
							 router/router
							 (fn [matched-route]
								 ;(swap! router/routes-state (fn [old-match] (if matched-route (assoc matched-route :controllers (rfc/apply-controllers (:controllers old-match) matched-route)))))
								 (when matched-route
									 (set-active-name! (-> matched-route :data :name))
									 (set-active-view! (-> matched-route :data :view))))
							 ;; set thi to "false" to enable HistoryAPI
							 {:use-fragment false})

Just to show the basic :parameters format for FE use

@juhoteperi thank you so much

Its still huge though, almost 167Kb

How can I remove swagger and other stuff not needed from here?

@juhoteperi Can you share me an implementation to use linear-router? I guess that would be the most optimised one.

I don't think I have that anywhere in use

But IIRC you could just call Reitit/linear-router instead of reitit/router. The reitit.frontend/router is just merging one option into the call, so you would need to add that yourself: https://github.com/metosin/reitit/blob/master/modules/reitit-frontend/src/reitit/frontend.cljs#L89-L95

so (rf/router routes {options}) -> (r/router-linear routes {:compile ... options})

But also the optimization isn't always working very predictably, there are cases when not calling a function doesn't remove all the code that function uses (like -get-apidocs and swagger code)

Ok, let me try to implement this and see if I face any issue.

We haven't had cases where the JS artifact size would have been super important, so optimizing Reitit-frontend size hasn't been a priority. If you are building a app for intranet like use, and you have just a few users it isn't super important. Or if you are using large JS libs.

I am using it for my internal users and it won't harm us to use large libraries but, I would very much want to use reitit on production apps too. I have spent quite deal of my time to setup multi level nesting with reitit for pages and subpages and would want to use this as a replacement for react router.

I think there should be a plan to improve reitit frontend to minimise the bundle size

{:name :routes.fms.events
									:comp-level 2
									:view ($ events/Events)
									:parent [:routes.fms]
									}
If you see my routes, have added comp-level for which level a components needs to be rendered and parent to show under which parent the component can be rendered. Removing all this will just be waste of time.