salesforce / lwc

⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
https://lwc.dev
Other
1.63k stars 395 forks source link

Server rendering flag #912

Open priandsf opened 5 years ago

priandsf commented 5 years ago

SSR Add a runtime flag so a component can query if it is rendering on the server or within a browser. import { isServer } from 'lwc'; It should also provide a Node variable at compilation time (process.env.RUNTIME). Both are not mutually exclusive, as we can think of precompiled libraries using the runtime flag instead of the compilation time variable.

caridy commented 5 years ago

I don't think this is possible, it has too many issues. Components will have branching logic, which is undesired. Client runtime will be penalized by such branching logic as well.

I think we should attempt to find ways to get most of the components to work on the server side out of the box, I will say that should cover 95% of the cases, then the remaining 5% can still do some branching logic, but not as a first class citizen export in lwc.

priandsf commented 5 years ago

I agree that we should make the components seamlessly working in both context, as much as possible. Now, when you Google for this capability in React, you'll find that many people are asking for a solution. So they all come with their home made solution (checking the browser global variables, React classes...). Vue provides $isserver, or Stencil isServer, to avoid using these tricks. Anyway, this is not a blocker per say.

caridy commented 5 years ago

Oh, I have no problem with user-land abstractions, you could do import { isServer } from 'foo';, I just don't think lwc is the right place to export that value because we don't know/care about client vs server yet.

tedconn commented 5 years ago

@priandsf do you have a use case for needing to know at runtime from a component level if you are in a server-rendered environment? I have looked online and I can't seem to find any interesting use cases other than some people doing some tricks for testing.

I feel that we should compel users to write universal code instead of allowing them to branch.

priandsf commented 5 years ago

Agree on the Universal JS push. Now, one use case has to do with pre-rendering: you only want to fetch() the actual data from the browser, and use empty data on the server side to display the static pieces while sizing the dynamic ones (img, ads... similar to AMP), to avoid resizing and flickering.

tedconn commented 5 years ago

@priandsf if users are using @wire for all data requests then those data dependencies can be statically analyzed and we can provide that data on the server -- if they aren't then I would argue their component does not qualify for being server-side rendered and good practices dictate they should use proper placeholders.

priandsf commented 5 years ago

@tedconn there are other use cases, like if your component uses a third party library that is not isomorphic. I know that leaflet, for example, was (still is?) one of them. In that case, you want to prevent calling any of its functions while running on the server. I'm sure that developers will come with their own use cases. And, at the end, this is just a facility we could provide.

caridy commented 5 years ago

there are two cases about 3 party libs:

  1. a library can be evaluated on the server side (independently on whether or not it can't be really used there).
  2. a library cannot be evaluated on the server side.

Since we don't support conditional imports at the moment, I don't think 2 is going to be possible, unless that the app or the comp marked them somehow to auto mock them or something else.

As for 1, it means that you can import it, but only use it in a callback of some sort, you will not be able to use it on the critical path during the rendering process unless it can fully function on the server side.

pmdartus commented 5 years ago

Now, one use case has to do with pre-rendering: you only want to fetch() the actual data from the browser, and use empty data on the server side to display the static pieces while sizing the dynamic ones (img, ads... similar to AMP), to avoid resizing and flickering.

IMO there are 2 invariants that can play in our favor with server-side rendering:

Because of those 2 invariants, I would recommend moving the logic that would need to run only on the client in the renderedCallback.


Since leaflet (and friend like d3, ...) are 3rd party libraries and code that is meant to run in the browser, I feel that they should be treated differently than the rest of the LWC modules. Those modules are good candidates for lazy loading (via a standard script or a dynamic import), since those modules are not required for SSR nor are part of the application critical path.