likes
comments
collection
share

webpack学习相关

作者站长头像
站长
· 阅读数 20

具体内容可以查看笔记

1、为什么使用webpack?

查看官方解释回顾一下历史,之前是如何开发项目的

第一阶段

(1)在每个html文件中都引用大量的<script>标签webpack学习相关

这种方式会造成网络瓶颈,并且加载大量的无用代码

(2)只引入一个js文件,这个js里包含了整个项目的所有代码,几万行甚至几十万行代码。这种方式会导致作用域不好区分,同时难以维护

第二阶段

IIFE立即执行函数(function(){})()解决大型项目的作用域问题,Grunt、Gulp就是使用这种模式。当然这样做也有一些问题,比如修改了一个文件也要重新构建整个项目。引入第三方库时,即使只需要少量代码,也要引入整个第三方库代码,不能做到代码分割。

第三阶段

使用webpack(本质上,webpack只是静态模块打包工具)

当然,webpack是基于以下的基础条件或者启发之后产生的(1)node.js的诞生,让js可以在浏览器之外的计算器和服务中运行(2)common.js引入了require机制,允许在当前文件中加载其他模块(浏览器不支持common.js的模块机制,因此出现了Browserify, RequireJS 和 SystemJS 等打包工具,允许我们编写能够在浏览器中运行的common.js模块)

2、webpack基本使用

查看官方文档

(1)安装webpack和webpack-cli

npm install webpack webpack-cli --save-dev

(2)创建配置文件

在根目录下创建3个文件

  • webpack.common.js
  • webpack.dev.js
  • webpack.prod.js

然后修改package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch",
    "start": "webpack serve --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

这样子,就可以在npm run start和npm run build时使用不同的配置了

webpack.common.js 具体代码
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");

/* 
通常module.exports={...配置对象}
当在命令行中输入webpack --env envName时,module.exports=(env)=>{}可以接收命令行中env参数 
*/
module.exports = (env) => ({
  entry: () =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve({
          index: { import: "./src/index.js" },
          print: { import: "./src/print.js" },
        });
      }, 50);
    }),
  output: {
    filename: "js/[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
    pathinfo: false,
    // 将项目打包成为库(还需要在package.json添加module: src/index.js,允许使用import的方式引用库)
    // library: {
    //   name: "libraryName",
    //   type: "umd",//兼容common.js(const library=require("library"))、ADM([require("library"),function(library){}])、script标签引用
    // },
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        // include: path.resolve(__dirname, "src"), //包含的目录
        use: [
          // path.resolve(__dirname, "webpackLoaders/aLoader/a-load.js"),
          "style-loader",
          "css-loader",
        ],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource", //webpack5.x内置,用于处理静态资源
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: "asset/resource", //webpack5.x内置,用于处理静态资源
      },
    ],
  },
  optimization: {
    // 添加新文件的引入时,不会改变原chunk的module.id,之前的chunk的hash值得以保留,浏览器会从缓存中读取
    moduleIds: "deterministic",
    /* runtimeChunk:single表示:
      a.js和b.js同时引入c.js(export default {count:0})时,该对象只实例化一次。如果html同时加载a、b两个js,则count++会执行两次。
      如果不设置runtimeChunk,则c.js会实例化两次,a、b两个js执行count++则互不影响
      runtimeChunk:single的目的是为了保存数据
      参考文档:https://bundlers.tooling.report/code-splitting/multi-entry/
    */
    runtimeChunk: "single",
    // 将node_modules中的库,单独存放到vendor这个chunk中
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendorName",
          chunks: "all",
        },
      },
    },
  },
  // externals配置不需要打包的外部依赖(即配置中的依赖需要通过script在html引用)
  // externals: {
  //   lodash: {
  //     commonjs: "lodash",
  //     commonjs2: "lodash",
  //     amd: "lodash",
  //     root: "_",
  //   },
  // },
  plugins: [
    new HtmlWebpackPlugin({
      title: "development",
    }),
  ],
});
webpack.dev.js 具体代码
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js")();
const HandleConsole = require("./webpackPlugins/handleConsole.js");

const config = merge(common, {
  mode: "development",
  devtool: "inline-source-map",
  devServer: {
    contentBase: "./dist",
    hot: true,
  },
  module: {
    rules: [
      {
        test: /\.text$/i,
        loader: path.resolve(__dirname, "webpackLoaders/aLoader/a-load.js"),
      },
    ],
  },
  // plugins: [new HandleConsole({ test: 123 })],
});
module.exports = config;
webpack.prod.js 相关代码
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js")();

module.exports = merge(common, {
  mode: "product",
  // devtool: "inline-source-map",
});

3、webpack打包library

大致操作跟上面的一样,只需要配置output中的library即可

  output: {
     library: {
       name: "libraryName",
       type: "umd",//兼容common.js(const library=require("library"))、ADM([require("library"),function(library){}])、script标签引用
     },
  }

如果想要通过import的方式引用library,还需要在package.json添加"module": "src/index.js"webpack学习相关

4、开发webpack-loader

参考文档loader的作用就是让各种资源,比如image、less等文件,转换成能够被webpack解析的模块,它的本质其实是一个返回值为string或者Buffer的函数。

module.exports = function (content) {
    return content
};

webpack提供了一些常用loader供使用,当然,它也提供了接口供第三方开发loader。比如,我们想要开发一个text-loader用来解析text文件,并且中间插入两个a-loader、b-loader用来做额外的处理。修改webpack.config.js

module.exports={
  ...
  resolveLoader: {
    modules: ["node_modules", path.resolve(__dirname, "webpackLoaders")],
    extensions: [".js", ".json"],
    mainFields: ["module", "main"],
  },
  module: {
    rules: [
      {
        test: /\.text$/i,
        use: [
          {
            loader: "text-loader",
            options: {
              test: "luyi",
            },
          },
          {
            loader: "b-loader",
            options: {
              test: "fanny",
            },
          },
          { loader: "a-loader" },
        ],
      },
    ],
  },
}

注意,loader是从右到左执行(官方文档),我们这里想要先执行a-loader,最后执行text-loader就需要倒着写。然后在根目录下创建这三个文件webpack学习相关具体代码a-loader

module.exports = function (content) {
  return `外来者aaa:${content}`;
};

b-loader

module.exports = function (content) {
  console.log("bbbbb", content);
  return `:${content},外来者b`;
};

text-loader是一个目录,包含了3个文件,package.json、index.js是包的逻辑代码,html.js是用来将内容放置到html页面内。index.js

const { getOptions, stringifyRequest } = require("loader-utils");
const path = require("path");

module.exports = function (content) {
  let options = getOptions(this);
  return `
    import handleHtml from ${stringifyRequest(
      this,
      require.resolve("./html.js")
    )};
    handleHtml('hello world:${content}');
  `;
};

html.js

export default function (content) {
  let endTip = document.createElement("div");
  endTip.innerHTML = content;
  document.body.appendChild(endTip);
}

一旦某个文件,引入text,就会在html中执行loader处理之后的内容比如,test.text内容

###这里是test.text的内容###

import "path/to/test.text"webpack学习相关

5、开发webpack-plugin

官方文档webpack-plugin可以影响打包流程,需要了解webpack内部的生命周期等钩子。plugin的本质是一个有apply属性的函数

const pluginName = "HandleConsole";

class HandleConsole {
  apply(compiler) {
    compiler.hooks.run.tap(pluginName, (compilation) => {
      console.log("################ 开始编译1 #######################");
      console.log(compilation);
      console.log("################ 开始编译2 #######################");
    });
  }
}

module.exports = HandleConsole;

以上就是一个基本的plugin还需要在webpack.config.js中添加配置

  const HandleConsole = require("./webpackPlugins/handleConsole.js");

  module.exports={
    ...
    plugins: [new HandleConsole({ test: 123 })],
  }