• 浅析webpack基础核心概念、Loader作用及执行流程理解及常用Loader介绍、Plugin作用及执行流程理解及常用Plugin介绍、Loader与Plugin区别


      Webpack 是一个用于现代JS应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

      我们看一下Webpack一些核心概念:

    1、Entry:入口,指示 Webpack 应该使用哪个模块,来作为构建其内部依赖图(dependency graph) 的开始

    2、Output:输出结果,告诉 Webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件

    3、Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块

    4、Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割

    5、Loader:模块代码转换器,让webpack能够去处理除了JS、JSON之外的其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中

    6、Plugin:扩展插件。在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。常见的有:打包优化,资源管理,注入环境变量

    6、Mode:模式,告知 webpack 使用相应模式的内置优化

    7、Browser Compatibility:浏览器兼容性,Webpack 支持所有符合 ES5 标准的浏览器(IE8以上版本)

      Webpack的作用非常多,并且我们还可以通过loader和plugin机制去进一步扩展能力,按照项目需要去实现个性化的功能。

      Loader 和 Plugin 在 Webpack 里是支柱能力。在整个构建流程中,Loader 和 Plugin 对编译结果起着决定性的作用,下面主要讲一下 Webpack 中一些常用的 Loader 和 Plugin。

    一、Loader 介绍

    1、Loader 是什么?Loader 用于对模块的"源代码"进行转换,在 import 或"加载"模块时预处理文件

      webpack中提供了一种处理多种文件格式的机制,这便是Loader,我们可以把Loader当成一个转换器,它可以将某种格式的文件转换成Webpack支持打包的模块。

      在Webpack中,一切皆模块,我们常见的Javascript、CSS、Less、Typescript、Jsx、图片等文件都是模块,不同模块的加载是通过模块加载器来统一管理的,当我们需要使用不同的 Loader 来解析不同类型的文件时,我们可以在 module.rules 字段下配置相关规则。

      Webpack做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中。如下图所示

      左边是“模块依赖”,然后打包成右边的“静态资源”。

      在Webpack内部中,任何文件都是模块,不仅仅只是JS文件。默认情况下,在遇到 import 或者 require 加载模块的时候,Webpack只支持对JS和JSON文件打包,像 css、sass、png 等这些类型的文件的时候,Webpack则无能为力,这时候就需要配置对应的 Loader 进行文件内容的解析。

      在加载模块的时候,执行顺序如下:entry  ->  loaders  ->  output

      当 Webpack 碰到不识别的模块的时候,Webpack会在配置的中查找该文件解析规则。关于配置 Loader 的方式有三种:

    (1)配置方式(推荐):在 webpack.config.js 文件中指定 loader

    (2)内联方式:在每个 import 语句中显式指定 loader

    (3)CLI 方式:在 shell 命令中指定它们

    2、配置方式:

    // 配置方式:关于loader的配置,是写在module.rules属性中,属性介绍如下:
    // rules是一个数组的形式,因此我们可以配置很多个loader
    // 每一个loader对应一个对象的形式,对象属性test为匹配的规则,一般情况为正则表达式
    // 属性use针对匹配到文件类型,调用对应的 loader 进行处理
    module.exports = {
      module: {
        rules: [
          {
            test: /.css$/,
            use: [
              { loader: 'style-loader' },
              {
                loader: 'css-loader',
                options: {
                  modules: true
                }
              },
              { loader: 'sass-loader' }
            ]
          }
        ]
      }
    };

    3、Loader的特性

      拿上述代码,来讲讲 Loader 的特性,从上述代码可以看到,在处理 css 模块的时候,use 属性中配置了三个Loader分别处理 css 文件

      因为Loader支持链式调用,链中的每个Loader会处理之前已处理过的资源,最终变为JS代码。顺序为相反的顺序执行,即上述执行方式为sass-loadercss-loaderstyle-loader

      除此之外,Loader 的特性还有如下:

    (1)loader 本质上是一个函数,output=loader(input) // input可为工程源文件的字符串,也可是上一个loader转化后的结果;

    (2)第一个 loader 的传入参数只有一个:资源文件(resource file)的内容;

    (3)loader支持链式调用,webpack打包时是按照数组从后往前的顺序将资源交给loader处理的。

    (4)支持同步或异步函数。

      可以通过 loader 的预处理函数,为JS生态系统提供更多能力。用户现在可以更加灵活地引入细粒度逻辑,例如:压缩、打包、语言翻译和更多其他特性

    3、代码结构通常如下:
    // source:资源输入,对于第一个执行的loader为资源文件的内容;后续执行的loader则为前一个loader的执行结果
    // sourceMap: 可选参数,代码的sourcemap结构
    // data: 可选参数,其它需要在Loader链中传递的信息,比如 posthtml/posthtml-loader 就会通过这个参数传递参数的AST对象
    const loaderUtils = require('loader-utils');
    module.exports = function(source, sourceMap?, data?) {
      // 获取到用户给当前 Loader 传入的 options
      const options = loaderUtils.getOptions(this);
      // TODO: 此处为转换source的逻辑
      return source;
    };

    二、常用的 Loader

    1、babel-loader基于babel,用于解析JS文件。babel有丰富的预设和插件,babel的配置可以直接写到options里或者单独写道配置文件里。

      Babel是一个Javscript编译器,可以将高级语法(主要是ECMAScript 2015+ )编译成浏览器支持的低版本语法,它可以帮助你用最新版本的JS写代码,提高开发效率。

    (1)常用预设:

    @babel/preset-env              ES2015+ 语法
    @babel/preset-typescript     TypeScript
    @babel/preset-react            React

    (2)插件和预设的执行顺序:

    插件比预设先执行
    插件执行顺序是插件数组从前向后执行
    预设执行顺序是预设数组从后向前执行

    (3)webpack配置代码

    // webpack.config.js
    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                ['@babel/preset-env', { targets: "defaults" }]
              ],
              plugins: ['@babel/plugin-proposal-class-properties'],
              // 缓存 loader 的执行结果到指定目录,默认为node_modules/.cache/babel-loader,之后的 webpack 构建,将会尝试读取缓存
              cacheDirectory: true,
            }
          }
        }
      ]
    }

      以上options参数也可单独写到配置文件里,许多其他工具都有类似的配置文件:ESLint (.eslintrc)、Prettier (.prettierrc)。

      配置文件我们一般只需要配置 presets(预设数组) 和 plugins(插件数组) ,其他一般也用不到,代码示例如下:
    // babel.config.js
    module.exports = (api) => {
        return {
            presets: [
                '@babel/preset-react',
                [
                    '@babel/preset-env', {
                        useBuiltIns: 'usage',
                        corejs: '2',
                        targets: {
                            chrome: '58',
                            ie: '10'
                        }
                    }
                ]
            ],
            plugins: [
                '@babel/plugin-transform-react-jsx',
                '@babel/plugin-proposal-class-properties'
            ]
        };
    };

    2、ts-loader 为webpack提供的 TypeScript loader,打包编译Typescript

    // 1、安装依赖:
    npm install ts-loader --save-dev
    npm install typescript --dev
    
    // 2、webpack配置如下:webpack.config.json
    module.exports = {
      mode: "development",
      devtool: "inline-source-map",
      entry: "./app.ts",
      output: {
        filename: "bundle.js"
      },
      resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: [".ts", ".tsx", ".js"]
      },
      module: {
        rules: [
          // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
          { test: /\.tsx?$/, loader: "ts-loader" }
        ]
      }
    };
    // 3、还需要typescript编译器的配置文件tsconfig.json:
    {
      "compilerOptions": {
        // 目标语言的版本
        "target": "esnext",
        // 生成代码的模板标准
        "module": "esnext",
        "moduleResolution": "node",
        // 允许编译器编译JS,JSX文件
        "allowJS": true,
        // 允许在JS文件中报错,通常与allowJS一起使用
        "checkJs": true,
        "noEmit": true,
        // 是否生成source map文件
        "sourceMap": true,
        // 指定jsx模式
        "jsx": "react"
      },
      // 编译需要编译的文件或目录
      "include": [
        "src",
        "test"
      ],
      // 编译器需要排除的文件或文件夹
      "exclude": [
        "node_modules",
        "**/*.spec.ts"
      ]
    }

    3、markdown-loader:markdown编译器和解析器

    4、raw-loader 可将文件作为字符串导入

    5、file-loader 用于处理文件类型资源,如jpg,png等图片。返回值为publicPath为准

    // file.js
    import img from './webpack.png';
    console.log(img); // 编译后:https://www.tencent.com/webpack_605dc7bf.png
    // webpack.config.js
    module.exports = {
      module: {
        rules: [
          {        test: /\.(png|jpe?g|gif)$/i,
            loader: 'file-loader',
            options: {
              name: '[name]_[hash:8].[ext]',
              publicPath: "https://www.tencent.com",
            },
          },
        ],
      },
    };
    // css文件里的图片路径变成如下:
    /* index.less */
    .tag {
      background-color: red;
      background-image: url(./webpack.png);
    }
    /* 编译后:*/
    background-image: url(https://www.tencent.com/webpack_605dc7bf.png);

    6、url-loader:它与 file-loader 作用相似,也是处理图片的,只不过 url-loader 可以设置一个根据图片大小进行不同的操作,如果该图片大小大于指定的大小,则将图片进行打包资源,否则将图片转换为base64字符串合并到 js 文件里。

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(png|jpg|jpeg)$/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  name: '[name]_[hash:8].[ext]',
                  // 这里单位为(b) 10240 => 10kb
                  // 这里如果小于10kb则转换为base64打包进js文件,如果大于10kb则打包到对应目录
                  limit: 10240,
                }
              }
            ]
          }
        ]
      }
    }

    7、style-loader 通过注入<style>标签将CSS插入到DOM中

    8、css-loader 仅处理css的各种加载语法(@import和url()函数等),就像 js 解析 import/require() 一样

    9、postcss-loader

      PostCSS 是一个允许使用 JS 插件转换样式的工具。 这些插件可以检查你的 CSS,支持 CSS Variables 和 Mixins, 编译尚未被浏览器广泛支持的先进的 CSS 语法,内联图片,以及其它很多优秀的功能。PostCSS 在业界被广泛地应用。PostCSS 的 autoprefixer 插件是最流行的 CSS 处理工具之一。

    10、less-loader:解析less,转换为css

    三、Plugin 介绍

    1、Plugin 是什么?以下来自于《深入浅出 Webpack》

    1、Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。

    这条生产线上的每个处理流程的职责都是单一的,多个流程之间又存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。

    插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。

    2、Webpack 通过 Tapable 来组织这条复杂的生产线。

    Webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。

    Webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。

    2、Plugin(Plug-in)是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功能。它是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,因为其需要调用原纯净系统提供的函数库或者数据

      webpack中的Plugin也是如此,Plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在webpack的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期。目的在于解决 Loader 无法实现的其他事。

    3、配置方式

      这里讲述文件的配置方式,一般情况,通过配置文件导出对象中 plugins 属性传入 new 实例对象。如下所示

    const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
    const webpack = require('webpack'); // 访问内置的插件
    module.exports = {
      ...
      plugins: [
        new webpack.ProgressPlugin(),
        new HtmlWebpackPlugin({ template: './src/index.html' }),
      ],
    };

    四、常用 Plugin

    1、html-webpack-plugin 基本作用是生成html文件

    (1)单页应用可以生成一个html入口,多页应用可以配置多个html-webpack-plugin实例来生成多个页面入口

    (2)为html引入外部资源如script、link,将entry配置的相关入口chunk以及mini-css-extract-plugin抽取的css文件插入到基于该插件设置的template文件生成的html文件里面,具体的方式是link插入到head中,script插入到head或body中。

    2、clean-webpack-plugin

      默认情况下,这个插件会删除webpack的output.path中的所有文件,以及每次成功重新构建后所有未使用的资源。

      这个插件在生产环境用的频率非常高,因为生产环境经常会通过 hash 生成很多 bundle 文件,如果不进行清理的话每次都会生成新的,导致文件夹非常庞大。

    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    module.exports = {
        plugins: [
            new CleanWebpackPlugin(),
        ]
    };

    3、mini-css-extract-plugin:该插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件。

    // 建议 mini-css-extract-plugin 与 css-loader 一起使用
    // 将 loader 与 plugin 添加到 webpack 配置文件中
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    module.exports = {
      plugins: [new MiniCssExtractPlugin()],
      module: {
        rules: [
          {
            test: /\.css$/i,
            use: [MiniCssExtractPlugin.loader, 'css-loader'],
          }
        ],
      },
    };

    4、webpack.HotModuleReplacementPlugin:模块热替换插件,又称为 HMR

      该功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。

      启动方式有2种:

    (1)引入插件 webpack.HotModuleReplacementPlugin,并且设置 devServer.hot: true

    (2)命令行加 --hot 参数

    // package.json配置:
    {
      "scripts": {
        "start": "NODE_ENV=development webpack serve --progress --mode=development --config=scripts/dev.config.js --hot"
      }
    }
    // webpack的配置
    plugins: [
      // 大多数情况下不需要任何配置
      new webpack.HotModuleReplacementPlugin(),
    ],
    devServer: {
      hot: true,
    }

      注意:HMR 绝对不能被用在生产环境。

    5、webpack.DefinePlugin 创建一个在编译时可以配置的全局常量。这会对开发模式和生产模式的构建允许不同的行为非常有用。

      因为这个插件直接执行文本替换,给定的值必须包含字符串本身内的实际引号。

    6、webpack-bundle-analyzer 可以看到项目各模块的大小,可以按需优化

      一个webpack的bundle文件分析工具,将bundle文件以可交互缩放的treemap的形式展示。

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    module.exports = {
      plugins: [
        new BundleAnalyzerPlugin()
      ]
    }

    7、SplitChunksPlugin 代码分割

    五、Loader和Plugin的区别

      上面我们有提到 loader plugin 对应的概念,先来回顾下:

    1、loader 是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中

    2、plugin 赋予了 webpack 各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader 无法实现的其他事

      从整个运行时机上来看,如下图所示:

      可以看到,两者在运行时机上的区别:

    1、loader 运行在打包文件之前

    2、plugins 在整个编译周期都起作用

      在 webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 api 改变输出结果

      对于 loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将A.scssA.less转变为B.css,单纯的文件转换过程

  • 相关阅读:
    验证码的编写 asp.net
    甲骨文收购Sun,IT业界进入三国时代
    动态加载css文件导致IE8崩溃的问题
    页面调试中关于Console应该注意的地方
    关于仿网易邮箱5.0的Neter UI框架的开源声明
    仿网易邮箱5.0(二):core.js
    仿网易邮箱5.0(三):panel.js
    仿网易邮箱5.0(一):页面基本样式
    Windows下配置Sass编译环境
    ASP+Access查询时按时间进行查询
  • 原文地址:https://www.cnblogs.com/goloving/p/16152555.html
Copyright © 2020-2023  润新知