前端学习篇 -- Webpack 学习

第一章 Webpack 简介

1.1 Webpack 是什么

  • 是一种前端资源构建工具,一个静态模块打包器(nodule bundle)
  • 前端所有资源文件(js/json/css/img…)都会作为模块处理
  • 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)

1.2 Webpack 打包流程

index.js
//引入 js 资源
>>import $ from 'jquery'
//引入样式资源
>>import './index.less'

1.首先告诉 Webpack 一个入口文件,如 index.js 为起点作为打包

2.将里面的所有依赖项引入进来,这些依赖会跟入口文件形成一个文件(代码块 chunk) ,再将这个代码块进行处理,比如把 less 文件编译成 css,js 资源编译成浏览器能识别的 js 语法等等操作,这些就叫做打包

3.将打包好的资源再输出出去,这个文件叫 bundle

简而言之,就是将静态资源(模块)进行处理(打包),所以就是静态模块打包器

1.3 Webpack 五个核心概念

  1. Entry

    入口(Entry)指示 Webpack 以哪个文件为入口起点开始打包,分析内部构件依赖图

  2. Output

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

  3. Loader

    Loader 能让 Webpack 处理非 JavaScript/json 文件(Webpack 自身只理解 JavaScript/json )

  4. Plugins

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

  5. Mode

    模式(Mode)指示 Webpack 使用相应模式的配置

    选项 描述 特点
    development(开发模式) 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin 能让代码本地调试运行的环境
    production (生产模式) 能让代码优化上线运行的环境

第二章 Webpack 初体验

  • src 文件夹 --项目源文件目录
  • build文件夹 --打包后文件输出目录
  • src 》 index.js --入口文件

运行指令

  1. 开发环境:webpack ./src/index.js -o ./build/build.js --mode=development
    • webpack 会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/build.js,打包环境是开发环境
    • 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成浏览器能识别的语法
  2. 生产环境:webpack ./src/index.js -o ./build/build.js --mode=production
    • 功能:在开发配置功能上多一个功能,压缩代码

重写运行指令

  • 执行 webpack 命令默认是找 webpack.config.js 文件

  • 执行其他文件进行打包

    • webpack --config 文件名

    • 或者在 package.json 文件里配置一项命令

      "scripts": {
        "build": "webpack --config ./base.config.js"
      }
      

      这样执行 npm run build就等于指定当前目录的 base.config.js

实例

index.js >>
function add(x, y) {
    return x + y;
}
console.log(add(1, 2));

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

index.html >>
<script src="../build/build.js"></script>
</body>
//3

结论

  • webpack 能够编译打包 js 和 json 文件

  • 能将 es6 的模块化语法转换成浏览器能识别的语法

  • 能压缩代码

第三章 打包其他资源

webpack.config.js --webpack 配置文件

  • 作用:指示 webpack 干哪些活(当运行 webpack 指令时,默认加载里面的配置)
  • 所有构建工具都是基于 node.js 平台运行,模块化默认采用 CommonJS

基本配置

const path = require('path');
//配置
module.exports = {
    //1.入口
    entry: path.join(__dirname,'src/index.js'),
    //2.输出
    output: {
        //输出文件名
        filename: 'build.js',
        //输出路径,使用绝对路径
        path: path.join(__dirname,'build')
    },
    //3.loader 配置
    module: {
        rules: [
            //详细的 loader 配置
        ],
    },
    //4.plugins 配置
    plugins:  [
        //详细的 plugins 配置
    ],
    //5.模式
    mode: 'development', //开发模式
    //mode: 'production' //生产模式
}

3.1 打包样式资源

需要安装 css-loader、style-loader 包,打包 less 还需安装 less 和 less-loader 包

// webpack.config.js
const path = require('path');

