Web Front-end Basics - Modern Javascript
- SPA Engineering Methodology
- Toolsets
- ES6 Modules
- Grunt - Javascript Task Runner
- Webpack - Assets Bundler
- Babel - ES Compiler
- Regular Expressions in Javascript
SPA Engineering Methodology
- Source file compiling:
- Typescript files compiling
- SASS file compiling
- Compiled file polyfill
- javascript polyfill to support backward compitable for ES version earlier than ES5
- CSS prefixing to support in multiple browsers
- Bundling: scan all files from entry point and bundle them into one single file of different type.
- Minify: Compress the bundled file into a minion size for better performance
Toolsets
NPM
npm init will initiate a npm project and create a package.json file.
2 types of npm packages:
- Dependency package:
--save, optional for newer npm versions. - Development tools:
--save-dev
2 types of intallation:
- Regular installs:
- Global installs: a package should be installed globally when it has commandline interface. And this requires the admin permissions.
- For example:
nodemon
- For example:
Local dev dependency package do not work the way as global installed package do. We can specify npm scripts in scripts in package.json file.
Usually we require core modules first, then 3rd-party modules and last our own modules.
Package Versioning
Take version ^1.18.11 in package.json for example:
- Major version:
1is the majar version. Should expects breaking changes, it might impact the codes we already have. - Minor version:
18is the minor version here. It introduces some new features but no breaking changes, it should always be backward compatitle. - Patch version:
11only for bug fixes. - Symbol comes in front of the version number specifies what releases we accept:
^symbol is specified by npm by default means we acceptminorandpatchreleases for update.~symbol means we only acceptpatchreleases.*symbol accepts all version releases.
package-lock.json describes detailed information including exact version, source, integrity for each direct and indirect dependency in order to make sure you are getting the exact same dependency tree if you’re using the same package-lock.json file. This file is generated for any operations where npm modifies either node_modules tree or package.json file, such as npm install. This file is intended to be committed into source control.
- babel: Convert ES6/ESNext back to ES5
- webpack: module bundler, bundle all modules into a single file
NPM commands notes:
npm install/uninstall (--save): save the package/tool as dependency.- use
@{version-number}to install a package with certain number.
- use
npm install/uninstall --save-dev: save the package/tool as development dependency.npm install/uninstall --global: install the package as cli and accessible everywhere.npm run {script-name}: execute the pre-defined script inpackage.jsonfile.startis the standard name that keeps running in the background and update the browser as soons as we change our code. Withstart, we don’t need to specifyrunbefore it.npm outdatedwill list out all packages that are outdated.npm update {package-name}will update the package according to what symbols specified in front of the version number.
NPM scripts
devbuildbabel-polyfill: add codes for functions or sytax which are not present in ES5 in order to make sure it works in ES5.
ES6 Modules
export defaultworks together withimport x from 'xxxxx';exportmultiple things works together withimport {x, y, z} from 'xxxxx'
Grunt - Javascript Task Runner
Installation
1 | $ npm install -g grunt-cli |
Note that grunt-cli is just the command line interface to control grunt. To get grunt actually work, we need to install
grunt,gruntpluginsand other modules to be installed.
For example, we install grunt and the JSHint task module:
1 | $ npm install grunt --save-dev |
We can checkout available gruntplugins at plugins page.
Gruntfile
The Gruntfile.js file is a valid JavaScript or CoffeeScript file that belongs in the root directory of your project, next to the package.json file, and should be committed with your project source. A Gruntfile is comprised of the following parts:
- The
wrapperfunction - Project and task configuration
- Loading Grunt plugins and tasks
- Custom tasks
Let’s see an example gruntfile:
1 | module.exports = function (grunt) { |
Every Gruntfile (and gruntplugin) uses this basic format, and all of your Grunt code must be specified inside this function:
1 | module.exports = function (grunt) { |
<% %>template strings may reference any config properties.- You may store any arbitrary data inside of the configuration object, and as long as it doesn’t conflict with properties your tasks require
Grunt plugins and tasks
grunt-contrib-uglify plugin’s uglify task expects its configuration to be specified in a property of the same name in config object:
1 | // Project configuration. |
Many commonly used tasks like concatenation, minification and linting are available as grunt plugins. As long as a plugin is specified in package.json as a dependency, and has been installed via npm install, it may be enabled inside your Gruntfile with a simple command like uglify above.
Default tasks
You can configure Grunt to run one or more tasks by default by defining a default task. In the following example, running grunt at the command line without specifying a task will run the uglify task:
1 | // Default task(s). |
This is functionally the same as explicitly running grunt uglify or even grunt default. Any number of tasks (with or without arguments) may be specified in the array.
Custom tasks
If your project requires tasks not provided by a Grunt plugin, you may define custom tasks right inside the Gruntfile. For example, this Gruntfile defines a completely custom default task that doesn’t even utilize task configuration:
1 | module.exports = function (grunt) { |
Custom project-specific tasks don’t need to be defined in the Gruntfile; they may be defined in external .js files and loaded via the grunt.loadTasks method.
See a list of gruntplugins at npm package.
Webpack - Assets Bundler
Firstly, we need to have a webpack.config.js file created under the project root directory which uses nodejs sytax to export an object:
1 | const path = require("path"); |
Webpack 4 core concepts:
entry point: we useentryproperty of the config file to tell webpack the starting point of the bundling. Multiple entry files are supported.output: we useoutputproperty in the config object to tell webpack where to save the bundle file.outputproperty receives an object value where we specifypathandfilename, value ofpathneeds to be an absolute path.loaders: allow us to import or load all kinds of files and to also process them. Like converting SASS to CSS code or converting ES6 code to ES5 javascript. For babel, we needbabel-loaderbecausebabelis the one that coverts ES6 back to ES5.plugins: plug-ins allow us to do complex processing of input files likehtml-webpack-pluginallows us to take an html template file and inject the bundled file automatically into that html file.
Packages around webpack to use:
webpack-cli: the commandline interface for webpack, we can use various arguments to specify options at npm scritps, such as--mode development/productionwebpack: the webpack core packagewebpack-dev-server: a seperated package that will automatically bundle all our javascript files and reload the app in the browser whenever we change a file. To use it, we need to config thedevServerproperty which receives an object value in thewebpack.config.jsfile:contentBase: the folder from which webpack should serve our filesport: the port to listen.--mode: the mode taken fromwebpack-cliand apply it towebpack-dev-server--open: to open the browser when the script gets executed.- 1 thing to note for
webpack-dev-serveris that when we’re runningwebpack-dev-server, webpack will bundle our modules together but it will actually not write it to a file on disk, instead it will automatically inject it into the html.webpack-dev-serveractually takes thepathfield ofoutputto look for the html file to inject. To fix it, we need to make sure aindex.htmlfile exists under the target folder.
html-webpack-plugin: plugins that simplifies creation of HTML files to serve your bundles. To use it, we need to configure it inwebpack.config.jsfile and construct it inplugins, where we also pass in an config object in the constructor function.filename: path of the final created html file which is relative to theoutput.pathpropertytemplate: path of the source html file, if not providingtemplatevalue, the plugin will try to create the html file from scrath.
Babel - ES Compiler
Babel is a javascript compiler in order to use next generation javascript. To use babel, we need:
babel-core: contains core functionality of the compiler.babel-preset-env: babel preset that will take care all modern javascript features are converted back to ES5.babel-loader: needed for webpack in order to actually load babel files.
To use it with webpack, first we need to specify the babel-loader for webpack in webpack.config.js, under the module property:
1 | module: { |
module.rules receives a bunch of loaders config object, for each of the loader:
test: property which receves a regex object.exclude: property receives a regex object to exclude the matched files or folders.useproperty:loader: to specify which loader to use, in this case is thebabel-loaderinstalled earlier.
This whole section of webpack.config.js means to use babel-loader package to process all .js files outside of node_modules folder. Next, we need to configure babel itself in a seperated .babelrc file where includes a JSON object:
1 | { |
Here we use the env preset. This will let babel automatically figure out which ES6 features need to be converted in order to work on the last five versions of all the browsers.
Polyfill
However, there are some features that can not really be converted because they were simply not present in the ES5 language, and so there is no way to simply convert them back from ES6 to ES5. These need to be polyfilled, which basically adds this to the code. For example, the promise object is not present in ES5, but we can write some ES5 code that basically implements the promise in ES5. To use pollyfill, we need to install babel-polyfill, and this is not a development tool because it will finally go to the code. With babel-polyfill in place, we should include it in webpack.config.js entry point in order to bundle it together with other code:
1 | entry: ["babel-polyfill", "./src/js/index.js"], |
Regular Expressions in Javascript
Regular expressions are objects in javascript, we can create regular expression objects in one of the 2 ways:
- Using a regular expression literal: which consists of a pattern enclosed between slashes
let re = /ab+c/;. Regular expression literals provide compilation of the regular expression when the script is loaded. If the regular expression remains constant, using this can improve performance. - Or calling the constructor function of the
RegExpobjectlet re = new RegExp('ab+c');. Using the constructor function provides runtime compilation of the regular expression. Use the constructor function when you know the regular expression pattern will be changing.
Advanced searching with flags.
Regular expressions have six optional flags that allow for functionality like global and case insensitive searching. These flags can be used separately or together in any order, and are included as part of the regular expression.
| Flag | Description |
|---|---|
| g | Global search |
| i | Case-insensitive search |
| m | Multi-line search |
| s | Allows . to match newline characters |
| u | “unicode”; treat a pattern as a sequence of unicode code points |
| y | Perform a “sticky” search that matches starting at the current position in the target string. |
To include a flag with the regular expression, use this syntax:
1 | let re = /pattern/afgls; |
or
1 | let re = new RegExp("pattern", "flags"); |
Notes when parsing string values to regex objects:
- When using
patternorng-patternto receive a string value for regex, no need to add the^and$sign. However, it is required to add^and&sign to be added when using literal string to create regex objects. - Compared with regex object in javascript:
/^[^\\/:*?\"]$/, in string value it needs to use quadra\to escape the\char 2 times. Finally it looks like"[^\\\\/:*?\"<>|]+". - HTML5
<textarea>does not supportpatternattribute.ng-patterncan be used to<textarea>