手记

快速入门webpack(3)- 常用配置拆分

4. webapck常用到的各点拆分

4.1 entry相关


4.1.1webpack的多入口配置

上例的简单配置中,只有一个入口文件,那么如果对应于一个页面需要加载多个打包文件或者多个页面想同时引入对应的打包文件的时候,应该怎么做?

entry : {
    app1 : './app1.js',
    app2 : './app2.js'
}

multi-entry文件夹执行webpack,打包信息如下

可见生成了两个入口文件,以及各自对应的chunk


4.2 output相关


4.2.1 output.publicPath

output: {
    path: "/home/proj/cdn/assets/[hash]",
    publicPath: "http://cdn.example.com/assets/[hash]/"
}

引用一段官网的话:

The publicPath specifies the public URL address of the output files when referenced in a browser. For loaders that embed <script> or <link> tags or reference assets like images, publicPath is used as the href or url() to the file when it’s different then their location on disk (as specified by path).

大致意思就是:publicPath指定了你在浏览器中用什么地址来引用你的静态文件,它会包括你的图片、脚本以及样式加载的地址,一般用于线上发布以及CDN部署的时候使用。

比如有下面一段配置:

var path = require('path');
var HtmlWebpackPlugin =  require('html-webpack-plugin');

module.exports = {
    entry : './app.js',
    output : {
        path : './assets/',
        filename : '[name].bundle.js',
        publicPath : 'http://rynxiao.com/assets/'
    },
    module : {
        loaders : [
            { test : /\.js$/, loader : 'babel' },
            { test : /\.css$/, loader : 'style!css' }
        ]
    },
    plugins : [
        new HtmlWebpackPlugin({
            filename: './index-release.html',
            template: path.resolve('index.template'),
            inject: 'body'
        })
    ]
};

其中我将publicPath设置成了http://rynxiao.com/assets/,其中设置到了插件的一些东西,这点下面会讲到,总之这个插件的作用是生成了上线发布时候的首页文件,其中script中引用的路径将会被替换。如下图:


4.2.2 output.chunkFilename

各个文件除了主模块以外,还可能生成许多额外附加的块,比如在模块中采用代码分割就会出现这样的情况。其中chunkFilename中包含以下的文件生成规则:

[id] 会被对应块的id替换.

[name] 会被对应块的name替换(或者被id替换,如果这个块没有name).

[hash] 会被文件hash替换.

[chunkhash] 会被块文件hash替换.

例如,我在output中如下设置:

output : {
    path : './assets/',
    filename : '[name].[hash].bundle.js',
    chunkFilename: "chunk/[chunkhash].chunk.js"
}

同时我修改了一下basic/app.js中的文件

require('./app.css');

require.ensure('./main.js', function(require) {
    require('./chunk.js');
});

document.getElementById("container").textContent = "APP";

其中对应的chunk.js就会生成带有chunkhashchunk文件,如下图:

这在做给文件打版本号的时候特别有用,当时如何进行hash替换,下面会讲到


4.2.3 output.library

这个配置作为库发布的时候会用到,配置的名字即为库的名字,通常可以搭配libraryTarget进行使用。例如我给basic/webpack.config.js加上这样的配置:

output : {
    // ...
    library : 'testLibrary'
    // ...
}

那么实际上生成出来的main.bundle.js中会默认带上以下代码:

