webpack

daiweishuai

webpack

打包工具的选择

INFO

Vue React的封装都使用rollup打包,那rollup打包工具和项目开发使用的webpack打包工具的区别是什么?

  • Vue github的package.json 1563094729353
  • react github的package.json 1563094729353

rollup

  • rollup的打包体积小
  • rollup推荐开发者去写esm模块,esm模块适合做静态代码分析,做tree shaking减少代码体积,esm是浏览器除了script标签外,拥有的模块化能力。
    • rollup诞生在浏览器支持esm模块之后,esm是未来js的模块发展方向,rollup直接使用esm模式进行开发。
  • 高版本浏览器原生支持esm模块,无需额外代码注入,打包后的代码结构清晰。
    • 不用像webpack写iife(自执行函数)

webpack

  • webpack的打包体积大,注入了很多的兼容代码。
    • webpack诞生在commonjs后,esm模块之前。
    • 浏览器当时只支持script标签代码引入,且都是自执行函数,webpack为了在浏览器端也有模块的引用,自己在浏览器端实现了一套commonjs语法__webpack_require__。
    • 后续浏览器支持了esm模块,但是webpack为了兼容之前的npm包,仍然延续了之前iife自执行函数和代码注入。
  • webpack不能生成esm模块的包,对静态代码分析及 tree shaking不友好
  • 适合做应用项目,对于引入、css、scss、图片、字体文件有更好的解析,用法多样化,生态大而全。

两者引用源代码模块的对比

纯esm纯cjs两者混用
webpack支持(有代码注入)支持(有代码注入)支持(有代码注入)
rollup支持(无注入)原生不支持(需增加插件@rollup/plugin-commonjs)原生不支持(需增加插件@rollup/plugin-commonjs)

项目中 webpack tree shaking 不生效的原因

INFO

注:先把sourcemap关闭,为了看到的是打包后的源码,而不是看到映射的源代码

1563094729353

代码

import { log } from '@xxx/sentry'

console.log(log, 'log====');

npm run dev 项目启动后查看 打印内容

1563094729353

INFO

结果代码并没有tree shaking,其他没有用到的方法也被打入了包中。 引入esm模块 按照命名导入,正常来说应该会tree shaking,此时却没有tree shaking,这是为什么?

打包后的代码

1563094729353

webpack的 tree shaking 只在production模式生效

INFO

webpack的tree shaking,默认只在 使用 mode 为 production 的配置项以启用。

此时npm run build 对打包结果本地启动服务跑项目查看。

15630947293531563094729353

批量导入后整体使用tree shaking不生效

如果使用了* as sentry 别名批量导入的写法时,由于是批量引入,也不会进行tree shaking。 如果HxSentry.log() 单独使用其中的一个方法是会进行tree shaking的。

代码

import * as sentry from '@xxx/sentry'

console.log(sentry, 'sentry====');

打印结果

1563094729353

打包后的代码

1563094729353

批量导入后单独使用tree shaking生效

代码

import * as sentry from '@xxx/sentry'

console.log(sentry.log, 'log====');

打印结果

1563094729353

打包后的代码

1563094729353

webpack

webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。

1563094729353

可以做的事情

代码转换、文件优化、代码分割、模块合并、自动刷新、代码校验、自动发布

webpack基础配置

webpack安装

  • 安装本地webpack

webpack webpack-cli -D

webpack可以进行0配置

  • 打包工具 -> 输出后的结果(js模块)
  • 打包(支持我们的js的模块化)

如果当前目录下有node.exe执行程序,会用这个执行程序执行webpack\bin目录下的webpack.js文件

1563095445080

1563095577931

在开发时会用到require()来引入文件,但是如果放到浏览器时不支持node这种规范的,这样的js文件是不可以直接用于浏览器。打包之后是可以的,把语法全部转化为es5了,手动创建一个html文件,引入刚打包好的main.js文件就可以使用了

1563096114045

手动配置webpack

  • 默认配置文件的名字是webpack.config.js
// webpack 是node写出来的 node的写法
let path = require('path');

module.exports = {
  mode: 'development', // 模式 默认两种 production development
  entry: './src/index.js', // 入口
  output: {
    filename: 'bundle.js', // 打包后的文件名
    path: path.resolve(__dirname, 'dist'),// 路径必须是一个绝对路径,加__dirname是以当前目录下加一个dist目录
  }
}

config.webpack.js文件名是默认的,webpack会默认调用webpack-cli中bin下的yargs.js文件执行。config.webpack.js名字也是可以更改的。

1563097791464

1563097743345

webpack打包出的文件解析

  • 打包好之后的js文件
(function (modules) { // webpackBootstrap
  // The module cache 先定义一个缓存
  var installedModules = {};
  // "./src/index.js" : {}
  // The require function 配置了 实现了require方法  因为require是不能在浏览器中运行的
  function __webpack_require__ (moduleId) { // "./src/index.js"
    // Check if module is in cache 检查这个模块是否在缓存中
    if (installedModules[moduleId]) { // 不在缓存中
      return installedModules[moduleId].exports;
    }
    // Create a new module (and put it into the cache)
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false, // 是否加载完成
      exports: {}
    };
    // Execute the module function 第一个参数this指向,然后是module模块, module.exports空对象
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    // Flag the module as loaded
    module.l = true;
    // Return the exports of the module
    return module.exports;
  }

  // Load entry module and return exports
  return __webpack_require__(__webpack_require__.s = "./src/index.js"); // 入口模块
})
  ({
    "./src/a.js": // key => 模块的路径
      (function (module, exports) { // value 函数
        eval("module.exports = 'zfpx'\n\n//# sourceURL=webpack:///./src/a.js?");
      }),
    "./src/index.js":
      (function (module, exports, __webpack_require__) {
        eval("let str = __webpack_require__( \"./src/a.js\")\r\nconsole.log(str)\n\n//# sourceURL=webpack:///./src/index.js?");
      })
  });
  • 需要自定义webpack.config.js名字

    • 直接在终端 npx webpack --config [filename]

    • 知道自己配置的文件名字直接在package.json文件中写 webpack --config [filename] 在终端需要

      npm run build

      1551320877018

      1551321001990

    • 如果不知道自己起的文件名字, 临时需要打包配置输入文件名

      在package.json文件中这样写

      webpack

      在终端需要在--config前面再加--,表示后面写的是字符串

      npm run build -- --config [filename]

      1551320974203

      1551321055652

html插件

每次打包完需要在手动打包后的文件夹中点击index.html在浏览器中打开,地址是file:\\,我们需要地址是localhost:,官方有webpack-dev-server,开发时直接打包进内存中,浏览器输入直接可以打开

webpack中有内置的express做的开发服务插件webpack-dev-server

安装

npm i webpack-dev-server -D

使用:并不会真的打包,会把打包好的存到内存中

命令行中:npx webpack-dev-server

package.json中:npm run dev

{
  "name": "webpack-dev-1",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "dev": "webpack-dev-server" // npm run dev
  },
  "devDependencies": {
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.35.3",
    "webpack-cli": "^3.3.5",
    "webpack-dev-server": "^3.7.2"
  }
}

webpack.config.js中配置

let path = require('path');

module.exports = {
  devServer: { // 开发服务器的配置
    port: 3000, //改端口号
    progress: true, // 加进度条
    contentBase: './build', // 在哪个文件夹下起服务
    open: true, // 自动打开浏览器
    compress: true, // gzip压缩
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build'),
  }
}

在打包的build文件夹中新建了index.html,引入指定的build.js,每次使用webpack-dev-server打包进内存,都是可以指定引入新的打包的build.js文件。但是,如果没有index.html呢,或许连build文件夹都没有。

html-webpack-plugin插件提供了打包后自动生成指定的自定义html模板并引入新打包的js文件,此时依然会产生两个文件,只是看不到

// webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入html-webpack-plugin

module.exports = {
  devServer: {
    port: 3000,
    progress: true,
    contentBase: './build',
    open: true,
    compress: true,
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins:[ // 数组 放着所有的webpack插件
    new HtmlWebpackPlugin({
      template: './src/index.html', // 以哪个html作为模板
      filename: 'index.html', // 设置打包后生成的html模板,不设置默认也是index.html
    })
  ]
}

配置各种参数:

// webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  devServer: {
    port: 3000,
    progress: true,
    contentBase: './build',
    open: true,
    compress: true,
  },
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js', // 打包后的文件名 [hash]加入哈希,产生不同的文件,防止覆盖和出现缓存的问题 [hash:8]只显示8位
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: { // 简化html
        removeAttributeQuotes: true, // 删除属性的双引号
        collapseWhitespace: true, // 折叠空行
      },
      hash: true, // 引用文件加哈希戳 防止缓存的问题
    })
  ]
}

打包后文件的变化

1563113688298

打包后模板引擎的变化

1563113728014

样式处理(1)

在index.html模板中是不允许引入css文件的,因为模板打包完会原封不动的到打包文件夹中,如果引入了css文件,此时会找不到打包文件夹外面的css文件,所以需要把css文件引入js文件中,一起打包,此时就需要css-loader,如果直接打包,js文件中是不会处理引入的 css文件的。

1563192809497

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <!-- 模板 -->
  <link rel="stylesheet" href="./index.css">不允许
</body>
</html>

打包提示需要引入loader

1563192873373

  • css-loader是解析css文件里专属的语法,比如@import
/* a.css */
body {
  color: yellow;
}
/* index.css */
@import './a.css';
body {
  background: red;
}
  • style-loader是把打包好的css文件插入到模板引擎中的
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  devServer: {
    port: 3000,
    progress: true,
    contentBase: './build',
    open: true,
    compress: true,
  },
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: {
        removeAttributeQuotes: true,
        collapseWhitespace: true,
      },
      hash: true,
    })
  ],
  module: { // 模块
    rules: [ // 规则 css-loader  解析 @import这种语法的
      // style-loader 它是把css 插入到head的标签中
      // loader的特点 希望功能单一
      // loader的用法 一个loader就用字符串
      // { test: /\.css$/, use: 'css-loader' }
      // 多个loader需要用 []
      // loader的顺序 默认是从右向左执行  从下到上执行
      // 先使用css-loader打包好之后再使用style-loader插入模板
      // { test: /\.css$/, use: ['style-loader','css-loader'] }
      // loader还可以写成 对象方式,好处是可以再写一个参数
      {
        test: /\.css$/, use: [
          {
            loader: 'style-loader',
            options: {
            }
          },
          'css-loader']
      }
    ]
  }
}
  • 我们自己在html模板head中写的样式优先级不是最高,可以配置style-loader的属性

1563194277543

{
    test: /\.css$/, use: [
        {
            loader: 'style-loader', // 插入模板head标签的位置
            options: {
                insertAt: 'top' // 插入head标签顶部
            }
        },
        'css-loader']
}

1563195134306

  • 配置less

安装yarn add less less-loader -D

less-loader需要用less来进行转化

/*index.less*/
body {
  div {
    border: 1px solid #dadade;
  }
}
/*index.js*/
let str = require('./a.js')
console.log(str)
require('./index.css')
require('./index.less')
/*webpack.config.js*/
module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
            options: {
              insertAt: 'top'
            }
          },
          'css-loader'
        ]
      },
      {  // 可以处理less文件
        test: /\.less$/,
        use: [
          { // 把css文件插入到模板中
            loader: 'style-loader',
            options: {
              insertAt: 'top' // 插入模板head标签的位置
            }
          },
          'css-loader', // @import 解析路径
          'less-loader' // 把less 解析成 css
        ]
      }
    ]
  }

同样可以配置sass需要安装node-sasssass-loadersass-loader需要放在下面先执行

stylus需要安装stylusstylus-loader

样式处理(2)

抽离模板中的style标签中css样式为link,抽离css插件mini-css-extract-plugin

安装yarn add mini-css-extract-plugin -D

  • 引入同一个'mini-css-extract-plugin'
/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css' // 抽离出css文件名
    }),
  ],
  module: {
    rules: [
      { // 我们不希望css抽离出来再放到模板的style标签中,要把style-loader去掉
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
      { // 引入同一个'mini-css-extract-plugin'会全都抽离成一个文件main.css。
      // 也可以拷贝,引入两个'mini-css-extract-plugin',分别new两个实例和用这两个的loader。比如一个用来抽离.css文件到main.css,一个用来抽离.less文件到main1.css,虽然是不同的两个css文件,但他们内容是一样的。有可能.less文件less-loader完成之后,和.css文件一起css-loader。
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
}

/*main.css*/
body {
  color: yellow;
}
body {
  background: red;
  transform: 45deg
}
body div {
  border: 1px solid #dadade;
}

1563201113774

  • 引入两个'mini-css-extract-plugin',分别new两个实例和用这两个的loader
/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let MiniCssExtractPlugin1 = require('mini-css-extract-plugin');
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
    new MiniCssExtractPlugin1({
      filename: 'main1.css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin1.loader,
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
}
/*main.css*/
body {
  color: yellow;
}
body {
  background: red;
  transform: 45deg
}
body div {
  border: 1px solid #dadade;
}
/*main1.css*/
body {
  color: yellow;
}
body {
  background: red;
  transform: 45deg
}
body div {
  border: 1px solid #dadade;
}
  • 希望添加的属性自动加前缀

插件包autoprefixer和loaderpostcss-loader

安装yarn add postcss-loader autoprefixer

需要新建postcss.config.js配置文件,这个配置文件里面要用到autoprefixer插件

1563263760659

/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader', // 解析css之前就要加前缀
          'postcss-loader',
          
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}

/*postcss.config.js*/
module.exports = {
  plugins: [require('autoprefixer')({
    "browsers": [  // 9.6.0版本之后不能使用"browsers"
      "defaults",
      "not ie < 11",
      "last 2 versions",
      "> 1%",
      "iOS 7",
      "last 3 iOS versions"
    ]
  })
  ]
}
// 更改后
module.exports = {
  plugins: [require('autoprefixer')({
    "overrideBrowserslist": [
      "defaults",
      "not ie < 11",
      "last 2 versions",
      "> 1%",
      "iOS 7",
      "last 3 iOS versions"
    ]
  })
  ]
}

打包后的main.css

/*main.css*/
body {
  color: yellow;
}
body {
  background: red;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg)
}
body div {
  border: 1px solid #dadade;
}
  • 压缩css文件、js文件体积 在npmjs中mini-css-extract-plugin包下推荐用optimize-css-assets-webpack-plugin压缩css文件体积,还有配套terser-webpack-pluginjs文件压缩体积。
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss =  require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization:{ // 优化项
    minimizer:[
      new TerserJSPlugin({}), // 压缩js文件体积
      new OptimizeCss({}), // 压缩css文件体积
    ]
  },
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader', // 解析css之前就要加前缀
          'postcss-loader',
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}

转化es6语法

在js文件中如果用到了箭头函数,没有引入babel,打包之后没有变成普通函数,仍然是箭头函数。

需要把源文件转化的加载器,转化成另一种文件,就要用到babel-loader

转化需要babel的核心模块@babel/core,调用transform方法

需要@babel/preset-env告诉如何将es6转化es5的转化模块,可以将标准的语法转换成低级的语法

如果有class A{}语法,打包提示要安装@babel/plugin-proposal-class-properties插件

如果有@log语法,打包提示需要安装@babel/plugin-proposal-class-properties,去babel官网看,还需要安装@babel/plugin-proposal-decorators

/* index.js */
let str = require('./a.js')
console.log(str)

require('./index.css')

require('./index.less')

fn = () => {
  console.log('sss')
}

@log // 类的装饰器 装饰类或者属性
class A { // new A() a=1 // es7写法
  a = 1;
}

let a = new A()
console.log(a.a)

function log (target) {
  console.log(target); // 查看被修饰的A类
}
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: 
    minimizer: [
      new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: { // options可以写在外面也可以写在里面 用babel-loader 需要把es6->es5
            presets: [ // 映射
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }] // 转化class A{}语法
            ]
          }
        }
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}

