The original intention of this article was to implement one of the most common build tools we use in our work, WebPack, although the build tool we implemented is far from the real thing. This is just a simple implementation, so if you’re interested, you can keep going.

However, before talking about automated construction, we still need to tell the history of modular development, which is a history that front-end ER has experienced, and it is worth us and everyone to review again!!

modular

Modularity refers to the decomposition of a complex system into modules for easy writing

The namespace

Developing web pages is about organizing code in namespaces

<script src="jquery.js">
Copy the code
  • Namespace conflicts. Two libraries may use the same name
  • Failed to properly manage project dependencies and releases
  • There is no easy way to control the loading order of dependencies

CommonJS

CommonJS is a JavaScript model management that uses a lot of JavaScript. The core idea is to load dependent modules synchronously through the require method and export exposed interfaces through module.exports

usage

The code for importing and exporting with CommonJS is as follows:

// import const A = require('./a.js'); fn(); // exports module.exports = a.exports;Copy the code
Principle of implementation
// a.js
module.exports = 'Just met you';

//b.js
const fs = require('fs'); // CommonJS simple implementationfunctionReq (pathName) {// Content indicates the content of the filelet content = fs.readFileSync(pathName, 'utf8'); // The last argument is the content body of the functionlet fn = new Function('exports'.'require'.'module'.'__filename'.'__dirname', content+'\n return module.exports');
    letmodule = { exports: {} }; // exports returns the value of module.exportsreturn fn(module.exports, req, module, __filename, __dirname);
}
const str = req('./a.js'); // Import a module console.log(STR); //'Just met you'
Copy the code

AMD

AMD is also a JavaScript modularization specification that differs from CommonJS in that it loads dependent modules asynchronously. The AMD specification mainly aims to solve the modularity problem for the browser environment, and the most representative implementation is RequireJS

The advantages of AMD

  • It runs directly in the browser without converting code
  • Multiple dependencies can be loaded
  • The code runs in the browser environment and Node environment

The disadvantage of AMD

  • The Js runtime environment does not support AMD natively, so it needs to import the library that implements AMD in order to use it properly.
usage
// define module define('song', [], () => {
    return 'Confession Balloon';
});
define('singer'['song'.'album'], (song, album) => {// Depends on the song and album moduleslet singer = 'Jay Chou';
    return `${singer}the${song}Belongs to the album${album}"`; }); define('album', [], () => {
    return 'Bedtime Stories'; }); // require require(['singer'], singer => { console.log(singer); // Jay Chou's confession balloon belongs to the album bed story});Copy the code
Principle of implementation

RequireJS has two methods, one is define and the other is require, so let’s define two functions first

