webpack的学习

webpack的学习

尚硅谷2020最新版Webpack5实战教程(从入门到精通),哔哩哔哩链接:https://www.bilibili.com/video/BV1e7411j7T5?p=1

webpack的学习_第1张图片

环境参数: Nodejs 10版本以上、webpack 4.26版本以上

预备技能: 基本Nodejs知识和Npm指令、熟悉ES6语法

一、webpack基本使用

1.webpack简介

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件

(js/json/css/img/less/...)都会作为模块处理。 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

webpack的学习_第2张图片

2.webpack五个核心概念

1 Entry

入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。例如从index.js这个起点出发

webpack的学习_第3张图片

2 Output

输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

3 Loader

Loader 让 webpack 能 够 去 处 理 那 些 非 JavaScript 文 件 (webpack 自 身 只 理 解 JavaScript),例如css、img

4 Plugins

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

5 Mode

webpack的学习_第4张图片

3.webpack初体验

1.在vscode中新建一个文件夹,右键在终端中打开

webpack的学习_第5张图片

2.输入 npm init ,初始化npm,

3.接着输入webpack的包名,其他按enter默认即可

webpack的学习_第6张图片

4.然后下载包并指定为全局安装,输入

npm i webpack webpack-cli -g    

这里下载了两个包,一个为webpack,一个为webpack-cli,使我们通过webpack-cli这个包的指令去使用webpack内部的功能,-g表示全局安装,将来可以直接通过指令去调用这两个包。这里即使你已经安装过这两个包,也可以再次输入,他会自动覆盖旧的包

webpack的学习_第7张图片

5.然后本地安装,输入 ,

npm i webpack webpack-cli -D

-D的意思是将webpack添加到开发依赖,webpack下载的所有东西都属于开发依赖,不属于生产依赖

6.之后新建的文件夹会出现以下的三个文件

webpack的学习_第8张图片

7.接着在自己建的文件夹下新建srcbulid文件夹,src表示源代码,bulid表示代码通过webpack打包处理之后输出的目录

8.在src下新建index.js作为webpack的入口起点文件

index.js

/*
  index.js: webpack入口起点文件

  1. 运行指令:
    开发环境:webpack ./src/index.js -o ./build/built.js --mode=development
      webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js
      整体打包环境,是开发环境
    生产环境:webpack ./src/index.js -o ./build/built.js --mode=production
      webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js
      整体打包环境,是生产环境,生产环境会压缩代码

   2. 结论:
    1. webpack能处理js/json资源,不能处理css/img等其他资源
    2. 生产环境和开发环境将ES6模块化编译成浏览器能识别的模块化~
    3. 生产环境比开发环境多一个压缩js代码。
*/
// import './index.css';

import data from './data.json';
console.log(data);

function add(x, y) {
  return x + y;
}

console.log(add(1, 2));

data.json

{
  "name": "jack",
  "age": 18
}

9.然后运行开发环境指令 ,

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

-o为Output,意思是输出到.build/built.js这个路径。

image-20200624151237704

Hash为每次打包都会生成一个唯一的哈希值,built.js为打包资源

webpack的学习_第9张图片

10.通过 node .\build\built.js 指令便可直接运行built.js文件

image-20200624151454805

11.在html中引入webpack打包后的资源

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
    
    <script src="./build/built.js">script>
head>
<body>
    
body>
html>

结果如下:

webpack的学习_第10张图片

二、webpack开发环境配置

1.打包样式(css、less)资源

因为webpack只能识别js文件,所以要想识别其他文件必须在webpack的配置文件里进行配置,将其转换为js。

1.新建index.css、index.js、index.less、webpack.config.js配置文件

webpack的学习_第11张图片

index.css

html, body{
  margin: 0;
  padding: 0;
  height: 100%;
  background-color: pink;
}

index.js

// 引入样式资源
import './index.css';
import './index.less';

index.less

#title {
  color: #fff;
}

webpack.config.js

