Webpack的理解和常用配置

261 阅读6分钟

在项目根目录下的文件webpack.config.js是在node环境下运行的webpack相关配置文件,让我们看看能配置什么东西吧~ 配置打包文件的出口和入口: 如果不配置,那么webpack会默认从src/index.js开始打包,然后输出到根目录下的dist/main.js文件中,看下面配置方法,相当简单,自己意会~

module.exports = {
  entry: "./srcc/main.js",
  output: {
    filename: "bundle.js",
    // 注意:这里必须是绝对路径
    path: path.join(__dirname, "output"),
  },
};

此时有没有发现命令行webpack出现了一个没有配置mode的警告,mode来表示webpack的工作模式,模式分别适用于开发或者生产(默认生产) 适用于开发当然就是方便开发,让程序员更好阅读调整打包过的代码 适用于生产当然就是服务于效率,不管是运行效率还是打包体积肯定要做到最优,当然此时的代码根本没法读!!

const path = require("path");

module.exports = {
  // development,production,none,深挖区别看官方文档
  mode: "development",
  entry: "./srcc/main.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
};

一. 拳打loader

Webpack的其中一个强大之处在于能打包处理多种文件,但是他默认只能处理js文件,我们想利用这个强大的功能必须手动配置,此时loader就登场了,比如我要打包一个css文件……

const path = require("path");

module.exports = {
  mode: "none",
  entry: "./srcc/main.css",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  module: {
    rules: [
      {
        test: /.css$/,
        // 注意1:loader执行顺序是从后至前的
        // 注意2: css引入必须要加上style-loader,style-loader负责把处理好的css文件挂载到html元素上(深入了解要看webpack打包原理)
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

再举一个例子,引入图片资源……

// main.js
import icon from "./assets/OIP-C.jpeg";
// string,图片的url
console.log(typeof icon, icon);

const img = new Image();
img.src = icon;
document.body.append(img);
// webpack.config.js
 ……
    rules: [
      {
        test: /.jpeg$/,
        use: ["file-loader"],
      },
    ]
    //或者
      {
        test: /.jpeg$/,
        use: ["url-loader"],
      },
……

file-loader是把图片copy到output文件夹,然后入口js正常引入实现 url-loader是把图片转换为dataUrl的形式直接放入js文件中 该用哪个要看文件大小,大文件用file-loader,小文件用url-loader

这样配置webpack会根据条件自动选择loader

      {
        test: /.jpeg$/,
        use: {
          loader: "url-loader",
          // 小于30kb选择url-loader,否则使用file-loader(这里webpack会自动找file-loader,不用配)
          options: {
            limit: 30 * 1024,
          },
        },
      },

这种选择题在前端相当常见:webpack到底何时用懒加载,分页到底后端做还是前端做,这种问题的根本就是在首次加载速度和日后切换体验这两种极端之间做出平衡

loader是Webpack实现前端模块化的核心,通过loader我们就可以加载任何类型的资源

loader就是一种从输入到输出的转换,也是一种管道的概念,从一个文件输入 -> loader1 -> loader2 …… -> 最终必须输出js代码

loader有三类: 编译转换类:css-loader,就是将不是js的代码转换为js的模块运行

文件操作类:file-loader,就是对文件路径,命名,复制粘贴删除等等操作自动化满足开发生产需求

代码检查类:eslint-loader,对开发的代码基本没有修改,作用也顾名思义

Webpack哲学:模块为代码所需才引入和加载,也就是说,代码需要什么资源那么去加载什么资源。

二. 脚踢plugin

除了资源加载这种模块化必须的条件,webpack为了增加其自动化的能力,其他所有功能都为plugin,丰富的plugin铸就了webpack的强大,给人一种webpack就是前端工程化的错觉

  1. 每次打包自动清除之前的输出目录 clean-webpack-plugin
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "none",
  entry: "./srcc/main.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  module: {
  },
  plugins: [new CleanWebpackPlugin()],
};

  1. 自动生成入口html:减少维护入口html的成本,解决维护路径等相关依赖的风险
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPliugin = require("html-webpack-plugin");

module.exports = {
  mode: "none",
  entry: "./srcc/main.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  module: {

  },
  plugins: [
    new CleanWebpackPlugin(),
    // 我们也可以实例化多个对象生成多个html文件,配置多页面应用
    // 具体文件路径,html文件内容都在传入的对象里配置,详情查阅文档
    new HtmlWebpackPliugin({
      title: " WWWWebpack!!!",
    }),
  ],
};

  1. 自动拷贝指定目录下的文件到输出目录
const CopyWebpackPlugin = require("copy-webpack-plugin");
……
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "srcc/assets",
          to: "assets",
        },
      ],
    }),
  ],
};

