webpack,一个广泛应用的模块打包器(一)——概念篇

鉴于我在学习的过程使用webpack的频率较高,所以想着要记录一下webpack的一些核心的知识点,此博文涉及的知识点可能不深,但涵盖了webpack的主要内容,主要目的是想将此次的学习过程记录下来,以便进行后续的深入学习,如果在理解上有偏差的话,还请大家不吝赐教~

概念篇

核心概念

webpack 是一个现代JavaScript应用程序的模块打包器。当webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成少量的bundle - 通常只有一个,由浏览器加载。在开始之前,我们需要先理解四个核心概念:入口(entry)、输出(output)、loader、插件(plugins)。

入口(Entry)

webpack创建应用程序所依赖的关系图时,图的起点就是我们的入口起点。入口起点告诉webpack从哪里开始,可以认为是app的第一个启动文件。

  • 单入口语法,用法:entry: string|Array

    1
    2
    3
    module.exports = {
    entry: '.src/app.js' //将src目录下的app.js文件作为入口文件
    }
  • 对象语法,用法:entry: {[entryChunkName: string]: string|Array}
    对象语法常用于:多页面应用程序,如

    1
    2
    3
    4
    5
    6
    7
    module.exports = {
    entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
    }
    }

表面上告诉我们,webpack需要创建3个依赖图,这些依赖图彼此之间完成分离,互相独立的。由于入口起点增多,多页应用能够复用入口起点之间的大量代码/模块,从而可以极大地从这些技术中受益。

出口(Output)

将所有文件归拢后,我们需要告诉webpack如何处理这些归拢在一起的文件

1
2
3
4
5
6
7
8
9
const path = require('path'); //引入path对象

module.exports = {
entry: '.src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack.bundle.js'
}
};

多个入口起点:出口处应该使用占位符来确保每个文件具有唯一的名称。

1
2
3
4
5
6
7
8
9
10
module.exports = {
entry: {
app: __dirname + '.src/app.js',
search: __dirname + './src/search.js'
},
output: {
path: __dirname + '/dist',
filename: 'bundle-[name].js' //dist目录下将生成"bundle-app.js""bundle-search.js"文件
}
};

我们通过output.filename和output.path属性,来告诉输出文件的名称以及路径。

Loader

webpack把每个文件(.css, .html, .less, .scss, .jpg等)都作为模块处理,然而 webpack 自身只理解 JavaScript。loader 可以使你在 import 或”加载”模块时预处理文件。
loader在文件被添加到依赖图中时,将文件转换为模块。

  • 识别出应该被对应的loader进行转换的文件。(test属性)
  • 转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle 中)。(use属性)

在使用前,我们需要提前安装相对应的loader,如:

1
$ cnpm install --save-dev vue-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const path = require('path'); //引入path对象

module.exports = {
entry: '.src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack.bundle.js'
},
module: {
rules: [
{ test: /\.vue$/,loader: 'vue-loader' } //用来解析vue后缀的文件
]
}
};

在应用程序中,我们有三种使用loader的方法:

  • 配置(推荐):在webpack.config.js文件中指定loader。
  • 内联:在每个 import 语句中显式指定 loader,如:

    1
    import Styles from 'style-loader!css-loader?modules!./styles.css';
  • CLI:在 shell 命令中指定它们,如:

    1
    webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

插件(Plugins)

由于loader仅在每个文件的基础上执行转换,而插件更常用于(但不限于)在打包模块的 “compilation” 和 “chunk” 生命周期执行操作和自定义功能。想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中,多数插件可以通过选项(option)自定义。插件目的在于解决loader无法实现的其他事。
由于插件可以携带参数/选项,你必须在webpack配置中,向plugins属性传入 new 实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const webpack = require('webpack'); //访问内置的插件
const path = require('path'); //引入path对象

module.exports = {
entry: '.src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack.bundle.js'
},
module: {
rules: [
{ test: /\.vue$/,loader: 'vue-loader' } //用来解析vue后缀的文件
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};

模块(Modules)

在模块化编程中,开发者将程序分解成离散功能块,并称之为模块,每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。
对比Node.js模块,webpack模块能够以各种方式表达它们的依赖关系,如:

  • ES2015 import语句
  • CommonJS require()语句
  • AMD define和require语句
  • css/sass/less 文件中的 @import 语句
  • 样式(url(…))或 HTML 文件img标签中的图片链接

webpack 通过 loader 可以支持各种语言和预处理器编写模块。

模块解析

resolver是一个库,用于帮助找到模块的绝对路径。
使用enhanced-resolve,webpack 能够解析三种文件路径:

  • 绝对路径
    1
    import "C:\\Users\\user\\file";

由于我们已经取得了文件的绝对路径,因此不需要进一步在做解析。

  • 相对路径
    1
    import "../src/file1";

在这种情况下,使用import或require的资源文件所在的目录被认为是上下文目录,在import/require中给定的相对路径,会添加此上下文路径,以产生模块的绝对路径。

  • 模块路径
    1
    import "module/lib/file";

模块将在resolve.modules中指定的所有目录内搜索。

构建目标(Targets)

因为服务器和浏览器代码都可以用JavaScript编写,所以webpack提供了多种构建目标。

1
2
3
module.exports = {
target: 'node' //webpack会编译为用于「类Node.js」环境
};

多个Target:尽管webpack不支持向target传入多个字符串,你可以通过打包两份分离的配置来创建同构的库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const path = require('path');
const serverConfig = {
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
}
//…
};

const clientConfig = {
target: 'web', // 默认是 'web',可省略
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
}
//…
};

module.exports = [ serverConfig, clientConfig ];

上面的例子将在你的dist文件夹下创建lib.js和lib.node.js文件。

模块热替换

模块热替换功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:

  • 保留在完全重新加载页面时丢失的应用程序状态。
  • 只更新变更内容,以节省宝贵的开发时间。
  • 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。

总结

webpack概念篇就差不多结束啦,其中包含webpack中核心的几个概念以及各自的用法,接下来我将继续记录webpack管理资源的相关知识点,希望能对大家有所帮助~

本文标题:webpack,一个广泛应用的模块打包器(一)——概念篇

文章作者:萌萌哒的邱邱邱邱

发布时间:2017年11月03日 - 21:11

最后更新:2018年02月06日 - 23:02

原始链接:https://qiuruolin.github.io/2017/11/03/webpack/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
感谢您的支持