/*
  webpack.config.js  webpack的配置文件
    作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

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

// resolve用来拼接绝对路径的方法
const { resolve } = require('path');

module.exports = {
  // webpack配置
  // 入口起点
  entry: './src/index.js',
  // 输出
  output: {
    // 输出文件名
    filename: 'built.js',
    // 输出路径
    // __dirname   为node.js的变量,代表当前文件的目录绝对路径,即代表“03.打包样式资源”这个路径,
    //后面的“build”表示这个目录下的build路径
    path: resolve(__dirname, 'build')
  },
  // loader的配置
  module: {
    rules: [
      // 详细loader配置
      // 不同文件必须配置不同loader处理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader进行处理
        use: [
          // 注意,use数组中loader执行顺序:从右到左,从下到上 依次执行
          // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
           // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          // // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
          'css-loader',
          // 将less文件编译成css文件,需要下载 less-loader和less
          'less-loader'
        ]
      }
    ]
  },
  // plugins的配置
  plugins: [
    // 详细plugins的配置
  ],
  // 模式
  mode: 'development', // 开发模式
  // mode: 'production'  生产模式
}

2.为了防止我们每新建一个案例就导一次包,我们可以利用node找包找不到,去上一级找的特点直接给整个项目导入包,我们输入

npm i webpack webpack-cli -D

再输入

npm i css-loader style-loader less less-loader -D       

这里下载了4个包,css-loaderstyle-loader为css样式打包所要用到的,还有less(这里less不下载也可以)和less-loader为将less文件转换为css所需要的

3.之后输入webpack或者webpack ./src/index.js -o ./build/built.js --mode=development进行打包

webpack的学习_第12张图片

4.新建index.html文件引入打包好的js文件,查看效果是否实现

index.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>
<script src="./build/built.js">script>
<body>
    <h1 id="title">111h1>
body>
html>

webpack的学习_第13张图片

2.打包html资源

1.对新建的项目(打包html)进行npm init,配置好信息后自动生成package.json、package-lock.json 和node-modules这三个文件,之后新建index.js打包入口文件、webpack.config.js配置文件,

webpack的学习_第14张图片

index.js

function add(x, y) {
  return x + y;
}

console.log(add(2, 3));

webpack.config.js

/*
  loader: 1. 下载   2. 使用(配置loader)
  plugins: 1. 下载  2. 引入  3. 使用
*/
const { resolve } = require('path');
// 引入下载的 html-webpack-plugin 这个插件
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
    ]
  },
  plugins: [
    // plugins的配置,打包html需要 html-webpack-plugin 这个包
    /* new HtmlWebpackPlugin()功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS),
    所以我们可以在里面加入 template: './src/index.html' 这样就会将我们的html引入它创建出来的html当中
    */
    // 需求:需要有结构的HTML文件
    new HtmlWebpackPlugin({
      // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

2.然后把这个案例右键在终端打开下载html-webpack-plugin这个包,输入

npm i html-webpack-plugin -D   

3.再输入webpack进行打包

3.打包图片资源

1.对新建的项目(打包图片资源)进行npm init,配置好信息后自动生成package.json、package-lock.json 和node-modules这三个文件,之后新建index.js打包入口文件、webpack.config.js配置文件、index.lessindex.html

index.js

import './index.less';

index.less

#box1{
  width: 100px;
  height: 100px;
  background-image: url('./vue.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

#box2{
  width: 200px;
  height: 200px;
  background-image: url('./react.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

#box3{
  width: 300px;
  height: 300px;
  background-image: url('./angular.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

index.html

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>
<body>
  <div id="box1">div>
  <div id="box2">div>
  <div id="box3">div>
  <img src="./angular.jpg" alt="angular">
body>
html>

webpack.config.js

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 问题:默认处理不了html中img图片
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 使用一个loader
        // 下载 url-loader和file-loader,因为url-loader依赖file-loader
        loader: 'url-loader',
        options: {
          // 图片大小小于8kb,就会被base64处理,这里的数字随便自己写
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024,
          // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
          // 解析时会出问题:[object Module],即在html中的引入的图片src值会
          // 变为angular
            
          // 解决:关闭url-loader的es6模块化,使用commonjs解析,新版webpack已解决这个问题
          esModule: false,
// 给图片进行重命名,如果不重命名的话图片默认使用生成的哈希值进行命名,导致图片名字过长,影响加载速度
          // [hash:10]取图片的hash的前10位
          // [ext]取文件原来扩展名
          name: '[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

结构目录如下:注意vue.jpg图片由于大小小于8k,已被默认转换为base64的图片编码显示,

webpack的学习_第15张图片

2.右键在终端打开,输入

npm i style-loader css-loader less-loader url-loader file-loader html-loader -D

style-loadercss-loader为css样式表所需要的,

style-loadercss-loaderless-loader为less所需要的,

url-loader依赖file-loader,为less或css中导入background-image: url('./react.png');这个图片所需要的,

html-loader为html中引入图片所需要的

3.再输入webpack进行打包,打包好后bulit.js引入的html文件如下

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>
<body>
  <div id="box1">div>
  <div id="box2">div>
  <div id="box3">div>
  //src值为自定义重命名的哈希值的前十位+后缀名
  <img src="830bf3c820.jpg" alt="angular">
<script type="text/javascript" src="built.js">script>body>
html>

webpack的学习_第16张图片

4.打包其他资源(字体图标等等)

目录结构:

webpack的学习_第17张图片

index.js

// 引入 iconfont 样式文件
import './iconfont.css';

webpack.config.js

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

index.html

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>
<body>
  <span class="iconfont icon-icon-test">span>
  <span class="iconfont icon-icon-test2">span>
  <span class="iconfont icon-icon-test3">span>
  <span class="iconfont icon-icon-test1">span>
body>
html>

打包好后bulit.js引入的index.html,查看效果

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>
<body>
  <span class="iconfont icon-icon-test">span>
  <span class="iconfont icon-icon-test2">span>
  <span class="iconfont icon-icon-test3">span>
  <span class="iconfont icon-icon-test1">span>
<script type="text/javascript" src="built.js">script>body>
html>

image-20200625152131825

5.devServer

需要先下载webpack-dev-server这个包,输入npm i webpack-dev-server -D

启动devServer指令为:npx webpack-dev-server

停止devServer指令:快捷键ctrl+c

webpack.config.js

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',

  // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  // 特点:只会在内存中编译打包,不会有任何输出,即不会更新(改变)bulid打包文件夹中的文件,
  // 即使把bulid删掉也能照样显示
  // 启动devServer指令为:npx webpack-dev-server
  devServer: {
    // 项目构建后路径
    contentBase: resolve(__dirname, 'build'),
    // 启动gzip压缩
    compress: true,
    // 端口号,即在浏览器中输入http://localhost:3000/  即可访问
    port: 3000,
    // 自动打开浏览器 即输入npx webpack-dev-server后自动打开浏览器
    open: true
  }
};

6.开发环境基本配置

webpack.config.js

/*
  开发环境配置:能让代码运行
    运行项目指令:
      webpack 会将打包结果输出出去
      npx webpack-dev-server 只会在内存中编译打包,没有输出
*/

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入一个对象
module.exports = {
  // 入口
  entry: './src/js/index.js',
  // 输出
  output: {
    // 文件名
    filename: 'js/built.js',
    /* 
     输出的文件路径,一般是一个绝对路劲,而绝对路径一般需要引入一个node.js的核心模块path,
     path的方法叫resolve方法,用来处理绝对路径
     
     __dirname   为node.js的变量,代表当前文件的目录绝对路径,即代表“开发环境配置”这个路径,
     后面的“build”表示这个目录下的build路径
    */
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 处理less资源,less和css会一起输出到bulit.js文件当中
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 处理(css、less)图片资源
        test: /\.(jpg|png|gif)$/,
        // url-loader通过es6-modules解析 
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
// 关闭es6模块化,使用commonjs解析,这是为了防止模块不一样导致解析失败,新版webpack已解决这个问题
          esModule: false,
          // 将图片输出到imgs下
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中img资源,html-loader通过commonjs解析
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        // url-loader是在file-loader基础上做了详细的优化,多了一个limit的功能,
        // 即转为base64的图片格式,file-loader就是原封不动的将资源输出出去
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          //  // 将其他资源输出到media下
          outputPath: 'media'
        }
      }
    ]
  },
  // 插件,处理html资源需要插件,插件需要引入才能使用,
  // 即上方的const HtmlWebpackPlugin = require('html-webpack-plugin');
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      // 表示以某个文件为模板
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true
  }
};

webpack的学习_第18张图片

打包后引入bulit.js的index.html文件

DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>开发环境配置title>
head>

<body>
  <h1>开发环境配置h1>
  <span class="iconfont icon-icon-test">span>
  <span class="iconfont icon-icon-test2">span>
  <span class="iconfont icon-icon-test3">span>
  <span class="iconfont icon-icon-test1">span>
  <div id="box">div>
  <img src="data:image/jpeg;base64,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" alt="vue">
  <img src="imgs/13c51607b8.png" alt="react">
<script type="text/javascript" src="js/built.js">script>body>

html>

webpack的学习_第19张图片

三、webpack生产环境配置

1.提取css为单独文件:mini-css-extract-plugin

需要先下载插件进行从js中提取css出来,输入

npm i mini-css-extract-plugin -D

webpack.config.js

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
//引入提取css为一个文件的插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 创建style标签,将样式放入输出的html页面上
          // 'style-loader', 
          // 这个loader取代style-loader。作用:提取js中的css成单独文件
          MiniCssExtractPlugin.loader,
// 将css文件整合到js文件中,然后在通过下方的mini-css-extract-plugin插件处理成一个css文件
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      // 对输出的css文件进行重命名
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

输入webpack即可打包,

2.css兼容性处理:postcss

需先下载插件,能帮助postcss识别某些环境,从而加载指令的配置,让兼容器做到精确到某一个浏览器的版本

npm i postcss-loader postcss-preset-env -D	

webpack.config.js

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

/* 设置nodejs环境变量,这里是为了测试在最近的如下添加的最近的一个chrome、火狐、safari中的兼容性。
即在package.json中配置的开发环境(development)的适配
   如果不设置这个的话,默认是会使用生产模式的,跟下方的 mode: 'development'是没有关系的*/
/*
  设置使用开发环境打包,会将display: flex; backface-visibility: hidden;编译
  为display: flex;-webkit-backface-visibility: hidden;backface-visibility: hidden;
       
  因为我们这里在开发模式中设置的是兼容最近一个chrome、火狐、safari的最新版本,
  
  所以他不会将display: flex;编译
        
  不设置使用开发环境打包,默认使用生产模式打包,会将display: flex; backface-visibility: hidden;
  编译为 display: -webkit-box; display: flex;
  
  -webkit-backface-visibility: hidden;backface-visibility: hidden;这里就对display:flex做了兼容
*/ 
// process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          /*
            css兼容性处理:postcss --> postcss-loader postcss-preset-env

            下方的require('postcss-preset-env')()表示 帮postcss找到package.json中
            browserslist里面的配置,通过配置加载指定的css兼容性样式,
            所以还要在package.json文件中加入browserslist的配置,如下:

            "last 1 chrome version",表示兼容最新的一个Chrome的版本
            "browserslist": {
              // 开发环境 --> 设置node.js环境变量:process.env.NODE_ENV = development
              "development": [
                "last 1 chrome version",   
                "last 1 firefox version",
                "last 1 safari version"
              ],
              // 生产环境:默认是看生产环境 ">0.1%",表示大于99.9%的浏览器
               "not dead",不要已经死了的浏览器,比如ie10
                "not op_mini all"  表示不要op_mini的浏览器,因为他已经不存在了,不需要再兼容
              "production": [
                ">0.1%",
                "not dead",
                "not op_mini all"
              ]
            }
          */
          // 使用loader的默认配置,即直接'postcss-loader',
          // 修改loader的配置
          {
            loader: 'postcss-loader',
            // 在option里修改配置
            options: {
              // 固定写法
              ident: 'postcss',
			 
              plugins: () => [
                // postcss的插件,要在package.json中实现browserslist里面的配置
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
   mode: 'development'
  // mode:'production'
};

在package.json中配置browserslist

{
  "name": "text88",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.4",
    "css-loader": "^3.6.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.0.0",
    "eslint-loader": "^3.0.3",
    "eslint-plugin-import": "^2.20.1",
    "file-loader": "^5.0.2",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.11.3",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^1.2.1",
    "terser-webpack-plugin": "^2.3.5",
    "thread-loader": "^2.1.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0",
    "workbox-webpack-plugin": "^5.0.0"
  },
  "dependencies": {
    "jquery": "^3.4.1"
  },
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.1%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  "sideEffects": [
    "*.css"
  ],
  "description": ""
}

设置为开发模式(development)打包,即在package.config.js中加入node.js环境变量process.env.NODE_ENV = 'development';表示用开发模式打包,跟其下方的mode: 'development'没有关系,对display:flex没有做兼容,因为这里我们是设置开发模式是适配最近的一个Chrome、火狐、Safari的版本:

image-20200626105124928

设置为生产模式(production)打包后的css兼容,默认的,不用设置,其对display:flex做了兼容:

image-20200626105031352

3.压缩css

下载压缩css的插件:

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

然后在webpack.config.js中引入,然后通过new OptimizeCssAssetsWebpackPlugin()调用即可压缩

const { resolve } = require('path');
/*为html文件中引入的外部资源如script、link动态添加每次compile后的hash,
  防止引用缓存的外部文件问题
  可以生成创建html入口文件,比如单页面可以生成一个html文件入口,
  配置N个html-webpack-plugin可以生成N个页面入口
*/ 
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入将css从打包好的js文件中抽取成一个独立的css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 引入压缩css插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      /*根据自己的指定的模板文件来生成特定的 html 文件。这里的模板类型可以是任意你喜欢的模板,
        可以是 html, jade, ejs, hbs, 等等,
        但是要注意的是,使用自定义的模板文件时,需要提前安装对应的 loader, 否则webpack不能正确解析。
      */ 
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      // 指定提取出来的css文件
      filename: 'css/built.css'
    }),
    // 通过new调用,压缩css
    new OptimizeCssAssetsWebpackPlugin()
  ],
  mode: 'development'
};

4.js语法检查:eslint

下载插件,

npm i eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import -D

webpack.config.js

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        语法检查: 需要eslint-loader而eslint-loader又依赖于eslint
          注意:只检查自己写的源代码,第三方的库是不用检查的,所以加入下方的 
          exclude: /node_modules/,表示排除node_modules,不写的话会出现许多错误
          设置检查规则,一般用airbnb规则:
            package.json中eslintConfig中设置~
              "eslintConfig": {
                "extends": "airbnb-base"
              }
               
              其中"extends": "airbnb-base"表示继承下载的eslint-config-airbnb-base,
              会自动加载里面的配置进行检查
              
            airbnb规则需要这个插件 --> eslint-config-airbnb-base ,
            而又依赖于  eslint-plugin-import eslint 这两个插件,
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自动修复eslint的错误,例如function add(x, y) { return x + y;}中x,后面没有加空格
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

在package.json中配置eslintConfig

{
  "name": "text88",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.4",
    "css-loader": "^3.6.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.2.0",
    "eslint-loader": "^3.0.4",
    "eslint-plugin-import": "^2.21.2",
    "file-loader": "^5.0.2",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.11.3",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^1.2.1",
    "terser-webpack-plugin": "^2.3.5",
    "thread-loader": "^2.1.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0",
    "workbox-webpack-plugin": "^5.0.0"
  },
  "dependencies": {
    "jquery": "^3.4.1"
  },
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.1%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  "sideEffects": [
    "*.css"
  ],
  "description": ""
}

注意:

function add(x, y) {
  return x + y;
}

/* console.log(add(2, 5));在实际的项目上线中是不需要的,eslint会警告,如果想要忽略的直接加入
// eslint-disable-next-line就可以,表示下一行eslint所有规则都失效
  (下一行不进行eslint检查),注意就是下方注释的形式
*/

// eslint-disable-next-line
console.log(add(2, 5));

5.js兼容性处理:eslint

因为es6的语法IE浏览器是不支持的,所以我们提供三种方法

1.基本js兼容性处理 --> @babel/preset-env

下载插件:

npm i babel-loader @babel/preset-env @babel/core -D

然后webpack,只能转换基本语法(将const转换为var,将箭头函数转换为function)

2.全部js兼容性处理 --> @babel/polyfill

下载插件

npm i @babel/polyfill -D

然后在index.js中引入即可,因为不是babel插件,不用在webpack.config.js中引入

import '@babel/polyfill';
 
// const属于es6的关键字,将function改为箭头函数
const add = (x, y) => {
  return x + y;
};
console.log(add(2, 5));

const promise = new Promise(resolve => {
  setTimeout(() => {
    console.log('定时器执行完了~');
    resolve();
  }, 1000);
});

console.log(promise);

然后webpack打包,只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~

3.按需加载 --> core-js

下载插件

npm i core-js -D

index.js

// import '@babel/polyfill';   这个为全部js兼容,但是体积过于大,不推荐

// const属于es6的关键字,将function改为箭头函数
const add = (x, y) => {
  return x + y;
};
console.log(add(2, 5));

const promise = new Promise(resolve => {
  setTimeout(() => {
    console.log('定时器执行完了~');
    resolve();
  }, 1000);
});

console.log(promise);

webpack.config.js

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        js兼容性处理:babel-loader @babel/core 
          1. 基本js兼容性处理 --> @babel/preset-env
            问题:只能转换基本语法(将const转换为var,将箭头函数转换为function),
            如promise高级语法不能转换,需要下载@babel/preset-env babel-loader @babel/core 这三个插件
          2. 全部js兼容性处理 --> @babel/polyfill  
            问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
          3. 需要做兼容性处理的就做:按需加载  --> core-js
      */  
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          // 预设:指示babel做怎么样的兼容性处理,默认情况我们用 '@babel/preset-env'
          presets: [
            [
              '@babel/preset-env',
              // {}表示一个对象
              {
                // 按需加载
                useBuiltIns: 'usage',
                // 指定core-js版本
                corejs: {
                  version: 3
                },
                // 指定兼容性做到哪个版本浏览器
                targets: {
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17'
                }
              }
            ]
          ]
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

6.压缩html和js

webpack.config.js

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 压缩html代码,不需要压缩html的话,直接去掉minify即可
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    })
  ],
  // js压缩,生产环境下会自动压缩js代码
  mode: 'production'
};

7.生产环境基本配置

webpack.config.js

const { resolve } = require('path');
// 从bulit.js中提取css为一个单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 压缩css
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 对html资源进行处理
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
// process.env.NODE_ENV = 'development';
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  // 替换style-loader,把css样式不放在html中,提取出一个css文件
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    //  postcss-loader表示对css样式进行兼容性处理,
    loader: 'postcss-loader',
    // 通过options修改postcss-loader的配置
    options: {
      // 表示默认配置为postcss
      ident: 'postcss',
      /* 表示用postcss-preset-env这个插件里面默认的配置对postcss进行处理,
      记住这里还需要在package.json中定义browserslist,
      
      browserslist默认使用的是生产环境的配置,要想使用开发模式的配置还需要设置node.js的环境变量,
      即上方的process.env.NODE_ENV = 'production';
      */
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    // rules表示规则
    rules: [
      {
        test: /\.css$/,
        // 注意use数组默认执行顺序是从下往上执行的
        use: [...commonCssLoader]
      },
      {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      /*
        正常来讲,一个文件只能被一个loader处理。
        当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
          先执行eslint 在执行babel,因为eslint是做语法检查的,而babel-loader会将es6语法转换为es5语法,
          所以如果先经过语法转换在检查就会出现语法错误
          
          所以要优先处理的loader加上enforce: 'pre'
      */
      {
        
        test: /\.js$/,
        // 通过exclude属性排除第三方库、插件的检查
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        // 注意此次还要在package.json中配置eslintConfig --> 我们使用airbnb这个规则
        loader: 'eslint-loader',
        options: {
          // 自动修复一些错误
          fix: true
        }
      },
      {
        // 对js做兼容性处理
        test: /\.js$/,
        exclude: /node_modules/,
        // babel-loader会将es6语法转换为es5语法
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: {version: 3},
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },
      {
        // 对图片进行处理
        test: /\.(jpg|png|gif)/,
        loader: 'url-loader',
        options: {
          // 这里是为了优化性能,将大小小于8k的图片转换为base64的图片编码格式,
          // 同时对文件的名字进行处理, outputPath: 'imgs',表示输出的路径
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
          // 使用commjs进行解析
          esModule: false
        }
      },
      {
        // 处理html中的图片问题
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他文件,通过排除法
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        // file-loader表示原封不动的输出文件资源,而url-loader是他的改进版,可以对资源进行改进
        loader: 'file-loader',
        options: {
          // 输出目录
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      // 以指定的html模板创建新的文件
      template: './src/index.html',
      minify: {
        // 对html进行压缩
        collapseWhitespace: true,
        // 去除html的注释
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

package.json

{
  "name": "text88",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.4",
    "css-loader": "^3.6.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.2.0",
    "eslint-loader": "^3.0.4",
    "eslint-plugin-import": "^2.21.2",
    "file-loader": "^5.0.2",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.11.3",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^1.2.1",
    "terser-webpack-plugin": "^2.3.5",
    "thread-loader": "^2.1.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0",
    "workbox-webpack-plugin": "^5.0.0"
  },
  "dependencies": {
    "jquery": "^3.4.1"
  },
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.1%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  "sideEffects": [
    "*.css"
  ],
  "description": ""
}

四、webpack性能优化

* 开发环境性能优化

​ * 优化打包构建速度

​ * HMR

​ * 优化代码调试

​ * source-map

* 生产环境性能优化

* 优化打包构建速度 * oneOf

* babel缓存 * 多进程打包

* externals * dll

* 优化代码运行的性能 * 缓存(hash-chunkhash-contenthash)

* tree shaking * code split

* 懒加载/预加载 * pwa

1.HMR:热模块替换

/*
  HMR: hot module replacement 热模块替换 / 模块热替换
    作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度

      开启HMR:直接在webpack.config.js中加入 hot: true 即可开启,
             但是注意当修改了webpack配置,新配置要想生效,必须重启webpack服务
      在终端按 ctrl+c,输入指令 y 终止webpack,然后输入npx webpack-dev-server
      
      样式文件:可以使用HMR功能:因为style-loader内部实现了~

      js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
        即在打包入口的js文件中加入如下代码:
          if (module.hot) {
            // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
            module.hot.accept('./print.js', function() {
              // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
              // 会执行后面的回调函数
              print();
            });
          }

        注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。因为入口文件会将其他文件引入,
        一旦入口文件发生变化,又会将其他文件引入重新加载

      html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ 
      (不用做HMR功能,因为只有一个文件,当他刷新时整个页面都会刷新)
        解决:修改entry入口,将html文件引入,
*/

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

module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    // 开启HMR功能
    // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
    hot: true
  }
};

例如:

webpack的学习_第20张图片

2.source-map:追踪源代码错误

/*
  source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

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

    开启方式:在webpack.config.js中加入   devtool:上方的组合值

    source-map:外部
      错误代码准确信息 和 源代码的错误位置
    inline-source-map:内联,即生成在bulit.js当中
      只生成一个内联source-map
      错误代码准确信息 和 源代码的错误位置
    hidden-source-map:外部,即生成了一个文件,bulit.js.map
      错误代码错误原因,但是没有错误位置
      不能追踪源代码错误,只能提示到构建后代码的错误位置
    eval-source-map:内联
      每一个文件都生成对应的source-map,都在eval
      错误代码准确信息 和 源代码的错误位置,提示为哈希值
    nosources-source-map:外部
      错误代码准确信息, 但是没有任何源代码信息
    cheap-source-map:外部
      错误代码准确信息 和 源代码的错误位置 
      只能精确到行
    cheap-module-source-map:外部
      错误代码准确信息 和 源代码的错误位置 
      module会将loader的source map加入

    内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

    开发环境:速度快,调试更友好
      速度快(eval>inline>cheap>...)
        eval-cheap-souce-map
        eval-source-map
      调试更友好  
        souce-map
        cheap-module-souce-map
        cheap-souce-map

      --> 推荐 eval-source-map  或 eval-cheap-module-souce-map  ,前者调试更友好,后者性能更好

    生产环境:源代码要不要隐藏? 调试要不要更友好
      内联会让代码体积变大,所以在生产环境不用内联
      nosources-source-map 全部隐藏
      hidden-source-map 只隐藏源代码,会提示构建后代码错误信息(即webpack打包后)

      --> source-map / cheap-module-souce-map
*/

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

module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true
  },
  devtool: 'eval-source-map'
};

外部的样式如下:

webpack的学习_第21张图片

内联的样式如下:

webpack的学习_第22张图片

追踪到源代码的举例:

webpack的学习_第23张图片

3.oneOf:提升构建速度

/*
	主要提示构建速度,如果不处理的话,正常情况下一个文件都要被多个loader过一遍
*/
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件,例如不能用两个loader处理js文件,
        // 要把其中的一个放出来,如上面所示,由上方的eslint-loader执行再由oneOf里面的babel-loader执行
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: {version: 3},
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

4.缓存

webpack.config.js

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  缓存:在代码上线时,对资源都要做一个缓存处理,即让用户第二次访问网站的时候不需要太长时间,
  速度会变快,但是也会带来一些问题,如果代码产生变化,但浏览器上缓存的资源在强制缓存期间不会访问服务器,
  直接读取本地的缓存,所以我们可以给静态资源(js、css)加一个版本号,我们更新资源后,
  静态资源的名称改变了,那么这个资源就会更新,我们采取的方法是加上一个哈希值,即下方的
  filename: 'js/built.[contenthash:10].js',
    
    babel缓存
      cacheDirectory: true
      --> 让第二次打包构建速度更快
    
    文件资源缓存:我们推荐使用contenthash,
      hash: 每次wepack构建时会生成一个唯一的hash值。
        问题: 因为js和css同时使用一个hash值。
          如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
      
      chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
        问题: js和css的hash值还是一样的
          因为css是在js中被引入的,所以同属于一个chunk
      
      contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样    
      
 --> 让代码上线运行缓存更好使用
*/

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

服务器代码server.js

/*
  服务器代码
  启动服务器指令:
  先安装:
    npm i nodemon -g
  然后输入:
    nodemon server.js
  或者直接:
    node server.js
  访问服务器地址:
    http://localhost:3000

*/
const express = require('express');

const app = express();
// express.static向外暴露静态资源
// maxAge 资源缓存的最大时间,单位ms,maxAge: 1000 * 3600表示一个小时,
// 表示在浏览器中的静态资源会被强制缓存一个小时
app.use(express.static('build', { maxAge: 1000 * 3600 }));

// 启动服务器,3000表示端口号
app.listen(3000);

index.js

import '../css/index.css';

// es6语法,用args的reduce去统计求和的方法,0表示初始化为0
function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// 下面的这个 // eslint-disable-next-line  的意思是忽略下一行的警告,
// 因为在项目上线中,console是不需要的,会影响性能

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

如:

webpack的学习_第24张图片

5.tree shaking:去除无用代码

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  tree shaking:去除无用代码
    前提:1. 必须使用ES6模块化  2. 开启production环境
    作用: 减少代码体积

    在package.json中配置 
      "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
        问题:可能会把css / @babel/polyfill (副作用)文件干掉,
        我们可以在package.json中加入下面一行代码:
            "sideEffects": ["*.css", "*.less"]
*/

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

package.json

{
  "name": "text88",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.4",
    "css-loader": "^3.6.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.2.0",
    "eslint-loader": "^3.0.4",
    "eslint-plugin-import": "^2.21.2",
    "file-loader": "^5.0.2",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.11.3",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^1.2.1",
    "terser-webpack-plugin": "^2.3.5",
    "thread-loader": "^2.1.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0",
    "workbox-webpack-plugin": "^5.0.0"
  },
  "dependencies": {
    "jquery": "^3.4.1"
  },
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.1%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  "sideEffects": [
    "*.css"
  ],
  "description": ""
}

test.js

export function mul(x, y) {
  return x * y;
}

export function count(x, y) {
  return x - y;
}

index.js

//只引入test.js中的mul函数
import { mul } from './test';

import '../css/index.css';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

6.code split:代码分割

共三种方案,我们一般采用第三种方案:

一、

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

module.exports = {
  // 单入口
  // entry: './src/js/index.js',
  entry: {
    // 多入口:有几个入口,最终输出就有几个bundle
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

二、

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

module.exports = {
  // 单入口
  // entry: './src/js/index.js',
  entry: {
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  /*
    1. 可以将node_modules中代码单独打包一个chunk最终输出
    2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
  */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

三、

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

module.exports = {
  // 单入口
  entry: './src/js/index.js',
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  /*
    1. 可以将node_modules中代码单独打包一个chunk最终输出
    2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
  */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

我们其他文件也希望打包成一个单独的chunk,可以通过书写js代码的形式实现

index.js

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

/*
  通过js代码,让某个文件被单独打包成一个chunk
  import动态导入语法:能将某个文件单独打包
*/
//下面的 /* webpackChunkName: 'test' */ 意思是文件的名字,如果不加的话,会根据id的名字取命名, 

import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) => {
    // 文件加载成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    // eslint-disable-next-line
    console.log('文件加载失败~');
  });

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));
我们没有加入/* webpackChunkName: 'test' */之前

webpack的学习_第25张图片

通过加入/* webpackChunkName: 'test' */后

webpack的学习_第26张图片

7.懒加载(lazy loading)和预加载

test.js

console.log('test.js文件被加载了~');

// export 表示暴露出来
export function mul(x, y) {
  return x * y;
}

export function count(x, y) {
  return x - y;
}

index.js

console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 懒加载~:当文件需要使用时才加载~
  // 预加载 prefetch:会在使用之前,提前加载js文件 
  // 加入这个即代表开启了预加载:webpackPrefetch: true
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源,
  // 但是兼容性较差,只能在高版本的浏览器中使用
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

index.html

DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>

<body>
  <h1>hello lazy loadingh1>
  <button id="btn">按钮button>
body>

html>

懒加载:

未点击:

webpack的学习_第27张图片

点击后:

webpack的学习_第28张图片

预加载:

webpack的学习_第29张图片

8.PWA:渐进式网络开发应用程序

webpack.config.js

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

/*
  PWA: 渐进式网络开发应用程序(没有网络也可离线访问,但是一些资源会加载失败,如图片),
  淘宝网页采用了这个技术
    我们通过 workbox 使用 -->下载 workbox-webpack-plugin  ,下载完之后引入,
    如上面已经引入,然后在下方的插件里面使用
*/

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker

        生成一个 serviceworker 配置文件,然后我们通过这个配置文件去注册serviceworker,
        我们一般在入口文件中进行配置,此处为index.js
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

index.js

import { mul } from './test';
import '../css/index.css';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

/*
  1. eslint不认识 window、navigator全局变量
    解决:需要修改package.json中eslintConfig配置
      "env": {
        "browser": true // 支持浏览器端全局变量
      }
   2. serviceWorker代码必须运行在服务器上
      -->一是通过 nodejs
      
      -->二是通过下载包
        npm i serve -g
        然后输入 serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
*/
// 注册serviceWorker,因为有兼容性问题,还要处理兼容性问题
// 判断serviceWorker是否在navigator属性中,
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    // 绑定事件,等全部资源加载完成去做serviceworker注册
    navigator.serviceWorker
      .register('/service-worker.js')
      // 通过then和catch方法监听是否注册成功
      .then(() => {
        console.log('sw注册成功了~');
      })
      .catch(() => {
        console.log('sw注册失败了~');
      });
  });
}

index.html

DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpacktitle>
head>

<body>
  <h1>hello cacheh1>
body>

html>

输入命令serve -s build后

webpack的学习_第30张图片

我们进入这个地址

webpack的学习_第31张图片

然后点击Application下的

webpack的学习_第32张图片

我们查看注册service Workers 成功缓存的数据

webpack的学习_第33张图片

最后

webpack的学习_第34张图片

9.多进程打包

下载:

npm i thread-loader -D

webpack.config.js

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            // 使用多个loader要用数组
            use: [
              /* 
                开启多进程打包。 
                进程启动大概为600ms,进程通信也有开销。
                只有工作消耗时间比较长,才需要多进程打包
              */
              {
                loader: 'thread-loader',
                options: {
                  workers: 2 // 进程2个
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  presets: [
                    [
                      '@babel/preset-env',
                      {
                        useBuiltIns: 'usage',
                        corejs: { version: 3 },
                        targets: {
                          chrome: '60',
                          firefox: '50'
                        }
                      }
                    ]
                  ],
                  // 开启babel缓存
                  // 第二次构建时,会读取之前的缓存
                  cacheDirectory: true
                }
              }
            ]
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker

        生成一个 serviceworker 配置文件~
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

10.externals:拒绝第三方库打包

通过externals拒绝将第三方库打包(如果我们在html中通过cdn导入),然后我们通过script的形式将其在html页面中引入

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'production',
  externals: {
    // 拒绝jQuery被打包进来
    jquery: 'jQuery'
  }
};

index.js

import $ from 'jquery';

console.log($);

11.dll:对第三方库单独打包

webpack.dll.js

/*
  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
   
    当你运行 webpack 命令时,默认查找 webpack.config.js 配置文件
      需求:需要运行 webpack.dll.js 文件(此处的文件可以任意命名)
        --> webpack --config webpack.dll.js
*/

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

module.exports = {
  entry: {
    // 最终打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的库是jquery
    jquery: ['jquery'],
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  },
  plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射库的暴露的内容名称
      // 输出文件路径,这个文件会建立起和上方输出的jQuery的映射关系
      path: resolve(__dirname, 'dll/manifest.json') 
    })
  ],
  mode: 'production'
};

webpack.config.js

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
// 引入下载的add-asset-html-webpack-plugin,将某个文件打包输出去,
// 并在html中自动引入该资源,就不用手动引入了,
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    // 告诉webpack哪些库不参与打包,同时使用时的名称也得变,因为manifest.json和
    // 输出的jQuery建立起了映射关系,所以此时webpack不会把jQuery打包到bulit.js中
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源,就不用手动引入了
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],
  mode: 'production'
};

index.js

// 这样默认会打包jQuery文件,但是我们设置了不打包jQuery,所以此处不打包
import $ from 'jquery';

console.log($);

webpack的学习_第35张图片

五、webpack配置详解

1.entry

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

/*
  entry: 入口起点,我们一般用第一个和第三个
    1. string --> './src/index.js'
      单入口
      打包形成一个chunk。 输出一个bundle文件。
      此时chunk的名称默认是 main
    2. array  --> ['./src/index.js', './src/add.js']
      多入口
      所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
        --> 一般不用,只用在HMR功能中让html热更新生效~
    3. object
      多入口
      有几个入口文件就形成几个chunk,输出几个bundle文件
      此时chunk的名称是 key

      --> 特殊用法
        {
          // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。一般用于dll
          index: ['./src/index.js', './src/count.js'], 
          // 形成一个chunk,输出一个bundle文件。
          add: './src/add.js'
        }

   module是指任意的文件模块,等价于commonjs中的模块
   chunks是webpack处理过程中被分组了的modules,如代码分割时一个异步加载的chunk可能包含多个module
   Bunldes是指打包出来的
*/

