Website template with React, Webpack, hot module replacement, and Express. MSDeploy to prepare deployment for Azure Web Apps.
Modern websites are not bunches of plain text files. Build process increases page load efficiency and overall page performance. This process involves:
- Concatenating multiple JavaScript files into a single file (a.k.a. bundling)
- Obfuscate and minify JavaScript files
- Re-compress JPEG and PNG files for better compression ratio
- Remove dead code or code that is only used in development mode
We use Webpack as a bundler for our build process. And the directory structure is designed to be able to host as a standalone Node.js server or IIS on Azure Web Apps and Azure VM.
It takes about 5-10 minutes to build for the first time (due to large npm install), have a little patience.
Clone it to your dev box. Then run npm install
.
Run npm run hostdev
, the development server will listen to port 80 and available at http://localhost/.
For clarity, HTML pages and JavaScript code are separated into different folders.
- HTML pages or assets
- Create new HTML file at
web/public/
- Save assets to
web/public/
- Create new HTML file at
- JavaScript code
- Create new JavaScript file at
web/src/
- To import packages, mark them as development dependencies, for example,
npm install react --save-dev
- Create new JavaScript file at
Add new API endpoints at prodserver/controllers/api.js
.
To import packages, mark them as production dependencies, for example, npm install serve-static --save
.
Lastly, restart the development server to pick up your new code.
Then, there are multiple ways to host the server:
- Host with vanilla Node.js
- Host with Azure Web App
- Continuous integration: deploy via GitHub
- Controlled release: deploy using MSDeploy
- Host with IIS
Build the website, run npm run build
. Then under dist/iisapp/
, run node server.js
.
The directory dist/iisapp/
contains everything that need to run the production server, including minified HTML files and assets. It can be copied to production server to run.
For load-balancing and scalability, it is recommended to use a process lifecycle manager to manage the server process. For example, PM2.
There are two deployment options for Azure Web App:
- Deploy via GitHub (Recommended)
- Deploy using MSDeploy
- For manual or controlled release, for example, deploy thru release manager, for example, VSTS Release Management
- Download publish settings file from Azure Dashboard or using Azure PowerShell
- Modify iisnode configuration to select Node.js version
- When deployed thru MSDeploy,
iisnode.yml
is not updated automatically, thus Node.js version cannot be selected automatically - Add a line to
iisnode.yml
:nodeProcessCommandLine: "D:\Program Files (x86)\nodejs\6.3.0\node.exe"
- When deployed thru MSDeploy,
- Then, run
npm run build
, to build the website todist/iisapp/
- Then, run
npm run pack
, to pack the website asdist/packages/web.zip
- Then, run
npm run deploy --publishsettings=yoursite.PublishSettings
This will use IIS Management Service feature to deploy the site.
- Run
npm run build
, to build the website and output todist/iisapp/
- Then, run
npm run pack
, to pack the website asdist/packages/web.zip
- Then, use MSDeploy to deploy the package to your IIS
Filename | Description |
---|---|
devserver/ |
Webpack development server, serve content from web/public/ and web/src/ |
dist/ |
Build output |
dist/iisapp/ |
Compiled web server ready to run by itself or hosted on IIS |
dist/iisapp/public/ |
Bundled content and static assets |
dist/packages/web.zip |
Web server packed by MSDeploy and ready to deploy to Azure Web Apps |
prodserver/ |
Express production server, serve content from dist/iisapp/public |
prodserver/controllers/api.js |
RESTful API for http://localhost/api |
prodserver/iisnode.yml |
iisnode configuration |
prodserver/web.config |
Web.config for hosting under IIS with iisnode |
scripts/ |
Gulpfile for building and packing the project |
web/public/ |
Asset source files |
web/src/ |
JavaScript source files |
To help building the project, there are several Gulp tasks exposed thru NPM scripts.
npm run build
will start the build processnpm run deploy
will deploy the website to Azure Web Appnpm run hostdev
will host a development server and bundle on-the-flynpm run hostprod
will host a production server using pre-bundled filesnpm run pack
will pack production server and bundled files into a ZIP file using MSDeploy
To build the website, npm run build
. The build output will be located at dist/iisapp/
.
You can specify production build by:
- Set environment variable
NODE_ENV
toproduction
, or - Run
npm run build -- --build production
Currently, the build favor (either development
or production
) is only used by transform-node-env-inline
. It helps reducing bundle size by excluding developer-friendly error messages in production build.
- Copy server code from
prodserver/
todist/iisapp/
, excludenode_modules
folder- After copy complete, will run
npm install
to install fresh production-only packages
- After copy complete, will run
- Bundle source files from
web/src/
todist/iisapp/public/dist/bundle.js
- Will use existing npm packages from
web/node_modules
- Will use existing npm packages from
- Copy static assets from
web/public/
todist/iisapp/public/
- Will minify image with gulp-imagemin
- Will minify HTML with gulp-htmlmin
The configuration file is located at web/webpack.config.js
. It controls how files are getting bundled into a monolithic dist/bundle.js
.
web/src/*.js
andweb/src/*.jsx
- Bundled by
babel-loader
- Enable React JSX with
preset-react
- Enable ES2015 with
preset-es2015
- Escape ES3 reserved keywords
- Transform
process.env.NODE_ENV
into"development"
or"production"
withtransform-node-env-inline
- Enable React JSX with
- Entrypoint is
web/src/index.js
- Bundled by
web/src/*.css
andweb/src/*.less
- Bundled by
less-loader
, then css-loader
, thenstyle-loader
- Bundled by
When running Webpack development server, additional configurations are required, for example, hot module replacement.
The configuration file is located at devserver/webpack.dev.config.js
.
When running under development server, we will add the following to webpack.config.js
:
- Serve assets from
web/public/
- Enable source map
- Use absolute path for source map for compatibility with Edge
- Also write a copy of
bundle.js
todist/webpack/bundle.js
for debugging purpose - Hot module replacement
- Support React component with
react-hot
loader
- Support React component with
The server targets local development environment where network speed is not a concern.
Instead of serving a monolithic bundle dist/bundle.js
, the development server will serve each source files separately. This also enables hot module replacement, when a source file is modified, the browser will only reload that source file and/or re-render related React component.
- Bundle on-the-fly, shorter build time
- Support hot module replacement (supersede LiveReload)
- Listening port
- Set environment variable
PORT
to8080
, or - Command-line switches:
npm run hostdev -- --port 8080
- Set environment variable
dist/bundle.js
will be bundled by Webpack on-the-fly*
if matching file exists, will be served fromweb/public/*
api/
will be served by Express router atprodserver/controllers/api.js
- Otherwise, will redirect to
web/public/index.html
- To support single-page application
The server is a simple Express server which host on port 80 at http://localhost/. All contents are served from dist/iisapp/public/
.
- Cross-platform
- Simple deployment
- Recommended to host under a process lifecycle manager, for example, PM2
- Listening port
- Set environment variable
PORT
to8080
, or - Command-line switches:
npm run hostprod -- --port 8080
- Set environment variable
*
if matching file exists, will be served fromdist/iisapp/public/*
- Also serve bundle
dist/bundle.js
fromdist/iisapp/public/dist/bundle.js
- Also serve bundle
api/
will be served by Express router atprodserver/controllers/api.js
- Otherwise, will be redirected to
web/public/index.html
- To support single-page application
Because the contents are served from dist/iisapp/public/
. After you modify your source files at web/src/
or assets at web/public/
, you will need to rerun npm run build
to rebuild the content to dist/iisapp/public/
.
This scenario is designed for deploying server code to Azure Web Apps, Azure VM, and on-premise IIS.
iisnode configuration is located at iisnode.yml
. We have overrode some defaults:
debuggingEnabled
is set tofalse
devErrorsEnabled
is set tofalse
loggingEnabled
is set tofalse
nodeProcessCountPerApplication
is set to0
- One worker process per CPU
node_env
is set toproduction
- We assume hosting the site in IIS is always in production mode
- Express is faster when environment variable
NODE_ENV
is set toproduction
, details here
- Can be deployed to Azure Web Apps, Azure VM, and on-premise IIS
- IIS-managed worker process lifecycle
- Auto recycle worker process as needed (hitting memory or CPU limit, or after number of hours)
- Fast and efficient serving on static files using kernel-mode driver (http.sys)
Largely same as hosting with standalone Express server. Except when serving *
, files will be served directly by IIS and not passing thru iisnode or Express. Static files served by IIS will be served and cached by kernel-mode driver (http.sys).
(This scenario is only supported on Windows because it requires MSDeploy.)
To pack the content and production server, npm run pack
.
It will create a MSDeploy ZIP file that can be deployed to any IIS server, including Azure Web App. This ZIP file contains Express server code and website contents in production favor.
Additional parameters added to MSDeploy ZIP file:
name | defaultValue | tags | kind | scope |
---|---|---|---|---|
IIS Web Application Name | Default Web Site |
IisApp |
ProviderPath |
IisApp |
Before packing the project, make sure your current build is up-to-date, or run npm run build
.
This project can be deployed to Azure Web App using continuous deployment with GitHub. Azure Web App is powered by Project Kudu.
To deploy to Azure, please click , or refer to this article.
To run Webpack on Azure, we prepared a custom deployment script for Project Kudu.
- Copy source files to intermediate folder (under
D:\home\site\intermediate\
) - Build the project (by running
npm install
) - Copy server and bundles from
D:\home\site\intermediate\dist\iisapp\
toD:\home\site\wwwroot\
- Update
iisnode.yml
by selecting Node.js version from engines inpackage.json
- Currently, there is a bug in Webpack that prevent us to use Node.js >= 6.0.0 to bundle
(This command is only supported on Windows because it requires MSDeploy)
This scenario is designed for manual or controlled release. Mostly paired with a release management tool, for example, VSTS Release Management.
Deployment thru MSDeploy will not trigger Project Kudu. Thus, Node.js version and binary location cannot be automatically selected from package.json
. Please add the followings to iisnode.yml
:
nodeProcessCommandLine: "D:\Program Files (x86)\nodejs\6.3.0\node.exe"
Then, build the server, npm run build
. This will output to dist/iisapp/
.
Then, pack the web server, npm run pack
. This will output a MSDeploy package file at dist/packages/web.zip
.
Then, deploy the package file to Azure Web App, npm run deploy -- --publishsettings=<yoursettings>.PublishSettings
.
The publish settings file can be downloaded from Azure Dashboard or using Azure PowerShell.
(This scenario is only supported on Windows because it requires MSDeploy)
Similar to Azure Web App, the website can also deploy to an on-premise IIS with iisnode and Node.js installed.
iisnode helps manage Node.js worker process:
- Automatic process recycle
- Redirect console output to file
After you have packed your website into a MSDeploy ZIP file (npm run pack
), use MSDeploy to "sync" the package to the server. For example,
"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe"
-verb:sync
-source:package="dist\packages\web.zip"
-dest:
auto,
ComputerName="https://<server>:443/msdeploy.axd?site=<appname>",
UserName='<username>',
Password='<password>',
AuthType='Basic'
-setParam:name="IIS Web Application Name",value="<appname>"
(Whitespace and line breaks added for clarity)
These are items we are working on or under consideration: