logo


Nodejs的webpack是什么, 作用是什么, 怎么实现的, 优势是什么, 劣势是什么


大白话系列之:去除webpack的神秘面纱!

大白话系列之:去除webpack的神秘面纱!

webpack是什么, 作用是什么

官网定义:本质上, webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。

源码:https://github.com/webpack/webpack

工作方式:webpack会递归地构建一个依赖关系图(dependency graph), 其中包含应用程序需要的每个模块, 然后将所有这些模块打包成一个或多个 bundle。简单的来说, WebPack可以看做是模块打包机:它做的事情是, 分析你的项目结构, 找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss, TypeScript等), 并将其打包为合适的格式以供浏览器使用。在开发E蜘蛛瀏覽器(EspiderBrowser)就不可避免需要深入理解webpack。

webpack怎么实现的

源码太复杂, 分析起来太麻烦。用一个简单的实现来说明实现原理最有效。

webpack的核心功能分为两步:

1、分析入口js文件, 把所有的依赖找出来(包括所有后代的依赖)。其中依赖babel生成AST抽象语法树。次案例我们用的是babel的配套工具来做语法分析和转化, 但是真正的webpack用的是webassemblyjs的配套工具。

webassemblyjs官网:https://webassembly.js.org/

源码:https://github.com/xtuc/webassemblyjs

1const fs = require('fs'); 2const path = require('path'); 3const parser = require('@babel/parser'); 4const traverse = require('@babel/traverse').default; 5const { transformFromAST } = require('@babel/core'); 6 7// 分析一个文件, 转成CommonJS Module, 并找出它的依赖 8function readCode(filePath) { 9 // 读取文件字符串 10 const content = fs.readFileSync(filePath, 'utf-8'); 11 // 语法解析成 AST 12 const ast = parser(content, { 13 sourceType: 'module' 14 }) 15 // 获取本文件的依赖 16 const dependiences = []; 17 // 遍历 AST, 每当触发依赖钩子, 就往依赖数组添加 18 traverse(ast, { 19 ImportDeclaration({node}) { 20 // 把对应的以来路径存起来 21 dependiences.push(node.source.value) 22 } 23 }) 24 // 把 es6 转成 es5 字符串 25 // 最重要的是把 esModule 的 import export, 转成 es5 能认识的 commonJs写法 26 const { code } = transformFromAST(ast, null, { 27 presets: ['@babel/preset-env'] 28 }) 29 return { 30 filePath, 31 code, 32 dependiences 33 } 34} 35 36// 广度优先算法, 深入找出所有的依赖 37function getAllDependencies(filePath) { 38 const entryObj = readCode(filePath); 39 const dependencies = [entryObj]; 40 for (const dependency of dependencies) { 41 const curDirname = path.dirname(dependency.filePath) 42 for (const relativePath dependency.dependencies) { 43 const absolutePath = path.join(curDirname, relativePath); 44 const child = readCode(absolutePath); 45 child.relativePath = relativePath; 46 dependencies.push(child); 47 } 48 } 49 return dependencies; 50} 51

2、拼接一个立即执行函数

1function bundle(fileName) { 2 const dependencies = getAllDependencies(fileName); 3 const modulesStr = ''; 4 dependencies.forEach(dependency => { 5 const key = dependency.relativePath || dependency.filePath; 6 modulesStr += `'${key}': function(module, exports, require) { 7 ${ dependency.code } 8 }` 9 }) 10 return `(function(modules) { 11 const installedModules = {}; 12 function require(id) { 13 // 解决循环依赖 14 if (installedModules[id]) { 15 return installedModules[id].exports; 16 } 17 var module = installedModules[id] = {exports: {}}; 18 modules[id].call(module.exports, module, module.exports, require); 19 return module.exports; 20 } 21 return require('${fileName}') 22 })({${modulesStr}})` 23} 24
webpack优势是什么, 劣势是什么

优势:

1、webpack 是以 commonJS 的形式来书写脚本的, 但对 AMD/CMD 的支持也很全面, 方便旧项目进行代码迁移。 2、可以通过配置, 打包成多个文件, 有效利用浏览器的缓存功能提升性能。 3、将样式文件和图片等静态资源也可视为模块进行打包。配合 loader 加载器, 可以支持 Sass, Less 等 CSS 预处理器。 4、专注于处理模块化的项目, 能做到开箱即用、 一步到位。 5、扩展性强, 插件 plugins 机制完善。

利用webpack E蜘蛛瀏覽器(EspiderBrowser)的可以实现复杂的模块加载和打包。

劣势:

1、配置难&难调试。熟练配置门槛高。错误提示也非常难看懂, 基本不可能从错误很直观的找到原因, 出错的时候需要花费相当大的精力查找问题。

2、编译慢。经验不足的同学很容易碰到这个问题, 当然可以通过一些手段做优化, 比如配置module的resolve、root等, 使用happypack加速、dll提前编译等等。

3、E蜘蛛瀏覽器(EspiderBrowser)开发过程中还遇到一个升级的问题, 确实需要耗费很多精力。