module.exports = {
  entry: {
    index: ['./src/index.js', './src/count.js'], 
    add: './src/add.js'
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

2.output

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

module.exports = {
  entry: './src/index.js',
  output: {
    // 文件名称(指定名称+目录)
    filename: 'js/[name].js',
    // 输出文件目录(将来所有资源输出的公共目录)
    path: resolve(__dirname, 'build'),
    // 所有资源引入公共路径前缀,一般用于生产环境 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
    publicPath: '/',
    // 对非入口chunk的名称进行重命名,不命名的话webpack默认给他命名为一个id值,
    // 从0开始,打包入口的chunk默认为main  
    chunkFilename: 'js/[name]_chunk.js',
    // library: '[name]', // 整个库向外暴露的变量名,通常结合dll使用
    // libraryTarget: 'window' // 变量名添加到哪个上 browser
    // libraryTarget: 'global' // 变量名添加到哪个上 node
    // libraryTarget: 'commonjs'
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

3.module

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    // 在rules里写loader的配置
    rules: [
      // loader的配置
      {
        // 指定检查某种类型的文件
        test: /\.css$/,
        // 多个loader用use
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.js$/,
        // 排除node_modules下的js文件
        exclude: /node_modules/,
        // 只检查 src 下的js文件
        include: resolve(__dirname, 'src'),
        // 优先执行
        enforce: 'pre',
        // 延后执行
        // enforce: 'post',
        // 单个loader用loader,还可以在下方加个options指定某些配置选项
        loader: 'eslint-loader',
        options: {}
      },
      {
        // 以下配置只会生效一个
        oneOf: []
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

4.resolve

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  // 解析模块的规则
  resolve: {
    // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路径的后缀名,但是要注意命名不能相同,例如index1.js和index1.css两个命名相同的话,
    // 它默认是按前到后的顺序解析,即它找到index1.css后就不会再去找后面的index1.css
    extensions: ['.js', '.json', '.jsx', '.css'],

    //加入以上两个配置后在index.js中引入index.css为import '$css/index';原先为../css/index.css

    // 告诉 webpack 解析模块是去找哪个目录,
    //  modules: [resolve(__dirname, '../../node_modules')]

    // 这里后面再加上一个 'node_modules' 是为了防止前面找不到(写错),
    //因为webpack当前目录找不到默认会一级一级的往上找
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  }
};

5.devServer

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  resolve: {
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  // devServer用于开发环境
  devServer: {
    // 运行代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload(重载)
    watchContentBase: true,
    watchOptions: {
      // 忽略node_modules文件
      ignored: /node_modules/
    },
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 自动打开浏览器
    open: true,
    // 开启HMR功能
    hot: true,
    // 不要显示启动服务器日志信息
    clientLogLevel: 'none',
    // 除了一些基本启动信息以外,其他内容都不要显示
    quiet: true,
    // 如果出错了,不要全屏提示~
    overlay: false,
    // 服务器代理 --> 解决开发环境跨域问题(浏览器和服务器之间存在跨域问题,比如域名、
    // 协议、端口号不一样就会产生跨域,服务器与服务器之间没有跨域)
      
    // 代码通过代理服务器运行,浏览器和代理服务器之间没有跨域问题,浏览器把请求发送到代理服务器上,
    // 代理服务器代替转发到另一个服务器上,因为服务器与服务器之间没有跨域问题,所以请求成功,
    // 代理服务器再把接收到的响应响应给浏览器,从而解决开发环境下的跨域问题
    proxy: {
// 一旦devServer(5000,上面设置的)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
      '/api': {
        target: 'http://localhost:3000',
        // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

6.optimization

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

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build'),
    chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'production',
  resolve: {
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  optimization: {
    // splitChunks用来做代码分割
    splitChunks: {
      chunks: 'all'
      // 默认值,可以不写~
      /* minSize: 30 * 1024, // 分割的chunk最小为30kb,即小于30kb就不分割
      maxSiza: 0, // 最大没有限制
      minChunks: 1, // 要提取的chunk最少被引用1次
      maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 3, // 入口js文件最大并行请求数量
      automaticNameDelimiter: '~', // 名称连接符
      name: true, // 可以使用命名规则
      cacheGroups: {
        // 分割chunk的组
        // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
        // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10
        },
        default: {
          // 要提取的chunk最少被引用2次
          minChunks: 2,
          // 优先级
          priority: -20,
          // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
          reuseExistingChunk: true
        } 
      }*/
    },
    // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
    // 解决:修改a文件导致b文件的contenthash变化,即当a.js变化时打包的main.js(主文件)也会变化,
    // 因为main.js要引入a.js文件,为了引入文件把哈希值也引入进来了
    // 当改动a.js后a.js的哈希值变化了就会导致main.js的哈希值也会改变就会导致有两个main.js,就会导致缓存失效
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    minimizer: [
      // 配置生产环境的压缩方案:js和css
      new TerserWebpackPlugin({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  }
};

webpack的学习_第36张图片

a.js

export function add(x, y) {
  return x + y;
}

index.js

// 引入a.js成功的话就提取add方法打印在控制台,/* webpackChunkName: 'a' */ 
// 表示对提取的文件进行重命名,用这个语法会单独打包为一个js文件
import(/* webpackChunkName: 'a' */'./a.js').then(({ add }) => {
  console.log(add(1, 2));
});
改变a.js后

webpack的学习_第37张图片

解决,

webpack的学习_第38张图片

六、webpack5

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'build')
  },
  mode: 'production'
};
//webpack5
module.exports = {
  mode: 'production'
};

此版本重点关注以下内容:

  • 通过持久缓存提高构建性能.
  • 使用更好的算法和默认值来改善长期缓存.
  • 通过更好的树摇和代码生成来改善捆绑包大小.
  • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

1.下载

  • npm i webpack@next webpack-cli -D

自动删除 Node.js Polyfills

早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。

尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。

webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。

迁移:

  • 尽可能尝试使用与前端兼容的模块。
  • 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。

2.Chunk 和模块 ID

添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

chunkIds: "deterministic", moduleIds: "deterministic"

3.Chunk ID

你可以不用使用 import(/* webpackChunkName: "name" */ "module") 在开发环境来为 chunk 命名,生产环境还是有必要的

webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了

4.Tree Shaking

  1. webpack 现在能够处理对嵌套模块的 tree shaking
// inner.js
export const a = 1;
export const b = 2;

// module.js
import * as inner from './inner';
export { inner };

// user.js
import * as module from './module';
console.log(module.inner.a);

在生产环境中, inner 模块暴露的 b 会被删除

  1. webpack 现在能够多个模块之前的关系
import { something } from './something';

function usingSomething() {
  return something;
}

export function test() {
  return usingSomething();
}

当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"

  1. webpack 现在能处理对 Commonjs 的 tree shaking

5.Output

webpack 4 默认只能输出 ES5 代码

webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

如:output.ecmaVersion: 2015

6.SplitChunk

// webpack4
minSize: 30000;
// webpack5
minSize: {
  javascript: 30000,
  style: 50000,
}

7.Caching

// 配置缓存
cache: {
  // 磁盘存储
  type: "filesystem",
  buildDependencies: {
    // 当配置修改时,缓存失效
    config: [__filename]
  }
}

缓存将存储到 node_modules/.cache/webpack

8.监视输出文件

之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

9.默认值

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

10.更多内容

https://github.com/webpack/changelog-v5

源码获取

至此,我们的webpack的学习就讲解完成了,源码素材可以通过关注我的微信公众号 我爱学习呀嘻嘻 ,回复关键字webpack源码素材进行获取哦。

image-20211108230322493

你可能感兴趣的:(webpack,javascript,前端)