Webpack5核心原理与应用实践(基础用法)一


前言

记录Webpack基本配置规则;根据场景、技术栈运用具体方法、工具与技巧。
主要有:搭建完善的JavaScript、CSS开发环境;搭建微前端、NPM包、桌面应用等。


一、Webpack配置项

1.Webpack编译流程

Webpak原生配置项数不胜数,最终都会作用于不同阶段

大致编译流程可分为四个流程:

输入—>模块处理—>后处理—>输出

  • 输入:从文件系统读入代码文件
  • 模块递归处理:调用 Loader 转译 Module(模块) 内容,并将结果转换为 AST(抽象语法树),从中分析出模块依赖关系,进一步递归调用模块处理过程,直到所有依赖文件都处理完毕;
  • 后处理:所有模块递归处理完毕后开始执行后处理,包括模块合并注入运行时产物优化等,最终输出 Chunk (按照某种规则的模块集合)集合;
  • 输出:将Chunk写出到外部文件系统;

2.Webpack配置

Webpack配置项可分为两大类

  • 流程类:作用于打包流程某个或若干个环节,直接影响编译打包效果的配置项
  • 工具类:打包主流程之外,提供更多工程化工具的配置项

(1)流程类配置

与流程类配置强相关配置有:

  • 输入输出:
    • entry:用于定义项目入口文件,Webpack 会从这些入口文件开始按图索骥找出所有项目文件;
    • context:项目执行上下文路径;
    • output:配置产物输出路径、名称等;
  • 模块处理:
    • resolve:用于配置模块路径解析规则,可用于帮助 Webpack 更精确、高效地找到指定模块
    • module:用于配置模块加载规则,例如针对什么类型的资源需要使用哪些 Loader 进行处理
    • externals:用于声明外部资源,Webpack 会直接忽略这部分资源,跳过这些资源的解析、打包操作
  • 后处理:
    • optimization:用于控制如何优化产物包体积,内置 Dead Code EliminationScope Hoisting、代码混淆、代码压缩等功能
    • target:用于配置编译产物的目标运行环境,支持 web、node、electron 等值,不同值最终产物会有所差异
    • mode:编译模式短语,支持 development、production 等值,可以理解为一种声明环境的短语

Webpack 首先需要根据输入配置(entry/context) 找到项目入口文件;之后根据按模块处理(module/resolve/externals 等) 所配置的规则逐一处理模块文件,处理过程包括转译、依赖分析等;模块处理完毕后,最后再根据后处理相关配置项(optimization/target 等)合并模块资源、注入运行时依赖、优化产物结构等。


(2)工具类配置

除了核心的打包功能之外,Webpack 还提供了一系列用于提升研发效率的工具,大体上可划分为:

  • 开发效率类:
    • watch:用于配置持续监听文件变化,持续构建
    • devtool:用于配置产物 Sourcemap(储存代码转换前后的对应位置信息) 生成规则
    • devServer:用于配置与 HMR(模块热更新) 强相关的开发服务器功能
  • 开发效率类:
    • cache:Webpack 5 之后,该项用于控制如何缓存编译过程信息与编译结果
    • performance:用于配置当产物大小超过阈值时,如何通知开发者
  • 日志类:
    • stats:用于精确地控制编译过程的日志内容,在做比较细致的性能调试时非常有用
    • infrastructureLogging:用于控制日志输出方式,例如可以通过该配置将日志输出到磁盘文件

3.配置逻辑

文件结构如下:

.
├── src
|   └── index.js
└── webpack.config.js

其中,src/index.js 为项目入口文件,webpack.config.jsWebpack 配置文件。在配置文件中,首先我们需要声明项目入口:

// webpack.config.js
module.exports = {
  entry: "./src/index"
};

声明产物输出路径:

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./src/index",
  output: {
    filename: "[name].js",
    path: path.join(__dirname, "./dist"),
  }
};

在前端项目中经常需要处理 JS 之外的其它资源,包括 css、ts、图片等,此时需要为这些资源配置适当的加载器:

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./src/index",
  output: {
    filename: "[name].js",
    path: path.join(__dirname, "./dist"),
  },
  module: {
    rules: [{
      test: /\.less$/i,
      include: {
        and: [path.join(__dirname, './src/')]
      },
      use: [
        "style-loader",
        "css-loader",
        // "./loader",
        {
          loader: "less-loader",
        },
      ],
    }],
  },
};

二、脚手架工具

对于项目的推进,配置管理将十分困难。所以社区提供了对应的脚手架工具来管理配置。

  • Vue CLI:用于帮助用户快速创建、运行 Vue.js 项目脚手架的命令行工具;
  • create-react-app:用于创建 React 项目脚手架的命令行工具;
  • @angular/cli:用于创建 angular 项目的命令行工具;
  • webpack-cli:Webpack 官方提供的命令行工具,提供了一套交互式生成配置文件的指令集,以及项目编译、开发、迁移等功能;
  • Neutrino:用于快速创建、运行现代 JavaScript 应用的工具,同时支持 React、Preact、Vue、Web、Node.js、Library 等场景;
  • react-starter-kit:用于创建 React + Relay + GraphQL 应用的脚手架工具,内置 SSR 支持。

1. Vue CLI 搭建项目脚手架

Vue CLI 底层调用 Webpack 实现针对 .vue 等资源的编译打包功能;调用 webpack-dev-server 实现包含 HMR 功能的开发服务器功能;还能通过插件方式整合 ESLintBabalLess 等工具。

安装依赖:(安装完成后 可以使用 vue -V 测试是否安装成功)

npm install -g @vue/cli

# 或者使用 yarn
yarn global add @vue/cli

创建项目:vue create 命令 (可使用 vue create --help 命令查看 create 的参数列表 仅限于 vue3中使用)

vue create 项目名

执行 create 命令后,CLI 会进一步询问使用何种脚手架方案:
通常使用第三种自定义

Vue CLI v4.5.15
? Please pick a preset: (Use arrow keys)Default ([Vue 2] babel, eslint)
  Default (Vue 3) ([Vue 3] babel, eslint)
  Manually select features

vue2使用 vue init webpack 项目名 来构建项目
基本回车就行倒数四项根据自身需求来

 Project name webpack5-dispose
? Project description A Vue.js project
? Author xxx <邮箱>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) yarn

Vue CLI 底层依赖于 Webpack 实现编译打包等工程化能力,开发者可通过 configureWebpackchainWebpack 配置项修改 Webpack 配置信息。

// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
  }
}

configureWebpack 的配置规则与 Webpack 一致,同样支持 plugins/module/resolve 等配置项。实际上,Vue CLI 内部最终会调用 webpack-merge 将 configureWebpack 值与其它上下文配置合并,生成最终的 Webpack 配置信息。

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .tap(options => {
          // modify the options...
          return options
        })
  }
}

使用 inspect 命令生成完整的 Webpack 配置信息:

vue inspect > output.js

2. CRA 搭建项目脚手架

使用如下代码可使用CRA创建一个react项目,webStrom创建react项目时可选CRA创建项目

npx create-react-app [项目名称]

Webpack5核心原理与应用实践(基础用法)一_第1张图片


默认规则创建的脚手架包含如下工程能力:

  • JSX、ES6、TypeScript、Flow 语法支持
  • CSS 自动添加 --webkit-- 前缀
  • 基于 Jest 的自动化测试能力
  • 支持 HMR 的开发服务器
  • 等等

必要时,也可以通过 npm run eject 命令导出完整的项目配置结构:
注意:可能有未被管理的文件需要先提交在使用该命令
会生成对应的配置:

Webpack5核心原理与应用实践(基础用法)一_第2张图片

导出配置后,直接修改 webpack.config.js 等相关配置文件即可控制各项功能行为。


三、如何借助 Babel+TS+ESLint 构建现代 JS 工程环境

1.使用Babel

作用:用于将代码向后兼容,在旧版本JS引擎中运行
因为ES6提供了许多JS新特性,这就会导致旧版本的JSNode等产生兼容性问题,Babel就是解决这个问题,它会将代码转译成旧版本的代码,使得旧版本引擎能正常使用。

// 使用 Babel 转译前
arr.map(item => item + 1)

// 转译后
arr.map(function (item){
  return item + 1;
})

Webpack 场景下,只需使用 babel-loader 即可接入 Babel 转译功能:

  1. 安装依赖
npm i -D @babel/core @babel/preset-env babel-loader
  1. 添加模块处理规则
module.exports = {
  /* ... */
  module: {
    rules: [
      {
        test: /\.js$/, // 规则 表示所有.js后缀文件
        use: ['babel-loader'], // 使用babel-loader处理
      },
    ],
  },
};
  1. 执行编译命令
npx webpack
  1. 可以使用 .babelrc 文件或 rule.options 属性配置 Babel 功能逻辑
// 预先安装 @babel/preset-env
// npm i -D @babel/preset-env
module.exports = {
  /* ... */
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
        ],
      },
    ],
  },
};

@babel/preset-env是Babel预设规则集----Preset,可以使用各种Preset资源:

  • babel-preset-react:包含 React 常用插件的规则集,支持 preset-flowsyntax-jsxtransform-react-jsx 等;
  • @babel/preset-typescript:用于转译 TypeScript 代码的规则集
  • @babel/preset-flow:用于转译 Flow 代码的规则集

vue-cli

在文件build/webpack.base.conf.js中脚手架已经配置了相关的配置项,但我们依然可以在webpack.config.js中修改一些配置 entry`入口文件

entry: {
    app: './src/main.js'
  },

output编译产物的目标位置

output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'// 判断是什么环境
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },

resolve解析规则

  resolve: {
    extensions: ['.js', '.vue', '.json'],//文件扩展名
    alias: {// 别名
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },

modele解析模块

 module: {
    rules: [
      {
        test: /\.vue$/, // 规则过滤条件,这里表示对所有 .vue 后缀文件
        loader: 'vue-loader',// vue-loader处理
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },

CRA

config/webpack.config.js中已经有脚手架给我门配置的一些

module: {
    rules: [
      {
        test: /\.js$/,// 声明该规则过滤文件,只有路径和文件类型对应才会生效,此处为所有的 .js 后缀的文件生效
        loader: 'babel-loader',// 所有命中文件传入Loader序列做转译
        include: [resolve('src'), resolve('test'), //包内容路径 这里时src和test下的文件  
        resolve('node_modules/webpack-dev-server/client')]// 以及node_modules中自己的包
      }
    ]
  }

不光配置了程序内的文件,还配置了文件外

{
              test: /\.(js|mjs)$/,
              exclude: /@babel(?:\/|\\{1,2})runtime/,
              loader: require.resolve('babel-loader'),
              options: {
                babelrc: false,
                configFile: false,
                compact: false,
                presets: [
                  [
                    require.resolve('babel-preset-react-app/dependencies'),
                    { helpers: true },
                  ],
                ],
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                
                // Babel sourcemaps are needed for debugging into node_modules
                // code.  Without the options below, debuggers like VSCode
                // show incorrect code and set breakpoints on the wrong lines.
                sourceMaps: shouldUseSourceMap,
                inputSourceMap: shouldUseSourceMap,
              },
            },

2.使用 TypeScript

作用:提供了一系列类型约束,能更早的发现错误,确保运行阶段的类型安全性,适合在多人协作的项目中使用。

Webpack 有很多种接入 TypeScript 的方法,包括 ts-loaderawesome-ts-loaderbabel-loader。通常可使用 ts-loader 构建 TypeScript 代码:

Vue CLi和 CRA均使用babel-loader引入TypeScript

  1. 安装依赖
npm i -D @babel/preset-typescript
  1. 配置 Webpack
// 预先安装 @babel/preset-env
// npm i -D @babel/preset-env
module.exports = {
  /* ... */
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-typescript'],
            },
          },
        ],
      },
    ],
  },
};

@babel/preset-typescript只是做了简单的代码转换,未做类型检查等,可以使用其它Preset


3.使用 ESLint

js作为一种灵活、弱类型脚本语言,对于一些开发过程中产生的语法、类型导致影响稳定的错误,这对开发效率和质量造成影响,ESLint就是为了解决这些问题。

作用:JS代码风格检查器,能将违反规则的代码修复。

Webpack 下,可以使用 eslint-webpack-plugin 接入 ESLint 工具,步骤:

  1. 安装依赖
# 安装 webpack 依赖
yarn add -D webpack webpack-cli

# 安装 eslint 
yarn add -D eslint eslint-webpack-plugin

# 简单起见,这里直接使用 standard 规范
yarn add -D eslint-config-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node
  1. 在项目根目录添加 .eslintrc 配置文件,内容:
// .eslintrc
{
  "extends": "standard"
}
  1. 添加 webpack.config.js 配置文件,补充 eslint-webpack-plugin 配置:
// webpack.config.js
const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin')

module.exports = {
  entry: './src/index',
  mode: 'development',
  devtool: false,
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  // 添加 eslint-webpack-plugin 插件实例
  plugins: [new ESLintPlugin()]
}
  1. 执行编译命令
npx webpack

也可以使用第三方扩展来进行特殊的代码风格检测

  • eslint-config-airbnb:Airbnb 提供的代码风格规则集,算得上 ESLint 生态第一个成名的规则集合
  • eslint-config-standard:Standard.js 代码风格规则集,史上最便捷的统一代码风格的方式
  • eslint-plugin-vue:实现对 Vue SFC 文件的代码风格检查
  • eslint-plugin-react:实现对 React 代码风格检查
  • @typescript-eslint/eslint-plugin:实现对 TypeScript 代码风格检查
  • eslint-plugin-sonarjs:基于 Sonar 的代码质量检查工具,提供圈复杂度、代码重复率等检测功能

总结

本文主要记录了Webpack配置底层逻辑结构以及如何借助Babel、TS、ESLint构建JS环境

你可能感兴趣的:(Webpack,webpack,javascript,前端)