插件学习方法:把常见插件过一遍官方文档,对用法心中有数。对于社区成百上千的插件,在想用对应自动化插件的时候去github搜索关键词

插件机制:划重点,要考!!!!

是钩子机制,webpack在工作过程的各个环节都埋下了钩子,在不同的钩子节点上完成插件对应的任务来扩展webpack的能力

webpack-dev-serever

webpack高度集成监听文件修改自动编译自动刷新浏览器的工具:webpack-dev-server 关于webpack-dev-server相关配置这样写~


module.exports = {
  mode: "none",
  entry: "./srcc/main.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  // 在这个属性上配置
  devServer: {
    // http服务可以访问的文件
    static: {
      // 可以访问的本地文件路径
      directory: path.join(__dirname, "srcc/assets"),
      // 最终在服务器上的访问路径
      publicPath: "/public",
    },
  },
  module: {
    rules: [
    ],
  },
  plugins: [
  ],
};

CORS代理服务器配置,典典典!!!

   proxy: [
      {
         // 以/api的请求地址都会被代理
        "/apl": {
          // 请求目标:(http://localhost:8080/api/user -> https://api.github.com/api/user)
          target: "https://api.github.com",
          // 替换字段:(https://api.github.com/api/user -> https://api.github.com/user)
          pathRewrite: {
            "^/api": "",
          },
          // 默认为客户端的主机名,修改为实际代理的主机名发请求
          changeOrigin: true,
        },
      },
      
    ],

由于打包过后的代码基本没有阅读的可能,为了快速找到项目bug,source-map出生,就是将打包过后的代码与源代码进行映射,应该类似反编译吧,基本配置:

 output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  devtool: "source-map",
  devServer: ……

webpack之竞争利刃:HMR,在页面不刷新的前提下还能支持模块更新

  devServer: {
    hot: true,
  },

你发现即使这样配置了,很多时候还是会刷新页面,并没有达到热替换的效果(只有css有效果) 所以我们需要单独手动处理js模块的HMR

// main.js
// 第一个参数是依赖模块的路径,第二个参数是模块更新的处理函数
module.hot.accept("./createH2", () => {
  console.log("模块更新了~");
});

加入了这段代码更新了依赖模块浏览器就不会刷新了,而是会执行回调 但是替换方案和逻辑完全需要自己写,又加入了和业务无关的代码,有利有弊

为什么一些脚手架工具就可以自由的热替换呢,是因为Vue,react等框架本身具有一定的使用规范,对于HMR可以统一规范开发团队也都实现了良好的HMR方案,告诉webpack“如何“进行热替换。但是自己写的js代码功能变化太大想要实现热替换逻辑太丰富太复杂webpack没法自动化就需要手动配置了

优化

DefinePlugin:在代码中全局注入全局变或者代码片段

    new webpack.DefinePlugin({
      CLG: '"console.log("hello Define!!")"',
    }),

TreeShaking: 打包时除去未引用的代码

……
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "output"),
  },
  optimization: {
    usedExports: true,
    minimize: true,
  },
  ……

sideEffect:标记代码的副作用,一些被标记为没有副作用的模块不会被webpack打包

分包代码分割:

  1. 多入口打包:多页应用程序
module.exports = {
  mode: "development",
  entry: {
    index: "./srcc/index.js",
    album: "./srcc/album.js",
  },
  optimization: {
    // 此配置会将公共部分提取出来
    splitChunks:{
      chunks: "all",
    }
  },
  output: {
    filename: "[name].bundle.js",
    path: path.join(__dirname, "output"),
  },
  // 同时要在HTMLPlugin上设置chunk属性,不然每个html都会将所有打包js引入,起不到分包的效果
}
  1. 动态加载:按需加载模块

在文件中用import函数来导入模块,返回Promise值在then方法中用依赖 webpack会根据动态导入自动分包,按需加载,框架中的动态路由就是这个原理