处理js语法及校验

代码转换babel

如果写了generator语法会报错

// generator语法,处理异步
function* gen (params) {
  yield 1;
}
console.log(gen().next())

regeneratorRuntime is not defined

@babel/plugin-transform-runtime将更高级语法转化低级语法

1564876556355

在生产环境需要npm install --save @babel/runtime

如果写'aaa'.includes('a')语法,上面的插件是不支持实例上的方法调用,还需要引入@babel/runtime-corejs3这里没有实现,有问题

polyfill这个选项在v7中通过将其设置为默认值而被删除。

/* a.js */
module.exports = 'aaaaa'
require('core-js')
class B {

}

// generator语法,处理异步
function* gen (params) {
  yield 1;
}
console.log(gen().next())

'aaa'.includes('a')
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                {
                  corejs: 3, // [].includes这样的实例方法只适用于core-js@3。
                  proposals: true
                }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader', // 解析css之前就要加前缀
          'postcss-loader',

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

校验规范eslint

可以到eslint官网自定义eslint配置后下载eslintrc.json文件,之后在文件前面加.使用

1564896462662

需要引入eslinteslint-loader

let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}), // 压缩js文件体积
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [ // loader 默认是从右边向左边执行 从下到上执行
      { // 为了代码解析和代码校验分开,写在两个对象中,也是可以写在一个{}中的,但是需要加enforce: 'pre'配置项;也可以代码校验写在代码解析的下边。
        test: /\.js$/,
        use: {
          loader: 'eslint-loader',
          options: {
            enforce: 'pre' // previous:强制执行顺序.js文件eslint-loader先执行 post:后执行
          }
        }
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

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

全局变量引入问题

loader的几种类型

pre:前面执行的loader

normal:普通loader

内联loader

postloader:后置loader

全局暴露变量

引入jquery被webpack打包之后是在闭包中,没有挂到window对象上,别的js文件并不能使用jquery。此时需要引入expose-loader

  1. expose-loader暴露到window上
  • 不需要在webpack.config.js配置的情况
// 把jquery使用expose-loader替换成$全局对象
import $ from 'expose-loader?$!jquery'
// expose-loader 暴露 全局的loader 内联的loader
console.log(window.$);
  • webpack.config.js配置的情况
// 使用
import $ from 'jquery'
console.log(window.$);
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  module: {
    rules: [
      {
        test: require.resolve('jquery'), // 只要引用了jquery就匹配到
        use: 'expose-loader?$'
      },
      // {
      //   test: /\.js$/,
      //   use: {
      //     loader: 'eslint-loader',
      //     options: {
      //       enforce: 'pre'
      //     }
      //   }
      // },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}
  • providePlugin 给每个模块中都注入$,但是没有在window上
let webpack = require('webpack');// webpack.ProvidePlugin
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
    new webpack.ProvidePlugin({ // 在每个模块中都注入$
      $: 'jquery'
    })
  ],
}
// 使用
console.log($); // 在每个模块中注入$对象,并没有在window上
  1. 使用CDN在index.html引入jquery,可以在每一个模块使用window.$或者$
<head>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
    <!-- 模板 -->
    <div>内容区</div>
</body>
  1. 引入不打包

如果已经把jquery变成每个模块都引入了,单个模块有强迫症,仍然要import jquery from 'jquery',这样会把单独引入的jquery会打包,需要在webpack.config.js配置忽略打包。

/* index.js */
import $ from 'jquery';
console.log($);
<!-- index.html -->
<head>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
    <!-- 模板 -->
    <div>内容区</div>
</body>
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}), 
      new OptimizeCss({}), 
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  externals: { // 打包时忽略
    jquery: '$'
  },
  module: {
    rules: [
      // {
      //   test: /\.js$/,
      //   use: {
      //     loader: 'eslint-loader',
      //     options: {
      //       enforce: 'pre'
      //     }
      //   }
      // },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

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

图片处理

  1. 在js中创建图片来引入

如果直接image.src='./logo.jpg'打包会直接认为是个字符串

需要引入file-loader,打包默认会在内部生成一张图片 到build目录下,把生成的图片的名字返回来

import logo from './logo.jpg' // 打包时会生成一个哈希戳的图片名字到打包文件夹下。把图片引入,返回的结果是一个新的图片地址url
console.log(logo)
let image = new Image();
image.src = logo; // 就是一个普通的图片
document.body.appendChild(image);
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  externals: {
    jquery: '$'
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: 'html-withimg-loader'
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: 'file-loader'
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}
  1. 在css引入 background('url)
/* index.js */
import './index.css'
div{
  width: 100px;
  height: 100px;
  background: url('./logo.jpg') 
  /* css中直接引入,因为有file-loader了,相当于require("./logo.jpg") */
}
  1. <img src="" alt=""/>在标签中引入图片,需要引入html-withimg-loader
<body>
    <!-- 模板 -->
    <div>内容区</div>
    <img src="./logo.jpg" alt="">
</body>
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  externals: {
    jquery: '$'
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: 'html-withimg-loader'
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: 'file-loader'
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}
  • 对打包图片做限制,如果有些图片比较小,我们不希望图片发送http请求,直接用base64第一次就加载出来。我们的图片要小于多少k的时候 用base64 来转化,否则用file-loader产生真实的图片
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      // new TerserJSPlugin({}),
      new OptimizeCss({}),
    ]
  },
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.[hash:8].js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'main.css'
    }),
  ],
  externals: {
    jquery: '$'
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: 'html-withimg-loader'
      },
      {
        test: /\.(png|jpg|gif)$/,
        // 做一个限制 当我们的图片 小于多少k的时候 用base64 来转化
        // 否则用file-loader产生真实的图片
        // use: 'file-loader' // 打包把图片生成一个新图片
        use: {
          loader: 'url-loader', // 转化为base64
          options: {
            limit: 200 * 1024 // 图片小于2M的时候直接转化为base64,不会打包成一个新图片
          }
        }
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ],
            plugins: [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }],
              [
                "@babel/plugin-transform-runtime",
                // {
                //   corejs: 3,
                //   proposals: true
                // }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',

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

打包文件分类

打包多页应用

配置source-map

watch的用法

webpack小插件应用

webpack跨域问题

resolve属性的配置

定义环境变量

区分不同环境

noParse

IgnorePlugin

dllPlugin

happypack

webpack自带优化

抽离公共代码

懒加载

热更新

tapable介绍

tapable

AsyncParralleHook

AsyncSeriesHook

AsyncSeriesWaterfall

webpack手写

webpack分析及处理

创建依赖关系

AST递归解析

生成打包结果

增加loader

增加plugins

loader

loader配置

babel-loader实现

实现file-loader和url-loader

less-loader和css-loader

css-loader

webpack中的插件

文件列表插件

内联webpack插件

打包后自动发布

Last Updated 6/28/2025, 8:51:06 AM