Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Server-Side Rendering (SSR) #306

Merged
merged 14 commits into from
Mar 22, 2018
Merged

Add Server-Side Rendering (SSR) #306

merged 14 commits into from
Mar 22, 2018

Conversation

zaaack
Copy link
Contributor

@zaaack zaaack commented Mar 4, 2018

SSR from fable-react!

This PR is actually working, however, it is still waiting fable-react been published, and elmish-react's withReactHydrate to be merged and published. Currently, it's using paket's GitHub dependences feature.

What's more, here are some breaking changes to add SSR:

  1. Urls are path not hash anymore, which means we would create more routes for each page. Also, click on a direct links would cause a browser refresh which not in the old hash urls, I added a helper function to execute Elmish.Browser.Navigation.Navigation.newUrl directly to avoid it, but not sure it's right. @MangelMaxime

  2. Add jwt to cookies to make SSR support rendering login state, but I didn't remove the jwt in localStorage, this might cause problems...

  3. Because the Server project is now depending on the Client project's files, edit view files would trigger the server to rebuild which takes too long. Perhaps we can optimize the development process, e.g. adding a fake target which only builds Server project when files in the Server folder changes?

@zaaack
Copy link
Contributor Author

zaaack commented Mar 4, 2018

Looks I forgot the ui test, I'll fix it tommorow or several days later...

@MangelMaxime
Copy link
Contributor

Seems like the helper is needed, other users mention it. This is because of how browser work, the solution used by react, is to create a custom "link" element.

Idea being, the helper is included by default in the custom "link" so usage is transparent.

@forki
Copy link
Member

forki commented Mar 6, 2018

can you please change it to the nuget package?

@zaaack
Copy link
Contributor Author

zaaack commented Mar 6, 2018

@forki Just planning to do it. 😉

@zaaack zaaack changed the title [WIP] Add Server-Side Rendering (SSR) Add Server-Side Rendering (SSR) Mar 6, 2018
@zaaack
Copy link
Contributor Author

zaaack commented Mar 6, 2018

@forki Updated to nuget packages.

I also tried some configuration in dotnet-watch to ignore client files in develop running, but have no luck:

<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <IsWatch Condition="'$(Configuration)' != 'DebugSSR'">false</IsWatch>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="../Client/ReleaseNotes.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/Pages.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/Style.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/views/Menu.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/pages/Home.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/pages/WishList.fs" Watch="$(IsWatch)" />
    <Compile Include="../Client/pages/Login.fs" />
    <Compile Include="../Client/Shared.fs" Watch="$(IsWatch)" />
  </ItemGroup>
</Project>

Edit: set to false directly will work

<ItemGroup>
    <Compile Include="../Client/ReleaseNotes.fs" Watch="false" />
</ItemGroup>

@@ -216,6 +217,30 @@ Target "Run" (fun _ ->
)


Target "RunSSR" (fun _ ->
runDotnet clientPath "restore"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this? what is it doing?

Copy link
Contributor Author

@zaaack zaaack Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will set a compile directive DEBUG_SSR to use webpack-dev-server's bundle file path in the server-rendered path. Ideally, in RunSSR we can trigger client and server to rebuild when we edit any file, so we can dev and debug with SSR; but in normal Run we would only trigger the server to rebuild when we edit back-end files, and we can dev with webpack-dev-server and use the server code as an API service.

But I cannot pass the configuration to dotnet-watch to make it work differently in different command, currently, I totally ignored client files' change when watch run the server...

https://github.com/SAFE-Stack/SAFE-BookStore/pull/306/files#diff-694c6c636cd70bcc23aa716434744995R12
https://github.com/SAFE-Stack/SAFE-BookStore/pull/306/files#diff-3b3c3fd8d4441c71716037ce14cb42b0R14

@forki
Copy link
Member

forki commented Mar 7, 2018

can you please rebase/merge with master

@zaaack
Copy link
Contributor Author

zaaack commented Mar 8, 2018

@forki Done.

nuget Fable.Elmish.Debugger
nuget Fable.Elmish.React
nuget Fable.Elmish.HMR
nuget Fable.React 3.0.0-alpha-002
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alfonsogarciacaro any timeline on when these things get released?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Fable.React 3 stablish version? At the end we're not going to touch more the API so I guess we can do it now (this PR is pending though). I'll have to make some minor but potentially breaking changes for Fable 2, but we can bump another major version then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the PR you linked, I think a new nuget should be created.

Bumping the major version for Fable 2, is ok no need to hold the prerelease for it I think.

@forki
Copy link
Member

forki commented Mar 19, 2018

nuget Fable.Elmish 2.0.0-beta-2
nuget Fable.Elmish.React 2.0.0-beta-2
nuget Fable.Elmish.Browser 2.0.0-beta-1
nuget Fable.Elmish.Debugger 2.0.0-beta-1
nuget Fable.Elmish.HMR 2.0.0-beta-1

are these things released properly?

@MangelMaxime @et1975 any chance for a release?

@forki
Copy link
Member

forki commented Mar 19, 2018

@zaaack what's that package-lock.json file?

@zaaack
Copy link
Contributor Author

zaaack commented Mar 19, 2018

@forki I'm afraid not, these prerelease versions are added to using this PR: elmish/react#19
although we can simply add a custom elmish render extension method to remove these.

what's that package-lock.json file?

Probably is added when I ran UITest locally...

@forki
Copy link
Member

forki commented Mar 19, 2018

@zaaack yes I'd rather not depend on prereleases for public sample

@MangelMaxime
Copy link
Contributor

@zaaack The package-lock.json is in general generared by npm5 command.

Perhaps there is a call somewhere to npm instead of yarn commands.

@forki I am currently porting my work app to the latest prerelease version. Problem, is if we want people to understand how to use this new version we need some more documentation.

@vbfox Should be working on a third article in it Fable + React series, explaining how to write the view functions with Elmish to benefit of react optimization.

IMO, we need to address this lack of documenation before releasing a stable version.

@forki
Copy link
Member

forki commented Mar 19, 2018

@MangelMaxime I'm very much in favor of extending docs - but if we don't break stuff then we should still release the versions and extend the samples. It's always bad to have samples on prereleases

@forki
Copy link
Member

forki commented Mar 19, 2018

@zaaack this PR does not run SSR on normal build or does it? what hapens if I do just "build.cmd"?

@MangelMaxime
Copy link
Contributor

@forki We are breaking stuff :)

