webpack


webpack编译代码解析 配置

# webpack

# 概念

webpack 是一个现代 JavaScript 应用程序的静态模块打包器,它包含以下几个方面

  • entry (入口): 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始
  • output (出口): 控制 webpack 如何输出项目
  • module (解析模块): 用于对模块的源代码进行转换(图片, es6, ts)
  • plugins(插件): 用于扩展 webpack 的功能
  • mode(模式): 告知 webpack 使用相应模式的内置优化

# 先编译源代码

src 目录

  • --| src
    • --| index.js
    • --| title.js
  • index.js
const title = require('./title')
console.log(title)
console.log('index.js')
1
2
3
  • title.js
module.exports = {
  name: 'title'
}
console.log('title.js')
1
2
3
4

编译 webpack 代码

npx webpack
1

编译后的 bundle.js

# webpack 编译代码解析

bundle.js 代码解析

    1. 有一个 IIFE(自执行)函数,接收一个形参 modules 形成一个闭包
;(function(modules) {})
1
    1. 给 IIFE 函数传参, 传入一个参数对象

对象的 key 是 文件目录,作为文件 id

对象的值是: 一个函数

    1. 内部实现了一套 commonjs规范, 也就是 __webpack_require__函数
    1. __webpack_require__ 添加一些静态方法或属性,

# 1. webpack_require 方法

;(function(modules) {
  // 创建缓存对象
  var installedModules = {}

  //创建 __webpack_require__ 函数, 接收参数 moduleId,也就是 对象的key
  function __webpack_require__(moduleId) {
    // 判断缓存里有木有这个文件
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports
    }
    // 否则的话,就把这个文件存到缓存里
    var module = (installedModules[moduleId] = {
      i: moduleId, // 模块的id
      l: false, // 模块是否加载过
      exports: {} // 导出的对象
    })
    //   对象的value 值是个函数, 执行这个函数同时绑定 this 上下文指向
    modules[moduleId].call(
      module.exports,
      module,
      module.exports,
      __webpack_require__
    )
    //  存过之后, 将标志置为 true,说明此文件已经加载过了
    module.l = true
    return module.exports
  }
  // 将执行结果返回
  return __webpack_require__('./src/index.js')
})({
  './src/index.js': function(module, exports, __webpack_require__) {
    var title = __webpack_require__(/*! ./title */ './src/title.js')
    console.log(title)
  },
  './src/title.js': function(module, exports) {
    exports.age = 10
    module.exports = {
      name: 'title'
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 配置文件

默认: webpack.config.js

webpack 是需要传入一个配置对象,然后导出这个对象

mkdir webpack
cd webpack
npm init -y
yarn add -D webpack webpack-cli
1
2
3
4

创建源码

mkdir src
cd src
touch index.js
1
2
3

index.js

console.log(10)
1

打包

npx webpack
1

此时打包会报警告 意思是缺少环境模式,需要你配置 mode 属性

# 配置打包指令

package.json

"scripts": {
    "build": "webpack"
}
1
2
3
yarn build
1

# mode(模式)

  • 配置打包的模式, 类型 string, 值可以是 development, production, none
module.exports = {
  mode: 'development'
}
1
2
3

# entry(项目入口)

  • 单入口:配置一个字符串
module.exports = {
  entry: './src/index.js'
}
1
2
3
  • 多入口:配置一个对象
module.exports = {
  entry: {
    main: './src/index.js',
    app: './src/app.js'
  }
}
1
2
3
4
5
6

# ouput(项目出口)

即使可以存在多个入口起点,但只指定一个输出配置。

用法:

  • filename: 用于输出文件的文件名。
  • path: 打包输出到哪个目录

# 单入口配置

const path = require('path')
module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}
1
2
3
4
5
6
7

# 多入口配置

const path = require('path')
module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js',
    app: './src/app.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 打包到指定的文件目录

const path = require('path')
module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js',
    app: './src/app.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'static/js/[name].[chunkhash:8].js'
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# loader(转换模块)

# 解析样式(style, css)

  • 解析顺序: 从右向左解析
yarn add -D css-loader style-loader
1

语法

  • module.rules
module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: 'css-loader' },
      { test: /\.ts$/, use: 'ts-loader' }
    ]
  }
}
1
2
3
4
5
6
7
8