var testLibrary = (//....以前的打包生成的代码);
// 这样在直接引入这个库的时候,就可以直接使用`testLibrary`这个变量


4.2.4 output.libraryTarget

规定了以哪一种方式输出你的库,比如:amd/cmd/或者直接变量,具体包括如下

"var" - 以直接变量输出(默认library方式) var Library = xxx (default)

"this" - 通过设置this的属性输出 this["Library"] = xxx

"commonjs" - 通过设置exports的属性输出 exports["Library"] = xxx

"commonjs2" - 通过设置module.exports的属性输出 module.exports = xxx

"amd" - 以amd方式输出

"umd" - 结合commonjs2/amd/root

例如我以umd方式输出,如图:


4.3 module相关


4.3.1 loader!代表的含义

require("!style!css!less!bootstrap/less/bootstrap.less");
// => the file "bootstrap.less" in the folder "less" in the "bootstrap"
// module (that is installed from github to "node_modules") is
// transformed by the "less-loader". The result is transformed by the
// "css-loader" and then by the "style-loader".
// If configuration has some transforms bound to the file, they will not be applied.

代表加载器的流式调用,例如:

{ test : /\.css\.less$/, loader : 'style!css!less' }

就代表了先使用less加载器来解释less文件,然后使用css加载器来解析less解析后的文件,依次类推


4.3.2 loaders中的includeexclude

include表示必须要包含的文件或者目录,而exclude的表示需要排除的目录

比如我们在配置中一般要排除node_modules目录,就可以这样写

{ 
    test : /\.js$/, 
    loader : 'babel',
    exclude : nodeModuleDir 
}

官方建议:优先采用include,并且include最好是文件目录


4.3.3 module.noParse

使用了noParse的模块将不会被loaders解析,所以当我们使用的库如果太大,并且其中不包含requiredefine或者类似的关键字的时候(因为这些模块加载并不会被解析,所以就会报错),我们就可以使用这项配置来提升性能。

例如下面的例子:在basic/目录中新增no-parse.js

var cheerio = require('cheerio');

module.exports = function() {
    console.log(cheerio);
}

webpack.config.js中新增如下配置:

module : {
    loaders : [
        { test : /\.js$/, loader : 'babel' },
        { test : /\.css$/, loader : 'style!css' }
    ],
    noParse : /no-parse.js/
}

当执行打包后,在浏览器中打开index.html时,就会报错require is not defined

4.4 resolve相关

4.4.1 resolve.alias

为模块设置别名,能够让开发者指定一些模块的引用路径。对一些经常要被import或者require的库,如react,我们最好可以直接指定它们的位置,这样webpack可以省下不少搜索硬盘的时间。
例如我们修改basic/app.js中的相关内容:

var moment = require("moment");

document.getElementById("container").textContent = moment().locale('zh-cn').format('LLLL');

加载一个操作时间的类库,让它显示当前的时间。使用webpack --profile --colors --display-modules执行配置文件,得到如下结果:

其中会发现,打包总共生成了104个隐藏文件,其中一半的时间都在处理关于moment类库相关的事情,比如寻找moment依赖的一些类库等等。

basic/webpack.config.js加入如下配置,然后执行配置文件

resolve : {
    alias : {
        moment : 'moment/min/moment-with-locales.min.js'
    }
}

有没有发现打包的时间已经被大大缩短,并且也只产生了两个隐藏文件。

配合module.noParse使用

module.noParse参看上面的解释

noParse: [/moment-with-locales/]

执行打包后,效果如下:

是不是发现打包的时间进一步缩短了。

配合externals使用

externals参看下面的解释

Webpack 是如此的强大,用其打包的脚本可以运行在多种环境下,Web 环境只是其默认的一种,也是最常用的一种。考虑到 Web 上有很多的公用 CDN 服务,那么 怎么将 Webpack 和公用的 CDN 结合使用呢?方法是使用 externals 声明一个外部依赖。

externals: {
moment: true
}

当然了 HTML 代码里需要加上一行

<script src="//apps.bdimg.com/libs/moment/2.8.3/moment-with-locales.min.js"></script>

执行打包后,效果如下:


4.4.2 resolve.extensions

resolve : {
    extensions: ["", ".webpack.js", ".web.js", ".js", ".less"]
}

这项配置的作用是自动加上文件的扩展名,比如你有如下代码:

require('style.less');

var app = require('./app.js');

那么加上这项配置之后,你可以写成:

require('style');

var app = require('./app');

4.5 externals

当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:

//webpack.config.js
module.exports = {
    externals: {
      'react': 'React'
    },
    //...
}

externals对象的key是给require时用的,比如require('react'),对象的value表示的是如何在global(即window)中访问到该对象,这里是window.React。

同理jquery的话就可以这样写:'jquery': 'jQuery',那么require('jquery')即可。

HTML中注意引入顺序即可:

<script src="react.min.js" />
<script src="bundle.js" />

4.6 devtool

提供了一些方式来使得代码调试更加方便,因为打包之后的代码是合并以后的代码,不利于排错和定位。其中有如下几种方式,参见官网devtool

例如,我在basic/app.js中增加如下配置:

require('./app.css');

// 新增hello.js,显然在文件夹中是不会存在hello.js文件的,这里会报错
require('./hello.js');

document.getElementById("container").textContent = "APP";

执行文件,之后运行index.html,报错结果如下:

给出的提示实在main.bundle.js第48行,点进去看其中的报错如下:

从这里你完全看不出到底你程序的哪个地方出错了,并且这里的行数还算少,当一个文件出现了上千行的时候,你定位bug的时间将会更长。

增加devtool文件配置,如下:

module.exports = {
    devtool: 'eval-source-map',
    // ....
};

执行文件,之后运行index.html,报错结果如下:

这里发现直接定位到了app.js,并且报出了在第二行出错,点击去看其中的报错如下:

发现问题定位一目了然。

13人推荐
随时随地看视频
慕课网APP

热门评论

确实蛮有收获的,,我总算把那个该死的externals配置项搞懂了,谢谢

虽然都是webpack1的教程,不过还是很有收获的

楼主你好,很感谢你写的这篇文章。只是您的文章里没有特意强调您用的webpack版本,

关于您最后提到的devtool选项,指定source 值有个小疑问。

我使用的是webpack2. 我试验了您提到的eval-source-map,首先,按照您提到的直接import 或者require 一个不存在的文件,直接打包时就会报错。于是,我使用了console.log(a)打印一个未定义的变量a

结果是,打包后的错误提示里,并没有出现原始文件的错误提示。

我试验了其他几个选项,发现只有 cheap-module-source-map 和source-map这两个值是有效的,会提示错误的原始文件和行数。

另外,如果我使用了uglifyjsPlugin插件,不进行uglifyjs的任何设置。最后虽然生成了map文件,但是错误提示也是打包后的文件,不是原始文件。

希望大神能解答一下,source-map这几个选项的意义和区别,以及uglifyjs插件配置选项中的sourcemap是啥意思。我看webpack官网上的解释,看不明白。

感谢大神!!!

查看全部评论