Zero, the beginning of the story
It used to be popular to say, “Give me a dollar for each of the 1.3 billion people in this country, and I’ll be a billionaire.”
Now the boss thinks this is a great idea, so let Zhang SAN to make a web page to collect money, the interface is simple as follows ~
You can see that there are only two logic here, click the red button to start the money, click the blue link to trigger the report. Isn’t it very simple ~
At this time the boss with Zhang SAN said: “don’t worry, the most urgent ~ 5 minutes later I want to see this web page”, at this time zhang SAN’s mood without fluctuations, what software engineering can be maintainable modular directly behind, full of only one sentence “old man to write code is a shuttle “.
First, the original noodle code
Five minutes later, When Zhang SAN finished writing the code, he directly SCP it to the directory of an existing full-static front-end project on the company server. After sending the link to the boss, he felt relieved…
The current code looks something like this:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>The Billionaire Project</title>
</head>
<body style="text-align: center;">
<h1>Help Nodreame become a billionaire</h1>
<div><button id="pay">You don't have to say anything</button></div>
<div><a href="#" id="inform">Report this hot bitch</a></div>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
<script src="Wechat Pay script.js"></script>
<script>
var payElem = document.getElementById('pay')
var informElem = document.getElementById('inform')
var payParams = {} // Payment parameters
var payLogic = _.debounce(function () {
1. Confirm the payment method (only wechat is supported for the time being, and the following payment process is only for learning)
// 2. Determine payment parameters such as amount
// 3. Adjust wechat Pay and wait for a callback
// 4. Enter the re-payment process or the thank you interface for payment completion according to the callback result
}, 200)
var informLogic = _.debounce(function () {
// Write the function logic directly after the popup window
alert('Thank you for your report, the result will be sent to your mobile phone within 7~15 working days')},400)
payElem.onclick = payLogic
informElem.onclick = informLogic
</script>
</body>
</html>
Copy the code
The current payment logic function payLogic seems to be ok, but now the boss is not satisfied: Wechat payment is obviously not enough, what if some local rich people just want to use Alipay, e-bank, paypal or even Bitcoin to pay?
No way, and the boss of the line is not wise, have to liver up to continue a shuttle, and then the code looks like this:
<script src="Wechat Payment library.js"></script>
<script src="Alipay payment library.js"></script>
<script src="Paypal payment library.js"></script>.<script>
var payParams_wx = {} // Wechat payment parameters
var payParams_zfb = {} // Alipay payment parameters
var payParams_pp = {} // Paypal payment parameters
var payLogic = _.debounce(function () {
// 0. Determine the mode of payment
if(wechat Pay) {// 1. Determine payment parameters such as amount
// 2. Set up wechat Pay and wait for a callback
// 3. Enter the re-payment process or the thank you interface for payment completion according to the callback result
} else if(Alipay) {// 1. Determine payment parameters such as amount
// 2. Call up alipay payment and wait for the callback
// 3. Enter the re-payment process or the thank you interface for payment completion according to the callback result
} else if (paypal) {
// 1. Determine payment parameters such as amount
// 2. Call paypal and wait for a callback
// 3. Enter the re-payment process or the thank you interface for payment completion according to the callback result}... },200)
</script>
Copy the code
As the number of payment methods increases, so does the number of scripts, payment parameters, and payment logic introduced. This web page, built for small needs, began to get complicated.
Zhang SAN remembered the overnight code analysis experience of receiving “Ancestral Dung Mountain” temporarily, and decided to try to optimize the project now in order to facilitate the maintenance of the project in the future.
At present, of course, the most important thing is to analyze the current problems of the project:
-
There is space for selflessness: the payment parameters of each payment channel only need to be accessible within the module;
-
Global variable contamination: The operation interface should not be exposed globally;
-
Dependency management: Payment logic has no explicit tag corresponding dependencies
OK, let’s start with the optimization of these three points
Second, the awakening of modular consciousness
Since each payment channel has corresponding payment parameters and payment process logic, the first thought is to split the different logic into different files:
In Java or C# this method does solve the problem of global variable contamination and free space (class wrapping), but in JS the above method does not solve the problem at all, even if the payment parameters and logic functions are separated into different files, they are still exposed to global variables.
In order to solve the problem of global variable pollution and selfless space, some predecessors put forward the original solution — using the immediate execution function to realize the “pseudo-module “. The global variable pollution problem is partially solved (the parameters are no longer exposed, and the method is still mounted globally) :
In addition, if you pass dependencies as arguments to immediate-execution functions (such as lodash), then “pseudo-modules” can “explicitly” depend on a library:
But clearly, this “explicit dependency” doesn’t really solve the problem of dependency management.
If the reference to LoDash is removed from the index.html file, then the execution of the pseudo-module logic must report an error. So the problem here is that the dependency is not explicitly declared at the invocation.
In order to better solve these problems, Zhang SAN decided to check the technical community to find alternative solutions.
Understand community norms
Zhang SAN visited the technology forum and learned about a bunch of community schemes such as CommonJS, AMD, CMD and UMD. He decided to look at them one by one before making a decision.
1. The CommonJS specification
The CommonJS specification is the standard for NodeJS to implement modularity reference.
NodeJS typically defines modules via module.exports or exports and loads modules via require.
Since the module loading design is synchronous, it does not affect the server to read modules from memory or hard disk, but it is not suitable for the browser that needs to download modules asynchronously over the network (in the case of slow network loading, the module loading speed is too slow, resulting in a long blank screen).
In order to learn from CommonJS idea to solve the modular problem of the browser side, we proposed two “asynchronous loading module” browser module specification of AMD and CMD. The two are the standardized output of RequireJS and SeaJS to module definition in the promotion process respectively.
2. AMD specification
1) overview
- Asynchronous Module Definition (AMD) is a specification proposed to solve the modularity problem on the browser.
- Implementation library: require.js
- Main API: module definition & module load require
- Features: Advocating dependence “dependency predisposition”
2) practice
After reading the documents and tutorials, Zhang SAN thinks the solution is feasible, so he reconstructs the payment module based on AMD specifications. The latest file directory is as follows (Pay /lib is a payment library provided by the third party) :
Move the original js logic into main.js, leaving only the introduction of require.js in HTML (requirejs will automatically load main.js) :
<script data-main="./main" src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.min.js"></script>
Copy the code
Then configure the path in main.js (convenient for importing dependencies) & write the original logic via require(dependent array, callback function), and put the payment related logic into the corresponding file and complete the definition according to the AMD specification:
As you can see from define and require above, the AMD specification advocates “dependency preloading”, that is, defining modules and declaring dependencies before writing logic.
When the page is refreshed, you can see that the print statement in the wx.js callback has been executed, indicating that the “dependency preloading” of the AMD specification causes the dependency to be preloaded and its callback to be executed:
3) summary
AMD specification realizes module definition and reference through DEFINE & require, and solves the problem of global pollution and privacy.
At the same time, the explicit management of modules is realized in the form of “dependency front”, but the writing method is relatively tedious.
3. CMD specification
1) overview
- CMD(Common Module Definition) is a Common Module Definition, which is similar to AMD’s goal, but different from AMD’s concept and Module processing method.
- Implementation library: sea-.js
- Main API: module definition define & module load require & use
- Characteristic: respect highly rely on nearby & lazy execution
2) writing
Sea.js is written in the same way as require.js is written, so it will not be rewritten.
define(function(require.exports) {
var a = require('./a'); // Get the interface of module A, write it nearby
a.doSomething();
});
Copy the code
It can be seen from the above that the writing method recommended by CMD is different from AMD’s dependency preloading, but requires a module reference only when it is used near a module function, which is called “dependency proximity “.
The CMD define function is called factory and contains three arguments: require, exports, modules, which are used to refer to modules and expose interfaces in factory.
4. UMD
UMD stands for Universal Module Definition. The goal of this specification is platform generality.
When you need to support both the browser side and the NodeJS side, you usually use the UMD specification packaged code, such as Vue:
The UMD specification’s code feature is that it determines the environment through some of the exports and define judgments, such as those in vue.js:
Community scheme is put forward in the JS is not support modularization period to resolve the problem, although enough but still slightly cumbersome, then zhang remembered the last time to go to a web site under the software in the computers are the swords of the archangels later formed the habit of every letter website, so I decided to first look at the official modular solution.
Four, the official force with
TC39 adds Modules to ECMAScript 2015 to separate files into Scripts & Modules, and uses import & export to import and export Modules. ES Module becomes a common Module solution for browsers and servers.
Zhang SAN looked at ES6 documents and daishen articles, and felt that this standard set by the official will definitely be a big trend in the future, and it will not be a loss to learn a wave and practice in the project, so he immediately took action.
Everything was fine and smooth, and all modules loaded successfully, with the exception of an error when the browser opened index.html:
This is caused by using type=”module” in
yarn global add http-server
http-server
Copy the code
Try accessing the web page using http://127.0.0.1:8080:
This error is already obvious, because the Lodash file is not exposed using ES Module mode. Here, use the CDN of Lodash-ES instead:
import * as _ from 'https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js'
Copy the code
OK Now the page has been successfully loaded and clicking the pay button will trigger the following print:
Zhang SAN stretched a long breath, this wave finally with the latest official way to achieve the front modular, hurriedly and the boss invited credit ~
Fifth, front-end construction
When zhang SAN took the initiative to optimize the project, the boss nodded his head, but the Leader passing by found something wrong and took him aside to ask how the equipment compatibility processing of the project was done
At this time zhang SAN in the mind clunked, finished did not consider ES6 browser compatibility! Cold sweat and even a little bit of a bucket run…
The Leader looked at the code for Zhang SAN and told him that he could use Webpack + Babel to deal with the project, that is, he could use the latest syntax to code happily and prevent the incompatibility of the browser with the earlier version. As time was urgent, the Leader told Zhang SAN Webpack and Babel’s knowledge directly and asked him to try it himself after listening to them.
1. webpack
The previous AMD/CMD schemes require the browser to download a require.js or sea-.js before executing the main logic code, and then run the main logic code. The resolution of module dependencies in these two schemes is done at runtime, so the loading speed will be slow as the project gets larger. Too many module files can also cause slow loading if HTTP/2.0 is not available.
To address this issue, ES6 was officially designed to be as static as possible. The advantage of this is that if you can do dependency analysis at compile time, you can do local optimizations ahead of time. To understand this concept, refer to Java and C++ to compile the executable file directly. You only need to run the executable file when you want to use it.
In the direction of static and precompiled, there is a need for tools to assist in dependency management and optimization. Webpack is one of the many build tools that stands out from the rest, focusing on packaging and providing more and more power to developers through various loaders and plug-ins.
2. Babel
Babel is a JS compiler. The JS standard is updated every year with better syntax and capabilities for developers to use, but many older browsers have compatibility problems if they code directly in the way given by the latest specification.
Babel allows developers to use new, more concise and efficient syntax during coding, and to generate more compatible executable code through migration at release packaging time. The process is as follows:
- Parsing: Code written by developers is parsed into ES6+ AST (Abstract syntax tree)
- Translation: THE ES6+ AST is translated into the more compatible ES5 AST through plug-ins
- Generation: Generate highly compatible final code based on ES5 AST
In actual projects, webpack + Babel can be combined to build a simple project packaging, convenient development can also achieve better compatibility support.
3. Webpack
After hearing what the Leader said, Zhang SAN felt that he was good again and immediately began to practice while checking materials
The first is the webpack installation and minor tweaks to the existing code:
yarn init -y
yarn add -D webpack webpack-cli html-webpack-plugin clean-webpack-plugin
yarn add lodash
Copy the code
A few tweaks to the existing project:
-
Index.html removes script tags that reference main.js
-
Webpack has the ability to compile CommonJS modules into ES Modules.
-
Write the webpack.config.js file as follows:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { mode: 'production'.entry: './main.js'.output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js',},plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: 'index.html'})]};Copy the code
OK, now that the project tuning is complete, you can use NPX WebPack to complete the packaging.
Now you can directly open the compiled index.html file preview page, after testing the “pay” function also works
4. Webpack + Babel7.x
The following dependencies need to be installed to use Babel:
yarn add -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-arrow-functions
yarn add @babel/polyfill
Copy the code
Here’s a breakdown of dependencies:
- Babel-loader: preprocessor for JS code, packaged with Webpack + Babel.
- @babel/core: The Babel core library, a must.
- @babel/preset-env: Configuration targets and useBuiltIns are received in.babelrc to determine the target browser version and polyfill requirements.
- @babel/polyfill: Used to add missing features to the target environment (not recommended after 7.4.0 but temporarily to avoid introducing too many concepts)
Configure babel-loader to handle js files in webpack.config.js:
Then create a.babelrc file in the root directory to store the configuration of Babel:
{
"presets": [["@babel/env",
{
"targets": { // Target platform
"edge": "17"."firefox": "60"."chrome": "67"."safari": "11.1",},// usage indicates the function that needs polyfill automatically based on target
"useBuiltIns": "usage",}]],"plugins": [
"@babel/plugin-transform-arrow-functions",]}Copy the code
To test if Babel really works, add two lines at the end of main.js to compare the results:
[1.2.3].map((n) = > n + 1);
Promise.resolve().finally();
Copy the code
Use the NPX webpack command to pack the webpack.
OK, the arrow function you just added has been converted to a normal function, finally can also search for two results, and the first finnally not shown should be its corresponding polyfill.
So through such finally roughly solved the compatibility problem, Zhang SAN again long breathe a sigh of relief…
Ending
Problem solved! The boss expressed his satisfaction and praised Zhang SAN verbally. Zhang SAN also began to fantasize about his promotion and salary increase…
Late at night, like Zhang SAN’s understanding of the front-end modular as deeper…
The journey is continuing, years (°, °) Blue ✿