简化语法

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10

# 单独抽离样式

yarn add -D mini-css-extract-plugin
1
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
              hmr: process.env.NODE_ENV === 'development'
            }
          },
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
      ignoreOrder: false // Enable to remove warnings about conflicting order
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 处理 css 自动添加浏览器前缀

yarn add -D postcss-loader autoprefixer
1

根目录下新健 postcss.config.js

module.exports = {
  plugins: [require('autoprefixer')]
}
1
2
3

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
              hmr: process.env.NODE_ENV === 'development'
            }
          },
          { loader: 'css-loader', options: { importLoaders: 1 } },
          'postcss-loader'
        ]
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 解析图片

  • 需要一个合适的 loader,对图片进行处理

file-loader: 指示 webpack 将所需对象作为文件发出并返回其公共 URL url-loader: 以 base64 编码的 URL 加载文件,减少 http 请求。

yarn add -D file-loader
1
  • file-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {}
          }
        ]
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 处理 es6 等语法

  • 使用 babel-loader
yarn add -D babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env
1
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/, //忽略文件, 不去解析里边的语法
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# plugins(插件)

# 处理 html 文件

yarn add -D html-webpack-plugin
1
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: 'webpack',
      //创建一个在内存中生成html页面的插件
      template: path.join(__dirname, './src/index.html'), //指定模板页面
      filename: 'app.html', //指定生成页面的名称,index.html浏览器才会默认直接打开
      //   以下配置开启各种html 压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true
      }
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# css 文件单独抽离

  1. 基本打包
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  plugins: [
    // 样式单独抽离
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
      ignoreOrder: false // Enable to remove warnings about conflicting order
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11

# css 文件抽离到指定目录

  1. 单独打到到 static 目录
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  plugins: [
    // 样式单独抽离
    new MiniCssExtractPlugin({
      // 单独打到 static 目录下
      filename: 'static/css/[name].[contenthash:8].css',
      chunkFilename: 'static/css/[name].[contenthash:8].css',
      ignoreOrder: false // Enable to remove warnings about conflicting order
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12

# 自动清理 dist 目录

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  plugins: [new CleanWebpackPlugin()]
}
1
2
3
4

# 配置打包进度条

const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const chalk = require('chalk')

module.exports = {
  plugins: [
    new ProgressBarPlugin({
      complete: '█',
      format: `${chalk.green('Building')} [ ${chalk.green(
        ':bar'
      )} ] ':msg:' ${chalk.bold('(:percent)')}`,
      clear: true
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 配置 resolve

# 配置路径别名

module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src/')
    }
  }
}
1
2
3
4
5
6
7

# 外部扩展(externals)

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖, 也就是拆包

# 配置动态 cdn

  • 1.自动注入到 html 文件中

例如引入 vue element cdn

webpack.config.js

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: 'webpack',
      //创建一个在内存中生成html页面的插件
      template: path.join(__dirname, './src/index.html'), //指定模板页面
      filename: 'app.html', //指定生成页面的名称,index.html浏览器才会默认
      cdn: {
        css: ['https://cdn.bootcss.com/element-ui/2.8.2/theme-chalk/index.css'],
        js: [
          'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
          'https://cdn.bootcss.com/element-ui/2.8.2/index.js'
        ]
      }
    })
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title><%= htmlWebpackPlugin.options.title %></title>
    <!-- import cdn css -->
    <% if(htmlWebpackPlugin.options.cdn) {%>
        <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
            <link rel="stylesheet" href="<%=css%>" />
        <% } %>
     <% } %>
  </head>
  <body>
    <div id="box"></div>
    <!-- import cdn js -->
    <% if(htmlWebpackPlugin.options.cdn) {%>
        <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
            <script src="<%=js%>"></script>
        <% } %>
    <% } %>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 配置分包

# 处理浏览器文件缓存

使用 hash 签名