Webpack 2 with Multipage Express web app - node.js

we are migrating our web app from ASP.NET and jQuery to Node.js with Express and React. We are using the typical multipage application (MVC), and we are confortable with this architecture and we need it for some special cases (basically SEO, and we know that with react now you can improve the SEO).
We want to maintain our routes without using React Router, we are going to use Express MVC Router but we want to use WebPack 2 with React.
The first step was migrating the BackEnd (we are almost done), and the second part is migrate the frontend from jQuery to React (yes, React like UI Library, we don't want Redux and we can work fine with just Component State because our pages are not very complex, and we want to reuse components).
Like we are using MVC Express routes we need to use Code Spliting from WebPack 2 to generate multiple entries for each page (js files), and we want to integrate typical stuff for make more productive: live reload, production build js, etc.
We have one example working of WebPack for creating the Entry libraries, and vendors and common, but we don't know how to integrate with our server.js (Express app), and have a good flow...
Example WebPack config:
var path = require('path')
var webpack = require('webpack')
var config = {
entry: {
vendors: ['react', 'lodash'],
common: ['lodash'],
home: path.resolve(__dirname, './src/main'),
page1: path.resolve(__dirname, './src/page1'),
page2: path.resolve(__dirname, './src/page2'),
},
output: {
path: path.join(__dirname, 'js'),
filename: '[name].bundle.js',
chunkFilename: '[id].chunk.js'
},
devtool: 'source-map',
devServer: {
inline: true,
contentBase: './src',
port: 3000
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
}
]
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false
},
compress: {
warnings: false,
screw_ie8: true
}
}),
new webpack.optimize.CommonsChunkPlugin({
names: ["common", "vendors"],
minChunks: Infinity
})
]
}
if (process.env.NODE_ENV === 'production') {
config.output.path = path.join(__dirname, 'dist/')
/*
Note: by default, React will be in development mode
see https://facebook.github.io/react/downloads.html
*/
config.plugins.push(new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
}))
}
module.exports = config
My question is... Can we get this working with Express and live reload? Like we do with create-react-app? Any link or tutorial to explain how to work with Multipage applications and WebPack together?
Thanks!

I'm the author of quickstart-express-react. We're apparently facing similar challenges, so I've updated the repo to better illustrate your usage scenario.
The basic idea, if you're looking to build an MPA and need both "generic" reloading and react hot reloading during development, you can use browsersync programatically as a proxy, and - very importantly run webpack-dev-middleware & webpack-hot-middleware inside browsersync and not your website/application.
It took me a week of trial & error to build that scenario, hence the decision to open-source it on GitHub.
Other things to consider:
Be careful to use the latest webpack & loaders/plugins, i.e. webpack#^2.2.1 and react-hot-loader#^3.0.0-beta.6 at the moment, keep an eye on their repos and update if you're facing problems;
If you're coming from ASP.NET, you'll probably be interested in Vash template engine.
IMO, at the moment webpack and its entire loaders/plugins ecosystem is cutting-edge and evolving fast, and as you've seen, building a realistic workflow configuration is close to madness. But the benefits of having full reloading during development are tremendous.
Hopefully the madness will change, now that webpack#2 is finally out of beta.
Hope this helps, and thank you starring the repo :-)

A quick update on a potentially different approach - although I published the quickstart-express-react two months ago, I like to keep an open mind and I'm still researching other alternatives for a better developer experience. And I might have found one.
Since you wrote you've almost finished porting the backend, this is probably not what you're looking for right now, but for future projects make sure to check and keep an eye on next.js, especially if you're interested in building isomorphic/universal react apps. I think the guys at ZEIT are doing an extraordinary job of simplifying the entire development workflow, especially considering the current state of the webpack ecosystem.
I'm currently looking for a sane way of building universal React websites/apps with react-toolbox components, and next.js does look promising, especially for a single/independent developer or a small team not wanting to deal with the full complexity of setting up a webpack scenario from scratch.
They're also offering a fantastic cloud deployment solution, but it's worth mentioning that next.js is fully open-source (MIT license) and it can be used in dev/prod on any infrastructure.
Disclaimer: I am in now way affiliated with ZEIT. I was just thrilled when I came across next.js a few days ago... :-)

