Webpack配置文件基础解析

241 阅读8分钟
**webpack**是一个现代 JavaScript 应用程序的**静态模块打包器**(module bundler),当 webpack 处理应用程序时,它会**递归地构建一个依赖关系图**(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块**打包成一个或多个bundle**。

一、概念

webpack由4个核心概念组成的:

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)

1、入口(entry)

****  入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的**。**

通过webpack配置文件中的entry属性可以设置一个或者多个入口文件,默认为.src/index.js

例子:

webpack.config.js //默认的webpack打包配置文件

modules.export = {
    entry: './path/to/my/entry/file.js'
}

配置单个入口:

打包形成一个chunk,输出一个bundle文件,默认chunkbundle名称为main

配置方式由以下几种:

//第一种
modules.export = {
    entry: './path/to/my/entry/file.js'
}

//第二种
modules.export = {
    entry: {
        main:'./path/to/my/entry/file.js'
}

//第三种
modules.export = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
};
//分离 应用程序(app) 和 第三方库(vendor) 入口

多入口配置方式:

   1.  数组array:

所有文件最终会形成一个chunk,输出出去的只有 一个bundle文件

modules.export = {
  entry: ['./src/app.js','./src/vendors.js']
};

  向 entry 属性传入**「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”**。在你想要多个依赖文件一起注入,并且将它们的依赖导向(graph)到一个“chunk”时,传入数组的方式就很有用。

   2. 对象object:

有几个文件就形成几个chunk,输出几个bundle文件, 此时chunk的名称为key

//对象
modules.export = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
};
//3 个独立分离的依赖图

注:每个 HTML 文档只使用一个入口起点。

2、出口(output)

output是告诉 **webpack需要将编译好的文件(bundle)输出到什么地方 **去,以及如何命名这些文件。默认是输出到./dist目录下。也可以通过output指定,最终整个程序的编译结果都将输出到你指定的输出路径文件夹下。

你可以通过配置中指定一个 output 字段,来配置这些处理过程:

  • filename 用于输出文件的文件名。
  • 目标输出目录 path 的绝对路径。
  • publicPath 该配置能帮助你为项目中的所有资源指定一个基础路径用于定义所有资源引入的公共前缀),对于输出CDN资源很有效。
  • chunkFileName:  非入口chunk的名称
  • library: 定义整个库向外暴露的变量名,方便外部调用
  • libraryTarget:配合library使用,library导出的变量名可以通过怎样的方式访问(window|global|commonjs

webpack.config.js

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'), //指定输出文件夹路径
    filename: 'my-first-webpack.bundle.js', //指定输出文件名称
    publicPath: '/', //添加公共前缀
    chunkFileName: 'js/[name]_chunk.js',
    library: [name], //向外暴露的变量名,这样默认为chunk名,
    libraryTarget: 'global' //给上面定义的变量的方式,这个代表可以通过global访问变量
    //libraryTarget: 'commonjs' //代表可以通过commonjs的方式require引入
  }
};

当entry配置多个入口时

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}

// 写入到硬盘:./dist/app.js, ./dist/search.js

高级用法

//使用 CDN 和资源 hash 的复杂示例
output: {
  path: "/home/proj/cdn/assets/[hash]",
  publicPath: "http://cdn.example.com/assets/[hash]/"
}

3. loader

loader让**webpack能够去处理那些非JavaScript文件**(webpack只能理解JavaScript和json文件),使用各种类型的loader可以将所有类型的文件转换为webpack能够处理识别的有效模块,然后就可以通过webpack将其打包....

在此步骤生成依赖关系图

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

loader两个属性

  • test 属性:  用于标识出应该被对应的 loader 进行转换的某个或某些文件。

  • use 属性:  表示进行转换时,应该使用哪个 loader。多个loader使用

  • loader属性:  使用单个loader进行转化时

  • exclude属性:  排除某些文件

  • include属性: 只检查(转换)某个路径下的文件

  • enforce属性: 当定义多个规则对同一种文件进行检查时,可以使用这个属性将某个规则的执行顺序改变。 值为'pre'优先执行, 值为'post'延后执行

  • option属性:  设置loader的某些参数设置,类似于url-loader可以设置option={limit:8*1024}代表此规则对8kb以下的文件使用base64编码.

例:如下定义了一个规则,就是在对require()/import 语句中被解析为 '.txt' 的路径文件,先使用raw-loader进行 转换一下。.css结尾的路径使用css-loader转换, .ts结尾的使用ts-loader转化。

const path = require('path');

const config = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
        { 
            test: /\.txt$/, 
            loader: 'raw-loader',
            exclude: /node_modules/, //排除node_modules下面的模块
            include: resolve(_dirname, 'src'), //只检查src路径下的文件
            enforce: 'pre', //当定义多个规则对同一种文件进行检查时,可以使用这个属性将某个规则的执行顺序改变
        },
        { test: /\.css$/, use: ['style-loader','css-loader'] },
        { test: /\.ts$/, use: 'ts-loader' },
        { test: /\.(png|jpg|gif)$/, loader: 'url-loader', option:{ limit: 8*1024}}
    ]
  }
};

module.exports = config;

注:loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。

loader中的use数组执行顺序:从右到左,从下到上,依次执行,上一个loader的返回值给下一个loader继续执行,最后一个返回给webpack

例: 先 sass-loader 再 css-loader 再 style-loader

module.exports = {
  module: {
    rules: [
      {
        // 增加对 SCSS 文件的支持
        test: /\.scss/,
        // SCSS 文件的处理顺序为先 sass-loader 再 css-loader 再 style-loader
        use: [
          'style-loader',
          {
            loader:'css-loader',
            // 给 css-loader 传入配置项
            options:{
              minimize:true, 
            }
          },
          'sass-loader'],
      },
    ]
  },
};

