blog icon indicating copy to clipboard operation
blog copied to clipboard

浅探webpack的module

Open renaesop opened this issue 8 years ago • 0 comments

本文主要说的是webpack编译中的make阶段,或者说webpack生成modules的整个流程。

webpack的make过程还是比较复杂的,主要流程如下

webpack-make

1.核心

我认为make阶段的核心是依赖,实际上,make阶段的一切模块都由依赖构建出来。

首先是entry。我们在webpack的配置文件中可以指定entry,entry中指定的文件可以是单个、也可以是多个(字符串),举个entry给多个文件的例子(伪代码):

指定配置::

{
     entry: {
        main: ['./src/a', './src/b'],
    }
}


webpack::

const entryMainModule = {
    dependencies: {
            './src/a': Dep('./src/a'),
            './src/b':Dep('./src/b')
    },
    // webpack的source是在make结束之后才被调用的
    source() {
        return `
                     __webpack_require__(${this.dependencies['./src/a'].id});
                     module.exports =  __webpack_require__(${this.dependencies['./src/b'].id});
                    `;
    }
}

可以看出,webpack会把entry转换成一个新增的MultipleModule,entry数组里的模块被指定为依赖。当然如果是指定的单个文件,则entry直接转化为 NormalModule。不过还是会有转化为Dependency的过程。总结如下:

entry --> 是否单文件 --是--> entry设定为为SingleModuleDependency --> 生成NormalModule(会执行loader)
             |
             否--> entry设定为MultipleModuleDependency --> 生成MultipleModule(无loader, parse)

2. 对引用路径的处理

造过构建工具轮子的人都知道,引用路径的处理是一块很麻烦的事情。webpack没有像babel他们,使用node的核心模块module, path等来简化一些处理,而是自己造了一个类Resolver, 这个类是支持插件的。不管是loader还是contextModule还是normalModule都是他处理,最后处理的结果都是把相对路径转化为绝对路径。(比较常规)

由于loader也是Resolver解析的,所以loader的路径也就可以很灵活了,不一定非要放到node_modules里面(好像没人提这点)。

另外,resolve是比较蠢的,比如引用./a, 这货会同时找文件夹和文件,文件还会带上extension找。

此外,每个module都会独立地匹配loader,并不存在什么缓存。

3. 对loader的处理

webpack的loader有pitch特性, 所以

['style', 'picth-enabled', 'css']

加载loader的顺序是从左到右的,虽然执行的顺序是相反的。

loader可以指定raw属性,选择传过来的source是否是buffer。

loader具有一些很实用的方法可以调用,尤其是emitFileemitFile的作用是给当前module附加一个assets(正常情况下assets其实是很后面的阶段才生成的),有没有一种钦定的赶脚。在我们常用的file-loader中就是用了这个特性。

4. loader之后的parse

很少有文章会提到webpack的parse,实际上这个才是真正意义上的“编译”嘛。在loader执行完之后,webpack会使用acorn将模块解析成ast,然后去遍历ast。

Parser模块支持插件,在遍历大多数语句的时候,都存在钩子。实际上给模块添加依赖就是遍历到"call commonjs:require"这种语句的时候,插件完成的,并且也将依赖模块的名字改为动态的了。

webpack的Parser相比babel还是很弱的。模块自身处理完了之后,都会去处理自身的依赖,也就是把依赖转化为模块。

5. module的大致结构

module: {
    request: './src/a',
    context: 'xxxx',
    loaders: [],
    id: 0, // entry的id为0
    source() {return xxxx},
    dependencies: {
          './b': {
               request: './src/a',
               context: 'xxxx',
               module: module,
          }
    }
}

renaesop avatar Oct 18 '16 10:10 renaesop