模拟webpack

1
2
3
   我们常用的webpack能够解析ES6的module.export、import、require等关键词,将依赖放进一个自执行函数中,依次执行。

要想模拟webpack,首先在项目中创建一个pack文件,里面放入bin文件夹,然后在bin文件夹中创建一个pack.js,通过npm link,将其链入package.json中的bin命令。

pack.js代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env node
//解释程序的作用
//这个文件是描述如何打包的
let entry = './src/index.js';//入口文件
let output = './dist/main.js';//出口文件
let fs = require('fs');
let script = fs.readFileSync(entry, 'utf8');
let path = require('path');
let modules = [];
let styleLoader = function(resource) {//将css代码插入到html中的style标签中
    let cssText = JSON.stringify(resource).replace(/(\n|\r)/g,'');//???为啥我的要写成这样
    return `
        let style = document.createElement('style');
        style.innerText = ${cssText};
        document.head.appendChild(style)
    `;
};
//处理依赖关系
script = script.replace(/require\(['"](.+?)['"]\)/g, function(){
    let reqname = path.join('./src', arguments[1]);
    let content = fs.readFileSync(reqname, 'utf8');
    if (/\.css$/.test(reqname)) {
        content = styleLoader(content);
    }
    modules.push({reqname, content});
    return `require('${reqname}')`;
});
let ejs = require('ejs');

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(var i=0; i<modules.length; i++){
        let module = modules[i];%>,
        "<%-module.reqname%>":
        (function(module, exports, require){
            eval(`<%-module.content%>`);
        })
    <%}%>
})`;
let result = ejs.render(template, {script, entry, modules});
fs.writeFileSync(output, result);
console.log('------------编译成功!------------');

相关知识点解析:

1、amd cmd规范

2、__filename

当前模块的文件名。 这是当前模块文件的已解析的绝对路径。

3、new Function 构造器创建函数的使用方法和特点

注意: 使用Function构造器生成的函数,并不会在创建它们的上下文中创建闭包;它们一般在全局作用域中被创建。 当运行这些函数的时候,它们只能访问自己的本地变量和全局变量,不能访问Function构造器被调用生成的上下文的作用域。 这和使用带有函数表达式代码的 eval 不同。

4、fs node功能

是node中的文件系统(file system)

readFileSync方法:同步的读取文件内容,异步的是readFile。

5、map foreach 区别

1)foreach 返回值是undefined,不可以链式调用;
2)map 会返回一个新数组。原数组不变;
3)不能终止循环;

举例: [“1”, “2”, “3”].map(parseInt); //结果 [1, NaN, NaN] 相当于 parseInt(‘1’, 0)  parseInt(‘2’, 1) parseInt(‘3’, 2) 如果想得到[1, 2,3]应该这么做:

1
2
3
4
5

function returnInt(element){
  return parseInt(element,10);
}
["1", "2", "3"].map(returnInt);  

这主要是因为 parseInt()默认有两个参数,第二个参数是进制数。当parseInt没有传入参数的时候,而map()中的回调函数时候, 会给它传三个参数,第二个参数就是索引,明显不正确,所以返回NaN了。

map的回调接受三个参数,第一个是当前的值,第二个是当前的索引,

而parseInt传的第二个参数小于等于第一个参数的时候会返回NaN, 所以是[1, NaN, NaN]

6、apply

1)改变作用域

2)扩充函数参数

7、npx node8.2版本的命令

npx webpack 执行node_module里面的.bin中的webpack

9、ejs包

一款js模板引擎,(=转义 -不转义)

10、bin 命令

11、replace用法解析