letfactories = {}; // Define module define three parameters: 1. Module name 2. dependencies 3. factory functionsfunctiondefine(name, depend, factory) { factories[name] = factory; factory.depend = depend; } // Use the module via requirefunction require(modules, callback) {
    letResult = modules.map(mod => {// returns an array of resultsletfactory = factories[mod]; // Get the corresponding function of the modulelet exports;
        letdepend = factory.depend; // Get the dependency on the function ['a']
        
        // require(['song'.'album'].function(song,album) {}) may have many dependencies require(depend, () => {// recurserequire exports = factory.apply(null, arguments); });returnexports; // exports returns a value like this:'Confession Balloon' , 'Bedtime Stories'}); callback.apply(null, result); // result is an array of results, so apply}Copy the code

★ Define is used to reserve the function that defines the module

This function is executed when required, and the result of the execution is passed to the callback

ES6 modular

  • ES6 modularization is the JS modularization specification proposed by ECMA, which realizes modularization at the level of language
  • Most importantly, it will replace CommonJS and AMD specifications as a common modular solution for browsers and servers
// import import {each,... } from'underscore.js'; Var _ = require('underscore.js'); // amd global import // exportexport{each, map, ... }; Module. Exports = _; // AMD global exposureCopy the code

Small regret: The ES6 module is the ultimate modular solution, but it has the disadvantage that it currently does not run directly in most JavaScript environments and must be converted to standard ES5 by tools

Well, the above content is the general development process of modularization

Today we start to use ES6 and other advanced syntax in our work to develop projects, but just like the “little regret” of ES6 modularity, there are some circumstances that do not support it, so in the spirit of national attitude, we need to convert it into recognizable code

As a result of this gradual emergence of automated build, simply put, is to convert source code to published online executable JS, CSS, HTML code, of course this is the main purpose, in addition to many other uses (file optimization, automatic refresh, module merge, code verification, etc.), I will not go into detail here. Let’s get right to the point and talk about webpack, one of the most popular build tools available today

webpack

Webpack is a tool to package modular JS, in Webpack all files are modules, through loader to convert files, through plugin to inject hooks, and finally output files composed of multiple modules. Webpack focuses on building modular projects

Install webpack

Install into the project

  • You need to initialize the project with NPM init
  • You are advised to install Node 8.2 or later
NPM I webpack-cli NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack- NPM I webpack webpack-cli -DCopy the code

★ NPM i-d is short for NPM install –save-dev. It refers to the devDependencies that install the module and save it to package.json

Install to global

npm i webpack -g
Copy the code

Note: It is recommended to install to the current project because it prevents conflicts caused by different projects relying on different versions of WebPack

Using webpack

SRC entry files are packaged by default

// Node v8.2 will have a NPX // NPX will execute the files in bin NPX webpack // Set mode to development mode, Packed files are not compressed NPX webpack -- Mode developmentCopy the code

This is done using webpack, which is the default index.js file in the SRC directory

SRC - index.js - a.js // a.js module.exports ='Just met you';
// index.js
let str = require('./a.js');
console.log(str);
Copy the code

The packaged code generates a dist directory, creates a main.js, and puts the packaged code in it, so let’s see what the packaged code is

What it looks like when compiled

Dist - - main.js //function (modules) {
    functionVar Module = {exports: {}}; require(moduleId) {var module = {exports: {}}; modules[moduleId].call(module.exports, module, module.exports, require);return module.exports;
    }
    return require("./src/index.js"); ({})"./src/index.js": (function (module, exports, require) {
        eval("let str = require(/*! ./a.js */ \"./src/a.js\"); \n\nconsole.log(str); \n\n//# sourceURL=webpack:///./src/index.js?");
    }),
    "./src/a.js": (function (module, exports) {
        eval(Module. exports = 'exports '; \n\n//# sourceURL=webpack:///./src/a.js?"); })});Copy the code
  • The whole package is in a self-executing function, modules, which is the object {} below ().
  • Modules [moduleId].call() evaluates the key in modules[‘./ SRC /index.js’] ()

Try writing one

Try to try, according to the packaged core code we will also implement a look, to make a similar Webpack scaffolding, no more talk, do it

// Pack directory pack-bin pack.jsCopy the code
  • First we will create a folder called Pack with the corresponding files in it
  • Then we want to pack directly from the command line
    • It must be a module to execute
    • NPM init -y in the pack directory
    • Change the pack path under bin to “bin/pack.js” in the initialized package.json file.
    • Reference the pack.js command to the NPM global, and then execute the pack command can be used directly
    • NPM link in the pack directory to place the pack package in the NPM global (sudo for MAC)
    • Every time you change pack.js, you need to re-npm link
  • You can pack again from the command line

The above items are an overview of the process, so let’s start implementing the main logic in Pack.js

// pack.js
#! /usr/bin/env node    // If the file is executed under Node, an error will be reportedlet entry = './src/index.js'; // Import filelet output = './dist/main.js'// Export filelet fs = require('fs');
let path = require('path');
let script = fs.readFileSync(entry, 'utf8');
letresults = []; Script = script.replace(/require\(['"] (. +?) ['"]\)/g, function() {
    let name = path.join('./src/',  arguments[1]);     // ./src/a.js
    let content = fs.readFileSync(name, 'utf8');
    results.push({
        name,
        content
    });
    return `require('${name}') `; // require('./src/a.js') }); // let ejs = require('ejs'); Dist /main.js let template = '(function (modules) {function require(moduleId) {var module = { exports: {} }; modules[moduleId].call(module.exports, module, module.exports, require); return module.exports; } return require("<%-entry%>"); ({})"<%-entry%>": (function (module, exports, require) { eval(\`<%-script%>\`); }) <%for(let i=0; i
      
       ,"
      ;><%-mod.name%>": (function (module, exports, require) {
                eval(\`<%-mod.content%>\`);
            })
        <%}%>
    });
`;

// result为替换后的结果,最终要写到output中
let result = ejs.render(template, {
    entry,
    script,
    results
});

try {
    fs.writeFileSync(output, result);
} catch(e) {
    console.log('编译失败', e);
}
console.log('编译成功');
Copy the code

The ejS template engine is used above, and the following is a simple usage

let name = 'Jay Chou'; console.log(<a><%-name%></a>); <a> </ A >Copy the code

Implementing a Loader

Let’s write another loader, loader is actually a function, let’s load a CSS style to compile see. Add a style.css file in the SRC directory

// style.css
* {
    margin: 0;
    padding: 0;
}
body {
    background: #0cc;} // index.js imports CSS fileslet str = require('./a.js');
require('./style.css');
console.log(str);
Copy the code

Add a style-loader to compile the CSS file according to the code

// pack.js // omit...letresults = []; // use style-loader +let styleLoader = function(SRC) {// SRC is the content of the stylereturn `
        let style = document.createElement('style');
        style.innerHTML = ${JSON.stringify(src).replace(/(\\r)? \\n/g, '')}; document.head.appendChild(style); `; +}; Script = script.replace(/require\(['"] (. +?) ['"]\)/g, function() { let name = path.join('src', arguments[1]); // ./src/a.js let content = fs.readFileSync(name, 'utf8'); + if (/\.css$/.test(name)) {+ content = styleLoader(content); + } results.push({ name, content }); return `require('${name}') `; // require('./src/a.js') });Copy the code

Here we use json.stringify to handle string newlines, as shown below

body {
    background: #0cc;
}
Copy the code

However, there is a slight flaw in replace which is that it takes a newline character, so replace handles the \r\n newline after stringify (MAC only \n).

Here, a simple compilation tool is completed, although it is not perfect, but also opens up the vision of how we commonly used Webpack from 0 to 1 process.

Of course, our implementation is not comparable to the mature Webpack, and the implementation of Webpack is more than that. I am also trying to continue to study how webpack is implemented, and I am also slowly learning about ast.

Hopefully, the next time I write it, I will give you a better implementation of WebPack!

Thank you for watching. Best regards!