liferay / liferay-frontend-projects

A monorepo containing assorted Frontend Infrastructure Team projects
Other
69 stars 68 forks source link

Question - recommended way for creating React portlets #1019

Closed Betakaro closed 1 year ago

Betakaro commented 1 year ago

Hi,

I'd like to know what is the recommended way to create React portlets.

We are currently using webpack 5 -> liferay-npm-bundler 2.28.3.

I've seen you were developing liferay-npm-bundler 3, but I think you abandonded the project?

I know there is adaptation approach, but we haven't ever used Create React App so I don't think we should use that. Another thing I came accros were remote apps but it looks like it usfeul mostly for developing purposes to be able to hot reload?

Basically this is really short summary of my current knowledge, I'm trying to stay in touch and alteast once a month go through new releases and pull requests to see if there is something new. But I'm kinda lost now. Would like to know if there are other approaches you are working on or other alternatives that could be better than our current approach. Some kind of roadmap, docs or something would be nice. Atleast if we should look forward to new things, that can be available in a order of months or try to do the best with current tools, because it can take longer time for the new features to be production ready.

Sorry for bothering you, just thought would be nice to have response from developers itself and maybe learn something new. Really appreciate work you are doing.

izaera commented 1 year ago

Hi there :wave:

We are currently using webpack 5 -> liferay-npm-bundler 2.28.3.

You mean you are using webpack 5 for some projects and liferay-npm-bundler for others? Or are you combining both tools in a build workflow? :thinking:

I've seen you were developing liferay-npm-bundler 3, but I think you abandonded the project?

Yes. Bundler 3 is officially frozen because we found a much better way to deploy JavaScript to Liferay directly based on webpack and browser modules. It follows the same philosophy of bundler 3, but it's almost a tailored direct invocation of webpack, whereas bundler 3 implied some pre and post processing.

I know there is adaptation approach, but we haven't ever used Create React App so I don't think we should use that.

I wouldn't recommend adaptation to anyone unless they really need it. It was intended for existing code bases as an incremental migration path towards real liferay-npm-bundler projects.

The problem with adaptations is that it tries to adapt an output artifact (namely the build from create-react-app), something which is problematic because it's not the source, but the final output. Thus the adaptation process has to make a lot of assumptions and sometimes it's even impossible to make some things work (ie: it has limitations).

Another thing I came accros were remote apps but it looks like it usfeul mostly for developing purposes to be able to hot reload?

Remote apps are not only to make development easier, they imply a new architecture based on custom elements which is far better than the old portlets approach. I would use it if you can.

The benefits of remote apps are that they are decoupled from Liferay and you can even create them with your favorite tool. For example, you may start to develop them with @liferay/cli, but you can then create more custom elements using create-react-app or any other toolchain. You could even have some remote apps built with @liferay/cli, some others directly hacked by hand, others using Vue, etc.

If gives you more freedom and it's definitely the way we are heading. This is not say that we won't remove liferay-npm-bundler some day in favor of the new build approach, which is much better, but I don't know when that will happen because it's not top priority.

In any case, wheter or not you decide to leverage remote apps, you should switch now to @liferay/cli (which uses the bundler under the hood for non remote app projects) and once we decide to remove the bundler and put the next generation build it will happen automatically, without breaking changes.

Regarding docs, the only ones that I know of are here, in this repo and there may be something more in https://liferay.dev/ ...

Betakaro commented 1 year ago

Thank you for claryfing the options we have. Based on you recommendations we will certainly switch to @liferay/cli.

You mean you are using webpack 5 for some projects and liferay-npm-bundler for others? Or are you combining both tools in a build workflow? 🤔

We are combining them. Basically each portlet has these two scripts:

"scripts": {
  "build": "webpack && liferay-npm-bundler",
  "deploy": "npm run build && lnbs-deploy"
}

We have one webpack config with common settings for every portlet, then each portlet has it's own config. The configs contains loaders, optimization settings based on environment, webpack plugins, aliases for resolve etc. I'm not sure if we could achieve same result only with liferay npm bundler.

And I'm not sure if we really want to. We are trying to be as less dependent on Liferay as we can. Our view on liferay-npm-bundler is that is something we have to use to be able to deploy into Liferay.

Following discussion probably doesn't belong to this topic, can move it there if you want.

To bring you closure to our solution. Except for the dashboards, every page has two portlets. One main React portlet for content a one MVC portlet (with info about currently signed user) that is part of main navigation menu that is created by Liferay theme. It's the only portlet that isn't React portlet but I think if we would really want to, we could get the needed information like sign out URL etc. from backend.

Most of the portlets share some source code at some point. Let's say we have two portlets Contacts and Organization. As I wrote each of them has it's own page. But if you open detail of some contact, there is link to it's organization and on detail of organization there can be links to many contacts.

Our main concern is the size of the JS bundle, because it can go wrong really quickly as our code base will grow. The single portlet now contains code for both portlets. The size of the portlet is about 2 MB in production environment at this time which could be worse, but the user needs to download the code even if he won't visit the Organization part.

I know we would probably can use the shared bundle that would help us. I haven't tried, maybe it would work for us just fine. But for me it's something that goes againts our "don't depend much on Liferay" rule. Another way I found and is much closer to our wanted approach is this https://github.com/liferay/liferay-js-toolkit/issues/17 where we would need to add babel to our build workflow.

Do you have any recommendations for this problem? Should we use liferay-npm-loader at all?

Thanks in advance.

Edit: So I did some research about the fragments and I think that can be the way to go. Basically everything what we need is some element (div or whatever) as root for React. We are building code with webpack already so that wouldn't be problem. But the main thing I'm struggling with is deploying the JS files to Liferay and loading them.

I even tried another approach still using liferay-npm-bundler. I split main JS file to chunks using React.lazy(), configured webpack to load chunks from the correct path with /o/js/resolved-module/__ but during loading the chunk I got error from Liferay AMD loader - Trying to define a module that was not registered... Then I figured out from some other discussion where you wrote that it's not possible to dynamically register modules so I abandoned this approach. If it wasn't for that error I thought I was near the success. :)

izaera commented 1 year ago

Do you have any recommendations for this problem?

The solution we provide is the shared bundle approach. We leverage webpack externals to achieve the same results in @liferay/cli's remote app projects. This is the build we use for those projects -> https://github.com/liferay/liferay-frontend-projects/blob/master/projects/js-toolkit/packages/portal-base/src/build/customElement.ts

Also, I don't know if it can be of any help, but there is support for React fragments in the Liferay Fragments Toolkit.

izaera commented 1 year ago

In general, we are trying to slowly move from AMD to browser modules and use webpack to deploy our JS code. Then we use the aforementioned externals feature to dedupe/share code across portlet boundaries.

izaera commented 1 year ago

I'm closing this as answered. Feel free to add more comments to the conversation in any case :+1: