# entry 和 output

output中的path必须是个绝对路径而不是相对路径, 而且path虽然默认是dist,但是如果不配置的话,会导致clean-webpack-plugin不清理dist包

const path = require('path') ;
let htmlwebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin }  = require('clean-webpack-plugin');
module.exports={
	mode:'production',
  // entry相对路径和绝对路径都行
	entry:{
		main:"./index.js",//main作为默认名
		sub:'./index.js'
	},
  // 必须是绝对路径
	output:{
		publicPath:'https://xx.cdn.com',//前缀
		chunkFileName:"[name].xxxx.js"//非入口文件的其他文件打包后命名
		filename:'[name].js',
    // filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中,这样文件按文件夹管理比较清晰
		path:path.resolve(__dirname,'zip')
	}
}
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>ceshi1</title>
	</head>
	<body>
	<div id="root"></div>
	<script type="text/javascript" src="https://xx.cdn.com/main.js"></script>
	<script type="text/javascript" src="https://xx.cdn.com/sub.js"></script></body>
</html>

# webpack entry单个入口(简写)语法

用法:entry: string | [string]
module.exports = {
  // 入口文件,默认src/main.js
  entry: './path/to/my/entry/file.js',
};

entry 属性的单个入口语法,是以下形式的简写:

module.exports = {
  entry: {
    main: './path/to/my/entry/file.js',
  },
};

也可以将一个文件路径数组传递给 entry 属性,这将创建一个所谓的 "multi-main entry"。想要一次注入多个依赖文件,并且将它们的依赖关系绘制在一个 "chunk" 中时,这种方式就很有用。

module.exports = {
  entry: ['./src/file_1.js', './src/file_2.js'],// 这两个文件打包成一个输出文件
  output: {
    filename: 'bundle.js',
  },
};

# webpack entry 对象写法

用法:entry: { <entryChunkName> string | [string] } | {}
module.exports = {
  entry: {
    app: './src/app.js',
    adminApp: './src/adminApp.js',
  },
};

对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。

entry:{
		main1:['./index.js','./demo'], //index.js 和demo.js可以合并到同一个文件,同时可以省略掉后缀
		merge:'./merge.js'
}
// 最后会生成两个文件main1 和 merge

# 描述入口的对象

用于描述入口的对象。你可以使用如下属性:

  • dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
  • filename: 指定要输出的文件名称。
  • import: 启动时需加载的模块。
  • library: 指定 library 选项,为当前 entry 构建一个 library。
  • runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
  • publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。请查看 output.publicPath。

runtime 和 dependOn 不应在同一个入口上同时使用,所以如下配置无效,并且会抛出错误:

module.exports = {
  entry: {
    a2: './a',
    b2: {
      runtime: 'x2',
      dependOn: 'a2',
      import: './b',
    },
  },
};

确保 runtime 不能指向已存在的入口名称,例如下面配置会抛出一个错误:

module.exports = {
  entry: {
    a1: './a',
    b1: {
      runtime: 'a1',
      import: './b',
    },
  },
};

另外 dependOn 不能是循环引用的,下面的例子也会出现错误:

module.exports = {
  entry: {
    a3: {
      import: './a',
      dependOn: 'b3',
    },
    b3: {
      import: './b',
      dependOn: 'a3',
    },
  },
}

# 常见场景

分离 app(应用程序) 和 vendor(第三方库) 入口 webpack.config.js

module.exports = {
  entry: {
    main: './src/app.js',
    vendor: './src/vendor.js',
  },
};

webpack.prod.js

module.exports = {
  output: {
    filename: '[name].[contenthash].bundle.js',
  },
};

webpack.dev.js

module.exports = {
  output: {
    filename: '[name].bundle.js',
  },
};

这是什么? 这是告诉 webpack 我们想要配置 2 个单独的入口点(例如上面的示例)。

为什么? 这样就可以在 vendor.js 中存入未做修改的必要 library 或文件(例如 Bootstrap, jQuery, 图片等),然后将它们打包在一起成为单独的 chunk。内容哈希保持不变,这使浏览器可以独立地缓存它们,从而减少了加载时间。

# 多页面应用程序

webpack.config.js

module.exports = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js',
  },
};

打包代码时会将所有 js 文件打包到一个文件中,体积太大了。如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。

所以需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。

代码分割(Code Split)主要做了两件事:

  • 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
  • 按需加载:需要哪个文件就加载哪个文件。
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  // 单入口
  // entry: './src/main.js',
  // 多入口
  entry: {
    main: "./src/main.js",
    app: "./src/app.js",
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    // [name]是webpack命名规则,使用chunk的name作为输出的文件名。
    // 什么是chunk?打包的资源就是chunk,输出出去叫bundle。
    // chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名无关。
    // 为什么需要这样命名呢?如果还是之前写法main.js,那么打包生成两个js文件都会叫做main.js会发生覆盖。(实际上会直接报错的)
    filename: "js/[name].js",
    clear: true,
  },
  mode: "production",
};

