webpack 原理 和 运行机制_webpack原理和机制 - CSDN
精华内容
参与话题
  • webpack 自己实现了一套模块机制,无论是 CommonJS 模块的 require 语法还是 ES6 模块的 import 语法,都能够被解析并转换成指定环境的可运行代码,以 web 为例看看 webpack 如何来实现模块机制。 示例代码 index....

    webpack 自己实现了一套模块机制,无论是 CommonJS 模块的 require 语法还是 ES6 模块的 import 语法,都能够被解析并转换成指定环境的可运行代码,以 web 为例看看 webpack 如何来实现模块机制。

    示例代码

    index.js

    import foo from './foo'
    import bar from './bar'
    
    console.log('run => index.js')
    console.log(`log => foo.name: ${foo.name}`)
    console.log(`log => bar.name: ${bar.name}`)
    
    export default {
      name: 'index'
    }
    复制代码

    foo.js

    import bar from './bar'
    
    console.log('run => foo.js')
    console.log(`log => bar.name: ${bar.name}`)
    
    export default {
      name: 'foo'
    }
    复制代码

    bar.js

    console.log('run => bar.js')
    
    export default {
      name: 'bar'
    }
    复制代码

    webpack.config.js

    const path = require('path')
    
    module.exports = {
      entry: './src/index',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
      },
    }
    复制代码

    webpack 打包后的代码 bundle.js 概览

    (function(modules) {
      // webpackBootstrap
    })([
      /* 0 */
      (function(module, __webpack_exports__, __webpack_require__) {
        // bar.js
      }),
      /* 1 */
      (function(module, __webpack_exports__, __webpack_require__) {
        // index.js
      }),
      /* 2 */
      (function(module, __webpack_exports__, __webpack_require__) {
        // foo.js
      })
    ]);
    复制代码

    将打包之后的代码简化后非常清晰,就是一个立即调用函数表达式(IIFE:Immediately-Invoked Function Expression)。执行函数 webpackBootstrap 有一个形参 modules,对应的实参是一个数组,数组包含多个函数代码块,每个函数代码块都表示一个模块,拥有 module __webpack_exports__ __webpack_require__ 三个形参。通过注释不难发现,每个模块对应的就是一个文件,并且拥有一个 id 值,根据 id 值大小顺序添加进 modules 数组。

    webpackBootstrap 启动函数的执行

    代码的执行框架搭建好了,那启动函数 webpackBootstrap 内究竟做了什么让模块之间联系提来?模块的三个形参到底是什么?

    先瞧瞧被简化后的 webpackBootstrap 函数核心代码:

    (function(modules) { // webpackBootstrap
      // The module cache
      var installedModules = {};
    
      function __webpack_require__(moduleId) {
        // Check if module is in cache
        if(installedModules[moduleId]) {
          return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
          i: moduleId,
          l: false,
          exports: {}
        };
    
        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    
        // Flag the module as loaded
        module.l = true;
    
        // Return the exports of the module
        return module.exports;
      }
    })([...])
    ...
    
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 1);
    复制代码

    没有删掉注释,建议仔细瞧瞧,很容易就能明白模块是如何正确加载并运行起来的。

    首先定义了一个 installedModules 对象,它的作用是用来缓存已经加载过的模块,然后声明 __webpack_require__ 函数,似曾相识对吧,没错,它就是模块的第三个形参对应的实参。最后返回 __webpack_require__(1) 执行结果。

    那再看看 __webpack_require__ 函数,顾名思义,它就是用来加载模块的函数,接收一个 moduleId 的形参,也就是模块的 id 值。

    首先判断 moduleId 对应的模块是否已被缓存,也就是在 installedModules 对象中能不能找到属性 moduleId 对应的值,如果找到了,则直接返回模块的输出 exports。从这里可以发现,无论被多少个模块所依赖的模块都只会被加载一次,结果相同,因为返回的是同一个对象的引用地址,所以如果某个模块修改了对象内的属性值,则会被同步反应到其它依赖此模块的对象。

    继续,当模块没有被加载过的情况下,定义一个模块对象,同步加入 installedModules 对象缓存起来,模块对象包含三个属性,i 表示模块 id 值,l 表示是否被加载,exports 表示模块的输出结果对象。

    接着就是模块执行的调用函数,通过 moduleId 从 modules 内找到模块的函数代码块,使用 call 方法绑定函数内的 this 指向 module.exports,传入三个实参 module module.exports __webpack_require__,与模块函数的形参一一对应。

    当模块函数执行完成并返回结果之后,模块标识为已加载状态,最后返回模块的输出对象。

    模块函数的执行

    目前浏览器还没有完全支持 ES6 模块语法,所以模块内的 import 语法会如何处理?以 foo.js 为例来瞧瞧模块函数代码块内的代码:

    /* 2 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bar__ = __webpack_require__(0);
    
    
    console.log('run => foo.js')
    console.log(`log => bar.name: ${__WEBPACK_IMPORTED_MODULE_0__bar__["a" /* default */].name}`)
    
    /* harmony default export */ __webpack_exports__["a"] = ({
      name: 'foo'
    });
    复制代码

    原来 import 语法被转换了,模块名称变为一个变量名称,值是使用 __webpack_require__ 函数根据依赖模块 id 值获取的输出结果,并且模块函数内的所有依赖模块名称都被转换成对应的变量名称,模块的输出结果被绑定在 __webpack_exports__ 对象中,这里 module.exports === __webpack_exports__,等于就是模块的输出。

    总结

    webpack 的模块机制包含三大要点:

    1. modules 保存所有模块
    2. __webpack_require__ 函数加载模块
    3. installedModules 对象缓存模块

    通过以上全部分析,webpack 实现模块机制的原理我也思考出了一个大概的轮廓,下面是我个人理解的描述,如有错误,还望有大佬能够指点、指教,不胜感激。

    1. 使用 acorn 将代码解析成 AST(抽象语法树)

      从入口(entry)模块开始,以及后续各个依赖模块。

    2. 分析 AST 根据关键词 import``require 加载并确定模块之间的依赖关系、标识 id 值以及其它

      Dependency Graph

    3. 生成输出内容的 AST 并将模块的 AST 根据 id 值顺序插入 modules 的 AST

      输出内容是打包后输出的文件内容,模块 AST 会被包裹之后插入(原本的模块代码会被包裹进函数内)。

    4. 修改 AST 将各模块引入依赖模块的语法进行转换并将模块内所有的依赖标识进行对应替换

      import 语法转换和模块标识替换,上文有描述。

    5. 输出结果

    以上只是大概轮廓,还有很多细节没有涉及到,比如 helper 代码、转换规则、模块类型等等。经过此番思考,对模块的加载也算是有了较为深刻的理解,在此之上,也能够对 webpack 更多其它特性进行探索,比如 code-splitting、tree-shaking、scope hoisting 等等,路还很长,一步一个脚印。

    展开全文
  • 下面说一下vue脚手架中webpack的打包原理: 1、针对静态资源: 很多人会把静态资源一部分存放在src/assets目录下,一部分存放在static目录下,我建议所有的静态资源都存放在src/assets下。 在w...

    vue脚手架写完代码后,要打包部署(打包)到web服务器上。

    1、运行命令 npm  run build。就完了,一步搞定。

     

    下面说一下vue脚手架中webpack的打包原理:

    1、针对静态资源:

             很多人会把静态资源一部分存放在src/assets目录下,一部分存放在static目录下,我建议所有的静态资源都存放在src/assets下。

              在webpack打包时,会在项目目录创建dist/static,在该目录存放所有的静态资源,同时还会在dist/static目录下分别创建目录 img,css,js等目录存放不同类型的静态资源。
         对于assets目录下的文件,会分类放入不同的文件夹下。

         对于static目录下的文件,原封不动的复制到dist/stiatc下。

    2、

    展开全文
  • Webpack打包原理

    万次阅读 2020-03-26 12:50:04
    webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。 webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。...

    webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。

    webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。

    webpack做的就是分析代码,转换代码,编译代码,输出代码。webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)

    webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列。

    // webpack.config.js
    module.exports = {
      entry:'./a.js',
      output:{
        filename:'bundle.js'
      }
    };
    
    // a.js
    var b = require('./b.js');
    
    console.log('a');
    
    b.b1();
    
    // b.js
    exports.b1 = function () {
      console.log('b1')
    };
    
    exports.b2 = function () {
      console.log('b2')
    };
    

    以上代码我们打包处理的js为

    // bundle.js
    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId])
    /******/            return installedModules[moduleId].exports;
    
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            exports: {},
    /******/            id: moduleId,
    /******/            loaded: false
    /******/        };
    
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    
    /******/        // Flag the module as loaded
    /******/        module.loaded = true;
    
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    
    
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    
    /******/    // __webpack_public_path__
    /******/    __webpack_require__.p = "";
    
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(0);
    /******/ })
    /************************************************************************/
    /******/ ([
    /* 0 */
    /***/ function(module, exports, __webpack_require__) {
    
        var b = __webpack_require__(1);
    
        console.log('a');
    
        b.b1();
    
    
    /***/ },
    /* 1 */
    /***/ function(module, exports) {
    
        exports.b1 = function () {
          console.log('b1')
        };
    
        exports.b2 = function () {
          console.log('b2')
        };
    
    /***/ }
    /******/ ]);
    

    我们看到_webpack_require是模块加载函数,接收模块id

    (对,webpack中每个模块都会有一个独一无二的id,其实也就是在IIFE传参数组中的索引值(0,1,2.....)
    a依赖b,所以在a中调用webpack加载模块的函数

    // 1是模块b的id
    var b = __webpack_require__(1);
    

    上面是使用的commonjs规范书写的
    webpack2是如何对实现es6 modules的
    webpack模块化原理-ES module

    无论什么模块规范书写。我们的webpack进行识别后打包的内容不会相差很多,webpack有优秀的语法分析能力,支持 CommonJs  AMD 等规范。
    webpack为什么能把任何形式的资源都视作模块呢?因为loader机制。不同的资源采用不同的loader进行转换。CMD、AMD 、import、css 、等都有相应的loader去进行转换。那为什么我们平时写的es6的模块机制,不用增加import的loader呢。因为我们使用了babel把import转换成了require。并且Webpack 2 将增加对 ES6 模块的原生支持并且混用 ES6、AMD 和 CommonJS 模块。这意味着 Webpack 现在可以识别 import 和 export 了,不需要先把它们转换成 CommonJS 模块的格式:Webpack 2 有哪些新东西webpack对于es模块的实现,也是基于自己实现的webpack_requirewebpack_exports ,装换成类似于commonjs的形式。es6 module是静态的依赖,所以在运行前进行代码转换,

    这里的实现是将所有导出项作为一个对象的属性,在入口文件执行时,去递归的加载模块。

    如何实现一个简单的webpack

    1. 读取文件分析模块依赖
    2. 对模块进行解析执行(深度遍历)
    3. 针对不同的模块使用相应的loader
    4. 编译模块,生成抽象语法树AST。
    5. 循环遍历AST树,拼接输出js。

     

    1. webpack 源码解析
    2. 细说 webpack 之流程篇
    3. 如何实现一个简单的webpack

    loader原理

    在解析对于文件,会自动去调用响应的loaderloader 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。当然,输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。webpack会按照从右到左的顺序执行loader。
    Ast语法树详解  https://segmentfault.com/a/1190000016231512?utm_source=tag-newest
    转自作者:lawpachi

    展开全文
  • webpack -- requireimport机制

    千次阅读 2019-01-11 00:28:28
    虽然我们很多人每天都在写项目,require或者import写的爽得很,但是有几个人真正的知道它背后的运行原理和所谓的规则机制。 开始 我们基于webpack开发,就拿基本的vue项目来举例子吧 假如我们项目中要...

    欢迎访问我的个人博客:http://www.xiaolongwu.cn

    前言

    虽然我们很多人每天都在写项目,require或者import写的爽得很,但还是有很大一部分人不清楚它背后的运行原理和所谓的规则机制。

    开始

    我们基于webpack开发,就拿基本的vue项目来举例子吧

    假如我们项目中要用到vue或者express框架,我们的代码就这样写

    import Vue from 'vue'
    
    //或者
    var Vue = require('vue')
    

    然后我们就能在下面轻松的用Vue这个变量,感觉很愉悦,但是你想过我们是怎么拿到Vue这个东西的吗?我们写的import或者require这行代码道理干了啥?

    首先,import是es2015的模块引入规范,而require是commonjs的模块引入规范;

    webpack支持es2015,commonjs,AMD等规范;

    工作机制

    前提是你在做web开发,试图用webpack或者rollup打包你的项目;

    首先会从本地的node_modules文件夹中找到vue文件夹,看是否存在package.json文件;

    如果找到了package.json,就会先找module字段,然后读取对应的路径下的文件,查找到此结束;

    如果没找到module字段,就会找main字段,然后读取对应的路径下的文件,查找到此结束;

    如果没有main字段,就会在vue文件夹下找index.js文件,然后读取文件,查找到此结束;

    如果以上都没找到就会返回异常,扔出not find异常

    如果不存在package.json就会找index.js文件,然后读取文件,查找到此结束;如果还没有就会抛出异常;

    简单说一下module字段

    说到module字段就不得不说一个和webpack很像的模块打包工具—rollup,

    rollup是一个轻量级的打包工具,一般被用来打包模块或者库,可以根据需要将模块打包为es,commonjs,AMD,CMD,UMD,IIFE等规范的模块;

    而webpack一般被用来打包应用程序;

    rollup提出了module这个字段,其原因是一般主流的模块或者库都是commonjs规范的模块,而es2015的模块规范才是js的未来,才应该是主流;

    所以,一般的package.json中的module对应的模块为es模块,而main对应的为commonjs模块,webpack和rollup都会默认优先读取module字段;

    github资源地址:webpack–require和import机制.md

    我的CSDN博客地址:https://blog.csdn.net/wxl1555

    如果您对我的博客内容有疑惑或质疑的地方,请在下方评论区留言,或邮件给我,共同学习进步。

    邮箱:wuxiaolong802@163.com

    展开全文
  • 前端工程化-webpack原理

    万次阅读 2020-10-11 11:17:56
    1、plugin通过钩子机制实现 2、往钩子上挂载任务,一般是通过一个函数或者是一个包含apply方法的对象 3、brower sync自动编译自动刷新
  • webpack插件运行原理及编写

    千次阅读 2019-10-23 15:30:03
    webpack是一个很好用的打包工具,怎么配置使用大家都比较熟悉了,其中在配置的过程中会用到很多plugin,如果想进一步了解webpack,就要了解plugin的工作机制,再进一步,就是自己动手封装一个plugin. 插件存在的...
  • webpack】使用webpack进行ES6开发

    万次阅读 2016-06-06 20:09:09
    本文主要介绍webpack的基本原理以及基于webpack搭建前端项目工程化解决方案的思路。 目录: 1、为什么要用webpack 2、webpack的安装 3、webpack的配置 4、ithub上的小例子
  • webpack打包原理和manifest文件分析

    万次阅读 2017-08-01 11:45:19
    分析了webpack打包的原理,并解读了webpack打包生成的代码其工作机制
  • webpack中loaderplugin的区别

    千次阅读 2019-03-25 15:32:03
    原文地址:详解webpack loaderplugin编写 一、webpack的常见配置 const webpack = require("webpack"); const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module....
  • webpack的面试题总结

    千次阅读 2019-07-25 20:53:51
    随着现代前端开发的复杂度规模越来越庞大,已经不能抛开工程化来独立开发了,如react的jsx代码必须编译后才能在浏览器中使用;又如sassless的代码...在众多前端工程化工具中,webpack脱颖而出成为了当今最...
  • centos7搭建webpack

    千次阅读 2017-07-23 17:42:08
    Webpack 有两种组织模块依赖的方式,同步异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包。 Loader Webpack 本身只能处理原生的 JavaScript 模块,但是 loader ...
  • webpack 性能调优与 Gzip 原理

    千次阅读 2019-01-12 19:37:48
    从本节开始,我们进入网络层面...对于 DNS 解析 TCP 连接两个步骤,我们前端可以做的努力非常有限。相比之下,HTTP 连接这一层面的优化才是我们网络优化的核心。因此我们开门见山,抓主要矛盾,直接从 HTTP 开始讲...
  • 我们都知道webpack是用于资源打包的,里面的所有资源都是“模块”,内部实现了对模块资源进行加载的机制。但是Webpack本身只能处理 js模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。 Loader 可以...
  • webpack打包原理

    千次阅读 2018-03-18 09:39:31
    chunk 多个文件组成的一个代码块,例如把一个可执行模块它所有依赖的模块组合一个 chunk 这体现了webpack的打包机制。loader 文件转换器,例如把es6转换为es5,scss转换为css。plugin 插件,用于扩展...
  • 前后端项目对接流程梳理

    万次阅读 2018-04-11 17:54:02
    课程目标 掌握一个电商网站从设计到上线的整个过程所涉及的流程 具备独立开发一个前端项目的能力 网址 ... 架构设计 ...Webpack NodeJs NPM Shell 效率工具 Sublime Chrome Charles Gi...
  • 文 基于webpack搭建前端工程解决方案探索 webpack  ...本篇主要介绍webpack的基本原理以及基于webpack搭建纯静态页面型前端项目工程化解决方案的思路。 下篇(还没写完)探讨下对于Nod
  • 说明:根据B站学习视频 :【2020新课程】Webpack原理到实战完整版-深入浅出,简单易学,前端工程师必学经典内容!学习整理,如有错误,欢迎随时指正,互相学习。 本次学习到的内容: webpack产生的背景 理解前端...
  • 热模块替换实现与原理

    千次阅读 2018-08-15 11:35:22
    1.作用 用于在开发过程中,实时预览修改后的页面,无需重新加载整个页面。 其主要通过一下几种方式来加快开发速度: 保留在完全重新加载页面时丢失的应用程序状态。... module:模块,webpack里一切皆模块...
  • webpack概念简介

    千次阅读 2020-01-18 18:17:19
    目录一、webpack概述1. 什么是webpack?2. 为什么要使用webpack?二、webpack相关概念1. 入口(entry)2. 出口(output)3. 加载器(loader)4. 插件(plugin)三、补充 - 热更新原理总结 一、webpack概述 1. 什么是...
  • 详解webpack loaderplugin编写

    千次阅读 2018-11-01 23:34:23
    首先我们先回顾一下webpack常见配置,因为后面会用到,所以简单介绍一下。 1.1 webpack常见配置 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 1...
1 2 3 4 5 ... 20
收藏数 4,363
精华内容 1,745
关键字:

webpack 原理 和 运行机制