赞
踩
官方文档: https://www.webpackjs.com/configuration/output/#output-filename
指定 webpack 由哪个模块作为项目构建的开始.通过配置 entry
属性,指定一个或多个起点,默认值 ./src
:
用法: entry: string|Array ,可以是一个字符串也可以是一个数组,还可以是一个对象。
module.exports = { // 字符串入口 entry: './path/leo/file.js' } module.exports = { // 数组,指定多个入口 entry: ['xxx','yyyy'] } module.exports = { // 为入口命名 entry: { file: './path/leo/file.js' } } module.exports = { entry: { // 为入口命名 main: ['./path/leo/file.js', './path/leo/index.js', './path/leo/server.js'] } }
所谓Chunk,就是在Webpack打包过程中,内部形成的代码块,代码块中的内容可能来源于一个文件中,也可能来源于多个文件中。比如入口文件 index.js ,它依赖于模块A,而模块A又依赖于模块B, 那么webpack 在打包后整个入口文件就会形成一个chunk。
如果entry 传入一个字符串或字符串数组,chunk 会被命名为 main
如果传入一个对象,则每个键(key)会是 chunk 的名称,该值描述了 chunk 的入口起点。比如 entry: './path/leo/file.js'
chunk的名称为main,entry: ['xxx','yyyy']
chunk的名称也为 main.
entry: {
file: './path/leo/file.js'
}
那么chunk的名称就为 file。
有了chunk 的命名,html在嵌入js文件的时候,就可以指定需要嵌入的 chunk。
指定 webpack 最终输出的文件输出位置和文件名等信息。默认输出位置为 ./dist
output: {
path: path.resolve(__dirname, 'dist'), //输出的目录绝对路径
filename: 'myjs-webpack.bundle.js' //输出的文件名称
}
// 占位符
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
更多占位符:
[hash] | 模块标识符(module identifier)的 hash |
---|---|
[chunkhash] | chunk 内容的 hash |
[name] | 模块名称 |
[id] | 模块标识符(module identifier) |
[query] | 模块的 query,例如,文件名 ? 后面的字符串 |
对于webpack来说,要处理的每个文件都是一个module,要想让webpack识别并处理这个module,那就需要使用 loader来进行解析。比如默认情况下 webpack只会处理 js文件,那么要处理 css文件,图片文件这些非js的文件,那就必须使用对应的 loader来解析这些文件。
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
所有的文件处理配置都应该放到 module中, 至于如何处理,那就要配置相关的规则 rules 就是指定规则,它是一个复数,表示多个规则,所以其值是一个数组。
使用loader的三种方式:
import Styles from 'style-loader!css-loader?modules!./styles.css';
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
loader特性:
让 webpack 能够执行更多任务,从优化和压缩,到重新定义环境中的变量,插件目的在于解决 loader 无法实现的其他事。
webpack本身内置了很多插件,也可以使用第三方插件。
使用时,只需要 require
它,并添加到 plugins
数组,通过 new
实例化即可:
// 通过 npm 安装 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 导入 webpack 是为了访问内置插件,如 webpack.DefinePlugin const webpack = require('webpack'); const config = { module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }, plugins: [ new HtmlWebpackPlugin({template: './src/index.html'}) ]}; module.exports = config;
通过配置 mode
参数,指定当前的开发模式,有 development
和 production
两个值:
module.exports = {
mode: 'production'
};
也可以通过 CLI 参数传递:
webpack --mode=production
development | 会将 process.env.NODE_ENV 的值设为development 。启用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
---|---|
production | 会将 process.env.NODE_ENV 的值设为 production 。启用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin ,OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 UglifyJsPlugin 。 |
process 是 NodeJS 应用全局对象,表示进程。 启动webpack的时候其实就启动了nodeJS应用
webpack 的配置文件,是导出一个对象的 JavaScript 文件,由 webpack 根据对象定义的属性进行解析。
因为 webpack 配置是标准的 Node.js CommonJS 模块,可以做到以下事情:
require(...)
导入其他文件;require(...)
使用 npm
的工具函数;?:
操作符;开发中将程序分解成离散功能块,称为模块。而 webpack 模块能够以各种形式表达他们的依赖关系。
import
语句;require()
语句;define
和 require
语句;css/sass/less
文件中的 @import
语句;url(...)
)或 HTML 文件(<img src=...>
)中的图片链接(image url
);模块解析:
使用 resolver
库来找到模块的绝对路径,帮助 webpack 找到 bundle 中需要引入的模块代码,这些代码包含在每个 require
/ import
语句中,在模块打包中,webpack 使用 enhanced-resolve 来解析文件路径
import "/home/me/file";
import "C:\\Users\\me\\file";
import "../src/file1";
import "./file2";
import "module";
import "module/lib/file";
npm init -y
目录结构
├── public 存放html模板页面 │ ├── include 存放公共的html代码片段 │ │ ├── header.html │ │ └── tail.html │ └── index.html 首页 │ ├── about.html 关于 │ ├── favicon.ico 站点图标 ├── src 存放源代码(需要编译) │ ├── assets 存放资源,包含css文件,字体文件,图片文件 │ │ ├── css │ │ ├── fonts │ │ └── img │ ├── common 公共js文件 │ └── index.js │ ├── about.js └── vendor 存放第三方提供的组件,src下js文件中需要 import才能使用 ├── static 静态文件,这些文件将会原封不动拷贝到发布的assets目录中 ├── readme.md ├── package.json 依赖的js文件,启动脚本
注意: public目录下只有html文件和 favicon.ico 文件(图标)。 每个html文件名都对应一个 src下的 同名的js文件。html文件名与js文件名一一对应(目录也要对应)。include 文件夹中是html片段,这些片段使用
<%= require('html-loader!./include/header.html')%>
的方式被包含到其它html页面中。
这里以简单的页头和页尾为例:
public/include/header.html
<div>
这是头部分
<a href="/production/index.html">产品中心</a>
<a href="/about.html">关于我们</a>
</div>
public/include/tail.html
<div>
这是页脚部分
</div>
在首页中嵌入页头和页脚, <%= xxxx %>
这种写法浏览器是无法识别的,需要通过webpack处理后才能被浏览器识别
public/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>这是首页</title> </head> <body> <%= require('html-loader!./include/header.html')%> <div class="box"> 首页内容 </div> <%= require('html-loader!./include/tail.html')%> </body> </html>
public/index.html 对应的入口文件为 src/index.js 。 这里使用了jQuery 库,输出简单的内容
import $ from 'jquery'
$(() => {
alert("这是index.js");
})
public/about.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>关于我们</title> </head> <body> <%= require('html-loader!./include/header.html')%> <div> 关于我们 </div> <%= require('html-loader!./include/tail.html')%> </body> </html>
public/about.html 对应的入口文件为 src/about.js :
import $ from 'jquery'
$(() => {
alert("about.js");
})
4.2 二级页面-产品首页
public/production/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>产品中心</title> </head> <body> <%= require('html-loader!../include/header.html')%> <div> 产品中心 </div> <%= require('html-loader!../include/tail.html')%> </body> </html>
public/production/index.html 对应的入口文件为 src/production/index.js
import $ from 'jquery'
$(() => {
alert("production------>index.js");
})
下面,通过webpack的配置,先让这几个页面能够在浏览器中运行起来。
目前没有安装任何依赖,下面安装依赖
npm i webpack webpack-cli clean-webpack-plugin html-webpack-plugin html-loader webpack-dev-server -D
npm i jQuery -S
webpack.config.js
const path = require('path')
module.exports = {
entry:{},
output:{},
resolve:{},
module:{
rules:[]
},
plugins:[]
}
entry:{
'index': './src/index.js',
'about': './src/about.js',
'production/index': './src/production/index.js'
},
入口文件的文件都使用相对路径,那么相对于哪个路径? webpack的配置中,有个 context选项配置,它是一个 绝对路径
,是一个基础目录, entry和 loader 中都是相对于这个基础路径,其默认值是:__dirname
即当前NodeJS运行的 webpack.config.js 文件所在的目录。以上的配置相当于:
context: path.resolve(__dirname),
entry:{
'index': './src/index.js',
'about': './src/about.js',
'production/index': './src/production/index.js'
},
这里的三个HTML文件,每个都对应一个js入口文件,其入口命名(Chunk )就是文件相对路径去掉 ./src
和 .js
后缀 。 之所以要用这样的规则,是因为一个项目中的HTML和入口文件会很多,将来不可能每个JS入口文件都这样来手工配置,到时会使用nodejs 访问磁盘目录,按照这个规则自动生成 entry配置。
webpack处理完毕后输出到指定的位置
output:{
path: path.resolve('./dist'), //必须是绝对路径
filename: '[name]-[chunkhash].js'
}
每个入口文件都对应一个HTML,该如何生成HTML,需要在 html-webpack-plugin 对象中指明输出的 HTML文件名,HTML模板等。详细配置可以参考官方文档
每个HTML文件都应该对应一个 html-webpack-plugin 对象。 HTML文件太多,后面会使用编码的方式来自动生成配置。
... const HtmlWebpackPlugin = require("html-webpack-plugin") ... plugins:[ new HtmlWebpackPlugin({ filename: 'about.html', template: './public/about.html', favicon: './public/favicon.ico', chunks: ['about'] }), new HtmlWebpackPlugin({ filename: 'index.html', template: './public/index.html', favicon: './public/favicon.ico', chunks: ['index'] }), new HtmlWebpackPlugin({ filename: 'production/index.html', template: './public/production/index.html', favicon: './public/favicon.ico', chunks: ['production/index'] }), ]
filename: 最终生成的HTML文件名称,可以使用子文件夹,将来会生成到 output 指定的文件夹中。
template: HTML模板。 我们将所有的HTML模板都放到了public目录,这个配置项默认是 ‘./src/index.ejs’
favicon: 自动向HTML模板中插入指定的 icon文件
chunks: 是一个数组,指定要引入的js文件。默认情况下会引入 webpack 输出的 js。比如上面有三个输出,如果不配置chunks,那么 index.js , abount.js 和 production/index.js 都会被引入到 HTML文件中。
"scripts": {
"build": "npx webpack --config webpack.config.js --mode production",
"dev": "npx webpack-dev-server --config webpack.config.js --color --inline --hot --progress --mode development --port 8080 --open"
}
process.env.NODE_ENV
来判断是哪个环境。npm run dev
启动后,会自动打开浏览器,访问 “http://localhost:8080”
webpack-dev-server 启动后会将 webpack 的输出结果映射到内存中,我们在磁盘上看不到webapck输出的结果。
查看首页 http://localhost:8080/index.html 源码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>这是首页</title> <link rel="icon" href="favicon.ico"></head> <body> <div> 这是头部分 <a href="/index.html">首页</a> <a href="/production/index.html">产品中心</a> <a href="/about.html">关于我们</a> </div> <div class="box"> 首页内容 </div> <div> 这是页脚部分 </div> <script src="index-be16bdb4c4cb8f5ff984.js"></script></body> </html>
再对照 public/index.html模板:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>这是首页</title> </head> <body> <%= require('html-loader!./include/header.html')%> <div class="box"> 首页内容 </div> <%= require('html-loader!./include/tail.html')%> </body> </html>
可以看到, 根据 模板生成的HTML做了如下改变:
<link rel="icon" href="favicon.ico">
<%= xxx %>
的内容被替换成了header.html 和 tail.html 中的HTML 片段npm run build
dist 文件夹中的内容
│ about-1c72cf882218d6c9ef0b.js
│ about.html
│ favicon.ico
│ index-0af568ee344bf0082880.js
│ index.html
└─production
index-0d30eb18d3678e852376.js
index.html
上面输出的html文件和 js文件都在同一个目录中,通过配置,可以将 js输出到指定的 assets/js 文件夹中,只需要修改 output.filename项,filename项上是可以带文件夹的。
output:{
path: path.resolve('./dist'),
filename: 'assets/js/[name]-[hash].js' //存放到path指定的目录下
}
再次 npm run build
后的结果:
│ about.html
│ favicon.ico
│ index.html
├─assets
│ └─js
│ │ about-1c72cf882218d6c9ef0b.js
│ │ index-0af568ee344bf0082880.js
│ │
│ └─production
│ index-0d30eb18d3678e852376.js
│
└─production
index.html
每次 npm run build
的时候,webpack不会清理 dist目录,因为只要文件有改动,那么生成的文件内容的Hash值都不一样,所以上一次编译的结果被保留了下来造成了混乱。配置 clean-webpack-plugin 插件可以每次build之前清理 dist目录
npm i clean-webpack-plugin -D
webpack.config.js
...
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
...
plugins:[
new CleanWebpackPlugin()),
...
]
注意: CleanWebpackPlugin 要写在 HtmlWebpackPlugin 插件的前面
下面使用Sass编写一个css样式文件,样式文件写到 src/assets/css文件夹中
├─assets
│ ├─css
│ │ index.scss
│ │
│ └─img
│ bg.png
src/assets/css/index.scss 内容:
$bg-color: #ee3;
.box{
background-color: $bg-color;
display: flex;
height: 500px;
width: 600px;
}
这里定义了一个类样式,如果要在 public/index.html 中使用,那么首先要在 public/index.html 对应的 src/index.js 入口文件中使用 import 导入css文件
src/index.js
...
import './assets/css/index.scss'
...
然后在 public/index.html 中使用类样式:
...
<div class="box"></div>
...
现在执行 npm run dev
会发现报错了。因为 我们在src/index.js 中导入了 index.scss 文件,这个文件 webpack是无法解析的。所以这就需要配置 loader 来解析 scss文件或者css。
npm set SASS_BINARY_SITE http://npm.taobao.org/mirrors/node-sass
npm i style-loader css-loader node-sass sass-loader -D
因为 sass-loader 依赖于 node-sass . 使用之前先设置 node-sass 环境变量,加速安装包下载
module:{
rules:[{
//它会应用到 .css .scss .sass 后缀的文件,
//use数组loader的名字是有顺序的,即先由sass-loader,再由css-loader处理,最后由style-loader处理
test: /\.(sc|c|sa)ss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}]
}
<style>...</style>
中,从而插入到 html文件中。现在再次 npm run dev
,服务启动后发现css生效了。
仔细观察发现css代码最终被放到了 html中的 style标签中。
src/assets/css/index.scss 中为box 类样式添加一个背景图片:
$bg-color: #ee3;
.box{
...
background: url(../img/bg.png);
...
}
执行 npm run dev
后发现报错。 原因是 css-loader 在加载 css 的时候,遇到了 url所指向的图片,图片这种资源 scss-loader 是无法处理的,因为图片文件需要被拷贝到最后的输出目录中,css中的url指向的路径也发生了改变。
webpack通过file-loader处理资源文件,它会将rules规则命中的资源文件按照配置的信息(路径,名称等)输出到指定目录,并返回其资源定位地址,默认的输出名是以原文件内容计算的MD5 Hash命名的。官方文档:https://www.webpackjs.com/loaders/file-loader/
npm i file-loader -D
webpack.config.js 文件配置 loader 规则:
rules:[
...
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, //匹配图片文件
loader: 'file-loader',
options: {
outputPath: 'assets/img' //将图片输出到 dist/assets/img文件夹下
}
}]
再次运行npm run dev
在开发者工具中查看:
这说明 file-loader 把 src/assets/img/bg.png 这个文件拷贝到了 dist/assets/img/992ac5d15ba3b012bb054bf80016a636.png ,即文件名已经变成了 992ac5d15ba3b012bb054bf80016a636.png , 然后返回了此时文件的路径。
可以运行 npm run build
来看看最终的编译结果:
├─assets
│ ├─img
│ │ 992ac5d15ba3b012bb054bf80016a636.png
│ │
│ └─js
│ │ about-83083e244833fcde3bfe.js
│ │ index-83083e244833fcde3bfe.js
│ │
│ └─production
│ index-83083e244833fcde3bfe.js
## 8.4 生成css文件
上面虽然能正常使用css了,但是我们发现最终生成的结果并没有css代码,那么这些css代码哪里去了?为什么运行的时候又没有问题?
因为 css代码是在 src/index.js 中 import的, webpack借助 相关的loader (‘style-loader’, ‘css-loader’, ‘sass-loader’) 将这些css代码全部处理成了字符串,编译到了js文件中了。可以打开 index-xxxxxx.js 这个文件,然后搜索 .box
发现那些css确实被处理成了javascript 字符串
js文件被浏览器加载后,js代码开始执行,此时它会在 html文档的head 节点下添加一个 style节点,然后将css代码输出到节点中。
那如何才能将这些css代码"提取" 出来?webpack4中使用 mini-css-extract-plugin 官方文档 https://webpack.js.org/plugins/mini-css-extract-plugin/#minimizing-for-production 来提取css样式, 使用 optimize-css-assets-webpack-plugin 来压缩css
npm i mini-css-extract-plugin optimize-css-assets-webpack-plugin -D
webpack.config.js
... const MiniCssExtractPlugin = require("mini-css-extract-plugin");//提取css到单独文件的插件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');//压缩css插件 ... module:{ rules:[{ // 将 style-loader 替换成 MiniCssExtractPlugin.loader // 因为最后一步不是生成 style标签,而是提取出单独的文件。 test: /\.(sc|c|sa)ss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] }, ...] }, ... plugins:[ ... new MiniCssExtractPlugin({ filename: "assets/css/[name]_[hash].css"//输出目录与文件 }), new OptimizeCssAssetsPlugin() ]
需要注意的是: MiniCssExtractPlugin 还需要在 loader上配置一下,MiniCssExtractPlugin 提供了一个loader,在module.rules.use 组数的第一个元素位置上配上这个loader表示文件处理的最后一步交给 这个loader来处理。一定要注意顺序。plugins中配置了插件,并指定了提取的css文件的存储路径和文件名。[name]是入口文件的name。
编译后的结果:
├─assets
│ ├─css
│ │ index_f9c026e959b5b2b1b2d1.css
│ ├─img
│ │ 992ac5d15ba3b012bb054bf80016a636.png
│ └─js
│ │ about-f9c026e959b5b2b1b2d1.js
│ │ index-f9c026e959b5b2b1b2d1.js
│ └─production
│ index-f9c026e959b5b2b1b2d1.js
在 index.html文件中, .css 文件使用了 link标签 引入了 css文件。
现在执行 npm run dev
发现图片无法加载:
Failed to load resource: the server responded with a status of 404 (Not Found)
查看生成的 css文件,发现 .box 类变成:
index_f9c026e959b5b2b1b2d1.css
.box {
background-color: #ee3;
display: flex;
background: url(assets/img/992ac5d15ba3b012bb054bf80016a636.png);
height: 500px;
width: 600px
}
这个css文件 位于 assets/css 文件夹中,而 url中使用的是一个相对地址
在css 文件中 ,url 如果使用的是相对地址,则是基于当前css文件的
也就是说 background实际加载的是 assets/css/assets/img/xxxx.png 这个路径显然是错误的。那怎么解决?
webpack 配置的 output.publicPath 可以解决。 它的意思是在输出的时候,在所有的路径前面都添加一个前缀,比如: http:// xxx.com/ ,如果将其设置为"/" 那输出的时候所欲的路径都会从服务器的根目录开始。
所以在 webpack.config.js 中对 output.publicPath 进行配置:
output:{
path: path.resolve('./dist'),
filename: 'assets/js/[name]-[hash].js',
publicPath: '/'
}
再次 'npm run dev '就发现正常了。此时在查看css文件,发现是这样的:
.box {
background-color: #ee3;
display: flex;
background: url(/assets/img/992ac5d15ba3b012bb054bf80016a636.png);
height: 500px;
width: 600px
}
因为url 是一个绝对路径,是从服务器的根目录开始的,所以这个路径就是正确的。
bootstrap 是常用的css样式库,使用之前先安装:
npm i bootstrap -S
不要直接在 html模板文件中引入,而是在 js文件中引入。 webpack 处理是从 js文件入口开始处理的,而不是 HTML文件。
在 src/index.js 中引入 bootstrap样式
import $ from 'jquery'
import './assets/css/index.scss'
import 'bootstrap/dist/css/bootstrap.css'
$(() => {
alert("index.js======");
})
在 public/index.html 模板中使用
...
<button class="btn btn-success">一个按钮</button>
...
现在 npm run dev
,bootstrap样式就生效了
因为在 js中引入了 自己的 assets/css/index.scss 和 bootstrap.css ,最终自己的css和 bootstrap的css文件被打包到了最终生成的css文件中了。如果其它js中也使用bootstrap,那就会又生成一份css文件。也就是说 boostrap库的css会不断的重复生成,这样会影响最终的性能。
为了减小css文件大小,需要将库css也分离出来,因为库css一般不会发生变化,而自己写的css会变,所以需要将 bootstrap库的css独立出来存放到单独的文件中。
库文件有 js库和 css库,这些都需要分离出来。webpack 4+ 版本使用内置的 SplitChunksPlugin 插件来进行公共部分的提取。因为 SplitChunksPlugin 是 webpack 4+ 版本内置的插件, 所以无需安装, 只需在 webpack.config.js 中配置:
plugins:[ ... ], // 提取公共模块,包括第三方库和自定义工具库等 optimization: { // 找到chunk中共享的模块,取出来生成单独的chunk splitChunks: { chunks: "all", // async表示抽取异步模块,all表示对所有模块生效,initial表示对同步模块生效 cacheGroups: { vendors: { // 抽离第三方插件 test: /[\\/]node_modules[\\/]/, // 指定是node_modules下的第三方包 name: "vendors", priority: -10 // 抽取优先级 }, utilCommon: { // 抽离自定义的公共库 name: "common", minSize: 0, // 将引用模块分离成新代码文件的最小体积 minChunks: 2, // 表示将引用模块如不同文件引用了多少次,才能分离生成新chunk priority: -20 } } }, // 为 webpack 运行时代码创建单独的chunk runtimeChunk:{ name:'manifest' } }
SplitChunksPlugin 是在 配置文件 optimization 选项来配置的,我们需要抽离的公共部分有如下:
本项目中用到了 jQuery 库 和 bootstrap库, 库文件都是在项目的 node_modules 中的,所以分离这部分代码的标准很简单,只要 chunk(代码片段) 是从 node_modules 来的,就是库代码,此时就把这些代码都抽离出来合并在一起,取名为 venders(提供者)
vendors: { // 抽离第三方插件
test: /[\\/]node_modules[\\/]/, // 指定是node_modules下的第三方包
name: "vendors",
priority: -10 // 抽取优先级
},
webpack在运行的过程中,如果发现某些chunk被引用了超过2次,比如 某个css或者 js被其它文件 import 超过了2次,那就将这些凡是超过2次的代码合并在一起,起名为 common,那么如果是css,那么将来打包后就独立成 common.css ,js 就是 common.js
utilCommon: { // 抽离自定义的公共库
name: "common",
minSize: 0, // 将引用模块分离成新代码文件的最小体积
minChunks: 2, // 表示将引用模块如不同文件引用了多少次,才能分离生成新chunk
priority: -20
}
最终生成的js代码中都是有webpack运行时代码的,这部分代码也不会变化,也应该分离出来
// 为 webpack 运行时代码创建单独的chunk
runtimeChunk:{
name:'manifest'
}
打包后的结果,dist/assets/ 目录
├─css
│ index_aafcf62739bb7ef43cdb.css
│ vendors_aafcf62739bb7ef43cdb.css
├─img
│ 992ac5d15ba3b012bb054bf80016a636.png
└─js
│ about-aafcf62739bb7ef43cdb.js
│ index-aafcf62739bb7ef43cdb.js
│ manifest-aafcf62739bb7ef43cdb.js
│ vendors-aafcf62739bb7ef43cdb.js
└─production
index-aafcf62739bb7ef43cdb.js
boostrap 代码被分离到了 vendors_aafcf62739bb7ef43cdb.css 文件中。
jquery 代码被分离到了 vendors-aafcf62739bb7ef43cdb.js 文件中
webpack运行时代码分离到了 vendors-aafcf62739bb7ef43cdb.js 文件中。
index.html文件中引入情况:
... <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>这是首页</title> <link rel="icon" href="/favicon.ico" /> <link href="/assets/css/vendors_aafcf62739bb7ef43cdb.css" rel="stylesheet"/> <link href="/assets/css/index_aafcf62739bb7ef43cdb.css" rel="stylesheet" /> </head> ... <body> ... <script src="/assets/js/manifest-aafcf62739bb7ef43cdb.js"></script> <script src="/assets/js/vendors-aafcf62739bb7ef43cdb.js"></script> <script src="/assets/js/index-aafcf62739bb7ef43cdb.js"></script> </body>
现在在 src/assets/img 文件夹中添加一个图片 nodejs.jpg
文件, 然后在 public/index.html中引入这个图片:
├─public
│ index.html
├─src
│ └─assets
│ └─img
│ nodejs.jpg
public/index.html
...
<img src="../src/assets/img/nodejs.jpg" />
...
然后 npm run build
之后发现 nodejs.jpg 这个图片并没有被处理到 dist/assets/img 目录中, 生成的 index.html 文件中 依然还是:
<img src="../src/assets/img/nodejs.jpg" />
没有做任何改变,生成的 img src 明显是错误的。
我们预期的结果应该是这样的:
<img src="/assets/img/123abcde456.jpg" />
如何才能正确处理img标签中的文件?在 html文件中使用 ejs语法
HtmlWebpackPlugin 在处理HTML模板的时候,是支持 ejs语法的,如 <%= xxxxxx%>
,前面将公共的头和尾嵌入到页面中就是使用了 ejs语法,借助于 html-loader 完成了html 内容的加载
<%= require('html-loader!./include/header.html')%>
现在需要让webpack能够正确的处理 html中的img,也可以使用 require语句来加载图片,写法如下:
<img src="<%=require('../src/assets/img/nodejs.jpg').default%>" />
注意后面有一个 .default
项目中有些时候会使用一些小的图片文件,这些小的图片文件可以直接被处理成 base64编码,放入到 img标签的src中或者 css文件中。 浏览器在渲染图片文件的时候,会向服务器发起请求将图片下载到本地,如果这样的小图片太多,那么浏览器会发起很多次请求,对性能造成影响。如果直接将图片base64文本嵌入到网页中,浏览器就可以直接解析,无需向服务器请求图片。
可以用 url-loader 来做这个优化
npm i url-loader -D
webpack.config.js
module:{
rules:[
...
,{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, //匹配图片文件
loader: 'url-loader',
options: {
limit:10*1024,//小于limit限制的图片将转为base64嵌入引用位置, 单位为字节(byte)
fallback:'file-loader',//大于limit限制的将转交给指定的file-loader处理
outputPath:'assets/img'//传入file-loader将图片输出到 dist/assets/img文件夹下
}
}]
},
媒体文件,如音频,视频以及 css样式中的用到的字体文件,同样需要webpack在编译的时候处理这些资源。这些资源统一使用 url-loader来处理
webpack.config.js
module:{ rules:[ ... ,{ test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'assets/media/[name].[hash:7].[ext]' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'assets/fonts/[name].[hash:7].[ext]' } }] },
对于多页面的H5 HTML页面很多,每添加一个HMTL文件,就对应有一个js 文件,这样就需要在 webpack.config.js 中不停的添加 entry 与 HtmlWebpackPlugin 对象。我们可以按照 public目录下的HTML模板的组织结构和 src下的 js文件组织结构,动态的生成配置,只需要遍历目录下的文件即可。
完整的 webpack.config.js 文件内容如下:
const path = require('path') const glob = require("glob") const HtmlWebpackPlugin = require("html-webpack-plugin") const { CleanWebpackPlugin } = require('clean-webpack-plugin') const MiniCssExtractPlugin = require("mini-css-extract-plugin");//提取css到单独文件的插件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');//压缩css插件 // HTML模板文件所在的文件夹 const htmlDir = path.join(__dirname, 'public/') // 入口文件所在文件夹 const srcDir = path.join(__dirname, 'src/') /** 扫描获取入口 */ function scanEntry() { var entry = {} glob.sync(srcDir + '/**/*.js').forEach(name => { name=path.normalize(name) // 如 index // about // production/index chunkName=name.replace(srcDir, '').replace(/\\/g,'/').replace('.js', '') entry[chunkName] = name }) return entry } /** 扫描获取所有HTML模板 */ function scnanHtmlTemplate() { var htmlEntry = {} // 扫描目录以及子目录下所有html结尾的文件,不包含 include 文件夹 glob.sync(htmlDir + '/**/*.html', { ignore: '**/include/**' }).forEach(name => { name=path.normalize(name) chunkName=name.replace(htmlDir, '').replace(/\\/g,'/').replace('.html', '') htmlEntry[chunkName] = name }) return htmlEntry } /** 构建HtmlWebpackPlugin 对象 */ function buildHtmlWebpackPlugins() { var tpl = scnanHtmlTemplate() var chunkFilenames = Object.keys(tpl) return chunkFilenames.map(item => { var conf = { // 如 index.html // about.html // production/index.html filename: item + ".html", template: tpl[item], inject: true, favicon: path.resolve('./public/favicon.ico'), chunks: [item] } return new HtmlWebpackPlugin(conf) }) } // 所有入口文件 const entry = scanEntry() // 插件对象 let plugins= [ new CleanWebpackPlugin({ verbose: true, }), new MiniCssExtractPlugin({ filename: "assets/css/[name]_[chunkhash].css"//输出目录与文件 }), new OptimizeCssAssetsPlugin() ] // 所有 HtmlWebpackPlugin插件 plugins=plugins.concat(buildHtmlWebpackPlugins()) module.exports = { entry, output:{ path: path.resolve('./dist'), filename: process.env.NODE_ENV==='production'? 'assets/js/[name].[chunkhash].js':'assets/js/[name].js', publicPath: '/' }, resolve:{}, module:{ rules:[{ //它会应用到普通的 `.css` 文件, //use数组loader的名字是有顺序的,即先由sass-loader,再由css-loader处理,最后由style-loader处理 test: /\.(sc|c|sa)ss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },{ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, //匹配图片文件 loader: 'url-loader', options: { limit:10*1024,//小于limit限制的图片将转为base64嵌入引用位置 fallback:'file-loader',//大于limit限制的将转交给指定的file-loader处理 //outputPath:'assets/img'//传入file-loader将图片输出到 dist/assets/img文件夹下 name: 'assets/img/[name].[hash:7].[ext]' } },{ test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'assets/media/[name].[hash:7].[ext]' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'assets/fonts/[name].[hash:7].[ext]' } }] }, plugins, // 提取公共模块,包括第三方库和自定义工具库等 optimization: { // 找到chunk中共享的模块,取出来生成单独的chunk splitChunks: { chunks: "all", // async表示抽取异步模块,all表示对所有模块生效,initial表示对同步模块生效 cacheGroups: { vendors: { // 抽离第三方插件 test: /[\\/]node_modules[\\/]/, // 指定是node_modules下的第三方包 name: "vendors", priority: -10 // 抽取优先级 }, utilCommon: { // 抽离自定义的公共库 name: "common", minSize: 0, // 将引用模块分离成新代码文件的最小体积 minChunks: 2, // 表示将引用模块如不同文件引用了多少次,才能分离生成新chunk priority: -20 } } }, // 为 webpack 运行时代码创建单独的chunk runtimeChunk:{ name:'manifest' } } }
webpack编译或运行的时候,控制台输出内容较多,可以通过一些开关来控制,详细信息参考官方文档
module.exports= {
...
stats: {
assets:false,
modules:false,
entrypoints:false
}
...
}
如今 ES6 语法在开发中已经非常普及,甚至也有许多开发人员用上了 ES7 或 ES8 语法。然而,浏览器对这些高级语法的支持性并不是非常好。因此为了让我们的新语法能在浏览器中都能顺利运行,就需要进行转换。Babel 这个工具就可以完成这样的转换。
因为Babel只是辅助工具,所以安装的时候加入 -D 参数, 这里安装的是 Babel 7 版本
npm i -D babel-loader @babel/core @babel/preset-env
因为webpack编译打包的时候是不知道 Babel存在的,而转换的工作是有Babel完成的,这就需要让webpack 将打包的js文件交给 Babel 完成转换。
在webpack中对于文件的处理,是由 loader来完成的,前面安装Babel的时候,就安装了一个 babel-loader ,将它配置到webpack中就可以做转换了。
webpack.config.js
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}]
},
虽然在webpack中配置了loader,但是Babel此时还不能发挥它的作用,需要在项目根目录下创建一个 .babelrc 文件(文件名前面有一个 .),内容如下:
.babelrc
{
"presets": [
"@babel/preset-env"
]
}
在webpack的 resolve 选项可以配置 require 或者 import 模块时默认的扩展名和别名
@ 表示 src文件夹
resolve:{
extensions: ['.js', '.json','.scss','.css'],
alias: {
'@': path.resolve('./src')
}
},
在js文件中,如果要 import 一个 .scss文件,原来是这样:
import './assets/css/index.scss'
因为配置可扩展名,和别名,现在可以这样用:
import '@/assets/css/index'
ESLint 是个代码检查工具,它可以配置在开发环境中,提供编码规范,帮助我们找出项目中不符合编码规范规则的代码并给出提示,告诉我们哪一个文件哪一行代码不符合哪一条编码规范,方便你去修改代码。在VS Code开发工具中,可以配置在文件保存的时候,自动启用 ESLint 代码检查,并按照规则自动修复代码。
npm i eslint -D
# 初始化ESLint 配置文件
npx eslint --init
npx eslint --init
可以初始化一个 配置文件,它是一个交互式命令,会让你做一些选择:
? How would you like to use ESLint? ...
To check syntax only
> To check syntax and find problems
√ How would you like to use ESLint? · style
√ What type of modules does your project use? · esm
√ Which framework does your project use? · none
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
√ How would you like to define a style for your project? · guide
√ Which style guide do you want to follow? · standard
√ What format do you want your config file to be in? · JavaScript
运行成功之后,会自动生成 .eslintrc.js 文件,这就是ESLint的配置文件。
webpack 此时并不知道 ESLint的存在 ,需要 安装 ESLint loader, 这样webpack在工作的时候才会启动 ESLint检查。
npm i eslint-loader eslint-friendly-formatter -D
webpack.config.js 中配置 ESLint Loader:
module: {
rules: [
...
, {
test: /\.js$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [path.resolve(__dirname, 'src')], // 指定检查的目录
options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine
formatter: require('eslint-friendly-formatter') // 指定错误报告的格式规范
}
},
...
]
},
VS Code 中安装 ESLint 插件, Settings.json 建议配置:
"files.autoSave": "off",
"eslint.validate": [
"javascript",
"javascriptreact",
"vue"
],
"eslint.run": "onSave",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。