The major breaking change being: arguments order has been reversed in view and update function

@forki
Copy link
Member

forki commented Mar 19, 2018

yes just saw that. how about minor then? with the hydrate stuff?

@MangelMaxime
Copy link
Contributor

@forki It's probably possible, wait for @et1975 answer.

I will try to update Fulma-Demo with support for the new API and good practice stuff. So we will be able to write the documentation needed for the stable release.

@zaaack
Copy link
Contributor Author

zaaack commented Mar 19, 2018

@forki Just did some refactoring for RunSSR, sorry that I still cannot pass some parameters to the project conf, but I think the current one is more reasonable.

./build.sh run would open webpack-dev-server automatically, server-side rendering is working but you won't know unless you open the server's url and build the client bundle.js manually.

./build.sh runssr would open the server's url automatically, and run dotnet fable webpack -- -w to build client code, you need to manually refresh your page if you edit your client code, but in this case, you can debug server-side rendering easier.

Neither of these would auto restart your server when you edit your client files.

@forki
Copy link
Member

forki commented Mar 19, 2018

well I'm not talking about dev mode. I'm talking about the "build.cmd Deploy"
What would that use SSR if I deploy to azure?

@forki
Copy link
Member

forki commented Mar 20, 2018

is it possible to start a command in SSR?

@forki
Copy link
Member

forki commented Mar 20, 2018

@zaaack in my app I have to kick of a bunch of actions after the page is served. Like loading a map and showing current pos

@zaaack
Copy link
Contributor Author

zaaack commented Mar 20, 2018

@forki Not really, you can start any command you like, but they would be ignored in SSR.

However, here have isomorphicExec and isomorphicView to exec different code or render different view between client-side rendering and server-side rendering in
Fable.Helpers.Isomorphic. You can use this to easily render a loading component in SSR and render the real component in the client.

@zaaack
Copy link
Contributor Author

zaaack commented Mar 20, 2018

I'm assuming the map component is written in js native, and imported by FFI, these native component cannot be rendered in SSR. Also including fetch api based on FFI, unless you are using some isomorphic http client library like this:haf/Http.fs#142 If this land I think we can figure out a way to run some initial fetch commands in SSR.

@zaaack
Copy link
Contributor Author

zaaack commented Mar 21, 2018

Looks Travis ci need a rerun again.

@forki
Copy link
Member

forki commented Mar 21, 2018

@zaaack I don't want them to be rendered server side. I just want that command to spin off after the client loaded the bundle.

@forki forki closed this Mar 21, 2018
@forki forki reopened this Mar 21, 2018
@zaaack
Copy link
Contributor Author

zaaack commented Mar 21, 2018

@forki Just add commands in your init function, they will be ignored in SSR, but executed in the client.

@forki
Copy link
Member

forki commented Mar 21, 2018

oh crazy. that works!

@forki
Copy link
Member

forki commented Mar 21, 2018

I think I'm ready to take it in. what are others saying?

@forki
Copy link
Member

forki commented Mar 21, 2018

still hoping we can find a way without cookies. It's such a hard thing in the EU

@zaaack
Copy link
Contributor Author

zaaack commented Mar 21, 2018

@forki This really depends on your needs. If you don't use cookies to store the login state, then the login state won't be auto carried when requesting pages, so SSR won't get the login state and render it. As far as I know, the most use case of SSR is SEO, pages need login are actually not much need to be rendered on the server side.

For SAFE-BookStore, then there only remains the home page's SSR makes sense if we don't use cookies, I can remove login/wishList's SSR if you want.

@forki
Copy link
Member

forki commented Mar 21, 2018

tbh I think that's what we should do. restrict it to when it's logged out.
It's still super valuable

@forki forki closed this Mar 21, 2018
@forki forki reopened this Mar 21, 2018
PageModel = HomePageModel },
Navigation.newUrl (toHash Page.Home)
PageModel = HomePageModel },
Cmd.batch [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need to batch here

@alfonsogarciacaro
Copy link
Collaborator

I just had a look at the infamous EU directive on cookies and it says:

Cookies clearly exempt from consent according to the EU advisory body on data protection- WP29pdf include:

  • authentication cookies, to identify the user once he has logged in, for the duration of a session

Maybe this can be applied to our case? The tricky part here is the duration of the session (persistent cookies do need user consent), would it be ok if the validity of the token is less than 24 hours for example?

@theimowski
Copy link
Member

This look really great. Wondering whether it should be by default or opt-in in SAFE-Template?

@forki forki merged commit e5f9da1 into SAFE-Stack:master Mar 22, 2018
@forki
Copy link
Member

forki commented Mar 22, 2018

merged! thank you so much @zaaack !

@forki
Copy link
Member

forki commented Mar 22, 2018

https://twitter.com/sforkmann/status/976736877586903042

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants