Webpack学习

107 阅读6分钟

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

Webpack五个核心概念
  1. Entry: 入口 以哪个文件以入口起点开始打包,分析构建内部依赖图
-  string:---- './src/index.js' 单入口打包 形成一个chunk,只有一个bundle文件输出,此时chunk默认名称是main
-  array: ---- ['./src/index.js', './src/add.js'] 多入口 所有的入口只会生产一个chunk, 输出出去的只有一个bundle文件  使用场景:在HRM功能中让html热更新生效
-  object: ---- {
                          index: './src/index.js',
                          home: './src/home.js
                        }  有几个入口文件就会生成几个chunk,输出几个bundle,此时的chunk名称是key
  1. Output: 输出 将打包后的资源bundles输出到哪里,以及命名
  2. Loader:处理那些非js文件(webpack自身自能处理js)
  3. Plugings: 用于执行范围更广的任务
  4. Mode: 使用相对应的模式配置(develoment production)
运行指令:
- 开发环境: webpack ./src/index.js -o ./build --mode=development
      webpack会以./src/index.js为入口文件开始打包,打包输出到./build 整体打包环境为开发环境
- 生产环境: webpack ./src/index.js -o ./build --mode=production
      webpack会以./src/index.js为入口文件开始打包,打包输出到./build 整体打包环境为生产环境

结论: webpack能处理js/json资源,不能处理scc/img等资源 能将ES6模块化编译成浏览器识别的模块化

webpack的配置文件webpack.config.js
  • 作用: 指示webpack干什么些活(当你运行webpack时会加载里面的配置)
  • 构建工具都是基于nodejs平台运行 模块化默认采用commonjs
  • webpack.config.js文件代码修改需要重新打包运行
webpack性能优化
  • 开发环境性能优化
    1. 优化打包构建速度
    2. 优化代码调试
  • 生产环境性能优化
    1. 优化打包构建速度
    2. 优化代码运行的性能
js兼容性处理:babel-loader @babel/core  @babel/prest-env @babel/polyfill  corejs
  1. @babel/prest-env ---> 只能转换基本的语法, 如promise不能转换
  2. @babel/polyfill ----> 全部js兼容 只需要js中引入
  3. corejs ---> 按需加载
  • HMR: hot module replacement 热模块替换

    1. 作用: 一个模块发生变化,之后重新打包这个模块, 而不是打包所以模块, 极大提升了速度
    2. 样式文件:可以使用HRM功能 原因是style-loader内部实现
    3. js文件: 默认不能使用HRM
    4. html:默认不能使用HRM,同时会导致HTML文件不能热更新
  • source-map: 一种提供源代码到构建后代码映射技术(应用构建后代码出错,通过映射可以追踪代码错误)

1. source-map: 外部
    错误代码准确信息以及源代码错误位置
2. line-source-map: 内联
    只生成一个内联的source-map  错误代码准确信息以及源代码错误位置
3. hidden-source-map: 外部
    错误代码原因 但追踪不到源代码错误位置以及代码错误
4. eval-source-map: 内联
    每一个文件都生成对应的source-map,都在eval  错误代码准确信息以及源代码错误位置
5. nosources-source-map: 外部
    错误代码准确信息但没有源码信息
6. cheap-source-map: 外部
    错误代码准确信息以及源代码错误位置 但只能精确到行
7. cheap-module-source-map: 外部
    错误代码准确信息以及源代码错误位置 module会将loader的source map加入
开发环境:需要速度快,调试更友好
速度快: eval-cheap-source ---> eval-source-map
调试更友好:  source-map ---> cheap-module-source-map ---> cheap-source-map
生成环境:源代码需不需要隐藏?调试要不要更友好
nosources-source-map --->  hidden-source-map 只隐藏
source-map ---> cheap-module-source-map
  • 缓存: bable缓存 ---> cacheDirectory: true ----> 让第二次打包构建速度更快

    文件资源缓存 ----> 让代码上线运行缓存更好使用

    1. hash: 每次webpack构建都会生成一个唯一的hash值。故js和css等文件都会使用一个hash值,如果只修改一个文件重新打包,会导致所有的缓存失效
    2. chunkhash: 根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。由于css是在js中被引用,所以同属于一个chunk,故js和css的hash值还是一样
    3. contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
  • code split代码分割

    PWA: 渐进式网络开发应用程序(离线状态下可访问) workbox ---> workbox-webpack-plugin

webpack.config.js

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const workboxWebpackPlugin = require('workbox-webpack-plugin')
const terserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
  // 入口起点 单入口
  entry: './src/index.js',
  // 多入口
  // entry: {
  //   main: './src/index.js',
  //   home: './src/home.js'
  // },
  // 输出
  output: {
    // 输出的文件名 [name]:取文件名(指定名称 + 目录)
    filename: '[name].[hash:10].js',
    // 输出的路径 __dirname nodejs的变量 代表当前文件目录的绝对路径
    path: path.resolve(__dirname, 'build'),
    // 所有资源引入公共路径前缀
    // publicPath: '/',
    // 非入口chunk的名称
    // chunkFilename: 'is/[name]_ckunk.js'
  },
  // loader配置
  module: {
    rules: [
      {
        // 匹配的文件
        test: /\.css$/,
        // 使用相对应的loader进行处理
        use: [
          // miniCssExtractPlugin 把打包后的js中的css提取成单独的文件
          // miniCssExtractPlugin.loader,
          // use数组中loader执行顺序 从右到左 从下往上 以此进行
          // 创建styleb标签, 将js中的样式资源添加插入到head中
          'style-loader',
          // 将css文件解析成commonjs模块 加载js中 里面是样式字符串
          'css-loader',
        ],
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // 将less文件编译成css文件
          'less-loader',
        ],
      },
      {
        // 排除资源
        exclude: /\.(js|css|html|less|png|gif|jpg)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          // 输出的路径在output输出路径下
          outputPath: 'media',
        },
      },
      // 处理图片资源
      {
        test: /\.(png|gif|jpg)$/,
        // 下载url-loader file-loader
        loader: 'url-loader',
        options: {
          // 图片文件大小小于8kb, 会被base64处理
          // 优点:减少请求数量 (减少服务器压力)
          // 缺点:图片体积变大(文件请求速度变慢)
          limit: 8 * 1024,
          // 问题:url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs 解析时会出现[Object Module]
          // 解决:关闭url-loader的es6de模块化,使用commonjs
          esModule: false,
          // [hash: 10] 取图片的hash的前10位
          // [ext]取文件原始扩展名
          name: '[hash:10].[ext]',
          // 输出的路径在output输出路径下
          outputPath: 'images',
        },
      },
      {
        test: /\.html$/,
        // 处理html文件中img标签的图片引入,从而能被url-loader进行处理
        // 需要下载html-loader
        loader: 'html-loader',
        options: {
          // 问题:url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs 解析时会出现[Object Module]
          // 解决:关闭url-loader的es6de模块化,使用commonjs
          esModule: false,
        },
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env', 
                // corejs
                {
                  // 按需加载
                  useBuiltIns: 'usage',
                  // 指定corejs 版本
                  corejs: {
                    version: 3
                  },
                  // 指定兼容性做到哪个版本浏览器
                  targets: {
                    browsers: ['> 5%', 'IE 10', 'iOS 7', 'Firefox > 20']
                  },
                }
              ]
            ],
            // 缓存
            cacheDirectory: true
          }
        }
      },
      /**
       * 语法检查: eslint-loader eslint
       * 注意:只检查自己写的源代码,第三方库不检查
       * 设置规则:
       * package.json中 eslintConfig中设置
      *       "eslintConfig": {
                "extends": "airbnb-base"
              }
              airbnb ---> eslint-config-airbnb-base   eslint-plugin-import eslint
       *
       */
        // {
        //   test: /\.js$/,
        //   // 排除
        //   exclude: /node_modules/,
        //   // 值检查src目录下的js文件
        //   include: path.resolve(__dirname, 'src'),
        //   // 优先执行
        //   enforce: 'pre',
        //   loader: 'eslint-loader',
        //   options: {
        //     fix: true,
        //   },
        // },
    ],
  },
  // plugins的配置
  plugins: [
    // html-webpack-plugin 会默认创建一个空的HTML, 自动引入打包输出所有资源(js/img/css)
    new htmlWebpackPlugin({
      // 会复制./index.html文件 并自动引入打包输出的所以资源(js/img/css)
      template: './index.html',
      // 压缩html
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true,
      },
    }),
    new miniCssExtractPlugin({
      // 文件重命名
      filename: 'css/index.css',
    }),
    // 压缩css
    new optimizeCssAssetsWebpackPlugin(),
    // 启动work-server
    new workboxWebpackPlugin.GenerateSW({
      /**
       * 1. 帮助serverwork快速启动
       * 2. 删除旧的 serverwork
       * 生产一个serverwork配置文件
       * 
       * // 注册serviceWorker 处理兼容性问题
        if ('serviceWorker' in navigator) {
          window.addEventListener('load', ()=> {
            navigator.serviceWorker.register('./service-work.js').then(()=> {

            }).catch(()=> {

            })
          })
        }
       * 
       */
      
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  // mode的配置development开发环境 production 生产环境 production 会自动压缩js
  mode: 'development',
  // 开发服务器devServer: 用来自动化(自动编译, 自动打开浏览器 以及自动刷新)
  // 只会内存中编译打包,不会有任何输出
  // 启动devServer指令:npx webpack-dev-server
  devServer: {
    // 构建后的项目路径
    contentBase: path.resolve(__dirname, 'build'),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 8082,
    // 域名
    host: 'localhost',
    // 自动打开浏览器
    open: true,
    // 开启HRM功能
    hot: true,
    // 监所contentBase目录下所有文件,一旦变化就会reload
    watchContentBase: true,
    // 忽略文件
    ignored: /node_modules/,
    // 不需要显示启动服务器日志
    clientLogLevel: 'none',
    // 除了一些基本信息外其他都不显示
    quiet: true,
    // 报错,不要全屏提示
    overlay: false,
    // 服务器代理 ---> 解决开发环境跨域的问题
    /*proxy: {
      // 一旦devServer服务器接受到/api/xxx的请求,就会转发到另外一个服务器上
      '/api': {
        target: 'http://localhost:3000',
        // 发送请求时,请求路径重写 将/api/xxx--->/xxx(去掉/api)
        pathRewrite: {
          '^api/': ''
        }
      }
    }*/
  },
  // 可以将node_modules中组件单独打包一个chunk最终输出  自动分析多入口chunk中,有木有公用引入的组件,会打包成单独的一个chunk
  optimization: {
    splitChunks: {
      chunks: 'all',
      /**
       * 默认值不用写
       */
      // 分割的chunk最小为30kb
     /* minSize: 30 * 1024,
      // 0表示最大限制没有
      maxSize: 0,
      // 要提取的chunk最少被引用1次
      minChunks: 1,
      // 按需加载时并行加载的文件数量最大值
      maxAsyncRequests: 5,
      // 入口js文件最大并行的数量
      maxInitialRequests: 3,
      // 名称连接符
      automaticNameDelimiter: '~',
      // 使用命名规则
      name: true,
      cacheGroups: {
        // 分割chunk的组
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10,
        }
      } */
    },
    // 解决当前模块记录其他模块的hash单独打包为一个文件 runtime
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    minimizer: [
      // 配置生产环境压缩方案 js和css
      new terserWebpackPlugin({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  },
  // 解析模块规则
  resolve: {
    alias: {
      // 配置解析模块路径别名: 简写路径,但没有提示
      $css: path.resolve(__dirname, 'asset')
    },
    // 配置省略文件路径的后缀名(引用的时候)
    extensions: ['.js','.json','.jsx'],
    // 告诉webpack 解析模块去找哪个目录
    modules: [path.resolve(__dirname, 'node_modules'), 'node_modules']
  },
  // source-map 构建后代码映射技术
  devtool: 'source-map',
}