Webpack配置-03

283 阅读16分钟

按照配置所影响的功能划分

*entry-配置模块入口

context

  • webpack寻找相对路径的文件时候,以context位根目录,context默认为执行启动webpack时所在的当前工作目录
  • context必须是一个绝对路径的字符串
module.exports = {
    context: path.resolve(__dirname,'app')
}

entry类型

类型栗子含义
string'./app/entry'入口模块文件路径,可以是相对路径
array['./app/entry1','./app/entry2']入口模块文件路径,可以是相对路径
object{a: './app/entry-a',b: ['./app/entry-b1','./app/entry-b2]'}配置多个入口,每个入口生成一个chunk
  • 注意array类型时,则搭配output.librqary配置项使用时,只有数组里最后一个入口文件的模块会被导出

chunk名称

webpack会位每个生成的chunk文件取一个名称

  • entry为string或者array时,只会生成一个chunk,chunk的名称为main
  • entry为object时,可能会出现多个chunk,chunk的名称是object键值对中键的名称

配置动态entry

例如,需要为每个页面配置一个entry,页面数量不断增长,不能写成静态的值,可以将entry设置成一个函数动态返回配置

// 同步函数
entry: () => {
    return {
        a: './pages/a',
        b: './pages/b'
    }
}
//异步函数
entry: () => {
    return new Promise((resolve) => {
        resolve({
            a: './pages/a',
            b: './pages/b'
        })
    })
}

output-配置如何输出最终想要的代码

filename-输出文件名称

  • 只有一个输出文件:filename: 'bundle.js'
  • 多个chunk要输出filename: '[name].js' [name]是内置name变量去替换[name] 内置变量列表
变量名含义
idchunk的唯一标识,0开始
namechunk的名称
hashchunk唯一标识的hash值
chunkhashchunk内容的hash值

hash/chunkhash的长度可指定[hash: 8],默认20

chunkFilename-无入口的chunk在输出时的文件名称

支持和filename一致的内置变量

path-输出文件存放的本地目录

必须是string类型的绝对路径,通常通过node.js的path模块取获取绝对路径 path: path.resolve(__dirname,'dist_[hash]')

pubilcPath-配置发布到线上资源的url前缀

string类型默认值空字符串'',即使用相对路径 path和pubilcPath支持模版字符串,但是内置变量只有hash

crossOriginLoading

webpack输出的那部分代码可能需要异步加载,异步加载通过JSONP方式实现,JSONP的原理是动态的向html中插入一个<script src='url'></script>标签取加载异步资源 所以crossOriginLoading用于配置这个异步插入标签的crossorigin值 crossorigin可以取的值:

  • anonymous(默认),在加载此脚本时候不会带上用户的cookies
  • use-credentials,在加载次脚本资源时会带上用户的cookies 通常设置crossorigin来获取异步加载的脚本执行时的详细错误信息

libraryTarget和library

webpack取构建一个可以背其他模块导入使用的库时,需要用到。

  • libraryTarget以何种方式导出库
  • library配置导出库的名称

libraryTarget支持的配置

var(默认)

编写的库将通过var被赋值给通过library指定名称的变量 假如配置了library = 'LibraryName'

// webpack输出的代码
var LibraryName = lib_code;
// 使用库的方法
LibraryName.doSomething();
// 假如library为空,直接输出,lib_code为导出库的代码内容,有返回值的自执行函数
lib_code

commonjs

编写的库通过CommonJS规范导出 假如配置了library = 'LibraryName'

// webpack输出的代码
export['LibraryName'] = lib_code;
// 使用库的方法
require('library-name-in-npm')['LibraryName'].doSomething();
// library-name-in-npm指模块被发布到Npm代码仓库时的名称

commonjs2

编写的库通过CommonJS2规范导出

// webpack输出的代码
module.exports = lib_code
// 使用库的方法
require('library-name-in-npm').doSomething();

与commonJS相似,导出方式不一样 libraryTarget为commonjs2时,配置的library没有意义

window

便携的库通过window赋值给通过library指定的名称

// webpack输出的代码
window['LibraryName'] = lib_code
// 使用库的方法
window.libraryName.doSomething();

global

编写库通过global赋值给通过library指定的名称

// webpack输出的代码
global['LibraryName'] = lib_code
// 使用库的方法
global.libraryName.doSomething();

libraryExport

配置要导出的模块中哪些子模块需要被导出,只有在libraryTarget为commonjs/commonjs2时使用才有意义 举个例子: 加入想要导出的模块代码源

export const a = 1;
export default b = 2;

现在构建输出的代码只想导出其中的a,则可以讲libraryTarget设置成a

module.exports = lib_code['a'];
require('library-name-in-npm') === 1;

module-配置处理模块规则

loader

rules配置模块的读取和解析规则,通常用来配置loader

  • 条件配置:通过test,include,exclude配置项选中loader要应用规则的文件
  • 应用规则:对选中的文件通过use配置项来应用loader,一个/一组
  • 重置顺序:一组loader的执行顺序默认从右向左,通过enforce选项可以将其中一个loader的执行顺序放到最前或者最后 enforce: 'post'/'pre'(最后/最前) test,include,exclude配置项数组的每项之间是的关系,只要满足数组中的一个条件就会被命中

noParse

可以让webpack忽略对部分没有采用模块化的文件的递归解析和处理-提高构建性能 可选配置项,类型:RegExp/[RehExp]/function中的一种

// 正则表达式
noParse: /jquery|chartjs/
// 使用函数-webpack3.0.0开始支持
noParse: (content) => {
    // content代表一个模块的文件路径
    // 返回true/false
    return /jquery|chartjs/.test(content);
}

注意,被忽略的文件不应该包含import/require/define等模块化语句,否则会导致再构建出的代码中包含无法再浏览器环境下执行的模块化语句

parser

可以更细粒度的配置哪些模块语法被解析,哪些不被解析。使用方法

module: {
    rules: {
        test: /\.js/,
        use: ['babel-loader'],
        parser: {
            amd: false, // 禁用AMD
            commonjs: false, // 禁用CommonJS
            system: false, // 禁用SystemJS
            harmony: false, // 禁用es6 import/export
            requireInclude: false, // 禁用require.include
            requireEnsure: false, // 禁用require.ensure
            requireContext: false, // 禁用require.context
            broserify: false, // 禁用broserify
            requireJs: false, // 禁用requirejs
        }
    }
}

resolve-配置寻找模块规则

alias

通过别名将原导入路径映射成一个新的路径

// 通俗理解就是将导入语句里面的compontents关键字替换成./src/compontents/
resolve: {
    alias: {
        components: './src/compontents/'
    }
}

可以通过$符号缩小范围

// 只会命中以react为结尾的导入语句
resolve: {
    alias: {
        'react$': '/path/to/reacr.min/js'
    }
}

mainFields

使用目的:一些第三方库会针对不同的环境提供几份代码,webpack会根据mainFields的配置去决定优先采用哪份代码 例如提供了es5和es6两份代码如下

{
    "jsmext:main": "es/index.js", // 采用es6语法的代码入口文件
    "main": "lib/index.js" // 采用es5语法的代码入口文件
}

mainFeilds默认mainFields: ['browser', 'main'] webpack会按照数组顺序再package.json中进行寻找,只会使用找到的第一个文件 如过我们想优先采用es6那份代码:mainFields: ['jsmext:main', 'browser', 'main']

extensions

使用目的:在导入语句没有后缀时,webpack会自动带上后缀尝试访问文件是否存在,extensions用于配置在尝试过程中用到的后缀列表。 extensions: ['.js', '.json'] 解释:当遇到import './data'时,webpack会优先寻找./data.js文件,找不到再找./data.json文件

modules

配置webpack去哪些目录下寻找第三方模块,默认取node_modules下寻找 modules: ['./src/components', 'node_modules'] 大量的导入文件都在./src/components下,如上配置,导入时可以简单通过import 'button'导入

descriptionFiles

配置描述第三方模块的文件名称descriptionFiles: ['package.json']

enforceExtension

所有导入文件是否必须带文件后缀,当为true时,所有导入文件必须加后缀,如开启前import './foo'可以正常工作,但是开启后必须写成import './foo.js'才能正常工作

enforceModuleExtension

和上一个作用相似,但是只对node_modules下的模块生效,一般搭配使用

enforceExtension: true,
enforceModuleExtension: false

plugins-配置扩展插件

配置流程:plugins配置项接受一个数组,数组里每一项都是一个要用的Plugin实例,Plugin需要的参数需要构造函数传入

const CommonChunkPlugin = require('webpack/lib/optimize/CommonChunkPlugin');
module.exports = {
    plugins: [
        // 所有页面都会用到的公共代码被提取到common代码模块中
        new CommonsChunPlugin({
            name: 'common',
            chunks: ['a','b']
        }),
    ]
}

DevServer-配置DevServer

hot:模块热替换

默认:在发现代码被更新后通过自动刷新整个页面来做到实时预览

inline

为什么使用:DevServer的实时预览功能依赖一个注入页面的代理客户端,去接收来自DevServer的命令并负责刷新网页的工作。inline用于配置是否将这个代理客户端自动注入将运行在页面中的Chunk里。默认自动注入。