配置了几个入口,至少输出几个 js 文件

# webpack之output

  • chunkFileName:当文件时import动态模式引入的会单独打包成一个文件,文件名是个随机数,可以借助这里进行配置
  • filename:文件名,可以借助一些变量拼接
  • path:文件打包路径,需要是个绝对路径,借助path模块
  • 静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径,默认publicPath=''
  • assetModuleFilename:当使用asset去解析文件时,如果没有配置路径,会走这个地方的地址。配置的话走自己的路径
output: {
	  // chunkFilename: '[name].[chunkhash:8].js',//非入口文件的其他文件打包后命名
	  filename:'[name].js',
	  path:path.resolve(__dirname,'zip'),
	  //publicPath:XXX
	  //一般情况下是用不到的,只有在生产环境中需要用到,
    //在上线的时候需要给项目中的静态资源统一配置资源前缀,代表客户端访问的上线资源地址。
	}
output.publicPath = '/dist/'

// image
options: {
 	name: 'img/[name].[ext]?[hash]'
}

// 最终图片的访问路径为
output.publicPath + 'img/[name].[ext]?[hash]' = '/dist/img/[name].[ext]?[hash]'

// js output.filename
output: {
	filename: '[name].js'
}
// 最终js的访问路径为
output.publicPath + '[name].js' = '/dist/[name].js'

// extract-text-webpack-plugin css
new ExtractTextPlugin({
	filename: 'style.[chunkhash].css'
})
// 最终css的访问路径为
output.publicPath + 'style.[chunkhash].css' = '/dist/style.[chunkhash].css'
模板 描述
[hash] 模块标识符(module identifier)的 hash
[chunkhash] chunk 内容的 hash
[name] 模块名称
[id] 模块标识符(module identifier)
[query] 模块的 query,例如,文件名 ? 后面的字符串
output: {
	// publicPath: "/assets/",
	chunkFilename: '[name].xxx.js', //非入口文件的其他文件打包后命名,比如动态import导入的文件
	filename: '[name].js',
	path: path.resolve(__dirname, 'dist'),
	assetModuleFilename: "img/[name]_[hash:6][ext]"//[ext]这里包括后缀的点
  // library: '[name]', // 整个库向外暴露的变量名
  // libraryTarget: 'window' // 变量名添加到哪个上 browser
  // libraryTarget: 'global' // 变量名添加到哪个上 node
  // libraryTarget: 'commonjs'
}

# import 动态导入实现按需加载

  • import 'xxx' 为静态导入,需要书写在顶级作用域中,否则会报错;静态导入的资源会在页面打开时一同加载
  • import ('xxx') 为动态导入,可书写在任意作用域中;动态导入可在页面满足一定条件后进行资源加载,避免资源浪费
  • 动态导入存在兼容问题,可引入@babel/plugin-syntax-dynamic-import 进行兼容
//webpackChunkName就是打包后的的[name],如果简单的import('aaa')的导入,那么[name]就是一串数字
setTimeout(()=>{
	  import( /* webpackChunkName: "ling" */ './ling').then((res) => {
	        console.log(res)
	        console.log(res.default())
	    })
},1000)
console.log("hello main");
// 再点按钮之前,就不会去加载,减少请求和打包体积
document.getElementById("btn").onclick = function () {
  // 动态导入 --> 实现按需加载
  // 即使只被引用了一次,也会代码分割
  import("./math.js").then(({ sum }) => {
    alert(sum(1, 2, 3, 4, 5));
  });
};
document.getElementById("btn").onclick = function () {
  // eslint不能识别动态导入需要,需要额外追加配置
  // webpack魔法命名: 
  // 1. webpackChunkName如果两个文件重复,那么会打包到一个文件中 
  // 2. 如果打包的文件没有加webpack魔法命名,那么格式前缀是数字不是webpackChunkName[mathtg.chunk.bbee906e61.js]=>[1321.chunck.bbee906e61.js]
  import(/* webpackChunkName: "mathtg", webpackPrefetch: true */ "./js/mathgg").then(({ mul }) => {
    console.log(mul(3, 3));
  });
  import(/* webpackChunkName: "mathtg1", webpackPrefetch: true */ "./js/test").then(res => {
    console.log(res);
  });
  
};

如果eslint不能识别import,eslint文件需要配置

module.exports = {
  // 继承 Eslint 规则
  extends: ["eslint:recommended"],
  env: {
    node: true, // 启用node中全局变量
    browser: true, // 启用浏览器中全局变量
  },
  parserOptions: {
    ecmaVersion: 6, // es6
    sourceType: "module", // es module
  },
  rules: {
    "no-var": 2, // 不能使用 var 定义变量
  },
  plugins: ["import"], // 解决动态导入import语法报错问题 --> 实际使用eslint-plugin-import的规则解决的
};

如果对返回的chunk名字需要修改整体结构,可以在output中配置chunkName进行修改。

最后更新: 5/1/2023, 4:47:21 PM