-
Notifications
You must be signed in to change notification settings - Fork 40
Frontend development stack
Warning
This page is now deprecated.
The up-to-date documentation can be found at:
Base elements of MXCuBE 3 frontend development stack are:
- Webpack
- ReactJS
- Babel
- Bootstrap (through react-bootstrap)
- react-hot-loader
Babel compiles Javascript code written with latest coding Javascript standards for instance ES6 to a Javascript version (for instance ES5) that have better support across a wide range of browsers. This allows to use new features like the module system in today's code, without worrying about web browsers compatibility.
Babel ships with built-in support for JSX, which completely replaces the need for the former jsx-transform.
React Hot Loader works with webpack-dev-server with hot reloading enabled. It keeps React components mounted, preserving the state, when JSX file is changed. It is very comfortable while developing, since edits are immediately in effect.
One of the best ways to handle the organization of a code base it to divide it into modules. Webpack is a module bundler, it generates static assets from the different modules and dependencies used in the project, these static assets are often referred to as bundles. Utilizing a module bundler enhances not only the development, but also the user experience since static assets cane be optimized to decrease load time.
There are a many popular module bundlers, some of which you might either used or heard about like Browserify or RequireJS. Both of these are extremely helpful and do a great job. Webpack however has, at the time of writing, some extra features:
- Webpack can easily split an application into multiple files: codebase can be split into multiple chunks and those chunks can be loaded on demand reducing the initial loading time of your application.
- Webpack can build and bundle CSS, preprocessed CSS, compile-to-JS languages (like CoffeeScript), images and more by utilising webpack loaders. It is similar to grunt or gulp tasks.
- plugins have the ability to inject themselves into the build process to do all sorts of crazy stuff.
- webpack-dev-server can be used during the development process, to serve produced bundles - it offers hot reloading and can proxy requests (AJAX or WebSockets) to another server transparently for testing or trying UI effectively while working on it
All those features can be achieved differently using other tools, but webpack bring them all in one tool.
Here is the webpack configuration file in use with MXCuBE 3, with detailed information regarding each part of the config. The configuration file format is Javascript:
var webpack = require("webpack");
var path = require("path");
var config = {
entry: 'main.jsx',
output: {
path: path.resolve(__dirname, 'mxcube3','static'),
filename: 'bundle.js',
publicPath: ''
},
resolve: {
root: path.resolve(__dirname, 'mxcube3/ui'),
extensions: ['', '.js', '.jsx']
},
entry
is the main file that webpack will read when producing output. In this case, it is set to
main.jsx
in mxcube3/ui
directory. __dirname
is a global variable pointing to the directory
where webpack.config.js
is located. The resolve
part of the config tells webpack where to find
MXCuBE files in particular.
The output
section indicates which file should be produced, in this case bundle.js
(default name),
and where: mxcube3/static
.
The following section is loaders configuration:
module: {
loaders: [
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
This is to add .css files support to webpack; doing require("mycssfile.css");
(or ES6: import "mycssfile.css"
) will work transparently.
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot', 'babel']
},
.jsx
and .js
files will go through babel, then React hot loader. So ES6 JS code is transformed to ES5,
JSX is transformed into JS and, if there are React components, React hot loader will do its magic in order
to enable the Hot Reloading feature. node_modules
directory is excluded, not to parse all files over there
(more on this later).
{
test: /\.(jpe?g|png|gif)$/i,
loaders: [
'url?limit=8192',
'img'
]
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
jp(e)g, png and gif files are handled by img
loader. If file size is below 8 KB, the image is converted
to Base64 and directly embedded into the bundle. This is to optimise loading time. If image is bigger,
it will be served via the web server like normal.
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
The file loader will take .ttf, .eot and .svg files and will put them in the output directory with an unique name. This allows to require/import those files transparently. This specific loader is needed by Bootstrap dependency (could be useful for others too).
{
test: /isotope-layout/,
loader: 'imports?define=>false&this=>window'
},
]
},
The imports
loader is a default webpack loader. It is needed to alter some globals when a JS module
is loaded, to make it compatible with webpack (read this). The isotope-layout
module used in MXCuBE 3 code for Sample Grid is not compatible
with webpack by default, but it can be solved with this magic.
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
This plugin is a standard webpack plugin; this makes modules with dependency on jQuery that assume jQuery is already loaded and available in global namespace to be made compatible with webpack. Everytime $
, jQuery
or window.jQuery
is seen in a file, webpack adds the corresponding require/import automatically. This was added for x-editable
dependency.
While working with frontend stuff, at some point AJAX calls or WebSocket connections need to be tested. In this case, webpack-dev-server is not enough because it doesn't know how to deal with requests, it is only there to serve produced bundles.
Then the proxy feature comes handy: webpack-dev-server can forward requests to another server. In the case of MXCuBE it allows to have the Flask development server (back-end) on a beamline, for example, receiving and replying to all requests while continuing to work on the frontend at the same time, without the need to generate a production bundle.
devServer: {
proxy: {
'/mxcube/api/*': {
target: backend_server,
secure: false,
ws: true
},
},
},
The backend_server
variable is used ; it contains the address of the backend server. The backend_server
is initialized at the very beginning of the config script:
var backend_server = require('./backend_server.js');
This allows to keep webpack.config.js
clean in git, the only file that is not under version control is
backend_server.js
as it changes over time and between different institutes:
module.exports = "http://aelita:8081"
webpack is built on top of Node. The package.json
file is used by npm, the
Node Package Manager, to start/stop node services and to handle dependencies. That's why a package.json
is also needed with webpack. The file is JSON encoded. Here is the basic configuration to be able
to do npm start
in order to start webpack-dev-server properly for MXCuBE 3 project:
{
"scripts": {
"start": "npm run dev",
"dev": "./node_modules/.bin/webpack-dev-server --hot --inline --colors --host 0.0.0.0 --port 8090 --display-error-details --content-base mxcube3/static"
},
"name": "mxcube3",
"version": "0.1.0",
"main": "main.jsx",
The webpack executable is found in ./node_modules/.bin/webpack-dev-server
, which means webpack has been
installed locally in the ./node_modules
directory.
Dependencies are specified in package.json
file:
"dependencies": {
"react": "0.14.0",
"react-dom": "0.14.0",
"isotope-layout": "2.2.2",
"classnames": "2.2.0",
"bootstrap": "3.3.5",
"bootstrap-webpack": "0.0.5",
"jquery": "2.1.4",
"x-editable": "1.5.1"
},
"devDependencies": {
"babel-core": "5.8.29",
"babel-loader": "5.3.2",
"css-loader": "0.21.0",
"exports-loader": "0.6.2",
"extract-text-webpack-plugin": "0.8.2",
"file-loader": "0.8.4",
"http-server": "0.8.5",
"img-loader": "1.2.0",
"imports-loader": "0.6.5",
"json5": "0.4.0",
"less": "2.5.3",
"less-loader": "2.2.1",
"react-bootstrap": "^0.27.3",
"react-hot-loader": "^1.3.0",
"script-loader": "0.6.1",
"style-loader": "0.13.0",
"url-loader": "0.5.6",
"webpack": "1.12.2",
"webpack-dev-server": "1.12.1"
This list is built automatically when node modules are installed thanks to the --saved
option:
npm -i module_name --saved
or
npm -i -D module_name --saved
(-D
means dependency goes to devDependencies
)
All dependencies can be installed in one go using npm install
.