例:

module.exports = {
  module: {
    rules: [
      {
        // 增加对 CSS 文件的支持
        test: /\.css/,
        // use数组中的loader执行顺序:从右到左,从上到下,依次执行
        use: [
          'style-loader', //创建style标签,将js中的样式资源添加到style标签中,然后放入head中生效
          'css-loader' //将import/require的css文件加载到js模块中。
        ],
      },
    ]
  },
};

4.插件(plugins)

plugins主要是webpack的扩展功能,就是当loader转化过后,可以通过plugins对转化后的模块进行再加工处理,例如打包,压缩等。

通过 require() 引入插件,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 **new** 操作符来创建它的一个实例

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'}) 
    //使用HtmlWebpackPlugin将输出后的bundle输出到index.html中
  ]
};

module.exports = config;

5.模式(Mode)

**可以通过设置mode参数为**

developmentproduction 之中的一个来启用相应模式下的 webpack 内置的优化。

module.exports = {
  mode: 'production'
};

或者从 CLI 参数中传递:

webpack --mode=production

6. resolve解析模块的规则

 有以下几个属性:

  • alias:  创建 importrequire 的别名,来确保模块引入变得更简单。优点:可以简写路径,缺点:写路径时没有提示。

例:当文件想访问某个路径比较长的文件时,可以采取别名的方式

webpac.config.js

//假设目录是:
-src
   -utilities
      -utilities.js   -templates
      -templates.js   -js
     -index.js
   -css
     -index.css

Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
webpac.config.js
//-----start----//
var { resolve } = require('path');
module.exports = {
    //
    reslove:{
         alias: {
          $css: resolve(_dirname, 'src/css'), //意思是给src/css路径命别名,
          Utilities: path.resolve(__dirname, 'src/utilities/'),
          Utilities: path.resolve(__dirname, 'src/templates/')    
        }
    }    
}
//-----end-----//

index.js 
//-----start-----//
import '../css/index.css';
import '../utilities/utility';
import '../templates/templates';
//可以换成下面
import '$css/index.css'; //因为经过webpack给路径弄了别名
import 'Utilities/utility.js';import 'Utilities/templates.js';
  • extensions: 配置省略文件路径的后缀名(自动解析确定的扩展)。对省略文件后缀的路径会按照数组中从左到右的补全后缀进行匹配,当都没有匹配到,就会报错 。默认值为:

    extensions: [".js", ".json"]
    

使用此选项,会覆盖默认数组,这就意味着 webpack 将不再尝试使用默认扩展来解析模块。对于使用其扩展导入的模块,例如,import SomeFile from "./somefile.ext",要想正确的解析,一个包含“*”的字符串必须包含在数组中。

module.exports = {
    reslove:{         alias: {
          $css: resolve(_dirname, 'src/css'), //意思是给src/css路径命别名,
          Utilities: path.resolve(__dirname, 'src/utilities/'),
          Utilities: path.resolve(__dirname, 'src/templates/')    
        },
        //从左到右添加后缀匹配,匹配成功则不会往后匹配
         extensions: ['.js', 'json', '.jsx', '*']
    }    }
  • modules:告诉 webpack 解析模块时应该搜索的目录。默认是

    modules:['node_modules']

如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索:

modules: [path.resolve(__dirname, "src"), "node_modules"]

7、devServer配置

用于开发环境的配置,有以下几个属性:

module.exports = {
    devServer: {
        //运行代码的目录
        contentBase: resolve(_dirname, 'build'),
        //监视文件comtentBase目录下的文件,一旦文件变化就会重载
        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: {
             //当服务器访问监视到/api开头的请求,将请求转发到配置的地址
            '/api': {
                target: 'http://localhost:3000',
                //发送请求是,请求路径重写:将/api/***--->/***
                pathRewrite: {
                    '^/api': ''
                }
            },
            //默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。
            //如果你想要接受,设置secure:false
            "/test": {
                target: "https://other-server.example.com",
                secure: false
            }
        }
    }
}

8. externals:配置选项提供了「从输出的 bundle 中排除依赖」的方法

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

例如,从 CDN 引入 jQuery,而不是把它打包:

webpack.config.js

externals: {
  jquery: 'jQuery'
}

这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行:

import $ from 'jquery';

$('.my-element').animate(...);

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>

9、optimization:代码分割

有以下几个属性::

module.exports = {
    optimization: {
        splitChunks:{
            chunks: 'all',
            //以下是默认值,可以不写
            minSize: 30*1024, //分割chunk最小为30kb,
            maxSize:0, //最大没有限制,
            minChunks: 1,//要提取的chunk最少被引用过一次
            maxAsyncRequests:3, //按需加载时并行加载文件的最大数量
            minInitialRequests:3, //入口js文件最大并行请求数量
            automaticNameDelimiter: '~',//名称连接符
            name: true, //可以使用命名规则
            cacheGroups: {
                //分割chunk数组
                //node_modules文件会被打包到vendors组的chunk中, ---verndors~xxx.js
                //满足上面的公共规则,如:大小超过30kb,至少被引用一次
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10 //优先级
                },
                default: {
                    minChunks: 2, //要提取的chunk最少被引用多少次
                    priority: -20, //优先级
                    //如果当前打包的模块和之前已经被提取的模块是同一个,就会复用,而不是重新打包
                    reuseExistingChunk: true 
                }
            }
        },
        //将当前模块记录在其他模块的hash值单独打包为一个文件runtime.js
        //解决:修改a文件导致b文件的contenhash值变化导致缓存失效的问题
        runtimeChunk: {
            name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer:{
            //配置生产环境的压缩方案: js和css
            ...
        }
    }

}