Webpack 知识体系 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 13 天

0x1 Webpack 入门

  1. 什么是 Webpack

    前端项目由资源构成,当资源较多且有一定的先后顺序时,引出了前端工程化工具,有了“前端工程”概念。

    Webpack 是一种前端工程工具,本质上是一种前端资源编译、打包工具

  2. Webpack 入门

    1. 实例

      1. 安装:npm i -D webpack webpack-cli

      2. 编辑配置文件 webpack.config.js

        module.exports = {
            entry: 'main.js',
            output: {
                filename: "[name].js",
                path: path.join(__dirname, "./dist")
            },
            module: {
                rules: [{
                    test: /\.less$/i,
                    use: ['style-loader', 'css-loader', 'less-loader']
                }]
            }
        }
        
      3. 执行编译命令:npx webpack

    2. 核心流程

      1. 入口处理

        从 entry 文件开始,启动编译流程

      2. 依赖解析

        从 entry 文件开始,根据 require 或 import 等语句找到依赖资源

      3. 资源解析

        根据 module 配置,调用资源转移器,将 png、css 等非标准 JS 资源转译为 JS 内容

      4. 资源合并打包

        将转译后的资源内容合并打包为可直接在浏览器运行的 JS 文件

    3. 模块化 + 一致性

      • 多个文件资源合并成一个从而减少 HTTP 请求数
      • 支持模块化开发
      • 支持高级 JS 特性
      • 支持 TypeScript、CoffeeScript 方言
      • 统一 svg、css、font 等其他资源的处理模型
      • ...

0x2 使用 Webpack

关于 Webpack 的使用方法,基本都围绕配置展开

  • 流程类配置:作用于流程中某个或若干个环节,直接影响打包效果的配置项
    • 输入:entry、context
    • 模块解析:resolve、externals
    • 模块转译:moudle
    • 后处理:optimization、mode、target
  • 工具类配置:主流程之外,提供更多工程化能力的配置项
  1. 一般文件结构

    根目录
    src
    index.js
    webpack.config.js
    1. 声明入口 entry

      module.exports = {
          entry: "./src/index"
      };
      
    2. 声明产物出口 output

      const path = require("path")
       
      module.exports = {
          entry: "./src/index",
          output: {
              filename: "[name].js",
              path: path.join(__dirname, "./dist")
          }
      }
      
    3. 运行 npx webpack

  2. 处理 CSS

    1. 在 src 目录下添加文件 index.css

    2. 在 index.js 文件中添加 const styles = require('./index.css')

    3. 安装 Loader:npm add -D css-loader style-loader

    4. 添加 module 处理 CSS 文件

      const path = require("path")
      
      module.exports = {
          entry: "./src/index",
          output: {
              filename: "[name].js",
              path: path.join(__dirname, "./dist")
          },
          
          module: {
              rules: [{
                  test: /\.css/i,
                  use: [
                      "style-loader",
                      "css-loader"
                  ]
              }]
          }
      }
      
  3. 接入 Babel

    1. 在 index.js 中添加

      class Person {
          constructor() {
              this.name = 'Tecvan'
          }
      }
      console.log((new Person()).name)
      const say = () => {}
      
    2. 安装依赖:npm i -D @babel/core @babel/preset-env babel-loader

    3. 声明产物出口 output

      const path = require("path")
      
      module.exports = {
          entry: "./src/index",
          output: {
              filename: "[name].js",
              path: path.join(__dirname, "./dist")
          },
          
          module: {
              rules: [{
                  test: /\.js?$/,
                  use: [{
                      loader: 'babel-loader',
                      options: {
                          presets: [
                              [
                                  '@babel/preset-env'
                              ]
                          ]
                      }
                  }]
              }]
          }
      }
      
    4. 执行:npx webpack

  4. 生成 HTML

    1. 安装依赖:npm i -D html-webpack-plugin

    2. 声明产物出口 output

      const path = require("path")
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      
      module.exports = {
          entry: "./src/index",
          output: {
              filename: "[name].js",
              path: path.join(__dirname, "./dist")
          },
          plugins: [new HtmlWebpackPlugin()]
      }
      
    3. 执行:npx webpack

  5. HMR

    Hot Module Replacement(模块热替换)

    1. 开启 HMR

      // filename: webpack.config.js
      moudle.exports = {
          // ...
          devServer: {
              hot: true
          }
      }
      
    2. 启动 Webpack:npx webpack serve

  6. Tree-Shaking

    树摇,用于删除 Dead Code

    • Dead Code
      • 代码没有被用到,不可到达
      • 代码的执行结果不会被用到
      • 代码只读不写
    • Tree-Shaking
      • 模块导出了,但未被其他模块使用

0x3 理解 Loader

  1. Loader 的作用

    Loader 是用于将资源翻译为标准 JS 的模块

  2. 使用 Loader

    1. 文件结构:

      根目录
      src
      a.less
      b.less
      index.js
      webpack.config.js
    2. 在 index.js 添加import styles from './a.less'

    3. 安装 Loader:npm add -D css-loader style-loader less-loader

    4. 添加 module 处理 CSS 文件

      module.exports = {
          module: {
              rules: [{
                  test: /\.less$/i,
                  use: [
                      "style-loader",
                      "css-loader",
                      "less-loader"
                  ]
              }]
          }
      }
      
  3. 认识 Loader

    1. 链式调用

      以上述 module 为例:

      1. less-loader:实现 less => css 的转换
      2. css-loader:将 CSS 包装成类似module.exports = "${css}"的内容,从而符合 JavaScript 语法
      3. style-loader:将 CSS 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签
      return
      less file
      webpack make
      less-loader
      css-loader
      style-loader
    2. 其他特性

      • 链式执行
      • 支持异步执行
      • 分 normal、pitch 两种模式
  4. 编写 Loader

    • eslint-loader/index.js

      import getOptions from './getOptions'
      import Linter from './Linter'
      import cacheLoader from './cacheLoader'
      
      export default function loader(content, map) {
          const options = getOptions(this)
          const linter = new Linter(this, options)
          this.cacheable()
          if(options.cache) {
              cacheLoader(linter, content, map)
              return
          }
          linter.printOutput(linter.lint(content))
          this.callback(null, contentm, map)
      }
      
    • loader.js

      module.exports = function(source, sourceMap?, data?) {
          // source 为 Loader 的输入,可能是文件内容,或上个 Loader 的处理结果
          return source
      }
      
  5. 常见 Loader

    常用 Loader
    JavaScript
    babel-loader
    eslint-loader
    ts-loader
    buble-loader
    vue-loader
    angular2-template-loader
    CSS
    css-loader
    style-loader
    less-loader
    sass-loader
    stylus-loader
    postcs-loader
    HTML
    html-loader
    pug-loader
    posthtml-loader
    Assets
    file-loader
    val-loader
    url-loader
    json5-loader

0x4 理解插件

插件架构精髓:对扩展开放,对修改封闭

  1. 插件围绕钩子展开

    // filename: somePlugin.js
    class SomePlugin {
        apply(compiler) {
            compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {})
        }
    }
    

    钩子的核心信息:

    1. 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生声明事情(如:compier.hooks.compilation
    2. 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息(如:compilation 等)
    3. 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口插件可以提供这些接口改变(如:dependencyFactories.set

0x5 学习路线

  1. 初级
    1. 理解打包流程
    2. 掌握常用配置项、Loader、插件的使用方法,能够灵活搭建集成工具的 Webpack 环境
    3. 掌握常见脚手架工具的用法
  2. 中级
    1. 理解 Loader、Plugin 机制,能够自行开发 Webpack 字间
    2. 理解常见性能优化手段并应用
    3. 理解前端工程化概念与生态环境
  3. 高级
    1. 理解 Webpack 编译、打包原理
    2. 参与共建
posted @ 2023-02-10 15:18  SRIGT  阅读(5)  评论(0编辑  收藏  举报  来源