  • 开启inline:DevServer在构建变化后的代码时通过代理客户端控制网页刷新
  • 关闭inline:DevServer无法直接控制要开发的网页。会通过iframe的方式去运行要开发的网页。在构建完变化后的代码时,会通过刷新iframe来实现实时预览

historyApiFallback

方便地开发使用了html5 history api的单页应用

这类单页应用要求服务器在针对任何命中的路由时都返回一个对应的html文件

最简单的配置historyApiFallback: true导致任何请求都会返回一个index.html文件,只能用于只有一个html文件的应用。 多个单页应用组成的应用

historyApiFallback: {
    rewrites: [
        //  /user开头的都返回user.html
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/game/, to: '/game.html' },
        // 其他的都返回index.html
        { from: /./, to: '/index.html' },
    ]
}

contentBase

配置DevServer HTTP服务器的文件根目录(默认情况下为当前的执行目录) 它只能用来配置暴露本地文件的规则,可以通过contentBase: false来关闭暴露本地文件 例:

// 想将根目录下的public目录设置成DevServer服务器的文件根目录
devServer: {
    contentBase: path.join(_dirname, 'public')
}

DevServer服务器通过http服务暴露文件的方式分为两类

  • 暴露本地文件
  • 暴露webpack构建出的结果,由于结果交给了DevServer,所以我们再使用DevServer时,会在本地找不到构建出的文件

header

可以再http响应中注入一些http响应头

devServer: {
    header: {
        'X-foo': 'bar'
    }
}

host

配置DevServer服务舰艇地址,,只能通过命令行参数传入

port

配置Devserver服务监听端口,默认使用8080

allowedHost

配置一个白名单列表,只有HTTP请求的HOST在列表里才能正常返回

allowedHost: [
    // 匹配单个域名
    'host.com',
    'sub.host.com',
    // host2.com和所有的子域名 *.host2.com都将匹配
    '.host2.com'
]

disableHostCheck

配置是否关闭用于DNS重新绑定的HTTP请求的HOST检查。默认只接受来自本地的请求,关闭后可以接收来自人意HOST的请求

https

DevServer默认使用http服务,也可以使用https服务,如下 devServer: { https: true } DevServer会自动为我们生成一份HTTPS证书 如果我们想用自己的证书,配置如下:

devServer: {
    https: {
        key: fs.readFileSync('path/to/server.key'),
        cert: fs.readFileSync('path/to/server.crt'),
        ca: fs.readFileSync('path/to/ca.pem'),
    }
}

clientLogLevel

配置客户端的日志等级,影响到我们再浏览器开发者工具控制台里看到的日志内容。取值:none, error, warning, info, 默认为info等级,即输出所有类型的日志,设置成none可以不输出任何日志

compress

配置是否启用Gzip压缩,为boolean类型,默认为false

open

用于在DevServer启动且第一次构建完时,自动用我们的系统的默认浏览器取打开要开发的网页,还提供了openPage配置项来打开制定的url的网页

其他配置项

介绍一些常用部分

Target

让webpack构建出针对不同运行环境的代码

target值描述
web针对浏览器,所有代码都集中再一个文件里
node针对node.js,使用require语句加载chunk代码
async-node针对node.js,异步加载chunk代码
webworker针对webworker
electron-main针对electron主线程
electron-renderer针对electron渲染线程

Devtool

配置webpack如何生成Source Map,默认值是false,即不生成Source Map,若想构建出的代码生成Source Map:

module.export = {
    devtool: 'source-map'
}

Watch和WatchOptions

前面介绍过webpack的监听模式,它支持舰艇文件更新,在文件发生变化时重新编译,使用webpack时,监听模式默认关闭,打开需要如下配置:

module.export = {
    watch: true
}

使用DevServer时,监听模式默认开启 watchOptions配置更灵活的控制监听模式