module.exports = {
    
    
  entry: path.join(__dirname,'src/index.js'),
  output: {
    
    
    filename: 'build.js',
    path: path.join(__dirname,'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        //匹配以 .css 结尾的文件
        test: /\.css$/,
        //使用哪些 loader 进行处理,use 数组中执行顺序从下到上依次执行
        use: [
          //创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
          'style-loader',
          //将 css 文件变成 CommonJS 模块加载 js 中,里面内容是样式字符串
          'css-loader'
        ]
      },
      {
    
    
        //匹配以 .less 结尾的文件
        test: /\.less$/,
        use: [
          'style-loader',
          //将 less 文件编译成 css 文件
          'css-loader',
          //需要安装 less-loader 和 less 包
          'less-loader'
        ]
      }
    ]
  },
  mode: 'development'
}

// index.js 
import './index.css'
import './index.less'

// index.html
<h1 id="title">hello less</h1>
<script src="../build/build.js"></script>

命令:webpack

3.2 打包 html 资源

需要安装 html-webpack-plugin 插件,配置 plugins

const path = require('path');
// 引入 html-webpack-plugin 插件
const HtmlPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  // 入口文件
  entry: path.join(__dirname, 'src/index.js'),
  // 输出
  output: {
    
    
    filename: 'build.js',
    path: path.join(__dirname, 'build')
  },
  plugins: [
    // 功能:默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS) 
    // 需求:需要有结构的 HTML 文件 
    new HtmlPlugin({
    
    
      // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS) 
      template: path.join(__dirname,'./src/index.html')
    })
  ],
  mode: 'development'
}
  • build 文件夹下会生成 build.js 和 index.html 文件

  • index.html 文件会自动引入 build.js

3.3 打包 img资源

需要安装 url-loader 和 file-loader 包(处理样式中的 img),处理 html 中的 img 还需要安装 html-loader 包

  • 小于 limit 的值使用 url-loader 处理
  • 大于 limit 的值使用 file-loader 处理
const path = require('path');
const HtmlPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  entry: path.join(__dirname, 'src/index.js'),
  output: {
    
    
    filename: 'build.js',
    path: path.join(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      // 处理 less 资源
      {
    
    
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader'
        ]
      },
      // 处理图片资源
      {
    
    
        // 问题:默认处理不了 html 中 img 图片,现在只能处理 less 中的 img
        test: /\.(jpg|png|gif)$/,
        
        loader: 'url-loader',
        options: {
    
    
          // 图片大小小于 8kb,就会被 base64 处理
          // 优点: 减少请求数量(减轻服务器压力) 
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024,
          
          // 问题:因为 url-loader 默认使用 es6 模块化解析,而 html-loader 引入图片是 commonjs 
          // 解析时会出问题:[object Module]
          // 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
          esModule: false,
          
          // 给图片进行重命名,[hash:10]取图片的 hash 的前 10 位,[ext]取文件原来扩展名
          name: '[hash:10].[ext]'
        }
      },
      // 处理 html 文件中的 img
      {
    
    
        test: /\.html$/,
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlPlugin({
    
    
      template: path.join(__dirname, 'src/index.html')
    })
  ],
  mode: 'development'
}

3.4 打包其他资源

除了css/js/html/less 资源,依赖 file-loader 打包

const path = require('path');
const HtmlPlugin = require('html-webpack-plugin');

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

3.5 清空 dist 目录

重新打包自动清空 dist 目录

npm install clean-webpack-plugin --save-dev

// 导入插件
const {
    
     CleanWebpackPlugin } = require('clean-webpack-plugin');
// 配置插件
module.exports = {
    
    
  plugins: [
    new CleanWebpackPlugin()
  ]
}

3.6 打包vue资源

npm install -D vue-loader vue-template-compiler

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
    
    
  module: {
    
    
    rules: [
      // ... 其它规则
      {
    
    
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}

第四章 devServer

用于开发环境

  • 自动编译,自动打开浏览器,自动刷新浏览器
  • 安装 webpack-dev-server 包
  • 启动命令: npx webpack-dev-server

配置:

devServer: {
    
    
  //运行代码目录(项目构建后路径)
  contentBase: path.join(__dirname,'build'),

  //监视 contentBase 目录下所有文件,一旦文件发生变化就 刷新
  watchContentBase: true,

  //改变文件页面实时刷新
  inline: true,

  //忽略监视 node_modules 目录下文件
  watchOptions: {
    
    
    ignored: /node_modules/
  }

  //启动gzip压缩
  compress:true,

  //域名,默认就是localhost
  host: 'localhost', 

  //端口号 5000
  port: 5000,

  //自动打开浏览器
  open:true,

  // 开启HMR功能
  hot: true,

  //不显示启动服务器日志信息
  clientLogLevel: 'none',

  //除了一些基本启动信息,其他内容都不要显示
  quiet: true,

  //如果出错,不要全屏提示
  overlay: false,

  //服务器代理,解决开发环境跨域问题
  proxy: {
    
    
    '/api': {
    
    
      target: 'http://localhost:3000',
      pathRewrite: {
    
    
        '/api': ''
      },
      changeOrigin: true,      
      secure: false 
    }
    /**
     * 参数说明: 
     * '/api': 如果浏览器向服务器发送'/api'标志的请求,则会转发到代理服务器上,然后由代理服务器去请求
     * target: 代理的IP地址(目标服务器)
     * pathRewrite: 路径重写,设置 '/api': '' 后,会去掉 /api
     * changeOrigin: target 参数是域名的话,需要设置为true
     * secure: 不检查安全问题,协议是https的话,需设置为false
     */
  }
}

第五章 开发环境配置

参考文档

第六章 CSS文件处理

提取css成单独文件,避免js体积过大或者出现闪屏现象

6.1 提取 css 文件

安装 mini-css-extract-plugin 插件

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

module.exports = {
    
    
  entry: path.join(__dirname,'src/js/index.js'),
  output: {
    
    
    filename: 'js/built.js',
    path: path.join(__dirname, 'build')
  },
  module: {
    
    
    rules: [{
    
    
      test: /\.css$/,
      use: [
        // 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件 
        MiniCssExtractPlugin.loader,
        
        // 将 css 文件整合到 js 文件中 
        'css-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: path.join(__dirname,'src/index.html')
    }),
    new MiniCssExtractPlugin({
    
    
      // 对输出的 css 文件进行重命名 
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
}

6.2 css兼容性处理

安装 postcss-loader 和 postcss-preset-env 包

module: {
    
    
  rules: [{
    
    
    test: /\.css$/,
    use: [
      // 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件 
      MiniCssExtractPlugin.loader,
      // 将 css 文件整合到 js 文件中 
      'css-loader',
      //修改 loader 配置
      {
    
    
        loader: 'postcss-loader',
        options: {
    
    
          ident: 'postcss',
          plugins: () => [
            // postcss 的插件 
            require('postcss-preset-env')()
          ]
        }
      }
    ]
  }]
}

修改 package.json

"browserslist": {
    
     
  // 开发环境配置
  "development": [ 
    "last 1 chrome version", 
    "last 1 firefox version", 
    "last 1 safari version" 
  ],
  //生产环境配置 
  "production": [ 
    ">0.2%", 
    "not dead", 
    "not op_mini all" 
  ] 
}

6.3 压缩css

1.安装插件

npm install optimize-css-assets-webpack-plugin

2.webpack.config.js 引包

const OptimizeCssAssetsWebpackPlugin = require(‘optimize-css-assets-webpack-plugin’ )

3.plugins 里配置

new OptimizeCssAssetsWebpackPlugin()

4.命令 webpack

css代码被压缩成一行

第七章 JS文件处理

7.1 JS语法检查(一般不用)

保证团队中 JS 语法一致,只检查自己写的源代码,第三方库不用检查

检查规则:以 airbnb JavaScript 语法为规则

eslint-disable-next-line:忽略下一行警告

npm install eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

// webpack.config.js
rules: [
  {
    
    
    test: /\.js$/,
    
    // 排除 node_modules 目录
    exclude: /node_modules/,
    
    // 优先执行,先检查,后兼容
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
    
    
      // 自动修复语法错误
      fix: true
    }
  }
]

// 在 package.json 中 eslintConfig 中设置
"eslintConfig": {
    
     
  "extends": "airbnb-base", 
  "env": {
    
     
    "browser": true
  } 
}

7.2 JS兼容性处理

1.装包

npm install babel-loader @babel/core @babel/preset-env @babel/polyfill core-js

2.处理

  • 基本处理 --> @babel/preset-env

问题:只能转换基本语法,ES6高级语法不能转换

  • 全部处理 --> @babel/polyfill

    • 入口文件 index.js 里引入 import ‘@babel/polyfill’

问题:只需处理部分兼容,但是将所有兼容性代码都引入了,体积太大

  • 按需处理 --> core-js

    • 使用这种方案就不能使用 @babel/polyfill
module: {
    
    
  rules: [{
    
    
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
    
    
      // 预设:指示 babel 做怎么样的兼容性处理
      presets: [
        [
          '@babel/preset-env',
        {
    
       // 按需加载 
            useBuiltIns: 'usage',
            // 指定 core-js 版本 
            corejs: {
    
     
              version: 3 
            },
            // 指定兼容性做到哪个版本浏览器 
            targets: {
    
     
              chrome: '60', 
              firefox: '60', 
              ie: '9', 
              safari: '10', 
              edge: '17' 
            }
          }
        ]
      ]
    }
  }]
}

7.3 压缩js和html

  1. 压缩js

    • 将 mode 调成 production(生产模式),打包就会自动压缩

      mode: 'production'
      
      
    • 开发模式压缩(不使用,一旦压缩代码可阅读性差,不利于开发)

      //1.装插件
      npm i uglifyjs-webpack-plugin -D
      
      //2.引入
      const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
      
      //3.plugins 配置,每次打包就会压缩 js
      new UglifyjsWebpackPlugin()
      
      

    2.压缩html

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

第八章 生产环境配置

参考文档

第九章 webpack 性能优化

  1. 开发环境性能优化
    • 优化打包构建速度
      • HMR
        • css --> style-loader
        • html --> 不需要
        • js --> 人为写
    • 优化代码调试
      • source-map
        • 开发环境:eval–source-map
        • 生产环境:source-map
  2. 生产环境性能优化
    • 优化打包构建速度
      • oneOf
      • babel 缓存
      • 多进程打包
      • externals
      • dll
    • 优化代码运行的性能
      • 缓存(hash-chunkhash-contenthash)
      • tree shaking 树摇
      • code split 分割
      • 懒加载/预加载
      • PWA

9.1 HMR 热模块替换

基于devServer,生产环境不需要devServer,所以生产环境不能用HMR功能

优化打包构建速度

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

devServer: {
    
    
  contentBase: path.join(__dirname, 'build'),
  compress: true,
  port: 3000,
  open: true, 
  // 开启HMR功能
  // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
  hot: true
}

  • 样式文件:可以使用HMR功能,因为style-loader内部实现了

    • 开发环境使用style-loader性能更好,打包速度更快
  • JS文件:默认没有HMR功能

    • 需要修改js代码,添加支持HMR功能,入口文件做不了,只能处理非入口js文件

      if (module.hot) {
              
              
        // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
        module.hot.accept('./print.js', function() {
              
              
          // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
          // 会执行后面的回调函数
          print()
        })
      }
      
      
  • HTML文件:默认没有HMR功能,同时会导致 html 文件不能热更新(即修改没有任何反应)

    • 解决不能热更新:修改entry入口,将html文件引入

    • 不用做HMR功能,因为只有一个html文件

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

9.2 source-map 映射

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

// webpack.config.js 
devtool: 'source-map'
/*
 * inline-source-map  // 内联
 * 只生成一个内联的 source-map
 * 错误代码准确信息 和 源代码准确位置
 */

/*
 * eval-source-map    // 内联
 * 每一个文件都生成对应的 source-map
 * 错误代码准确信息 和 源代码准确位置
 */

/*
 * source-map    // 外部 
 * 错误代码准确信息 和 源代码准确位置
 */

/*
 * hidden-source-map  // 外部
 * 错误代码信息,追踪不到位置
 */

内联 和 外部的区别

  • 外部生成了文件,内部没有
  • 内联构建速度快

开发环境:速度快,调试更友好

  • 速度快
    • eval-source-map
  • 调试更友好
    • source-map

推荐 --> eval-source-map

生产环境:源代码要不要隐藏?调试要不要更友好?

  • 内联会让代码体积变大,所以在生产环境不用内联

推荐 --> source-map

9.3 oneOf 优化

默认情况下,假设设置了7、8个loader,每一个文件都得通过这7、8个loader处理(过一遍),浪费性能,使用 oneOf 找到了就能直接用,提升性能

正常来讲,一个文件只能被一个 loader 处理,不能有两个配置处理同一种类型文件

rules: [
  {
    
    
    // 在package.json中eslintConfig --> airbnb
    test: /\.js$/,
    exclude: /node_modules/,
    // 优先执行
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
    
    
      fix: true
    }
  },
  {
    
    
    oneOf: [
      /*
        正常来讲,一个文件只能被一个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'
                }
              }
            ]
          ]
        }
      }
    ]
  }
]

9.4 缓存

项目中js代码是最多的,开启 babel 缓存,当一个 js 文件发生变化时,其它 js 资源不用变,适用于生产环境

缓存:

  • 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
  }
}

浏览器访问网站后会强缓存资源,第二次刷新就不会请求服务器(一般会定个时间再去请求服务器),假设有了bug改动了文件,但是浏览器又不能及时请求服务器,所以就用到了文件资源缓存(改变文件名的hash值)

  • 文件资源缓存
    • hash: 不管文件变不变化,每次wepack构建时都会生成一个唯一的hash值
      • 问题: 因为js和css同时使用一个hash值
      • 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
    • chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
      • 问题: js和css的hash值还是一样的
      • 因为css是在js中被引入的,所以同属于一个chunk
    • contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
      • –> 让代码上线运行缓存更好使用
new MiniCssExtractPlugin({
    
    
  filename: 'css/built.[contenthash:10].css'
})

9.5 tree shaking 树摇

去除无用代码

前提:必须使用 ES6 模块化 和 开启 production 环境

  • 满足这两个条件打包就能自动去除无用代码

作用:减小代码体积

在package.json中配置

“sideEffects”: false 所有代码都没有副作用(都可以进行tree shaking)

​ 问题:可能会把css / @babel/polyfill (副作用)文件干掉

“sideEffects”: [".css", ".less"]

9.6 code split 分割

将js文件打包分割成多个bundle,避免体积过大

多入口

entry: {
    
    
  // 多入口:有一个入口,最终输出就有一个bundle
  index: './src/js/index.js',
  test: './src/js/test.js'
}

多入口公共的文件打包成一个bundle

  1. 可以将node_modules中代码单独打包一个bundle最终输出

  2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个bundle

// webpack.config.js
entry: {
    
    
  // 多入口:有一个入口,最终输出就有一个bundle
  index: './src/js/index.js',
  test: './src/js/test.js'
},
optimization: {
    
    
  splitChunks: {
    
    
    chunks: 'all'
  }
}

单入口将某个文件单独打包

entry: './src/js/index.js',
optimization: {
    
    
    splitChunks: {
    
    
        chunks: 'all'
    }
}

index.js >>
//通过js代码,让某个文件被单独打包成一个chunk
//import动态导入语法:能将某个文件单独打包
//webpackChunkName: 'test',设置文件名为test
import(/* webpackChunkName: 'test' */'./test')
  .then(({
    
     mul }) => {
    
    
    // 文件加载成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  });

9.7 懒加载和预加载

懒加载:当文件需要使用时才加载

预加载 prefetch:会在使用之前,提前加载js文件,等其他资源加载完毕,浏览器空闲了,再偷偷加载资源

正常加载可以认为是并行加载(同一时间加载多个文件)

懒加载

index.js >>
document.getElementById('bth').onclick = function() {
    import(/* webpackChunkName: 'test' */'./test').then(({ mul }) => {
        console.log(mul(4, 5));
    });
}

test.js >>
console.log('test.js文件被加载了~'); //这段话一开始不会加载

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

预加载

webpackPrefetch: true

index.js >>
document.getElementById('bth').onclick = function() {
    import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
        console.log(mul(4, 5));
    });
}

test.js >>
console.log('test.js文件被加载了~'); //这段话一开始不会加载

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

9.8 PWA 网站离线访问

  1. 安装插件 workbox-webpack-plugin , 然后引入

    const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
    
    
  2. plugins 里配置

    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker
        打包后生成一个 serviceworker.js.map 配置文件
      */
      clientsClaim: true,
      skipWaiting: true
    })
    
    
  3. 入口文件 index.js 里注册 serviceworker

    // 注册serviceWorker
    // 处理兼容性问题
    if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker
            .register('/service-worker.js')
            .then(() => {
                console.log('sw注册成功了~');
            })
            .catch(() => {
                console.log('sw注册失败了~');
            });
    });
    
    /*
    问题:
      1. eslint不认识 window、navigator全局变量
        解决:需要修改package.json中eslintConfig配置
          "env": {
            "browser": true // 支持浏览器端全局变量
          }
       2. sw代码必须运行在服务器上
          --> nodejs
          -->
            npm i serve -g
            serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
    */
    
    

9.9 多进程打包

开启多进程打包,主要处理js文件(babel-loader干的活久),进程启动大概为600ms,只有工作消耗时间比较长,才需要多进程打包,提升打包速度

  1. 安装包 thread-loader ,在 babel-loader 上面配置

    use: [
          {
            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
            }
          }
        ]     
    
    

9.10 externals 拒绝打包

有些第三方库不希望打包,而是通过CDN的方式引入,从而提升打包速度

直接配置

externals: {
    // 拒绝jQuery被打包进来
    jquery: 'jQuery'
}

然后 index.html 文件需手动引入 CDN
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

9.11 dll 打包第三方库

  • code split将第三方库都打包成一个bundle,这样体积过大,会造成打包速度慢
  • dll 是将第三方库打包成多个bundle,从而进行速度优化
  1. 另外写一个配置文件 webpack.dll.js,单独打包库

    • 运行 webpack 时,默认查找 webpack.config.js 配置文件
    • 运行指令:webpack --config webpack.dll.js
    const path = require('path');
    const webpack = require('webpack');
    
    module.exports = {
          
          
      entry: {
          
          
        //打包生成的 [name] --> jquery
        //打包的库是 --> jquery
        jquery: ['jquery']
      },
      output: {
          
          
        filename: '[name].js',
        path: path.join(__dirname,'dll'),
        //打包向外暴露出去的名字
        library: '[name]_[hash:10]'
      },
      plugins: [
        // 打包生成一个 manifest.json --> 提供和jquery映射
        new webpack.DllPlugin({
          
          
          //映射库的暴露的内容名字,跟上面library一样
          name: '[name]_[hash:10]',
          // 输出文件路径
          path: path.join(__dirname,'dll/manifest.json')
        })
      ],
      mode: 'production'
    }
    
    
  2. webpack.config.js 里配置哪些库不参与打包

    • 安装插件 add-asset-html-webpack-plugin
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const webpack = require('webpack');
    const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'js/build.js',
        path: path.join(__dirname,'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
        // 告诉webpack哪些库不参与打包,同时使用时的名称也得变
        new webpack.DllReferencePlugin({
          manifest: path.join(__dirname,'dll/manifest.json')
        }),
        // 将某个文件打包输出去,并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
          filepath: path.join(__dirname,'dll/jquery.js')
        })
      ],
      mode: 'production'
    };
    
    

第十章 配置详解

10.1 entry 入口

  1. String(单入口)

    entry: './src/index.js'
    
    
    • 打包形成一个chunk,输出一个bundle
    • chunk的名称默认是main,如果output不设置输出文件名,那么打包后就是 main.js
  2. Array(多入口) --> 一般不用

    entry: ['./src/js/index.js','./src/index.html']
    
    
    • 所有入口文件最终只会形成一个chunk,输出一个bundle
    • 只有一个作用,在HMR功能中让html热更新生效
  3. Object(多入口)

    entry: {
          
          
      index: './src/js/index.js',
      add: './src/js/add.js'
    }
    
    //特殊用法
    entry: {
          
          
      //所有入口文件最终只会形成一个chunk,输出一个bundle
      index: ['./src/js/index.js','./src/js/count.js'],
      //形成一个chunk,输出一个chunk
      add: './src/js/add.js'
    }
    
    
    • 有几个入口文件就形成几个chunk,输出几个bundle
    • chunk名称是 key

10.2 output 输出

output: {
    
    
  //文件名称(指定名称+目录)
  filename: 'js/[name].js',

  //输出文件目录(将来所有资源输出的公共目录)
  path: path.join(__dirname,'build'),

  //所有资源引入公共路径前缀 --> 
  //'js/build.js' => 'dist/js/build.js'
  publicPath: 'dist/'

  //非入口chunk名称
  chunkFilename: 'js/[name]_chunk.js'

  //打包后整个库向外暴露的变量名(参考 dll)
  library: '[name]',

  //变量名加到哪个属性上
  libraryTarget: 'commonjs' //以commonjs标准暴露
}

10.3 module

module: {
    
    
  //loader 的配置
  rules: [
    {
    
    
      //多个loader用use
      test: '/\.css$/',
      use: ['style-loader','css-loader']
    },
    {
    
    
      //单个loader用 loader
      test: /\.js$/,
      loader: 'eslint-loader',

      //排除node_modules下的js文件
      exclude: /node_modules/,

      //只检查 src 下的js文件
      include: path.join(__dirname,'src'),

      //优先执行, 'post' 延后执行,不写就是按顺序执行
      enfore: 'pre'

      //loader配置项
      options: {
    
    }
    },
    {
    
    
      //以下配置只会生效一个
      oneOf: []
    }
  ]
}

10.4 resollve 解析模块

resolve: {
    
    
  //配置解析模块路径别名:优点简写路径 缺点路径没有提示
  alias: {
    
    
    //定义变量$css,后续引入css文件只需 import '$css/index.css'
    $css: path.join(__dirname,'src/css'),

    //以 .vue 结尾的文件指定使用版本 runtime-compiler 代码中可以有 template
    "vue$": 'vue/dist/vue.esm.js'
  },

  //配置省略文件路径的后缀名,默认可以省略 js 和 json
  //import './src/index',默认先找 js ,再找 json,避免出错,文件名不要取相同的
  extensions: ['.js','.json','.jsx'],

  //告诉webpack解析模块是去找哪个目录
  modules: path.join(__dirname,'node_moudles')
}

10.5 optimization

只在生产环境中使用

optimization: {
    
    
  chunks: 'all',
  / 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
  // 解决:修改a文件导致b文件的contenthash变化
  runtimeChunk: {
    
    
    name: entrypoint => `runtime-${
      
      entrypoint.name}`
  },
  // 配置生产环境的压缩方案:js和css,安装插件 terser-webpack-plugin
  minimizer: [     
    new TerserWebpackPlugin({
    
    
      // 开启缓存
      cache: true,
      // 开启多进程打包
      parallel: true,
      // 启动source-map
      sourceMap: true
    })
  ]
}

第十一章 Webpack 5

11.1 安装

npm i webpack@next webpack-cli -D

11.2 关注内容

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

11.3 优化

  1. 自动删除 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

    • 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 会被删除

    • webpack 现在能够多个模块之前的关系
    import {
          
           something } from './something';
    
    function usingSomething() {
          
          
      return something;
    }
    
    export function test() {
          
          
      return usingSomething();
    }
    
    

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

    • webpack 现在能处理对 Commonjs 的 tree shaking
  5. Output

    • webpack 4 默认只能输出 ES5 代码

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

    如:output.ecmaVersion: 2015

  6. SplitChunk

    // webpack4
    minSize: 30000; //最小30kb
    
    
    // webpack5
    minSize: {
          
          
      javascript: 30000, //js最小30kb
      style: 50000, //css最小50kb
    }
    
    
  7. Caching

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

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

  1. 监视输出文件

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

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

  2. 默认值

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

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

猜你喜欢

转载自blog.csdn.net/weixin_44257930/article/details/109012502
今日推荐