Related

Build multiple pages using quasar

I need to build a multi-page website using Quasar to host on Netlify but Quasar is only building the index.html.
I have tried to use Quasar build and Quasar build --mode ssr. Both have failed in building more than the index.html.
Is there a way to accomplish multiple pages?
The concept of SPA is excellent, however it still does not suite everybody's needs, an example is having a backend app (for example django) and not willing to spend a lot of time to develop a rest api or graphql for it to serve the SPA contents.
Quasar cli at the end of the day in its core is just a webpack config that you can customize to achieve whatever you want that webpack can do, including multi page apps, here is a quick way to achieve this:
apps/myapp/pages.js
module.exports = {
main_page: {
entry: `${__dirname}/pages/main/main_page.js`
}
blog_entry_page: {
entry: `${__dirname}/pages/blog/blog_entry_page.js`
}
...
}
quasar.conf.js:
const HtmlWebpackPlugin = require('#quasar/app/node_modules/html-webpack-plugin')
const pages = require('./apps/myapp/pages')
module.exports = function (ctx) {
return {
...
build: {
...
extendWebpack (cfg) {
...
Object.keys(pages).forEach(function (page) {
// Add page webpack entry
cfg.entry[page] = pages[page].entry
// Output an html file for the page
cfg.plugins.push(
new HtmlWebpackPlugin({
template: 'mytemplate.html',
filename: `html/${page}.html`,
chunks: [page]
})
)
})
...
}
}
}
The above is a simplified example just to demonstrate the concept, for a complete and functional example see: https://gitlab.com/noor-projects/noor/tree/master/quasar-cli
(check this commit to know about what was added to enable MPA support)
Quasar framework builds single page apps by default, that is, your index.html page is the only page, and instead of going to different static pages, it uses the Vue router to load dynamic pages and components to your hearts content :)
Check out this Quasar document that explains with SPA (Single Page Apps) are and how they work (and why it's the best thing to do):
https://quasar.dev/quasar-cli/developing-spa/introduction#Introduction
Check out this Quasar document to learn how to add more routes (pages) to your app:
https://quasar.dev/layout/routing-with-layouts-and-pages#Defining-Routes
Quasar Framework is definitely the way to go, so keep at it. I'd be happy to walk you through anything. I've used the framework for nearly a dozen clients and many have gone off to recently be valued in the millions by investors. Quasar is the best mix for any platform you can think of, from a simple site to a complex web and mobile app that will be used by millions of people; it gets the job done with flying colors.
I've been writing apps and web sites for 20 years, nothing as easy to work with as Quasar has ever come along before it. You will love it!

Can't make Webpack "live reload" features with an Express+Pug (Jade) web app

I've searched here and there very few questions using Pug (Jade) and even fewer answers. I thought looking code in GitHub would give me an answer but has only brought more confusion, outdated code, and repos that don't work.
What I want is very simple: develop a simple Node+Express+Postgress+Pug site with webpack's live reloading features. Postgress hasn't entered the picture yet, since using Webpack as a dev aid hasn't worked.
Using HMR and html-webpack-plugin, I expected a swift development experience. My *.pug files should show data sent down by the controller, as they do when I run a node server instead of Webpack's webpack-dev-server. Also, it fails to refresh the browser on changes. Everything else I have no problems with bundling works well; it quickly reloads server changes, etc.
Because I'm not doing a SPA, and I've seen you have to spawn a new plugin object per *.pug page, I made a simple js utility to collect all the *.pug pages so I can do this:
new HtmlWebPackPlugin({
filename: 'index.html',
template: './src/views/pages/index.pug',
inject: true,
}), ...pugPages.pages(env),
I've tested and it works so that's a ton of silly code I don't need to write and update.
With that hack, I get to see the PUG pages rendered in the browser. But they don't show the data sent down by the Express controller. For example:
index.js:
router.get('/', (req, res, next) => {
res.render('index', { msg: 'index page' });
next();
});
index.pug::
extends ../layout
block head
include ../partials/head
block content
h1 You are in #{msg}
//h1= htmlWebpackPlugin.options.title
//p Welcome to #{htmlWebpackPlugin.options.title}
//script.
// console.log(!{JSON.stringify(htmlWebpackPlugin.options)})
This just shows "You are in".
Again, if run by Node, it shows the correct "You are in index page". As you can tell I was trying with htmlWebpackPlugin.options.title. But if that's the only way this works (through that object), how can I get pug the data from Express? Does html-webpack-plugin make templates static, therefore, rendering pug useless?
I'm using:
Node.js v10.16.0
darwin 16.7.0
npm 6.9.0
webpack 4.29.0
html-webpack-plugin 3.2.0
I made a leaner branch with everything in place for easy helping. Here it is:
https://github.com/nmaxcom/Express-pug-webpack
I had the same question but hopefully I have figured it out. Webpack will bundle your code before your server is up and running - this is different from Express which will take a request from the user, process it, and send a response. Therefore, when you are using the html-webpack-plugin and bundling your pug files, everything has to happen at compile time rather than request processing time.
However, that does not really mean that using pug is useless. You can use composition (using includes and extends) to greatly speed up your web development and by using a combination of pug files and options, you can create several html files using just one pug template. For dynamically loading content based on user request, you can either serve those dynamic pages through NodeJS or you can have a ajax call on client side to populate the data
Regarding hot reloading, please note that for HMR to work you need to ensure the following:-
Add 'webpack-hot-middleware/client?reload=true' as the first item for all entries in your webpack config
Ensure that your pug file (that you are trying to hot reload) is in your entry
Ensure you have use new Webpack.HotModuleReplacementPlugin() as a plugin in your webpack config (where Webpack is just require('webpack')
So, possible setup would be
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: [
'webpack-hot-middleware/client?reload=true',
path.resolve(__dirname, '../src/client/assets/js/main.js'),
path.resolve(__dirname, '../src/client/templates/views/index.pug'),
],
},
.
.
.
plugins: [
// OccurrenceOrderPlugin is needed for webpack 1.x only
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
// Use NoErrorsPlugin for webpack 1.x
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/client/templates/views/index.pug'),
filename: 'index.html'
chunks: ['index']
}),
]
.
.
.
Note: If in the above example main.js requires index.pug, then you can remove index.pug from the entry