module.export = {
    // 只有再开启监听模式时,watchOptions才有意义
    // 默认为false,也就是不开启
    watch: true,
    // 监听模式运行时的参数
    // 开启监听模式时才有意义
    watchOptions: {
        // 不坚挺的文件或者文件夹,支持正则匹配
        // 默认不为空
        ignored: /node_modules/,
        // 监听到变化后会等300ms再去执行动作,防止文件更新太快导致从新变异频率太高
        // 默认为300ms
        aggregateTimeout: 300,
        // 判断文件是否发生变化,是通过不停的询问系统指定文件有没有变化实现的
        // 默认每秒询问1000次
        poll: 1000
    }
}

External

告诉webpack要构建的代码中使用了哪些不用被打包的模块(即这些模版是从外部环境提供的,webpack打包时可以忽略他们) 举个例子:

module.export = {
    externals: {
        // 将导入语句里面的jquery替换成运行环境的全局变量jQuery
        jquery: 'jQuery'
    }
}

ResolveLoader

告诉webpack如何寻找Loader

module.exports = {
    resolveLoader: {
        // 去哪个目录下寻找Loader
        module: ['node_modules'],
        // 入口文件的后缀
        extensions: ['.js', '.json'],
        // 指明入口文件位置的字段
        mainFields: ['loader', 'main']
    }
}

整体配置结构

接下来看一下每个配置项的所处位置和数据结构

