• 掌握webpack


    webpack官网

    webpack打包原理

    1. npm init -y
    2. yarn add @babel/core @babel/parser @babel/preset-env @babel/traverse
    3. 新建下面几个文件,并写入代码

    ./src/index.js

    import add from "./add.js";
    console.log(add(1 , 2))
    
    • 1
    • 2

    ./src/add.js

    export default (a, b) => a + b 
    
    • 1

    ./index.js

    const fs = require("fs");
    const path = require("path");
    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const babel = require("@babel/core");
    
    function getModuleInfo(file) {
      // 读取文件
      const body = fs.readFileSync(file, "utf-8");
    
      // 转化AST语法树
      const ast = parser.parse(body, {
        sourceType: "module", //表示我们要解析的是ES模块
      });
    
      // 依赖收集
      const deps = {};
      traverse(ast, {
        ImportDeclaration({ node }) {
          const dirname = path.dirname(file);
          const abspath = "./" + path.join(dirname, node.source.value);
          // console.log('abs', abspath);
          deps[node.source.value] = abspath;
        },
      });
    
      // ES6转成ES5
      const { code } = babel.transformFromAst(ast, null, {
        presets: ["@babel/preset-env"],
      });
      const moduleInfo = { file, deps, code };
      return moduleInfo;
    }
    // const info = getModuleInfo("./src/index.js");
    // console.log("info:", info);
    
    
    
    
    // 模块解析
    function parseModules(file) {
      const entry = getModuleInfo(file);
      const temp = [entry];
      const depsGraph = {};
    
      getDeps(temp, entry);
    
      temp.forEach((moduleInfo) => {
        depsGraph[moduleInfo.file] = {
          deps: moduleInfo.deps,
          code: moduleInfo.code,
        };
      });
      return depsGraph;
    }
    
    // 获取依赖
    function getDeps(temp, { deps }) {
      Object.keys(deps).forEach((key) => {
        const child = getModuleInfo(deps[key]);
        temp.push(child);
        getDeps(temp, child);
      });
    }
    
    // 打包
    function bundle(file) {
      const depsGraph = JSON.stringify(parseModules(file));
      return `(function (graph) {
            function require(file) {
                function absRequire(relPath) {
                    return require(graph[file].deps[relPath])
                }
                var exports = {};
                (function (require,exports,code) {
                    eval(code)
                })(absRequire,exports,graph[file].code)
                return exports
            }
            require('${file}')
        })(${depsGraph})`;
    }
    
    const content = bundle("./src/index.js")
    console.log('content',content);
    
    // 生成dist文件
    !fs.existsSync("./dist") && fs.mkdirSync("./dist");
    fs.writeFileSync("./dist/bundle.js", content);
    
    • 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
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    1. node index.js
    2. 于是就会在根目录下生成dist文件,并且生成bundle.js文件,执行它就会生成对应对应的结果
    3. 分析文件相互依赖如何实现打包的
      1. js文件按照键值对的形式,键为路径值,值为文件内容
      2. 需要导入到html文件,所以最开始是需要require函数,导入入口文件的
      3. 入口文件有个局部变量_export,防止文件间相互污染
      4. 根据文件路径,选择eval去执行对应的code字符串代码
      5. 如果在code内部含有require函数,则递归调用就行
    !(function (list) {
        function require(file) {
            var _export = {}
            !(function (_export, code) {
                eval(code)
            })(_export, list[file])
            return _export
        }
        // 入口
        require('./index.js')
    })({
        './add.js': '_export.default = function (a, b) {return a + b}',
        './index.js': `
            var add = require('./add.js').default
            console.log(add(1 , 2)) 
        `,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    webpack的核心概念和作用

    在这里插入图片描述

    1. 打包所有的脚本
    2. 打包所有的样式
    3. 打包所有的图片
    4. 打包所有的资源
      在这里插入图片描述
      本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
      在这里插入图片描述
      先进的浏览器,已经支持模块化使用js脚本了,,但是老浏览器不支持,为了都支持,所以需要webpack打包一下
      从 webpack v4.0.0 开始,可以不用引入一个配置文件。然而,webpack 仍然还是高度可配置的。在开始前你需要先理解四个核心概念:
      • 入口(entry)
      • 输出(output)
      • loader
        • loaderwebpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
        • 本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
        • 注意,loader 能够import导入任何类型的模块(例如.css文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。
        • 在更高层面,在webpack的配置中 loader 有两个目标:
          • test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
          • use 属性,表示进行转换时,应该使用哪个 loader。
          • module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }
          • 以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:
          • “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用 raw-loader `转换一下。”
          • 重要的是要记得,在 webpack 配置中定义loader时,要定义在 module.rules 中,而不是 rules。然而,在定义错误时 webpack 会给出严重的警告。为了使你受益于此,如果没有按照正确方式去做,webpack 会“给出严重的警告”
      • 插件(plugins)
        • loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
        • 想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。
        • webpack.config.js 配置如下:
        • webpack 提供许多开箱可用的插件!查阅我们的插件列表获取更多信息。
        • webpack 配置中使用插件是简单直接的,然而也有很多值得我们进一步探讨的用例。
    const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
    const webpack = require('webpack'); // 用于访问内置插件
    
    const config = {
      module: {
        rules: [
          { test: /\.txt$/, use: 'raw-loader' }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({template: './src/index.html'})
      ]
    };
    
    module.exports = config;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    前端网页功能丰富,网页资源相互穿插应用,错综复杂,webpack可以帮我们去理清这种文件依赖关系。
    前端开发目前面临哪些复杂问题?

    1. 开发用模块化开发
    2. 提高开发效率,使用es6+,Ts,Sass,less
    3. 开发中,希望监听文件变化,且反应到浏览器上,提高开发效率
    4. 代码压缩,合并,还有其他相关的优化

    日常使用:

    在这里插入图片描述

    Webpack的基本功能有哪些?

    • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
    • 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
    • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
    • 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
    • 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
    • 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
    • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

    gulp,rollup 与 webpack,vite区别是什么?

    gulp rollup webpack vite

    1. gulp 和 webpack
      1. gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。gulp是基于任务和流(Task、Stream)的。
        类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。

      2. webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。

      3. gulp强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task处理的事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,来让gulp执行这些task,从而构建项目的整个前端开发流程。

      4. webpack是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。

      5. gulp与webpack上是互补的,还是可替换的,取决于你项目的需求。如果只是个vue或react的单页应用,webpack也就够用;如果webpack某些功能使用起来麻烦甚至没有(雪碧图就没有),那就可以结合gulp一起用。

      6. 在这里插入图片描述

    在这里插入图片描述

    webpack是解决什么问题而生的

    如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。

    你是如何提高webpack构件速度的

    多入口情况下,使用CommonsChunkPlugin来提取公共代码

    通过externals配置来提取常用库

    利用DllPlugin和DllReferencePlugin预编译资源模块通过DllPlugin来对那些我们

    引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。

    使用Happypack 实现多线程加速编译

    使用webpack-uglify-paralle来提升uglifyPlugin的压缩速度。

    原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
    使用Tree-shaking和Scope Hoisting来剔除多余代码

    npm打包时需要注意哪些?如何利用webpack来更好的构建

    Npm是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。

    你可能只是JS模块的使用者,但是有些情况你也会去选择上传自己开发的模块。

    关于NPM模块上传的方法可以去官网上进行学习,这里只讲解如何利用webpack来构建。

    NPM模块需要注意以下问题:

    1. 要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则。
    2. Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传。
    3. Npm包大小应该是尽量小(有些仓库会限制包大小)
    4. 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
    5. UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里。

    前端为什么要进行打包和构建?

    代码层面:

    • 体积更小(Tree-shaking、压缩、合并),加载更快
    • 编译高级语言和语法(TS、ES6、模块化、scss)
    • 兼容性和错误检查(polyfill,postcss,eslint)

    研发流程层面:

    • 统一、高效的开发环境
    • 统一的构建流程和产出标准
    • 集成公司构建规范(提测、上线)

    webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全

    Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:\

    • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
    • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
    • 确定入口:根据配置中的 entry 找出所有的入口文件;
    • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
    • 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
    • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
    • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

    在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。\

    怎么配置单页应用?怎么配置多页应用

    单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述

    多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。多页应用中要注意的是:

    • 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
    • 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置

    Loader机制的作用是什么

    webpack默认只能打包js文件,配置里的module.rules数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换打包成js。

    注意:

    use属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
    每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如css-loader?minimize中的minimize告诉css-loader要开启 CSS 压缩。

    常用loader

    css-loader读取 合并CSS 文件
    style-loader把 CSS 内容注入到 JavaScript 里
    sass-loader 解析sass文件(安装sass-loader,node-sass)
    postcss-loader自动添加浏览器兼容前缀(postcss.config配置)
    url-loader将文件转换为base64 URI。
    vue-loader处理vue文件。\

    Plugin(插件)的作用是什么

    Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。

    Webpack 是通过plugins属性来配置需要使用的插件列表的。plugins属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。

    什么是bundle,什么是chunk,什么是module

    bundle:是由webpack打包出来的文件

    chunk:是指webpack在进行模块依赖分析的时候,代码分割出来的代码块

    module:是开发中的单个模块

    常见Plugins

    HtmlWbpackPlugin自动在打包结束后生成html文件,并引入bundle.js
    cleanwebPackPlugin打包自动删除上次打包文件\

    ExtractTextPlugin插件的作用

    ExtractTextPlugin插件的作用是提取出 JavaScript 代码里的 CSS 到一个单独的文件。

    对此你可以通过插件的filename属性,告诉插件输出的 CSS 文件名称是通过[name]_[contenthash:8].css字符串模版生成的,里面的[name]代表文件名称,[contenthash:8]代表根据文件内容算出的8位 hash 值, 还有很多配置选项可以在ExtractTextPlugin的主页上查到。\

    sourceMap

    是一个映射关系,将打包后的文件隐射到源代码,用于定位报错位置

    配置方式:

    例如:devtool:‘source-map’
    加不同前缀意义:\

    • inline:不生成映射关系文件,打包进main.js
    • cheap: 1.只精确到行,不精确到列,打包速度快 2.只管业务代码,不管第三方模块
    • module:不仅管业务代码,而且管第三方代码
    • eval:执行效率最快,性能最好

    最佳实践:
    开发环境:cheap-module-eval-source-map
    线上环境:cheap-mudole-source-map\

    HMR热模块更新

    借助webpack.HotModuleReplacementPlugin(),devServer开启hot\

    场景1:实现只刷新css,不影响js
    场景2:js中实现热更新,只更新指定js模块

    if (module.hot) {  module.hot.accept(’./library.js’, function() {    // Do something with the updated library module…  });}
    
    • 1

    webpack如何配置多入口文件

    entry: { home: resolve(__dirname, "src/home/index.js"), about: resolve(__dirname, "src/about/index.js")}
    
    • 1

    用于描述入口的对象。你可以使用如下属性:

    • dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
    • filename: 指定要输出的文件名称。
    • import: 启动时需加载的模块。
    • library: 指定 library 选项,为当前 entry 构建一个 library。
    • runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
    • publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址

    babel 相关: polyfill 以及 runtime 区别, ES stage 含义,preset–env 作用等等

    1.polyfill 以及 runtime 区别

    babel-polyfill 的原理是当运行环境中并没有实现的一些方法,babel-polyfill会做兼容。

    babel-runtime 它是将es6编译成es5去执行。我们使用es6的语法来编写,最终会通过babel-runtime编译成es5.也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5.所以就有很多冗余的代码。

    babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染。

    babel-runtime: 它不会污染全局对象和内置对象的原型,比如说我们需要Promise,我们只需要import Promise from 'babel-runtime/core-js/promise’即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。

    2.stage-x:指处于某一阶段的js语言提案
    Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
    Stage 1 - 建议(Proposal):这是值得跟进的。
    Stage 2 - 草案(Draft):初始规范。
    Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
    Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。

    3. 理解 babel-preset-env
    babel-preset-es2015: 可以将es6的代码编译成es5.
    babel-preset-es2016: 可以将es7的代码编译为es6.
    babel-preset-es2017: 可以将es8的代码编译为es7.
    babel-preset-latest: 支持现有所有ECMAScript版本的新特性

    什么是模热更新?有什么优点

    模块热更新是webpack的一个功能,它可以使得代码修改之后,不用刷新浏览器就可以更新。

    在应用过程中替换添加删出模块,无需重新加载整个页面,是高级版的自动刷新浏览器。

    优点:只更新变更内容,以节省宝贵的开发时间。调整样式更加快速,几乎相当于在浏览器中更改样式\

    lazy loading(模块懒加载)

    借助import()语法异步引入组件,实现文件懒加载:prefetch,preloading
    webpack提倡多写异步代码,提升代码利用率,从而提升页面性能
    先加载主业务文件,prefetch利用网络空闲时间,异步加载组件

    import(/* webpackPrefetch: true / ‘LoginModal’);
    
    • 1

    preload和主业务文件一起加载,异步加载组件

    import(/ webpackPreload: true */ ‘ChartingLibrary’);
    
    • 1

    什么是长缓存?在webpack中如何做到长缓存优化

    浏览器在用户访问页面的时候,为了加快加载速度,会对用户访问的静态资源进行存储,但是每一次代码升级或者更新,都需要浏览器去下载新的代码,最方便和最简单的更新方式就是引入新的文件名称。

    在webpack中,可以在output给出输出的文件制定chunkhash,并且分离经常更新的代码和框架代码,通过NameModulesPlugin或者HashedModulesPlugin使再次打包文件名不变。

    什么是Tree-shaking

    1. 什么是Tree-shaking

      1. Tree Shaking是一个术语,在计算机中表示消除死代码(dead_code)
      2. 指打包中去除那些引入了但在代码中没用到的死代码。
      3. 最早的想法起源于LISP,用于消除未调用的代码(纯函数无副作用,可以放心的消除,这也是为什么要求我们在进行函数式编程时,尽量使用纯函数的原因之一)
      4. 后来Tree Shaking也被应用于其他的语言,比如JavaScript、Dart;
      5. 在wepack中js treeshaking通过UglifyJsPlugin来进行,css中通过purify-CSS来进行。
    2. JavaScript的Tree Shaking:

      1. 对JavaScript进行Tree Shaking是源自打包工具rollup
      2. 这是因为Tree Shaking依赖于ES Module的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系);
      3. webpack2正式内置支持了ES2015模块,和检测未使用模块的能力;
      4. 在webpack4正式扩展了这个能力,并且通过 package.json的 sideEffects属性作为标记,告知webpack在编译时,哪里文件可以安全的删除掉;
      5. webpack5中,也提供了对部分CommonJS的tree shaking的支持;
    3. webpack实现Tree Shaking,两种方案:

      1. usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的;
      2. psideEffects:跳过整个模块/文件,直接查看该文件是否有副作用;
    4. usedExports

      1. 将mode设置为development模式: 因为在 production 模式下,webpack默认的一些优化会带来很大影响。
      2. 设置usedExports为true和false对比打包后的代码:在usedExports设置为true时,会有一段注释:unused harmony export mul;告知Terser在优化时,可以删除掉这段代码
      3. 这个时候,我们讲 minimize设置true:usedExports设置为false时,mul函数没有被移除掉;usedExports设置为true时,mul函数有被移除掉;
      4. 所以,usedExports实现Tree Shaking是结合Terser来完成的。
    5. sideEffects

      1. sideEffects用于告知webpack compiler哪些模块时有副作用的: 副作用的意思是这里面的代码有执行一些特殊的任务,不能仅仅通过export来判断这段代码的意义;
      2. 在package.json中设置sideEffects的值:如果我们将sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports;如果有一些我们希望保留,可以设置为数组;
      3. 比如我们有一个format.js、style.css文件:该文件在导入时没有使用任何的变量来接受;那么打包后的文件,不会保留format.js、style.css相关的任何代码;想保留window.abc的代码,那么就应该卸载副作用文件里在这里插入图片描述在这里插入图片描述在这里插入图片描述
    6. 综上,Webpack中tree shaking的设置

      1. 在optimization中配置usedExports为true,来帮助Terser进行优化;
      2. 在package.json中配置sideEffects,直接对模块进行优化;
    7. css的Tree Shaking:

      1. 在早期的时候,我们会使用PurifyCss插件来完成CSS的tree shaking,但是目前该库已经不再维护了
      2. 目前我们可以使用另外一个库来完成CSS的Tree Shaking:PurgeCSS,也是一个帮助我们删除未使用的CSS的工具;
      3. npm install purgecss-webpack-plugin -D
      4. 配置PurgeCss:
      5. paths:表示要检测哪些目录下的内容需要被分析,这里我们可以使用glob;
      6. 默认情况下,Purgecss会将我们的html标签的样式移除掉,如果我们希望保留,可以添加一个safelist的属性;在这里插入图片描述

    什么是HTTP压缩?

    1. HTTP压缩是一种内置在 服务器 和 客户端 之间的,以改进传输速度和带宽利用率的方式;
    2. HTTP压缩的流程什么呢?
      1. 第一步:HTTP数据在服务器发送前就已经被压缩了;(可以在webpack中完成)
      2. 第二步:兼容的浏览器在向服务器发送请求时,会告知服务器自己支持哪些压缩格式;在这里插入图片描述
      3. 第三步:服务器在浏览器支持的压缩格式下,直接返回对应的压缩后的文件,并且在响应头中告知浏览器;在这里插入图片描述
      4. 目前的压缩格式非常的多:
      5. compress – UNIX的“compress”程序的方法(历史性原因,不推荐大多数应用使用,应该使用gzip或deflate);
      6. deflate – 基于deflate算法(定义于RFC 1951)的压缩,使用zlib数据格式封装;
      7. gzip – GNU zip格式(定义于RFC 1952),是目前使用比较广泛的压缩算法;
      8. br – 一种新的开源压缩算法,专为HTTP内容的编码而设计;

    Webpack对文件压缩

    1. webpack中相当于是实现了HTTP压缩的第一步操作,我们可以使用CompressionPlugin。
    2. 第一步,安装CompressionPlugin:npm install compression-webpack-plugin -D
    3. 第二步,使用CompressionPlugin即可:在这里插入图片描述
    4. HTML文件中代码的压缩
    5. 使用HtmlWebpackPlugin插件来生成HTML的模板,事实上它还有一些其他的配置:在这里插入图片描述
    6. InlineChunkHtmlPlugin将一些chunk出来的模块,内联到html中:
    7. 比如runtime的代码,代码量不大,但是是必须加载的;
    8. 那么我们可以直接内联到html中;
    9. 这个插件是在react-dev-utils中实现的,需安装一下npm install react-dev-utils -D
    10. 在production的plugins中进行配置:在这里插入图片描述

    封装Library 和 打包Library

    Library : [ˈlaɪbrəri] 图书馆,软件库

    1. webpack可以帮助我们打包自己的库文件,比如我们需要打包一个whyutils的一个库。在这里插入图片描述
    2. 配置webpack.config.js文件在这里插入图片描述在这里插入图片描述
    3. 我们在html页面可以直接使用啦在这里插入图片描述

    webpack-dev-server 和 http服务器的区别

    webpack-dev-server使用内存来存储webpack开发环境下的打包文件,并且可以使用模块热更新,比传统的http服务对开发更加有效。

    webpack3和webpack4的区别

    mode/–mode参数,新增了mode/–mode参数来表示是开发还是生产(development/production)production 侧重于打包后的文件大小,development侧重于goujiansud移除loaders,必须使用rules(在3版本的时候loaders和rules 是共存的但是到4的时候只允许使用rules)移除了CommonsChunkPlugin (提取公共代码),用optimization.splitChunks和optimization.runtimeChunk来代替支持es6的方式导入JSON文件,并且可以过滤无用的代码

    webpack学习

    webpack包 和 webpack-cli包,需要都安装,但是webpack-cli不是必须的,如vue源码,就替换为了自己的cli,不需用webpack-cli
    在这里插入图片描述

    1. webpack依赖关系图在这里插入图片描述

    2. webpack是不知道如何加载css模块的,我们需要借助loader来完成该功能,yarn add css-loader

    3. css-loader的使用方案(webpack官网提供的loader使用方案):在这里插入图片描述

      1. import "css-loader!../css/index.css" 开发中不会这样使用

      2. webpack --module-bind 'css=css-loader' --config wk.config.js 开发不会这样使用

      3. loader存在简写方式,如图css-loader
        在这里插入图片描述

      4. 处理less样式的 yarn add less -D,可以npx less ./src/css/component.less > component.css,但是在webpack中用需要装less-loader,yarn add less-loader -D

      5. 综合上面的代码如下:(loader链式调用,从后往前逆序调用)在这里插入图片描述

      6. 不同浏览器,css样式存在兼容性写法,browserlist的配置包共享出不同版本浏览器列表,方便其他包去统一使用(单独的.browserlistrc文件,或者package.json内部配置,创建vue项目时候有给选择)在这里插入图片描述在这里插入图片描述在这里插入图片描述

      7. browserlist包是如何查询的在这里插入图片描述在这里插入图片描述

      8. postcss的使用,yarn add postcss -D,为了想在命令行上使用命令需要额外安装postcss-cli,yarn add postcss-cli -D,为了使得webpack能使用postcss,于是需要额外安装postcss-loader,yarn add postcss-loader -D
        在这里插入图片描述

      9. 自动给css样式添加基于配置.browserlist的兼容代码,使用autoprefixer,对于一般的css代码,autoprefixer足以做兼容处理,但是color: #12345678;,颜色8位数,有些浏览器不识别,那么需要转换为rgba格式,这种转换autoprefixer做不到,这里有个新的包postcss-preset-env去替换它在这里插入图片描述
        npx postcss --use autoprefixer -o result.css ./src/css/test.css 命令行输出

      10. 上面写的postcss-loader,匹配less文件,又要重复写一套代码配置,如何提取放到公共的位置,在根目录下新建postcss.config.js文件,内容为module.exports = { plugins: [ 'postcss-preset-env' ] },不用再重复写postcss-loaderoptions配置了,代码可以简写为:(这里只写了css的use,less的类似,这里就不重复写了)在这里插入图片描述

    4. 处理图片在这里插入图片描述

    5. webpack5之前,加载资源需要一些loader,比如url-loader

    6. webpack之后,使用资源模块类型 (asset module type),来替代上面的loader在这里插入图片描述

    7. 认识plugins在这里插入图片描述

    8. 每次打包删除之前的包,yarn add clean-webpack-plugin -D在这里插入图片描述

    9. 生成html文件,基于template的html模板有,打包会报BASE_URL 错误,需要设置DefinePlugin在这里插入图片描述在这里插入图片描述

    10. 复制文件,template的html模板生成html,内部可能有引入的资源,需要将资源文件复制一下在这里插入图片描述

    11. yarn add webpack-dev-server
      webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)

    12. 模块热替换是指在 应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可;在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading;在这里插入图片描述
      但是如果要热更新math.js文件,仍然需要如下代码:在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    13. webpack热更新原理在这里插入图片描述
      在这里插入图片描述

    14. 将css提取到一个独立的css文件中yarn add mini-css-extract-plugin -D

    15. 直接用插件,不需要手动导入axios,直接用get函数和axios函数在这里插入图片描述

    commonjs和esmodule如何实现在浏览器运行的(兼容低版本浏览器)

    1. commonjs

    // 导出文件
    const dateFormat = (date) => {
        return "3000-12-12";
    }
    const priceFormat = (price) => {
        return "100.00";
    }
    module.exports = {
        dateFormat,
        priceFormat
    }
    // 导入文件
    const { dateFormat, priceFormat } = require('./js/format');
    console.log(dateFormat("abc"));
    console.log(priceFormat("abc"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    打包之后的代码:

    // 定义了一个对象
    // 模块的路径(key): 函数(value)
    var __webpack_modules__ = {
      "./src/js/format.js":
        (function (module) {
          const dateFormat = (date) => {
            return "3000-12-12";
          }
          const priceFormat = (price) => {
            return "100.00";
          }
          // 将我们要导出的变量, 放入到module对象中的exports对象
          module.exports = {
            dateFormat,
            priceFormat
          }
        })
    }
    
    // 定义一个对象, 作为加载模块的缓存
    var __webpack_module_cache__ = {};
    
    // 是一个函数, 当我们加载一个模块时, 都会通过这个函数来加载
    function __webpack_require__(moduleId) {
      // 1.判断缓存中是否已经加载过
      if (__webpack_module_cache__[moduleId]) {
        return __webpack_module_cache__[moduleId].exports;
      }
      // 2.给module变量和__webpack_module_cache__[moduleId]赋值了同一个对象
      var module = __webpack_module_cache__[moduleId] = { exports: {} };
      // 3.加载执行模块
      __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
      // 4.导出module.exports {dateFormat: function, priceForamt: function}
      return module.exports;
    }
    // 具体开始执行代码逻辑
    // 1.加载./src/js/format.js
    const { dateFormat, priceFormat } = __webpack_require__("./src/js/format.js");
    console.log(dateFormat("abc"));
    console.log(priceFormat("abc"));
    
    • 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

    2. esmodule

    // 导出模块
    export const sum = (num1, num2) => {
      return num1 + num2;
    }
    export const mul = (num1, num2) => {
      return num1 * num2;
    }
    // 导入模块
    import { sum, mul } from "./js/math";
    console.log(mul(20, 30));
    console.log(sum(20, 30));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    webpack打包之后的代码:

    // 1.定义了一个对象, 对象里面放的是我们的模块映射
    var __webpack_modules__ = {
      "./src/es_index.js":
        (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
          // 调用r的目的是记录时一个__esModule -> true
          __webpack_require__.r(__webpack_exports__);
          // _js_math__WEBPACK_IMPORTED_MODULE_0__ == exports
          var _js_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/math.js");
    
          console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.mul(20, 30));
          console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.sum(20, 30));
        }),
      "./src/js/math.js":
        (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
          __webpack_require__.r(__webpack_exports__);
          // 调用了d函数: 给exports设置了一个代理definition
          // exports对象中本身是没有对应的函数
          __webpack_require__.d(__webpack_exports__, {
            "sum": function () { return sum; },
            "mul": function () { return mul; }
          });
          const sum = (num1, num2) => {
            return num1 + num2;
          }
          const mul = (num1, num2) => {
            return num1 * num2;
          }
        })
    };
    // 2.模块的缓存
    var __webpack_module_cache__ = {};
    // 3.require函数的实现(加载模块)
    function __webpack_require__(moduleId) {
      if (__webpack_module_cache__[moduleId]) {
        return __webpack_module_cache__[moduleId].exports;
      }
      var module = __webpack_module_cache__[moduleId] = {
        exports: {}
      };
      __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
      return module.exports;
    }
    // __webpack_require__这个函数对象添加了一个属性: d -> 值function
    __webpack_require__.d = function (exports, definition) {
      for (var key in definition) {
        if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
          Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
        }
      }
    };
    // __webpack_require__这个函数对象添加了一个属性: o -> 值function
    __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
    // __webpack_require__这个函数对象添加了一个属性: r -> 值function
    __webpack_require__.r = function (exports) {
      if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
      }
      Object.defineProperty(exports, '__esModule', { value: true });
    };
    __webpack_require__("./src/es_index.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
    • 57
    • 58
    • 59
    • 60

    3. commonjs和esmodule混用

    // commonjs导出
    const dateFormat = (date) => {
      return "3000-12-12";
    }
    const priceFormat = (price) => {
      return "100.00";
    }
    module.exports = {
      dateFormat,
      priceFormat
    }
    
    // esmodule 导出
    export const sum = (num1, num2) => {
      return num1 + num2;
    }
    export const mul = (num1, num2) => {
      return num1 * num2;
    }
    
    
    // es module导出内容, CommonJS导入内容
    const { sum, mul } = require("./js/math");
    // CommonJS导出内容, es module导入内容
    import { dateFormat, priceFormat } from "./js/format";
    console.log(sum(20, 30));
    console.log(mul(20, 30));
    console.log(dateFormat("aaa"));
    console.log(priceFormat("bbb"));
    
    • 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

    打包后的代码:

    var __webpack_modules__ = ({
      "./src/index.js":
        (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
          "use strict";
          __webpack_require__.r(__webpack_exports__);
          var _js_format__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/format.js");
          var _js_format__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_js_format__WEBPACK_IMPORTED_MODULE_0__);
          // es module导出内容, CommonJS导入内容
          const math = __webpack_require__("./src/js/math.js");
          // CommonJS导出内容, es module导入内容
          console.log(math.sum(20, 30));
          console.log(math.mul(20, 30));
          console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().dateFormat("aaa"));
          console.log(_js_format__WEBPACK_IMPORTED_MODULE_0___default().priceFormat("bbb"));
        }),
      "./src/js/format.js":
        (function (module) {
          const dateFormat = (date) => {
            return "2020-12-12";
          }
          const priceFormat = (price) => {
            return "100.00";
          }
          module.exports = {
            dateFormat,
            priceFormat
          }
        }),
    
      "./src/js/math.js":
        (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
          __webpack_require__.r(__webpack_exports__);
          __webpack_require__.d(__webpack_exports__, {
            "sum": function () { return sum; },
            "mul": function () { return mul; }
          });
          const sum = (num1, num2) => {
            return num1 + num2;
          }
          const mul = (num1, num2) => {
            return num1 * num2;
          }
        })
    });
    var __webpack_module_cache__ = {};
    // The require function
    function __webpack_require__(moduleId) {
      if (__webpack_module_cache__[moduleId]) {
        return __webpack_module_cache__[moduleId].exports;
      }
      var module = __webpack_module_cache__[moduleId] = {
        exports: {}
      };
      __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
      return module.exports;
    }
    __webpack_require__.n = function (module) {
      var getter = module && module.__esModule ?
        function () { return module['default']; } :
        function () { return module; };
      __webpack_require__.d(getter, { a: getter });
      return getter;
    };
    __webpack_require__.d = function (exports, definition) {
      for (var key in definition) {
        if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
          Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
        }
      }
    };
    __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
    __webpack_require__.r = function (exports) {
      if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
      }
      Object.defineProperty(exports, '__esModule', { value: true });
    };
    __webpack_require__("./src/index.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
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    sourcemap的配置

    devtool配置

    在这里插入图片描述
    在这里插入图片描述

    source-map 比 eval信息更加的全,不仅仅展示出错误的源文件,而且把源文件的目录都给展示出来,这样方便用户查看项目结构。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    babel的使用

    @babel/core:babel的核心代码,必须安装;
    @babel/cli:可以让我们在命令行使用babel;

    yarn add @babel/cli @babel/core
    
    • 1

    输入命令:
    src:是源文件的目录;
    –out-dir:指定要输出的文件夹dist;
    npx babel src --out-dir dist

    使用插件帮助解析:

    yarn add @babel/plugin-transform-arrow-functions -D // 箭头函数 -> function
    yarn add @babel/plugin-transform-block-scoping -D // const -> var
    
    • 1
    • 2

    输入命令:
    npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping ,@babel/plugin-transform-arrow-functions
    对应的webpack配置:
    在这里插入图片描述

    如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):
    yarn add @babel/preset-env -D

    在这里插入图片描述
    目标版本优先级:

    1. @babel/preset-env 里的 targets
    2. package.json 里的 browserslist 字段
    3. .browserslistrc 配置文件

    多个预设联用:
    在这里插入图片描述

    stage-x:

    在这里插入图片描述

    babel编译器原理:
    在这里插入图片描述
    babel的配置文件:

    在这里插入图片描述

    module.exports = {
      presets: [
        ["@babel/preset-env", {
          // false: 不用任何的polyfill相关的代码
          // usage: 代码中需要哪些polyfill, 就引用相关的api
          // entry: 手动在入口文件中导入 core-js/regenerator-runtime, 根据目标浏览器引入所有对应的polyfill
          useBuiltIns: "entry",
          corejs: 3
        }],
        ["@babel/preset-react"]
      ],
      // plugins: [
      //   ["@babel/plugin-transform-runtime", {
      //     corejs: 3
      //   }]
      // ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    promise和generator,symbol转换不了,需要装polyfill

    Hash、ContentHash、ChunkHash区别

    在这里插入图片描述

    cdn服务器

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    webpack代码分离

    在这里插入图片描述
    多入口起点:
    在这里插入图片描述

    Entry Dependencies(入口依赖):
    在这里插入图片描述
    加粗样式:
    在这里插入图片描述
    SplitChunks自定义配置:

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    webpack 打包分析

    打包时间分析

    1. 影响 npm run build 打包时间:node版本,电脑性能。

    2. 默认情况下,打包完成展示,打包的总时间,并没有告诉我,每个插件使用所消耗的时间在这里插入图片描述

    3. 希望看到每个loader,每个plugin消耗的打包时间,可以借助一个插件:speed-measure-webpack-plugin,该插件有webpack版本兼容问题

    4. npm install speed-measure-webpack-plugin

    5. 有部分插件不支持,可能不支持,需要给注释一下

    打包文件大小分析

    方法一:官方提供的

      "scripts": {
        "stats": "webpack --config ./config/webpack.common.js --env production --profile --json=stats.json"
      },
    
    • 1
    • 2
    • 3

    --profile --json=stats.json 加了这个 生成stats.json文件,把该文件上传到
    https://webpack.github.io/analyse/网站上,可以看到可视化分析

    方法二:插件提供的

    1. npm install webpack-bundle-analyzer -D
    2. const { BundleAnalyzerPLugin } = require(" webpack-bundle-analyzer")
    3. new BundleAnalyzerPlugin( )
    4. npm run build 自动打开网页 127.0.0.1:8888

    webpack-cli 只是处理参数,且执行webpack命令

    1. npm install webapck webpack-cli --save-dev

    2. "scripts": { "build": "webpack --config wk.config.js" }

    3. 当在命令行中执行 npm run build 时,会执行node_modules/.bin下的webpack可执行文件在这里插入图片描述

    4. 我们看下上面webpack文件的内容在这里插入图片描述

    5. 从代码中可以看到,会执行node_modules/webpack/bin/ 目录下的webpack.js,该文件主要代码如下:在这里插入图片描述

    6. 该文件最重要的函数就是 runCli ,该函数可以执行 webpack-cli 包中bin目录下的cli.js 文件,也就是说在此之前的步骤只是为了找到cli.js文件,在此之后,webpack-cli 才发挥作用。

    7. 同时,webpack.js 文件也做了一些辅助判断,首先查看你是否安装了webpack-cli,如果没有安装,就会询问你是否安装(或手动安装)该包,如果选择不安装,那么程序运行到这就停止了。

    8. 接着打开webpack-cli/bin/cli.js在这里插入图片描述

    9. 该文件的主要函数为 runCLI,而 runCLI 又来自 bootstrap.js 文件,打开 bootstrap.js 文件在这里插入图片描述

    10. new WebpackCLI(),然后继续调用实例的run方法在这里插入图片描述

    11. 在constructor内,获取了webpack包的函数库在这里插入图片描述

    12. 注意,到了这里才真正用到了 webpack-cli 暴露出的接口,cli.run(args) 用来处理命令行参数,此时args参数为:在这里插入图片描述

    13. 最终,从以上整个过程,我们可以知道 webpack-cli 是用来处理命令行参数,并通过参数构建 compiler 对象,然后才是对代码进行打包的过程。

    14. 所以说,webpack-cli对于文件打包不是必需的。

    15. 既然 webpack-cli只是为了处理命令行参数,那我们同样可以构建自己的cli来处理参数,比如 lyx-cli。在第三方框架中,React 和 Vue(未使用Vite的版本)也没有使用 webpack-cli.

    16. 自己写代码替换webpack-cli,咱们在webpack源码下做实验在这里插入图片描述

    17. zanlan/src/utils/math.js在这里插入图片描述

    18. zanlan/src/main.js在这里插入图片描述

    19. zanlan/build.js 在这里插入图片描述

    20. zanlan/webpack.config.js在这里插入图片描述

    21. 执行node zanlan/build.js,可以看到在zanlan文件夹下生成了build文件夹,在内部有bundle.js,bundle.js.map两个文件。

    22. webpack启动流程图在这里插入图片描述

    webpack源码学习

    1. webpack源码地址,点击Tags,可以选择不同的版本源码在这里插入图片描述
    2. git clone --branch v5.72.1 https://github.com/webpack/webpack.git
    3. 源码默认不支持写es6语法的,修改一下eslint配置在这里插入图片描述
    4. 反向安装依赖:npm install
    5. webpack-cli只是收集配置参数,执行webpack命令
    6. 我们自己可以模拟写个简单的对wbepack的调用,不走webpack-cli,在这里插入图片描述
  • 相关阅读:
    微信公众号消息接入(普通消息+模板消息)
    【实战项目开发技术分享】如何设置机器人禁行区/虚拟墙
    [SUCTF 2019]EasyWeb
    SpringBoot+POI方式导出excel【加水印】
    传统Spring项目的创建和使用xml文件来保存对象和取对象
    [golang 流媒体在线直播系统] 3.发布Golang云直播代码到远程服务器、配置域名解析、配置Https播放视频
    Python学习笔记--多线程编程
    shiro的简单介绍
    【编程必备知识】文件内容的读写
    【2023 · CANN训练营第一季】应用开发(初级)——第一章 AscendCL概述
  • 原文地址:https://blog.csdn.net/formylovetm/article/details/125764970