Supporting webpack dependant imports during node dev server execution (a React server rendering context)

I have a universal React project configured in which I use Webpack to create bundles for both the client and the server code.
This works fine when I run the server directly via the targetting the bundled output by Webpack.
node ./build/server/main.js
However I am having issues though when I try to run a "live" development server. In this case I don't want to target the bundled server files, instead I just target the source files directly which will allow me to run the webpack hot middleware for live code changes. Below is a stripped down version of the main file for the server.
src/server/index.js
import express from 'express'
import universalReactAppMiddleware from './middleware/universalReactApp'
const server = express()
// Get the client bundle webpack configuration.
const webpackClientConfig = require('../../webpack.client.config.js')
// If we are in development mode we will add the webpack hot
// reloading middleware.
if (process.env.NODE_ENV === 'development') {
const webpack = require('webpack')
const clientCompiler = webpack(webpackClientConfig)
const createWebpackMiddleware = require('webpack-dev-middleware')
const createWebpackHotMiddleware = require('webpack-hot-middleware')
server.use(
createWebpackMiddleware(clientCompiler, {
noInfo: true,
publicPath: webpackClientConfig.output.publicPath,
stats: {
colors: true,
hash: false,
timings: true,
chunks: false,
chunkModules: true,
modules: false
}
})
)
server.use(
createWebpackHotMiddleware(clientCompiler)
)
}
// Configure static serving of our webpack bundled client files.
server.use(
webpackClientConfig.output.publicPath,
express.static(webpackClientConfig.output.path))
// Bind our universal react app middleware for all GET requests.
server.get('*', universalReactAppMiddleware)
server.listen(process.env.SERVER_PORT)
An example execution of this now being:
NODE_ENV=development babel-node ./src/server
It starts up okay, but the moment the universalReactAppMiddleware handles a request it will attempt to perform server rendering for a resolved Component. This then falls over because some of my components import images/css, for example:
src/shared/components/Foo
import './styles.css'
import background from './background.jpg'
function FooComponent() {
return <img src={background} />
}
Computer says no!
Upon execution my express server throws out an exception saying unexpected syntax. It basically tries to parse the css and image imports as Javascript. These types of imports depend on my webpack loaders to operate correctly.
So now I am trying to look for a mechanism of spoofing the Webpack behaviour so that I can execute these types of components. I am investigating Pete Hunts webpack-require but have been having difficulty with it.
Does anyone know of any other approaches that will work for this context?
Update 2016/06/15
Boom! I've managed to pull this off without any 3rd party libs to help me. universal-webpack is pretty cool and much cleaner than the previous webpack-isomorphic-tools but I am liking that I have a minimal configuration set up in which as little as possible of the universal webpack configuration bleeds into my production code.
I'm pretty stoked with the results. Client bundle is backed by the lastest react-hot-loader v3 beta which is giving me an awesome HMR experience, and my Server bundle gets rebuilt on any file changes so not having to restart my server manually either. Making for a pretty sweet development experience.
I am going to throw this into a boilerplate (yes I know, yet another) but perhaps it will be useful to someone else.
I created a working solution achieving everything that I wanted from the configuration.
It's all within the following boilerplate repo:
https://github.com/ctrlplusb/react-universally

Webpack for back-end?

I was just wondering, I started using Webpack for a new project and so far it's working fine. I almost would say I like it better than Grunt, which I used before. But now I'm quite confused how and or I should use it with my Express back-end?
See, I'm creating one app with a front-end (ReactJS) and a back-end (ExpressJS). The app will be published on Heroku. Now it seems like I should use Webpack with ExpressJS as well to get the app up and running with one single command (front-end and back-end).
But the guy who wrote this blogpost http://jlongster.com/Backend-Apps-with-Webpack--Part-I seems to use Webpack for bundling all back-end js files together, which is in my opinion really not necessary. Why should I bundle my back-end files? I think I just want to run the back-end, watch my back-end files for changes and use the rest of Webpack's power just for the front-end.
How do you guys bundle the front-end but at the same time run the back-end nodejs part? Or is there any good reason to bundle back-end files with Webpack?
Why to use webpack on node backend
If we are talking about react and node app you can build isomorphic react app. And if you are using import ES6 Modules in react app on client side it's ok - they are bundled by webpack on the client side.
But the problem is on server when you are using the same react modules since node doesn't support ES6 Modules. You can use require('babel/register'); in node server side but it transipile code in runtime - it's not effective. The most common way to solve this problem is pack backend by webpack (you don't need all code to be transpile by webpack - only problematic, like react stuff in this example).
The same goes with JSX.
Bundling frontend and backend at the same time
Your webpack config can have to configs in array: one for frontend and second for backend:
webpack.config.js
const common = {
module: {
loaders: [ /* common loaders */ ]
},
plugins: [ /* common plugins */ ],
resolve: {
extensions: ['', '.js', '.jsx'] // common extensions
}
// other plugins, postcss config etc. common for frontend and backend
};
const frontend = {
entry: [
'frontend.js'
],
output: {
filename: 'frontend-output.js'
}
// other loaders, plugins etc. specific for frontend
};
const backend = {
entry: [
'backend.js'
],
output: {
filename: 'backend-output.js'
},
target: 'node',
externals: // specify for example node_modules to be not bundled
// other loaders, plugins etc. specific for backend
};
module.exports = [
Object.assign({} , common, frontend),
Object.assign({} , common, backend)
];
If you start this config with webpack --watch it will in parallel build your two files. When you edit frontend specific code only frontend-output.js will be generated, the same is for backend-output.js. The best part is when you edit isomorphic react part - webpack will build both files at once.
You can find in this tutorial explanation when to use webpack for node (in chapter 4).
This is my second update to this answer, which is beyond outdated by now.
If you need full a stack web framework in 2023, I'd recommend nextjs (which is built on top of react). No need to go around setting up anything, it just works out of the box, and is full stack.
On the other hand, if you need to compile your nodejs project written in typescript (which you should use as much as you can for js projects), I would use tsup-node.
You don't need to be a genius to imagine that in 3-5 years I'll come back to this and say this is really outdated, welcome to javascript.
This answer is outdated by now since node now has better support for ES modules
There's only a couple of aspects I can redeem the need to use webpack for backend code.
ES modules (import)
import has only experimental support in node (at least since node 8 up to 15). But you don't need to use webpack for them work in node.
Just use esm which is very lightweight and has no build step.
Linting
Just use eslint, no need to use webpack.
Hot reloading/restart
You can use nodemon for this. It's not hot reloading but I think it's way easier to deal with.
I wished I could refer to a more lightweight project than nodemon, but it does do the trick.
The blog post you shared (which is dated by now) uses webpack for having hot reloading. I think that's an overkill, opens a can of worms because now you have to deal with webpack config issues and hot reloading can also lead to unexpected behaviour.
The benefits we get from using tools like webpack on the frontend don't really translate to backend.
The other reasons why we bundle files in frontend is so browsers can download them in an optimal way, in optimal chunks, with cache busting, minified. There's no need for any of these in the backend.
Old (and terrible, but maybe useful) answer
You can use webpack-node-externals, from the readme:
npm install webpack-node-externals --save-dev
In your webpack.config.js:
var nodeExternals = require('webpack-node-externals');
module.exports = {
...
target: 'node', // in order to ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
...
};
to use Webpack for bundling all back-end js files together, which is in my opinion really not necessary.
I think you are absolutely right. It's not necessary at all. I've been researching on this topic for a while. I've asked lots of questions on this topic, and up to this day, I haven't found yet a single "real" reason for one to use webpack on a Node.js back-end.
I'm not saying you can't or shouldn't set up a webpack-dev-server to develop your back-end code locally. But you definitely don't need to bundle your backend code on your build process.
webpack bundles are meant for the browser. Take a look at its official docs: Why webpack?. Historically, browsers never had a built-in module system, that's the reason why you need webpack. It basically implements a module system on the browser. On the other hand, Node.js has a built-it module system out of the box.
And I do re-use all of my front-end code for SSR on my server. The very same front-end files are run on my server as-is without any bundling (they are just transpiled, but the folder structured and number of files is the same). Of course I bundle it to run on the browser, but that's all.
To run on your Node.js server, simply transpile it with babel, without webpack.
Just use ["#babel/preset-env", { targets: { node: "12" }}], on your babel config. Choose the Node version of your backend environment.
backend
dist_app // BABEL TRANSPILED CODE FROM frontend/src
dist_service // BABEL TRANSPILED CODE FROM backend/src
src
index.tsx
frontend
src
App.tsx
public // WEBPACK BUNDLED CODE FROM frontend/src
You can perfectly render your frontend code on the server, by doing:
backend/src/index.tsx
import { renderToString } from "react-dom/server";
import App from "../dist_app/App";
const html = renderToString(<App/>);
This would be considered isomorphic, I guess.
If you use path aliases on your code, you should use babel-plugin-module-resolver.

Can I use webpack on the client side without nodejs server?

I am trying to build a web app where I want to store all html, js and css files on amazon s3, and communicate with a restful server through api.
I am trying to achieve lazy loading and maybe routing with react router. It seems that webpack has this feature code splitting that would work similarly as lazy loading.
However, all of the tutorial and examples I found involves webpack-dev-server, which is a small node express server. Is there anyway I could generate bundle at build time and upload everything to amazon s3 and achieve something similar to Angular's ocLazyLoading?
It's definitely possible to create a static bundle js file, which you can use in your production code that does not include webpack-dev-server.
See this example as a reference (note: I am the owner of this repo). webpack.prod.config.js does create a production ready bundle file using webpack via node.js which itself does not require node.js anymore. Because of that you can simply serve it as a simple static file (which is done in the live example).
The key difference is how the entry points are written in the dev- and production environments. For development webpack-dev-server is being used
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
// ...
}
In the production environment you skip the webpack-dev-server and the hot reloading part
module.exports = {
entry: [
'./src/index'
],
// ...
}
If you want to split your code into more than one bundle, you might want to have a look at how to define multiple entry points and link the files accordingly.

Resources