const path = require('path');
modules.exports = {
    // entry表示入口,webpack执行构建的第一步从entry开始,可以抽象成输入
    // 类型可以是string,object,array
    entry: './app/entry', // 只有一个入口,入口只有一个文件
    entry: ['./app.entry1', './app/entry2'], // 只有一个入口,入口文件有两个文件
    entry: { // 有两个入口
        a: './app/entry',
        b: ['./app.entry1', './app/entry2']
    },
    // 输出结果,在webpack经过一系列处理后,如何输出最终想要的代码
    output: {
        // 输出文件存放目录,必须是string类型的绝对路径
        path: path.resolve(__dirname, 'dist'),
        // 输出文件名称
        filename: 'bundles.js', // 完整的名称
        filename: '[name].js', // 配置了多个entry时,通过名称模版为不同的entry生成不同的文件名称
        filename: '[chunkhash].js', // 根据文件内容的Hash值生成文件的名称,用于浏览器长时间缓存文件
        // 发布到线上所有环境的URL前缀,为string类型
        publicPath: '/assets/', // 放到指定目录下
        pubilcPath: '',放到根目录下
        pubilcPath: 'https://cdn.example.com/', // 放到CDN上
        // 导出库的名称,为string类型
        // 不填它时,默认的输出格式是匿名的立即执行函数
        library: 'MyLibrary',
        // 导出库的类型,为美剧类型,默认是var
        // 可以是um,umd2,commonjs,commonjs2,amd,this,var,assign,window,global,jsonp
        libraryTarget: 'umd',
        // 是否包含有用的文件路径信息到生成的代码中,为boolean类型
        pathinfo: true,
        //附加Chunk的文件名称
        chunkFilename: '[id].js',
        chunkFilename: '[chunkhash].js',
        // JSONP异步加载资源时的会滴哦啊函数名称,需要和服务端搭配使用
        jsonpFunction: 'myWebpackJsonp',
        // 生成的Source Map文件的名称
        sourceMapFilename: '[file].map',
        // 浏览器开发者工具例显示的源码模块名称
        devtoolModuleFilenameTemplate: 'webpack:///[resource-path]',
        // 异步加载跨域的资源时使用的方式
        crossOriginLoading: 'use-credentials',
        crossOriginLoading: 'anonymous',
        crossOriginLoading: false,
    },
    module: {
        rules: [ // 配置Loader
            {
                test: /\.jsx?$/, // 正则匹配命中要用的Loader的文件
                include: [ // 只会命中这里面的文件
                    path.resolve(__dirname, 'app')
                ],
                exclude: [ // 忽略这里面的文件
                    path.resolve(__dirname, 'app/demo-files')
                ],
                use: [ // 使用哪些Loader,有先后次序,从后向前执行
                    'style-loader', // 直接使用Loader的名称
                    {
                        loader: 'css-loader',
                        options: { // 向html-loader传一些参数
                            
                        }
                    }
                ]
            },
        ],
        noParse: [ // 不用解析和处理的模块
            /special-library\.js$/  // 用正则匹配
        ],
    },
    // 配置插件
    plugins: [],
    // 配置寻找模块的规则
    resolve: {
        modules: [ // 寻找模块的根目录,为array类型,默认以node_modules为根目录
            'node_modules',
            path.resolve(__dirname, 'app')
        ],
        extensions: ['.js', '.json',  '.jsx', '.css'], // 模块的后缀名
        alias: { //模块别名配置,用于映射模块
            // 将‘module’映射成‘new-module’,同样,‘module/path/file’也会被映射成‘new-module/path/file’
            'module': 'new-module',
            // 使用结尾符$后,将‘only-module’映射成‘new-module’
            // 但是不像上面的,‘module/path/file’不会被映射成‘new-module/path/file’
            'only-module$': new-module,
        },
        alias: [ // alias还支持使用数组来更想洗进行配置
            {
                name: 'module', // 老模块
                alias: 'new-module', // 新模块
                // 是否只映射模块,如果是true,则只有‘module’会被映射,如果是false,则‘module/inner/path’也会被映射
                onlyModule: true,
            }
        ],
        symlinks: true, // 是否跟随文件的软连接取搜寻模块的路径
        descriptionFiles: ['package.json'], // 模块的描述文件
        mainFields: ['main'], // 模块的描述文件里描述入口的文件的字段名
        enforceExtension: false, // 是否强制导入语句写明文件后缀
    },
    // 输出文件的性能检查配置
    performance: {
        hints: 'warning', // 有性能问题时输出警告
        hints: 'error', // 有性能问题时输出错误
        hints: 'false', // 关闭性能检查
        maxAssetSize: 200000, // 最大文件的大小(单位为bytes)
        maxEntrypointSize: 400000, // 最大入口文件的大小(单位为bytes)
        assetFilter: function(assetFilename) { // 过滤要检查的文件
            return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
    },
    devtool: 'source-map', // 配置source-map类型
    context: __dirname, // webpack使用的根目录,string类型必须是绝对路径
    // 配置输出代码的运行环境
    target: 'web', // 浏览器,默认
    target: 'webworker', // webworker
    target: 'node', // node.js,使用require语句加载chunk代码
    target: 'async-node', // node.js异步加载chunk代码
    target: 'node-webkit', // nw.js
    target: 'electron-main', // electron,主线程
    target: 'electron-renderer', // electron,渲染线程
    externals: { // 使用来自javascript运行环境提供的全局变量
        jquery: 'jQuery'
    },
    stats: { // 控制台输出日志控制
        asset: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
    },
    deServer: { // DevServer相关的配置
        proxy: { // 代理到后端服务器接口
            'api': 'http://localhost:3000'
        },
        contentBase: path.join(__diename, 'pubilc'), // 配置DevServer Http服务器的文件根目录
        compress: true, // 是否开启Gzip压缩
        histroyApiFallback: true, // 是否开发html5 histroy api网页
        hot: true, // 是否开启模块热替换功能
        https: false, // 是否开启https模式
    },
    profile: true, // 是否捕捉webpack构建的性能信息,用于分析是什么原因导致构建性能不佳
    cache: false, // 是否启用缓存来提升构建速度
    watch: true, // 是否开始
    watchOptions: { // 监听模式选项
        ignored: /node_modules/,
        // 舰艇到变化发生后,等300ms再执行动作,截流,防止文件更新太快导致重新编译频繁太快,默认为300ms
        aggregateTimeout: 300
        // 不停的询问系统指定文件有没有发生变化,默认每秒询问1000次
        poll: 1000
    },
}

多种配置类型

除了通过导出一个object来描述webpack所需的配置,还有其他更灵活的方式,以简化不同场景的配置

导出一个function

使用方法:

const path = require('path');
const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
modules.exports = function (env = {}, argv) {
    const plugins = [];
    const isProduction = env['production'];
    // 生成环境中才压缩
    if(isProduction) {
        plugin.push(
            // 压缩输出的js代码
            new UglifyJsPlugin()
        )
    }
    return {
        plugins: plugins,
        // 在生成环境中不输出Source Map
        devtool: isProduction ? undefined : 'source-map',
    };
}

env: 当前运行时webpack专属环境变量,object argv: 在启动webpack时,通过命令行传入的所有参数

导出一个返回Promise的函数

module.exports = function(env = {}, argv) {
    return newPromise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                // ...
            })
        }, 5000)
    })
}

导出多份配置

webpack3.1.0开始支持

module.exports = [
    // 采用object描述的一份配置
    {
        // ...
    },
    // 采用函数描述的一份配置
    function() {
        return {
            // ...
        }
    },
    // 采用异步函数描述的一份配置
    function() {
        return Promise();
    }
]