1. A brief description of JavaScript modularization 👦
1.1 Why modularity
- Before there is no modular project, often in a JS file there will be a lot of functional code, which makes the file is very large, classification is not strong, naturally not easy to maintain;
- Then we will be a large JS file according to a certain specification divided into several small files will be easy to manage, can improve the reuse, then, can play a divide-and-conquer effect;
- A complex project must have many similar functional modules, and it can be time-consuming and labor-intensive to have to rewrite the modules every time. Similarly, a function others have made the wheel, we will transfer to use it, then it is necessary to cite others to write modules, the premise of reference is to have a unified “open posture”, if everyone has their own writing, so it will be chaotic, so it will lead to modular specifications;
- There are four commonly used JavaScript modularity specifications:
Commonjs
,AMD
,CMD
,ES6 modular
. Personally, ES6 modularity is the mainstream.
1.2 Module definition
- Encapsulate a complex program into several blocks (files) according to certain rules (specifications) and combine them together
- The internal data of a block is relatively private, exposing only interfaces (methods) to communicate with other external modules
So, we find that learning or building a module is about two things: how do you introduce a module? How do I expose modules?
1.3 Definition of modularity
Coding is done module by module, and the whole project is a modular project
1.4 Advantages of modularity
- Easy maintenance of code, better separation, load on demand
- Improve code reuse
- Reduce code coupling (decouple)
- The divide-and-conquer idea — modularity is more than just reuse. Whether or not you reuse a piece of code in the future, you have a good reason to divide and conquer it into a module. (We sometimes have a module in development, but we only use it once, but we still pull it out as a single independent module. This is the idea of divide-and-conquer software engineering, and the same applies to front-end modularization.)
2. The evolution of modularity 👺
2.1 Global Function mode
Module1.js (define a module1)
/ / data
let data1 = 'module one data'
// A function that manipulates data
function foo() {
console.log(`foo() ${data1}`)}function bar() {
console.log(`bar() ${data1}`)}Copy the code
Module2.js (define a module2)
let data2 = 'module two data';
function foo() { // Conflicts with functions in module 1
console.log(`foo() ${data2}`)}Copy the code
Test.html (to use the defined modules 1 and 2)
// if the function conflicts, <script type="text/javascript" SRC ="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> foo() //foo() module two data bar() //bar() module one data </script>Copy the code
Description:
- Global function pattern: Encapsulate different functions into different global functions
- Problem: Global is contaminated and can easily cause naming conflicts (e.g. in modules)
data1
data2
All global variables)
2.2 namespace mode
Module1.js (define a module1)
let moduleOne = {
data: 'module one data',
foo() {
console.log(`foo() The ${this.data}`)
},
bar() {
console.log(`bar() The ${this.data}`)}}Copy the code
Module2.js (define a module2)
let moduleTwo = {
data: 'module two data',
foo() {
console.log(`foo() The ${this.data}`)
},
bar() {
console.log(`bar() The ${this.data}`)}}Copy the code
Test.html (to use the defined modules 1 and 2)
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> moduleOne.foo() //foo() module one data moduleOne.bar() //bar() module one data moduleTwo.foo() //foo() module two data moduleTwo.bar() //bar() module two data moduleOne.data = 'update data' Foo () //foo() update data </script>Copy the code
Description:
- Namespace mode: Simple object encapsulation
- Effect: Reduces global variables (such as two modules’
data
Is not a global variable, but a property of an object. - Problem: Unsafe. Data inside the module can be directly modified
2.3 IIFE mode
Module1.js (define a module1)
(function (window) {
/ / data
let data = 'IIFE module data'
// A function that manipulates data
function foo() { // The function used for exposure
console.log(`foo() ${data}`)}function bar() {// The function used for exposure
console.log(`bar() ${data}`)
otherFun() // Internal call
}
function otherFun() { // Internal private function
console.log('privateFunction go otherFun()')}// Expose foo and bar functions
window.moduleOne = {foo, bar}
})(window)
Copy the code
Test.html (to use the defined module 1)
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript"> moduleOne.foo() //foo() IIFE Module data moduleOne. Bar () //bar() IIFE module data privateFunction go otherFun() //moduleOne. OtherFun is not a function console.log(moduleOne. Data) //undefined because THERE is no data in the exposed moduleOne object moduleOne Foo () IIFE module data </script> </script>Copy the code
Description:
- IIFE mode: Anonymous function self-invocation (closure)
- IIFE: Immediate-Invoked function expression(Invoke function expression immediately)
- What it does: Data is private and can only be manipulated externally by exposed methods
- Question: What if the current module depends on another module? See the IIFE enhanced version below (module depends on jQuery)
2.4 Enhanced IIFE mode
Introduce jquery into the project
Module1.js (define a module1)
(function (window,$) {
/ / data
let data = 'IIFE Strong module data'
// A function that manipulates data
function foo() { // The function used for exposure
console.log(`foo() ${data}`The $()'body').css('background'.'red')}function bar() {// The function used for exposure
console.log(`bar() ${data}`)
otherFun() // Internal call
}
function otherFun() { // Internal private function
console.log('privateFunction go otherFun()')}// Expose foo and bar functions
window.moduleOne = {foo, bar}
})(window,jQuery)
Copy the code
Test.html (to use the defined module 1)
<! <script type="text/javascript" SRC ="jquery-1.10.1.js"></script> <script type="text/javascript" SRC ="module1.js"></script> <script type="text/javascript"> moduleOne. Foo () //foo() IIFE Strong module data and the page background will change color </script>Copy the code
Description:
-
IIFE schema enhancement: Introduce dependencies
-
This is the cornerstone of modern modular implementations. In fact, it’s very similar, both introduction and exposure.
-
The problem: A page needs to introduce more than one JS problem
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module3.js"></script> <script type="text/javascript" src="module4.js"></script> Copy the code
Too many requests: a script tag is a request dependency ambiguity: can’t tell who depends on whom? It’s hard to see which modules have dependencies. Difficult to maintain: Messy internal dependencies are also difficult to maintain
3. Modern modular solutions 🏆
3.1 CommonJS
CommonJS is the specification for server-side modules, which is adopted by Node.js. But it’s also currently available on the browser side, requiring pre-compiled packaging using Browserify.
CommonJS modularity is introduced using require; An exposed way to use module.exports or exports.
CommonJS is based on the server
-
Download and install Node.js
-
Creating a project structure
|-modules |-module1.js |-module2.js |-module3.js |-index.js Copy the code
-
Module1.js (define a module1) defines a module without dependencies that is used to define configuration constants
const newsUrl = 'http://localhost:3000/news'; const commentsUrl = 'http://localhost:3000/comments'; // Exports exports.newsUrl = newsUrl; exports.commentsUrl = commentsUrl; Copy the code
Module2.js (define a module2) defines a dependent module (this module2 also depends on module 1, so module 1 needs to be introduced) to simulate a module that sends requests for data
// Introduce dependencies const m1 = require('./module1'); // Define the method to send the request function getNews(url) { console.log('Send a request for data at:' + url); return 'newsData'; } function getComments(url) { console.log('Send a request for data at:' + url); return 'commentsData'; } const newsData = getNews(m1.newsUrl); const commentsData = getComments(m1.commentsUrl); // exports exposes modules through module.exports module.exports = { newsData, commentsData } Copy the code
Module3.js (define a module3) defines a module to display user data
// Define the method to display the content function showData(data) { console.log('Information to display:' + data); } // exports exposes modules through module.exports module.exports = showData; Copy the code
Index.js (the main module used to start the entire project) needs to import all modules that need to be started
const m2 = require('./modules/module2'); const showData = require('./modules/module3'); showData(m2.newsData); showData(m2.commentsData) Copy the code
Result output:
Send a request for data at HTTP://localhost:3000/newsSend a request for data at HTTP://localhost:3000/commentsInformation to display: newsData Information to display: commentsDataCopy the code
-
Run the node index.js command to run node index.js
CommonJS is browser-based
-
Creating a project structure
|-dist // The directory where the build files are packaged |-src // The directory where the source code is located |-module1.js |-module2.js |-module3.js |-index.js // Apply the main source file (just pack the main module) |-index.html Dist () {dist () {dist () {dist () {dist () {dist (); Copy the code
-
NPM install Browserify -g
-
Module1.js defines a module without dependencies that is used to define configuration constants
// Define configuration constants const newsUrl = 'http://localhost:3000/news'; const commentsUrl = 'http://localhost:3000/comments'; // Expose exports.newsUrl = newsUrl; exports.commentsUrl = commentsUrl; Copy the code
Module2.js defines a dependency module (dependency module 1) to simulate a module that sends requests for data
// Introduce dependencies const m1 = require('./module1'); // Define the method to send the request function getNews(url) { console.log('Send a request for data at:' + url); return 'newsData'; } function getComments(url) { console.log('Send a request for data at:' + url); return 'commentsData'; } const newsData = getNews(m1.newsUrl); const commentsData = getComments(m1.commentsUrl); // Expose the module module.exports = { newsData, commentsData } Copy the code
Module3.js defines a module to display user data
// Define the method to display the content function showData(data) { console.log('Information to display:' + data); } // Expose the module module.exports = showData; Copy the code
Index.js (the main module of the application) The main module used to launch the entire project. All modules that need to be started need to be imported.
const m2 = require('./module2'); const showData = require('./module3'); showData(m2.newsData); showData(m2.commentsData); Copy the code
To process index.js, run the following command: Js -o dist/bundle.js SRC /index.js -o dist/bundle.js outfile dist/bundle.js Dist /bundle.js
Use import in the main index.html page: Import the main module directly, since it has dependencies on the main module, which will automatically parse the packaging process.
<script type="text/javascript" src="dist/bundle.js"></script> Copy the code
Result output:
Send a request for data at HTTP://localhost:3000/newsSend a request for data at HTTP://localhost:3000/commentsInformation to display: newsData Information to display: commentsDataCopy the code
An error is reported if you refer directly to unpackaged index.js:
Introduction: <script SRC ="src/index.js"></script>The error message is UncaughtReferenceError: require is not defined--> Copy the code
We are now using it browser-based. It is only possible to import unpackaged index.js directly in the Node environment, where global methods such as exports, modular, and require are available. Function (exports, require, module, filename, dirname) {}, so importing browserify automatically sets these parameters.
Once and for allmodule.exports
andexports
The difference between:
In NodeJS, Module is a global variable, just as window is a global variable on the browser side.
Module. exports was originally set to {}, exports also pointed to the empty object.
The internal code implementation is:
var module = {
id: 'xxxx'.I must know how to find him
exports: {}, // exports is an empty object
}
var exports = module.exports; //exports is a reference to module.exports
// exports now points to the same memory address as module.exports
Copy the code
Exports is a reference to module.exports. Both refer to the same object.
Exports is not visible, and the exports object we use when writing modules is actually a reference to module.exports. (exports = module.exports).
We can use exports.a = ‘XXX’ or exports.b = function () {} to add methods or properties, which essentially also add to the object to which module.exports points. Exports = {a: ‘XXX ‘}. Exports = {a:’ XXX ‘}. Module. exports does not refer to the same object as it does to module.exports. Nodejs requires the same object that module.exports refers to.
module.exports
function foo() {
console.log('foo');
}
function bar() {
console.log('bar');
}
Copy the code
To expose both functions, you can use exports directly
exports.foo = foo;
exports.bar = bar;
Copy the code
Module.exports can also be assigned
module.exports = {
foo: foo,
bar: bar
}
Copy the code
Exports cannot be directly assigned
/ / error
exports = {
foo: foo,
bar: bar
}
Copy the code
This only changes the reference to exports, not module.exports. Ok, end of play. Let me make that clear.
CommonJS features: Synchronous loading, with cache usage: (catch introduction and expose)
- Exposed to the module
exports
module.exports
- The introduction of the module
Require (path parameter)
Path: Custom module: Path must be set to. /
or../
At the beginning
Third-party module/Built-in module/core module: The path uses the module name
It is mainly used on the server side, but can also run on the browser side and is compiled with Browserify.
3.2 AMD
The CommonJS specification loads modules synchronously, meaning that subsequent operations cannot be performed until the load is complete. Because Node.js is mainly used for server programming, module files usually exist in the local hard disk, so it is fast to load, so there is no problem with synchronous loading. But if it’s browser side, synchronous loading can easily block, and then the AMD specification comes out. The AMD specification loads modules asynchronously and allows you to specify callback functions. Therefore, the browser side will generally use AMD specifications.
AMD is the canonical output of RequireJS’s module definition in its promotion process.
-
Download require.js and import
Website: http://www.requirejs.cn/ making: https://github.com/requirejs/requirejs will require. Js import project: js/libs/require. Js
-
Creating a project structure
|-libs |-require.js |-modules |-alerter.js |-dataService.js |-main.js |-index.html Copy the code
-
The module code dataService that defines require.js (defining a module without dependencies)
define(function () { let msg = 'hello world lyuya'; function dataServer() { return msg.toUpperCase(); } // Expose module return dataServer; }); Copy the code
Define ([‘ module 1’, ‘module 2′,’ module 3′], function (m1, m2, m3) {}
// Do not forget to pass the parameter define(['dataServer'].function (dataServer) { let msg = dataServer(); function alerter() { alert(msg); } return alerter; }); Copy the code
-
App main (entry) : main.js (main module)
// Configure the module path requirejs.config({ baseUrl:'/'.// Configure the common path for all imported modules (base path) // Module id and module path mapping paths : { // Module name (must correspond to the imported module name) : path of the module dataServer: 'modular/dataServer'.// Must not write file suffix, it will automatically complete alerter: 'modular/alerter'.// The library/framework implements its own modularity, defining the names of exposed modules jquery: 'libs/jquery - 1.10.1'}})// The main module can be replaced by requirejs, which is cacheable asynchronously requirejs(['alerter'.'jquery'].function (alerter,$) { alerter(); $('body').css('background'.'pink')});Copy the code
-
Use modules in the page index.html
<! SRC introduces the Requirejs module to resolve the main module --><script data-main="./main" src="./libs/require.js"></script> Copy the code
Summary requireJS features: Asynchronous loading with caching: (catch introduction and exposure)
- Exposed to the module
Used within modulesreturn
- Definition module
Define ([' module name '], function (module exposed content) {})
Require ([' module name '], function (module exposed content) {})
It can be used inside the modulerequire
Defining asynchronous modules - The main module:
Requirejs.config ({}) configures the module path to use
Requirejs ([' module name '], function (module exposed content) {})
- HTML files introduce script tags
<script data-main='app.js' src='require.js'></script>
AMD(Common Module Definition) is primarily used in browsers.
3.3 CMD
CMD is based on CommonJS and AMD. CMD(generic module definition) and AMD(asynchronous module definition) are relatively similar. RequireJS follows the AMD (Asynchronous module definition) specification, while SeaJS follows the CMD (common module definition) specification. SeaJS was founded by Ali, a Native of China.
-
Download sea-.js and import
Website: http://seajs.org/ making: https://github.com/seajs/seajs will sea. Js import project: libs/sea. Js
-
Creating a project structure
|-libs |-sea.js |-modules |-module1.js |-module2.js |-module3.js |-module4.js |-main.js |-index.html Copy the code
-
Define the module code module1.js for sea-.js
define(function (require, exports, module) { /* require: exports of dependent modules: exposed modules module: exposed modules */ const msg = 'moduleone'; function getMsg() { console.log('module1 getMsg() ' + msg); return msg; } // Expose the module module.exports = getMsg; }) Copy the code
module2.js
define(function (require, exports, module) { exports.msg1 = 'lyuya'; exports.msg2 = 'hello'; }) Copy the code
module3.js
define(function (require, exports, module) { // Synchronously import modules const getMsg = require('./module1'); let msg = getMsg(); msg = msg.toUpperCase(); module.exports = { msg } }) Copy the code
module4.js
// Asynchronously import modules require.async('./module2'.function (m2) { console.log(m2.msg1, m2.msg2); }) console.log('Module4 executed ~~~'); }) Copy the code
Main.js: main (entry) module
define(function (require) { const m3 = require('./module3'); require('./module4'); console.log(m3.msg); }) Copy the code
index.html:
<script type="text/javascript" src="libs/sea.js"></script> <script type="text/javascript"> seajs.use('./modules/main') </script> Copy the code
Result output:
module1 getMsg() moduleone =====module1.js:12Module4 Executed ~~~ =====module4.js:9 MODULEONE =====main.js:9 lyuya hello =====module4.js:7 Copy the code
Conclusion seaJS
Features: asynchronous loading, with cache usage:
-
Define module define(function (require, exports, module) {})
-
Async ([‘ module name ‘], function (module exposed content) {})
-
Exports exposed module module. Exports
-
HTML file introduces the script tag
Seajs, like RequireJS, is primarily used in browsers. In fact, both are rarely used. Commonjs and es6 modularity will be introduced soon.
3.4 ES6 Modularization ⭐⭐
ES6 modular appearance, to the front end of greater convenience. Designed to be a common modular solution for both browsers and servers, but primarily for the browser side. Its module functions are mainly composed of two commands: export and import. Many projects now use the ES6 modularity specification.
-
Define the package.json file
-
Install babel-CLI, babel-PRESET – ES2015 and Browserify
NPM install Babel -cli browserify -g NPM install Babel -es2015 –save-dev preset (preset to all plug-ins to convert ES6 to ES5)
-
Define the.babelrc file
{ "presets": ["es2015"]}Copy the code
-
The code module1.js separately exposes the complete definition (variable or function definition) required later.
export function foo() { console.log('module1 foo()'); } export function bar() { console.log('module1 bar()'); } export const DATA_ARR = [1.3.5.1] Copy the code
Module2.js unified exposes an object, and the data to be exposed is added as the object’s properties/methods
let data = 'module2 data' function fun1() { console.log('module2 fun1() ' + data); } function fun2() { console.log('module2 fun2() ' + data); } export {fun1, fun2} Copy the code
Module3.js silent exposure exposes only one content, the essence of default exposure: define the default variable, assign the following value to the default variable, and expose it
export default { name: 'Tom'.setName: function (name) { this.name = name } } Copy the code
The main app.js module imports modules with import
import {foo, bar} from './module1' import {DATA_ARR} from './module1' import {fun1, fun2} from './module2' import person from './module3' import $ from 'jquery' // Import third-party jQuery module NPM install jquery@1 --save $('body').css('background'.'red') foo() bar() console.log(DATA_ARR); fun1() fun2() person.setName('JACK') console.log(person.name); Copy the code
Output result:
module1 foo() module1 bar() [1.3.5.1] module2 fun1() module2 fun2() JACK Copy the code
-
Compile ES6 to ES5 code using Babel (but with CommonJS syntax) : Babel src-d build builds JS with Browserify: Browserify build/app.js -o dist/bundle.js
Summary ES6 features: Dynamic import (load on demand), no caching usage: catch import and expose
- Introduce module use
import
- For uniform exposure/separate exposure
Import {module exposed contents} from 'module path';
orimport * as m1 from './module1'
The essence of these two exposures is the object, which can only be received in the way of object deconstruction assignment - For default exposure
Direct use ofImport module exposed contents from 'module path'
Default exposure, exposure of any data type, exposure of what data type, what data type to receive
- For uniform exposure/separate exposure
- Use of exposed modules
export
- Separate exposure (rarely used)
- Unified exposure (exposing multiple contents)
- Default exposure (exposing individual content)
It is mainly used in browsers, but also on the server side. However, the current browser and server do not support ES6 modular syntax, so use tools to compile and run
babel
Compile es6-ES5 (ES6’s modularity syntax intocommonjs
)browserify
willcommonjs
The syntax is compiled into a syntax that the browser can recognize
3. Modular extension reading
Front-end modular development history that Javascript modular programming @ ruan The difference between zhihu column | AMD and CMD
4. What I want to say 🙈 🙈
Since talking about modularization, actually I want to talk more about modularization and componentization. Both of these concepts are already common in the front-end world.
There is modularization before componentization. Componentization is an evolution, a variation, based on the idea of modularization. So, we see a phrase in software engineering systems: modularity is the cornerstone of componentization.
The ideas of componentization and modularization are divide-and-conquer ideas. But there are subtle distinctions, and they have different priorities.
Componentization is a more UI-oriented “building block” that can display content independently, such as a header component of a page that contains structural HTML, style CSS, logical JS, and a combination of static resource images. A page is made up of many components, like a “castle” of many “building blocks”; Modularity is more inclined to function or data encapsulation, generally composed of several components or 1 component with a certain function of the assembly;
To quote the understanding of componentization by @Zhang Yunlong “👈 Daishen” :
title
header
Combined with the modular development mentioned earlier, the front-end project can be divided into several development concepts:
application
page
page
component
Not only that, multi-terminal has become an inevitable trend at present and in the future, mobile terminal, PC terminal, touch screen, intelligent equipment, Internet of Things and so on, I believe that there will be a better solution for the front-end in the cross-terminal field.
But if we look at software engineering as a whole, we realize a painful truth: the front end engineer’s role in systems engineering as a whole is far too low. The front end is upstream of the system software (user entry), so no other system will call on the services of the front end system. The back-end is in the lower reaches of software development. On the one hand, the back-end provides interface services for the front end, and on the other hand, requests services from the middle background and data layer. The docking level will be more and the status will be higher. As a result, the front-end of every demand assessment is often the last hurdle, because the upstream relies on the downstream, so the downstream can only take the lead. As a whole, the front-end’s participation in the business is too low.
Even 2019. There are still a lot of teams that categorize front-end development under product or design jobs, well, I don’t know what to say.
What is the organizational structure at the front end of your company? 🙋 🙋
The future of the front end will not be bad, just like in the field of artificial intelligence and big data, not only the front end, the front end can be integrated and refined.
In the words of an ant partner, the main contradiction in the first two years was between the increasingly new technology and the immobile front-end program, but now the main contradiction has changed to the contradiction between the front-end’s growing demand for engineering status and the limitations of front-end engineering. (This person definitely scored high in politics for graduate school!)
The rise of the phase senior | for the front-end engineering and programming
Under such new contradictions, we must change passivity into initiative and acceptance into influence.
All right, study hard and be a PI person. It takes a good blacksmith to make steel. Confidence ~ 🙌
Today is March 8, 2019, the second day of the second lunar month (Longtou), Friday, cloudy weather. I wish every female compatriots in Shenzhen a happy holiday forever beautiful, bless male compatriots single happiness ~biubiu💖💗💙💚 💚 💖 💖
I will take a bath and go to bed early tonight. I will go to Peking University Shenzhen Hospital tomorrow.
The author of this document: Lyu Ya
CSDN home page: https://blog.csdn.net/LY_code
The nuggets homepage: https://juejin.cn/user/3667626521532855
If there are mistakes, timely mention, learn together, progress together. thank you 😝 😝 😝