【webpack】webpack的基本使用

目录

一. 什么是webpack

二. 核心概念

1. entry

2. output

3. loader

4. plugins

5. mode

三. 关于npm

1. npm项目初始化 

2. 关于package-lock.json

3. 常用命令

4. 解决npm被墙

使用cnpm

yarn常用指令

基于nrm切源提高npm速度

5. 什么情况下把模块安装在全局?

四. webpack开发环境配置

1. 使用webpack打包js文件

2. webpack打包静态资源

1.1. webpack打包css样式资源

1.2. 使用webpack打包less样式资源

1.3. 使用webpack打包html资源

1.4. 使用webpack打包图片资源

1.4. 使用webpack打包其它资源

3. devServer

4. webpack分模块打包

5.HMR热模块切换

 五. webpack生产环境配置

1. 提取css为单独文件

2. css兼容性处理

3. 压缩css

4. 配置js语法检查eslint

5. js兼容问题

6. js的压缩

7. html的压缩

8. 缓存

1)babel缓存

2) 文件资源缓存

9. tree shaking

六. webpack性能优化

1. source-map

2. oneof

3. code split

1.optimization配置

2.单页面应用将多个模块分别打包

4.js懒加载和预加载

七.resolve配置

总结


一. 什么是webpack

是一个前端资源构建工具静态模块打包器

何为资源构建工具?

将一系列操作整合成大的工具处理。

何为静态模块打包器?

会对项目入口js文件中的各个模块,如less,vue等模块的依赖关系引入,形成一个chunk(代码块),再对这个代码块进行处理,比如将less编译成css等,这些操作就是打包,打包后将这些资源输出成bundle。

二. 核心概念

1. entry

即入口,指webpack以哪个文件为入口起点开始打包,分析构建内部依赖关系;

1.写成String格式:打包形成一个chunk,输出一个bundle文件,此时chunk默认名称为main

entry:'./src/js/index.js',

2.写成Array格式: 所有入口文件最终会形成一个chunk,输出一个bundle文件,用于HMR让html热更新生效。

entry:['./src/js/index.js','./src/js/test.js'],

3.写成Object格式: 用几个入口文件就形成几个chunk,输出一个bundle文件,chunk文件名称为对象的key。

entry:{
	index:'./src/js/index.js',
	test:'./src/js/test.js'
},

4.特殊用法

entry:{
    // 所有入口文件最终会形成一个chunk,输出一个bundle文件
	index:['./src/js/index.js','./src/js/index1.js'],
    // 形成一个chunk,输出一个bundle文件
	test:'./src/js/test.js'
},

2. output

即出口,webpack打包后的资源bundles输出到哪里去,以及如何命名;

publicPath指定引入公共路径的前缀,比如打包后的js会提供script标签src路径引入html文件,会在此路径前添加该前缀;

chunkFilename指定非入口chunk的名称,非入口chunk指的是非entry指定生成的chunk,一般为import语法分割的chunk或optimization对node_modules的包进行分割的chunk。

打包生成的js文件是以匿名函数定义模块,内部模块无法在外部引用,若要暴露函数内部的模块使用library配置项指定整个库向外暴露的变量名,libraryTarget指定此变量名添加到哪个browser,即使用什么什么模块化yu'f浏览器端指定的是‘window’,nodejs指定的是‘global’或'commonjs‘等

output:{
    // 输出文件名
    filename:'js/built.[name].[contenthash:10].js',
    // 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
    path:resolve(__dirname,'build'),
    // 所有资源引入公共路径前缀
    publicPath:'/',
    // 非入口chunk的名称
    chunkFilename:'[name]_chunk.js',
    library:'[name]',
    libraryTarget:'window',
},

3. loader

让webpack能够处理哪些非js的文件,因为webpack只能处理js文件;

4. plugins

即插件,可以执行范围更广的任务,包括从打包优化和压缩,一直到重新定义环境中的变量等;

5. mode

指示webpack使用相应模式的配置,包含两种选项development(开发模式),production(生产模式),不同的模式webpack会启用不同的插件。

三. 关于npm

npm,全称是 Node Package Manager,通俗的说就是使用一台服务器集中管理一些第三方的依赖库,比如jQuery、Bootstrap等,方便下载使用。

在www.npmjs.com可以搜索第三方包,开发者发布在这个网站上的,npm命令就是在这个网站上下载第三方包。

npm的第二层含义就是一个命令行工具,只要你安装了node就已经安装了npm,npm也有版本的概念,输入 npm –version查看版本,升级npm npm install - -global npm

安装node环境:

1.前往node官网下载node安装包 下载 | Node.js 中文网

2.选择对应操作系统的.msi安装包,下载到本地后一键点击下一步安装即可,安装完成后在cmd控制台中输入node -v查看当前安装的版本,若能够查看安装版本,则证明node环境安装成功。

3.随后在控制台中输入指令 npm install npm -g 全局安装npm,这样才能在全局环境下使用npm

4. 随后就可以在电脑的任何路径打开控制台使用npm命令了

1. npm项目初始化 

建议每一个项目都要有一个package.json文件(包描述问价,相当于产品的说明书),这个文件可以通过 npm init的方式初始化出来,该文件中最有用的是dependencies选项,可以用来保存第三方包的依赖信息。

安装的依赖包会统一安装到项目的node_modules文件下

建议执行npm install 包名的时候 都加上- - save这个选项,目的是在package.json的dependencies选项下保存生产依赖项信息(开发和部署时候都需要的)。如果node_modules删除了只需要 npm install就会自动把package.json中的dependencies中的所有依赖项下载回来,版本5以后就不用加了会自动添加;devDependencies开发依赖模块(只有开发的时候需要的模块);scripts配置本地可执行命令

示例1:在script中配置一条npm可执行命令用于将项目中的less文件转换为css

{
    "scripts":{
        "less":"lessc 1.less 1.min.css -x"
    }
}

 这样就能在当前项目的终端下使用npm run less 进行less文件的转换操作,相当于执行了lessc 1.less 1.min.css -x这条webpack命令。

示例2:在配置可执行脚本命令的时候,基于process的环境变量区分开发环境还是生产环境

"scripts":{
    "serve":"set NODE_EVN=dev&&node index1.js",
    "build":"set NODE_EVN=pro&&node index1.js"
}

上述配置中 set NODE_ENV=dev 代表设置全局环境变量,但在MAC中使用 export NODE_ENV=dev

在index1.js中获取script中设置的环境变量进行判断

let n = process.env.NODE_EVN;
if(n === 'dev') {
    console.log('开发环境下执行');
} else {
    console.log('生产环境下执行');
}

2. 关于package-lock.json

在npm5以前是不会有package-lock.json这个文件的,之后才加入。当安装包的时候npm会自动生成或者更新package-lock.json这个文件,它会保存node_modules中所有包的信息(版本,下载地址),这样重新执行npm install的速度会更快一些; 也可以锁定版本,防止执行npm install时下载新的版本(如果项目依赖1.1.1版本,但重新下载会下载最新版本)

3. 常用命令

  1. npm init   npm init –y:快速生成package.json文件,跳过向导,所有选项默认
  2. npm install 包名:下载指定的模块
  3. npm install 包名 --save:把模块保存在清单生产依赖中 (简写npm i –S 包名) 
  4. npm install 包名 --save-dev:把模块保存在清单开发依赖中
  5. npm install:一次性把dependencies中的所有依赖项下载回来(开发 + 生产环境)
  6. npm install --production:只安装生产依赖的模块
  7. npm install 包名 -g(--global):把模块安装在全局环境中
  8. npm uninstall 包名:只删除,如果有依赖项会依然保存
  9. npm uninstall --save删除的同时也会把依赖项给删除 npm un –S 包名
  10. npm help:查看npm使用帮助
  11. npm install 包名@版本号 --save:下载指定版本的包
  12. npm 命令 --help:查看指定命令的使用帮助
  13. npm config list:查看npm配置信息
  14. npm view 包名 versions > 包名.versions.json:查看某个模块的版本信息,输出到指定的模块中
  15. npm root -g:查看全局安装模块的目录

4. 解决npm被墙

使用cnpm

npm存储包的服务器在国外,有时会被墙,速度很慢,淘宝的开发团队把npm在国内做了一个备份  --->  中国 NPM 镜像

安装淘宝的cnpm:npm install –global cnpm

接下来安装包的时候把之前的npm替换成cnpm,npm就会通过淘宝的服务器下载(在任意目录下都可以执行,global是安装到全局的意思)

如果不想安装cnpm又想使用淘宝的服务器来下载安装:

npm install jquery –registry=https://registry.npm.taobao.org

 可以将这个选现加入配置文件中

npm config set registry https://registry.npm.taobao.org

 只要经过上面命令的配置,所有的npm install都会默认从淘宝的服务器下载。

yarn常用指令

可以使用yarn代替npm安装项目依赖,它的安装速度要比npm要快(npm 的两倍至三倍),使用 npm i yarn -g 安装yarn

  1. yarn init -y:生成package.json文件
  2. yarn install:一次性把dependencies中的所有依赖项下载回来(跑环境)
  3. yarn add 包名@x.xx.xx:下载指定的模块
  4. yarn remove 包名:移除指定的模块

基于nrm切源提高npm速度

首先安装nrm:npm install nrm -g

查看有哪些源:nrm ls

切源:nrm use xxx

一般切源到taobao,即使用指令 nrm use taobao

5. 什么情况下把模块安装在全局?

使用npm install 包名 -g安装在全局下的模块,对任何项目都有作用(可能会导致版本冲突),只能基于命令的方式管理,不能基于CommonJS中的require导入使用(不能导入到文件中基于代码来处理)。

可以使用指令npm root -g查看全局安装模块的目录,一般安装在本地电脑c盘AppData/Roaming/npm文件夹下,之所以可以对安装在全局下的xxx模块使用对应的命令进行操作,是因为在该文件下存在xxx.cmd的文件;

安装在本地的模块,可以在项目中导入基于代码操作,不能基于命令来操作。但是可以通过package.json中的scripts配置一些npm的可执行命令,配置后通过 npm run xxx 运行。

四. webpack开发环境配置

1. 使用webpack打包js文件

新建文件,使用npm init初始化项目,生成package.json文件,下载webpack执行如下指令:

npm i webpack@4 webpack-cli@3 -g
npm i webpack@4 webpack-cli@3 -D  

创建文件加src,作为项目打包的入口文件,其下创建index.js作为入口js;创建文件build,作为项目的打包的出口文件。

在index.js输入简单js代码:

function add(a,b) {
    return a + b;
}
console.log(add(100,200));

随后执行命令:含义是以./src/index.js为入口文件打包到./build/built.js

npx webpack ./src/index.js -o ./build/built.js --mode=development

 此时发现build目录下出现了built.js文件,这就是打包完成后生成的文件,内容如下:

 以生产打包运行一次:

npx webpack ./src/index.js -o ./build/built.js --mode=production

生成的built.js文件是经过压缩的js代码

将该文件引入html文件运行,控制台成功输出 300.

同样,webpack如果将json文件引入入口js,webpack也能进行打包;

2. webpack打包静态资源

webpack可以打包js和json类型的文件,但例如样式,图片,html格式的文件是无法打包的,若需要打包它们,需要使用配置文件的形式进行。

所有构建工具都是基于node.js平台运行的,模块化默认采用commonjs

webpack.config.js基础配置如下:

module.exports = {
    // 入口起点
    entry:'./src/index.js',
    // 输出
    output:{
    	// 输出文件名
    	filename:'built.js',
 		// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
    	path:resolve(__dirname,'build')
	},
    // loader配置
    module:{
        rules:[
    
        ]
    },
    // 插件配置
    plugins:[
       
    ],
    // 模式'production'--生产模式,'development'--开发模式
    mode:'development'
}

以上是webpack配置文件的基本要素,包括之前介绍的webpack的5个核心概念。

1.1. webpack打包css样式资源

打包样式资源需要在module中添加loader加载器,处理样式资源,需要style-loader,css-loader两个loader,其作用分别是:

style-loader:创建style标签,将js中的样式资源插入到head中生效

css-loader:将css文件变成commonjs模块加载到js中,里面内容是样式字符串

需要下载这两个loader:

 cnpm i css-loader@3  style-loader@1 -D

添加loader需要书写一些配置项,书写在rules数组中,test属性使用正则表达式确定检测文件类型;use属性决定使用哪些loader进行处理。

注意use是一个数组,数组中的loader执行顺序是从左到右,从下到上依次执行

const {resolve} = require('path');

module.exports = {
    entry:'./src/index.js',
    output:{
    	filename:'built.js',
    	path:resolve(__dirname,'build')
	},
    // loader配置
    module:{
        rules:[
            {	
                // 匹配哪些文件
                test:/\.css$/,
                // 使用哪些loader进行处理
                use:[
                   // loader执行顺序,从右到左,从下到上
                   // 创建你style标签,将js中的样式资源插入到head中生效
                  'style-loader',
                   // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
                  'css-loader'
                ]
            }
        ]
    },
    plugins:[
        
    ],
    mode:'development'
}

最后运行webpack项目:

npx webpack

控制台显示如下:

1.2. 使用webpack打包less样式资源

打包处理less文件,其实是在打包css文件操作之前添加一个less-loader,其作用是将less文件编译成css文件,再做上述css文件的打包工作。

在rules数组中再添加一个配置less-loader配置对象,一个配置对象只能处理一种类型的文件

{
    test:/\.less$/,
    use:['style-loader','css-loader','less-loader']
},

 下载less-loader和less

npm i less-loader@5 -D
npm i less -D

 运行webpack,终端输出如下:

1.3. 使用webpack打包html资源

plugins:[
    // 需求:需要有结构的html文档
    new HtmlWebpackPlugin({
        // 复制./src/index.html,并自动引入打包输出的所有资源
        template:'./src/index.html'
    })
],

打包html需要使用Plugin处理,首先是下载:

npm i html-webpack-plugin@3 -D

在webpack配置文件中引入:

const HtmlWebpackPlugin = require('html-webpack-plugin');

该插件默认会创建一个html文件,它没有任何结构和样式,会自动引入输出打包的所有资源,如:js/css

随后添加Plugins配置:先在src目录下创建一个index.html文件并使用template属性将上述html复制到该插件默认创建的html文件中,除了引入html外插件还会自动引入打包输出的所有资源。

plugins:[
    // 需求:需要有结构的html文档
    new HtmlWebpackPlugin({
        // 复制./src/index.html,并自动引入打包输出的所有资源
        template:'./src/index.html'
    })
],

运行webpack

 会在built文件下生成一个index.html,该文件会复制src/index.html,并将打包后的js/css/less文件添加到这个html中。

1.4. 使用webpack打包图片资源

打包图片资源需要使用url-loader,而使用url-loader依赖于file-loader,所以使用前需要下载这两个loader.

npm i url-loader@3 file-loader@5 -D

1) 通过样式引入图片

在less文件中使用背景图片,这里引入两张图片,一张大小大于8kb,一张小于8kb

#wrapper {
    color:#bbffcc;
    font-size: 14px;
    background: url(./img/logo.jpg) no-repeat 100% 100%;
}

#img-wrapper {
    background: url(./img/naruto.jpg) no-repeat 100% 100%;
}

在index.html中书写对应的结构

<body>
    <div id="wrapper"></div>
    <div id="img-wrapper"></div>
</body>

随后在配置文件中做配置

loader属性指定使用的loader,当需要使用的loader只有一个时可以这样使用,使用的loader为多个需要写成use数组的形式;

使用options属性指定该loader的具体配置,这里限制文件大小小于8kb时将图片转换为base64的格式,base64会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析;base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢;所以要进行折中考虑,只有小图片才能进行base64处理。

{
    // 处理图片资源(默认不能处理img标签的图片)
    test:/\.(jpg|png|gif)$/,
    loader:'url-loader',
    options:{
        // 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
        // base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
        // 所以要进行折中考虑,只有小图片才能进行base64处理
        limit:8 * 1024
    },
},

最后执行打包指令,终端输出如下:

打包输出的目录如下

  

只有一张图片参与了打包,是大小超过8kb的,而大小小于8kb的图片转换为了base64字符串,在built.js中可见

2)打包img标签中的图片

以上打包的的配置无法处理img标签引入的图片,需要使用html-loader处理,它的作用是引入img,从而能被url-loader打包处理

下载loader

npm i html-loader@0  -D

由于url-loader默认使用es6模块化进行解析,但html-loader引入图片使用的是commonjs,需要在opions对象中使用esModule:false关闭url-loader的es6模块化,使用commonjs解析。

打包生成的图片名默认会以唯一hash值命名,可以使用name属性name:'[hash:10].[ext]定义图片名,[hash:10]即取图片的hash值前十位,[ext]即取原片原来的扩展名。

{
    test:/\.(jpg|png|gif)$/,
    loader:'url-loader',
    options:{
        limit:8 * 1024,
        esModule:false,
        name:'[hash:10].[ext]',
    },
},
{
    test:/\.html$/,
    loader:'html-loader',
},

打包后输出如下:

 打包后生成的html文件:

1.4. 使用webpack打包其它资源

使用file-loader打包其它资源;

exclude配置排除指定的资源,options配置项给打包生成的文件重命名

{
    // 处理其它文件,排除css,js,html等资源
    exclude:/\.(css|js|html|less|jpg|png|gif)$/,
    loader:'file-loader',
    options:{
        name:'[hash:10].[ext]'
    },
}

3. devServer

项目开发是一个持续的过程,目前当我们修改了戴拿想看最新的页面效果需要重新打包构建一次,可以使用devServer自动构建打包,其作用是构建一个开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器它只会在内存中进行打包,不会有任何输出(不会生成built.js)。

需要在webpack.conjig.js进行配置,在module.exports暴露的暴露的对象里添加配置项如下:

    devServer:{
        // 构建后的项目路径
        contentBase:resolve(__dirname,'build'),
    	// 启动gzip压缩
        compress:true,
        // 端口号
        port:3000,  
        // 域名
        host:'localhost',
        // 自动打开浏览器
        open:true,
        // 开启HMR功能
        hot:true,
    }

使用需要下载 webpack-dev-server:

npm i webpack-dev-server@3 -D

启动devServer指令:

npx webpack-dev-server

启动后终端如下显示,会自动打开本地默认浏览器,此时程序一直运行,修改代码会自动编译,自动刷新浏览器:

4. webpack分模块打包

将打包输出的全部在build目录下,包括了多种类型的文件,不好维护,需要创建多个文件夹存放不同后缀名的文件。 

首先修改output配置,将输出的js文件放在build的js目录下:

output:{
    // 输出文件名
    filename:'js/built.js',
    // 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
    path:resolve(__dirname,'build')
},

其次将输出的图片和其他资源分别打包输出在build文件的imgs和media目录下,需要在对应的options配置中添加outputPath属性,指定输出的路径,路径不需要写完整路径,webpack会以output配置项的path配置为根路径,在此路径目录下创建outputPath指定的文件,存放打包后的内容。

{
    exclude:/\.(css|js|html|less|jpg|png|gif)$/,
    loader:'file-loader',
    options:{
        name:'[hash:10].[ext]',
        outputPath:'media'
    }
},
{
    test:/\.(jpg|png|gif)$/,
    loader:'url-loader',
    options:{
        limit:8 * 1024,
        esModule:false,
        name:'[hash:10].[ext]',
        outputPath:'imgs'
    },
}

打包完成后的build文件如下

5.HMR热模块切换

默认情况下修改一个模块的内容,所有模块的内容会重新打包,这样性能较差.使用HMR(hot module replacement)热模块切换

1) 作用

一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度。

​2) 使用

1. 样式文件

可以使用HMR功能,因为style-loader内部实现了,在开发服务器配置中添加,hot:true属性,这样devServer只会重新构建样式文件,其它类型的文件不会变。

devServer:{
    // 构建后的项目路径
    contentBase:resolve(__dirname,'build'),
    // 启动gzip压缩
    compress:true,
    // 端口号
    port:3000,  
    // 自动打开浏览器
    open:true,
    // 开启HMR功能,当修改了webpack配置,新配置想要生效,必须重新启动webpack服务
    hot:true
}

2. js文件

hot:true添加后默认不能使用HMR功能。

需要修改js代码,添加支持HMR功能的代码,只能处理非入口文件的js;在入口js文件中添加如下代码:

// 全局寻找module,若module.hot为true,说明开启了HMR功能 
if(module.hot) {
    // 让HMR代码生效
	module.hot.accept('./模块路径.js',function(){
        //该方法会监听xxx.js文件的变化,一旦发生变化,其它模块不会重新打包构建,会执行后面的回调函数
        xxx();
    })
}

3. html文件

hot:true添加后默认不能使用HMR功能,同时html文件不能热更新了。

需要修改entry选项,将html文件引入(html文件不需要做HMR,因为一个项目只有一个index.html文件,修改其他的文件其必定会改变)。

module.exports = {
    entry:['./src/js/index.js','./src/index.html']
}

完整配置代码:

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // 入口起点
    entry:'./src/js/index.js',
    // 输出
    output:{
        // 输出文件名
        filename:'js/built.js',
        // 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
        path:resolve(__dirname,'build')
    },
    // loader配置
    module:{
        rules:[
            {	
                // 匹配哪些文件
                test:/\.css$/,
                // 使用哪些loader进行处理
                use:[
                   // loader执行顺序,从右到左,从下到上
                   // 创建你style标签,将js中的样式资源插入到head中生效
                  'style-loader',
                   // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
                  'css-loader'
                ]
            },
            {
                test:/\.less$/,
                use:['style-loader','css-loader','less-loader']
            },
            {
                // 处理图片资源(默认不能处理img标签的图片)
                test:/\.(jpg|png|gif)$/,
                loader:'url-loader',
                options:{
                    // 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
                    // base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
                    // 所以要进行折中考虑,只有小图片才能进行base64处理
                    limit:8 * 1024,
                    esModule:false,
                    name:'[hash:10].[ext]',
                    outputPath:'imgs'
                },
            },
            {
                test:/\.html$/,
                // 处理html文件的img图片,负责引入html从而能被页面的url-loader处理
                loader:'html-loader',
            },
            {
                // 处理其它文件,排除css,js,html资源
                exclude:/\.(css|js|html|less|jpg|png|gif)$/,
                loader:'file-loader',
                options:{
                    name:'[hash:10].[ext]',
                    outputPath:'media'
                }
            }
        ]
    },
    // 插件配置
    plugins:[
        // 需求:需要有结构的html文档
        new HtmlWebpackPlugin({
            // 在src目录下创建一个index.html文件并复制到该插件默认创建的html文件中
            // 除了引入html外插件还会自动引入打包输出的所有资源
            template:'./src/html/index.html'
        }),
    ],
    // 模式'production'--生产模式,'development'--开发模式
    mode:'development',
    // 开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器
    // 启动指令:npx webpack-dev-server,启动前需要下载
    devServer:{
        // 构建后的项目路径
        contentBase:resolve(__dirname,'build'),
    	// 启动gzip压缩
        compress:true,
        // 端口号
        port:3000,  
        // 自动打开浏览器
        open:true
    }
}

 五. webpack生产环境配置

1. 提取css为单独文件

打包后的css文件会自动引入到html的style标签中,会使html文件体积过大,在生产上线阶段需要分离;

1)提取css文件需要下载插件 mini-css-extract-plugin

npm i mini-css-extract-plugin@0 -D

2)style-loader会将css代码插入到style标签中,生产环境不需要使用,替换为MiniCssExtractPlugin.loader;

{	
    test:/\.css$/,
    use:[
        MiniCssExtractPlugin.loader,
        'css-loader'
    ]
},

3)添加插件,并对输出的css文件重命名

new MiniCssExtractPlugin({
    // 1.1对输出的文件进行重命名
    filename:'css/built.css'
}),

重新打包,终端输出如下:

打包输出文件如下:

2. css兼容性处理

部分浏览器不能兼容如flex等css样式需要做兼容,使用postcss-loader处理兼容性问题,它依赖于插件postcss-preset-env

1)安装

npm i postcss-loader@3  postcss-preset-env@6 -D

2)添加postcss-loader配置,这里由于不适用该loader的默认配置,需要在use数组中将该loader写成一个配置对象的形式,在其中添加postcss-loader插件postcss-preset-env,其作用是帮助postcss找到package.json中browserlist里面的配置,通过配置加载指定的css兼容性样式

{	
    test:/\.css$/,
    use:[
        MiniCssExtractPlugin.loader,
        'css-loader',
        // 修改loader配置
        {
            loader:'postcss-loader',
            options:{
                ident:'postcss',
                plugins:() => [
                    // postcss插件 
                    require('postcss-preset-env')()
                ]
            }
        }
    ]
}

3)修改package.json,添加browserlist配置

"browerslist":{
    "development":[
        // 距离最近的xxx版本
        "last 1 chrome version",
        "last 1 firefox version",
        "last 1 safari version"
    ],
    // 生产环境(默认找生产环境)
    "production":[
        // 大于99.8%浏览器
        ">0.2%",
        // 不要兼容已死的浏览器如IE10
		"not dead",
        "not op_mini all",
    ]
}
// !!package.json不能添加注释

browerslist配置默认使用的是生产环境的配置,如果要使用其中开发环境配置需要在webpack配置文件中修改环境变量

process.env.NODE_ENV = 'development'

打包输出的css文件中对部分样式按照package.json配置要求做了兼容性处理;

3. 压缩css

使css文件大小减小,加快项目加载速度。使用插件optimize-css-assest-webpack-plugin

1)安装

npm i optimize-css-assets-webpack-plugin@5 -D

2)配置

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

......

plugins:[
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin()
],

打包后css文件经过了压缩

4. 配置js语法检查eslint

eslint规范了项目代码风格,检查语法错误,团队开发中要求书写代码格式的一致性,正确性。

使用eslint-loader,只检查自己写的代码,第三方的库是不用检查的,排除node modules

推荐使用airbnb规则,这是一种代码风格库,依赖eslint-config-aribnb-base eslint-plugin-import eslint 这些插件。

1)安装

npm i eslint-config-airbnb-base@14 eslint-plugin-import@2 eslint@6 eslint-loader@3 -D

2)配置webpack

添加fix:true配置可以让不符合规范的代码自动修改,不出现警告

{
    // 3.1语法检查loader
    test:/\.js$/, 
    exclude:/node_modules/,
    // 优先执行
    enforce:'pre',
    loader:'eslint-loader',
    options:{
        // 自动修复eslint错误,发现了代码规范错误,自动修复,不用手动修改
        fix:true
    }
},

3)配置package.json

配置eslintConfig,需要继承库airbnb-base,会自动加载其中的配置,进行语法检查。

"eslintConfig": {
  "extends": "airbnb-base"
}

此外在不想被eslint检查的代码行上一行加上 // eslint-disable-next-line 后一行内容不进行eslint检查

5. js兼容问题

js兼容性处理,解决低版本ie不兼容es6语法的问题,使用babel-loader,以及@babel/preset-env处理预设环境,这两者结合可以解决基本的js兼容问题,而对于Promise等高级语法还需要使用@babel/polyfill进行兼容,这三者结合解决全部js兼容性问题。当使用@babel/polyfill是将全部兼容性代码引入项目,会使项目体积过大,可以使用core-js按需加载。

1) 下载

 npm i babel-loader@8 @babel/preset-env@7 @babel/core@7 -D
 npm i @babel/polyfill@7 -D
 npm i core-js@3 -D

2)使用

{
    // 4.1解决js兼容性loader
    test:/\.js$/,
    exclude:/node_modules/,
    loader:'babel-loader',
    options:{
        presets:[
            [
                '@babel/preset-env',
                {
                    // 按需加载
                    useBuiltIns:'usage',
                    // 指定core-js版本
                    corejs:{
                        version:3
                    },
                    // 指定兼容性做到哪个版本浏览器
                    targets:{
                        chrome:'60',
                        firefox:'60',
                        ie:'9',
                        safari:'10',
                        edge:'17'
                    }
                }         
            ]
        ]
    }
},

使用@babel/polyfill非按需加载时,由于@babel/polyfill不是插件,只要在js文件中引入即可。

import '@babel/polyfill';

6. js的压缩

webpack生产环境会自动加载UglifyJsPlugin,只需将mode选项设置为production即可压缩js代码.

7. html的压缩

使用HtmlWebpackPlugin压缩,配置如下:

new HtmlWebpackPlugin({
    template:'./src/html/index.html',
    // 压缩html代码
    minify:{
        // 移除空格
        collapseWhitespace:true,
        // 移除注释
        removeComments:true
    }
}),

8. 缓存

1)babel缓存

在生产模式中,无法使用HMR提高打包速度,所以使用babel缓存,让第二次打包速度更快,配置如下

{
    test:/\.js$/,
    exclude:/node_modules/,
    loader:'babel-loader',
    options:{
        presets:[
            ........
        ],
        // 开启babel缓存
   	    // 第二次构建时,会读取之前的缓存
        cacheDirectory:true
    }
}

2) 文件资源缓存

作用:让代码上线运行缓存更好使用,上线性能优化

浏览器获取服务器文件时,会在本地做一个缓存,在指定时间内就不会再向浏览器请求数据,直接使用本地缓存加载页面。但如果此时服务器文件发生变化,会造成资源不同步的问题。

解决方法:提供维护hash是否变化,判断资源是否更新

​1.hash:每次webpack构建时会生成一个唯一的hash值,用hash值去命名打包后的文件。但js和css文件同时使用一个hash值,如果只修改某个js文件,重新打包,会导致所有文件缓存失效。

​2.chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk(css是在js中被引入的,所以同属于一个chunk.js,所有在同一个入口js中引入的文件都属于一个chunk),那么hash值就是一样的,如果同一个chunk的css文件发生变化而js文件没有变化,依旧会改变chunkhash。

​3.contenthash:根据文件的内容生成hash值。不同内容的文件的hash值一定不同。

给输出的js和css文件使用contenthash前十位命名 :

output:{
    filename:'js/built.[contenthash:10].js',
    path:resolve(__dirname,'build')
},
plugins:[
    new MiniCssExtractPlugin({
        // 1.1对输出的文件进行重命名
        filename:'css/built.[contenthash:10].css'
    }),
],

 打包后终端显示结果,只要这些文件内容不变,不管打包几次它们的hash值都不变

9. tree shaking

作用:去除项目中没有使用的js/css代码,

使用前提:

