2-2 模块化开发简答

118 阅读3分钟

theme: vue-pro

1、Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。

webpack的概述:
webpack是一个模块打包工具,它将一切文件都视为模块,通过loader编译转换文件,通过plugin注入钩子,最后 将输出的资源模块组合成文件。主要的配置信息有entryoutputmoduleplugins

构建流程:

  • 创建compiler实例,用于控制构建流程,compiler实例包含webpack基本环境信息
  • 根据配置项转换成对应的内部插件,并初始化options配置项
  • 执行compiler.run
  • 创建comppilation实例,每次构建都会新建一个comppilation实例,包含了这次构建的基本信息
  • entry开始递归分析依赖,对每个依赖模块会进行buildModule,通过Loader将不同类型的模块转换成webpack模块
  • 通过Parser.parse将上面的结果转换成AST树
  • 遍历AST树,收集依赖dependency,并保存在compilation实例的dependiencies属性中
  • 生成chunks,不同entry生成不同chunk, 动态导入也会生成自己的chunk,生成chunk后还会进行优化
  • 使用template基于compilation的数据生成结果代码

总结:
webpack打包输出的文件其实就是一个闭包,传入的参数是一个对象,键值为所有输出文件的路径,内容为eval包裹的文件内容;闭包内重写了模块的加载方式,自己定义了__webpack_require__方法,来实现模拟的common.js规范模块加载机制。
webpack实际是基于事件流的,通过一系列的插件运行。webpack利用tapable库提供各种钩子来实现对于整个构建流程各个步骤的控制。

2、Loader 和 Plugin 有哪些不同?请描述一下开发 Loader 和 Plugin 的思路。

Loader
用于对模块文件进行编译转换和加载处理,在modules.rules数组中进行配置,它用于告诉webpack在遇到什么类型的文件,应该采用哪些Loader进行加载和转换,loader可以通过querystringobject的方式指定选项参数。处理一类文件可以使用多个loader,loader的执行顺序类似出栈的方式(从后向前执行)

Plugin:
主要是通过webpack内部的钩子机制,在webpack构建的不同阶段执行一些额外的工作。从打包 优化和压缩,到从新定义环境变量,功能强大到可以用来处理各种各样的任务。pluginwebpack的机制更加灵活,他的编译过程中留下的一系列生命周期钩子,通过调用这些钩子来实现在不同编译结果时对源模块进行处理。它的插件是一个函数或者一个包含apply方法的对象,接收一个compile对象,通过webpack的钩子来处理资源

开发Loader的思路:

  • 通过module.export 导出一个函数
  • 函数的默认参数为要处理的文件source
  • 函数体中处理资源
  • 返回处理后结果(交给下一个loader 进行处理)
const marked = require('marked')

module.exports = source => { 
    // console.log(source)
    const html = marked(source)
    // 返回一段js代码
    // return `export default ${JSON.stringify(html)}`
    return html
}

开发Plugin的思路:

  • 通过钩子机制实现,在生命周期的钩子中挂载函数实现扩展
  • 函数方法体内通过webpack提供的api获取资源做相应处理
  • 将处理完的资源通过webpack提供的方法返回
class MyPlugin {
    apply (compiler) {
        console.log('自定义插件')
        // tap方法注册钩子函数(emit是其中一个钩子)
        compiler.hooks.emit.tap('MyPlugin', compilation => {
            // compilation可以理解为此次打包的上下文
            for (const name in compilation.assets) {
                if (name.endsWith('.js')) {
                    const contents = compilation.assets[name].source()
                    const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
                    compilation.assets[name] = {
                        source: () => withoutComments,
                        size: () => withoutComments.length
                    }
                }
            }
        })
    }
}