​    1.必须使用es6模块化

​    2.开启production环境

在package.json中配置:

​    默认“sideEffect”:false -- 所有代码都没有副作用,都可以进行tree shaking,这样会把css以及@babel以及ployfill文件干掉,要添加配置:

"sideEffect":["*.css","*.less"]

将css和less后缀结尾的文件不放在tree shaking范围内。

以上操作webpack.config.js的完整代码:

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 2.3设置node.js环境变量
process.env.NODE_ENV = 'production'

module.exports = {
    // 入口起点
    entry:'./src/js/index.js',
    // 输出
    output:{
        // 输出文件名
        filename:'js/built.js',
        // 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
        path:resolve(__dirname,'build')
    },
    // loader配置
    module:{
        rules:[
            {	
                // 匹配哪些文件
                test:/\.css$/,
                // 使用哪些loader进行处理
                use:[
                    // loader执行顺序,从右到左,从下到上
                    // 创建你style标签,将js中的样式资源插入到head中生效
                    MiniCssExtractPlugin.loader,
                    // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
                    'css-loader',
                    // 修改loader配置
                    {
                        loader:'postcss-loader',
                        options:{
                            ident:'postcss',
                            plugins:() => [
                                // postcss插件 
                                require('postcss-preset-env')()
                            ]
                        }
                    }
                ]
            },
            {
                // 3.1语法检查loader
                test:/\.js$/, 
                exclude:/node_modules/,
                // 优先执行
                enforce:'pre',
                loader:'eslint-loader',
                options:{
                    // 自动修复eslint错误,发现了代码规范错误,自动修复,不用手动修改
                    fix:true
                }
            },
            {
                // 4.1解决js兼容性loader
                test:/\.js$/,
                exclude:/node_modules/,
                loader:'babel-loader',
                options:{
                    presets:[
                        [
                            '@babel/preset-env',
                            {
                                // 按需加载
                                useBuiltIns:'usage',
                                // 指定core-js版本
                                corejs:{
                                    version:3
                                },
                                // 指定兼容性做到哪个版本浏览器
                                targets:{
                                    chrome:'60',
                                    firefox:'60',
                                    ie:'9',
                                    safari:'10',
                                    edge:'17'
                                }
                            }         
                        ]
                    ]
                }
            },
            {
                test:/\.less$/,
                use:['style-loader','css-loader','less-loader']
            },
            {
                // 处理图片资源(默认不能处理img标签的图片)
                test:/\.(jpg|png|gif)$/,
                loader:'url-loader',
                options:{
                    // 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
                    // base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
                    // 所以要进行折中考虑,只有小图片才能进行base64处理
                    limit:8 * 1024,
                    esModule:false,
                    name:'[hash:10].[ext]',
                    outputPath:'imgs'
                },
            },
            {
                test:/\.html$/,
                // 处理html文件的img图片,负责引入html从而能被页面的url-loader处理
                loader:'html-loader',
            },
            {
                // 处理其它文件,排除css,js,html资源
                exclude:/\.(css|js|html|less|jpg|png|gif)$/,
                loader:'file-loader',
                options:{
                    name:'[hash:10].[ext]',
                    outputPath:'media'
                }
            }
        ]
    },
    // 插件配置
    plugins:[
        // 需求:需要有结构的html文档
        new HtmlWebpackPlugin({
            template:'./src/html/index.html',
            // 压缩html代码
            minify:{
                // 移除空格
                collapseWhitespace:true,
                // 移除注释
                removeComments:true
            }
        }),
        // 分离js和css
        new MiniCssExtractPlugin({
            // 1.1对输出的文件进行重命名
            filename:'css/built.css'
        }),
        // 压缩css
        new OptimizeCssAssetsWebpackPlugin()
    ],
    // 模式'production'--生产模式,'development'--开发模式
    mode:'development',
    // 开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器
    // 启动指令:npx webpack-dev-server,启动前需要下载
    devServer:{
        // 构建后的项目路径
        contentBase:resolve(__dirname,'build'),
    	// 启动gzip压缩
        compress:true,
        // 端口号
        port:3000,  
        // 自动打开浏览器
        open:true
    }
}

六. webpack性能优化

1. source-map

一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射可以追踪源代码的错误.

1) 使用

 在webpack.config.js中添加如下属性

devtool:'source-map'

打包后在js目录下出现了built.js.map文件,外文件提供了源代码和构建后代码的映射关系。

2) devtool参数

[inline-|hidden-|eval-]  [nosources-]  [cheap-[module-]]  source-map

  • source-map

       代码映射文件位置:外部

       能提示到错误代码的准确信息和源代码的错误位置

  • inline-source-map

        代码映射文件位置:内联   

        只生成一个source-map

        能提示到错误代码的准确信息和源代码的错误位置

  • hidden-source-map

        代码映射文件位置:外部   

        xxx.js.map 能提示到错误代码的错误原因,只有构建后代码的错误位置(半隐藏)

  • eval-source-map

       代码映射文件位置:内联   

       每一个参与打包的文件都生成了对应的source-map,都在eval函数中

       能提示到错误代码的准确信息和源代码的错误位置,错误提示处多了一个hash值

  • nosources-source-map

       代码映射文件位置:外部   

       错误代码准确信息,源代码错误位置,但是没有任何源代码和构建后代码信息(全隐藏

  • cheap-source-map

       代码映射文件位置:外部   

       能提示到错误代码的准确信息和源代码的错误位置,cheap只精确到行不是列(一行多句代码)

  • cheap-module-source-map

        代码映射文件位置:外部   

        能提示到错误代码的准确信息和源代码的错误位置,module会将loader的source-map加入,这样对于代码调试是最友好的

内联和外部的区别

1. 外部生成了.map映射文件,内联没有,source-map文件直接生成在js内容后(base64格式)

2. 内联构建速度快 

开发环境:速度快,调试友好  使用eval-source-map,是主流框架的脚手架配置

生产环境:代码隐藏,内联会让代码体积变大,不能使用,一般使用source-map

2. oneof

一个项目会有很多loader如果正常情况下一种类型的文件会依次匹配rules数组中的所有loader,即使匹配到了相应的loader依旧会向下匹配,使用oneof配置,只要文件匹配到执行loader后就不会继续向下匹配,但要注意这只是用一类文件匹配一个loader的场合,需要匹配多个loader则需要将其余的loaders放在oneof配置对象之外。

module:{ 
    rules:[
        // 不需要使用oneof的loader
        { ...... },
        {	
            // 以下loader只会匹配一个,如css文件匹配到/\.css$/就不继续了,oneof中不能有两项配置处理同一个文件
            oneof:[
                {	
                    test:/\.css$/,
                    use:['style-loader','css-loader']
                },
                ......
            ]
    	}
    ]
},

3. code split

开发完成的应用可能非常的庞大,需要使用webpack的code split即代码分割技术进行文件拆分,从而实现按需加载等项目性能优化处理。

 配置单入口:用于单页面应用,最终会输出一个bundle,所有文件都会集合在一起。

entry:'./src/js/index.js'

配置多入口:用于多页面应用,一个页面打包成一个chunk.js

entry:{
	index:'./src/js/index.js',
	test:'./src/js/test.js'
},
output:{
    // 输出文件名
    filename:'js/built.[name].[contenthash:10].js',
    // 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
    path:resolve(__dirname,'build')
},

打包后输出如下:打包输出两个chunk 

1.optimization配置

1).可以将node_modules中的代码单独打包成一个chunk,即第三方代码单独打包。

2).自动分析多入口chunk中,有没有公共的文件,如果有打包成单独的chunk(比如多个页面依赖jquery.js时,会将其单独带包成chunk一次) 

在module.exports的对象中添加属性:

optimization:{
    splitChunks:{
        chunks:'all'
    }
}

为单入口文件index.js中引入vue

打包后终端显示如下:

2.单页面应用将多个模块分别打包

optimization配置只能将js中node_module中的代码单独打包成一个chunk,但不能单独打包非第三方的代码,使用es10的import动态导入语法。

在入口index.js中添加如下代码,无需额外引入test.js。

import(/* webpackChunkName:'test' */'./test.js')
	.then((result) => {
		// 文件加载成功
    console.log(result.add(123,211));
	})
	.catch(() => {
		// 文件加载失败
	})

终端输出如下,test.js独立打包成一个chunk了 。

4.js懒加载和预加载

懒加载指在文件需要时候被加载 ,多次调用会走缓存,和代码分割一样,使用import动态导入语法,写在某个异步回调中。

document.getElementById('btn1').onclick = function() {
    import(/* webpackChunkName:'test' */'./test.js')
	.then((result) => {
		// 文件加载成功
    console.log(result.add(123,211));
	});
};

预加载Perfetch:会在使用之前提前加载js文件,正常加载会认为是并行加载,同一时间加载多个文件,预加载是等其它资源加载完毕浏览器空闲了,再去加载指定为预加载的文件。需要预加载,需添加webpackPerfetch:true

document.getElementById('btn1').onclick = function() {
    import(/* webpackChunkName:'test',webpackPerfetch:true */'./test.js')
	.then((result) => {
		// 文件加载成功
    console.log(result.add(123,211));
	});
};

七.resolve配置

resolve配置用于解析模块规则,常用配置如下:

alias配置路径别名,用来简写路径;

extension用于省略文件后缀名规则,里面的指定的后缀名在项目中使用时可以省略。

module用于告诉webpack解析模块前往的目录,默认是node_modules,可以使用参数一指定node_modules相对于当前文件的路径,这样可以减少程序查找node_modules的过程

resolve:{
    // 配置解析模块的路径别名
    alias:{
        $css:resolve(__dirname,'src/css')
    },
    // 配置省略文件后缀名规则
    extension:['js','json','css'],
    // 解析模块时方便查找node_modules
    module:[resolve(__dirname,'./node_modules'),'node_modules']
},

总结: 


其实在一周前的2月7日,vue官方将vue3.0设为了默认版本,在新版本中官方大力提倡使用Vite进行构建,Pinia取代vuex做状态管理......但是当下的绝大多数项目还是基于webpack构建的,掌握好webpack依旧重要。不过话虽如此,也要时刻关注了解新技术的发展。哎,真是学海无涯!!!

猜你喜欢

转载自blog.csdn.net/weixin_43655896/article/details/122870982