webpack 是目前基于 React 和 Redux 开发的应用的主要打包工具.我想使用 Angular 2 或其他框架开发的应用也有很多在使用 Webpack.
当我第一次看到 Webpack 的配置文件时,它看起来非常的陌生,我非常的疑惑.经过一段时间的尝试之后我认为这是因为 Webpack 只是使用了比较特别的语法和引入了新的原理,因此会让使用者感到疑惑.这些也是导致 Webpack 不被人熟悉的原因.
因为刚开始使用 Webpack 很让人疑惑,我觉得有必要写几篇介绍 Webpack 的功能和特性的文章以帮助初学者快速理解.此文是最开始的一篇.
Webpack 的核心原理
1. 一切皆模块
正如 js 文件可以是一个 "模块(module)" 一样,其他的(如 CSS,image 或 html)文件也可视作模 块.因此,你可以 require('myJSfile.js') 亦可以 require('myCSSfile.css').这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用等的目的.
2. 按需加载
传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的 bundle.js 文件.但是在真实的 app 里边,"bundle.js" 文件可能有 10M 到 15M 之大可能会导致应用一直处于加载中状态.因此 Webpack 使用许多特性来分割代码然后生成多个 "bundle" 文件,而且异步加载部分代码以实现按需加载.
好了,下面来看看那些令人困惑的部分吧.
1. 开发模式和生产模式
首先要知道的是 Webpack 有许许多多的特性,一些是 "开发模式" 下才有的,一些是 "生产模式" 下才有的,还有一些是两种模式下都有的.
通常使用到Webpack如此多特性的项目都会有两个比较大的Webpack配置文件
为了生成 bundles 文件你可能在 package.json 文件加入如下的 scripts 项:
2. webpack CLI 和 webpack-dev-server
"scripts": {
// 运行npm run build 来编译生成生产模式下的bundles
"build": "webpack --config webpack.config.prod.js",
// 运行npm run dev来生成开发模式下的bundles以及启动本地server
"dev": "webpack-dev-server"
}
值得注意的是,Webpack 作为模块打包工具,提供两种用户交互接口:
Webpack CLI tool:默认的交互方式(已随 Webpack 本身安装到本地)
webpack-dev-server:一个 Node.js 服务器(需要开发者从 npm 自行安装)
Webpack CLI(有利于生产模式下打包)
这种方式可以从命令行获取参数也可以从配置文件(默认叫 webpack.config.js)获取,将获取到的参数传入 Webpack 来打包
当然你也可以从命令行(CLI)开始学习Webpack,以后你可能主要在生产模式下使用到它.
用法:
方式1 :
方式2 :
// 全局模式安装webpack
npm install webpack--g
// 在终端输入
$ webpack // <--使用webpack.config.js生成bundle
webpack-dev-server(有利于在开发模式下编译)
// 费全局模式安装webpack然后添加到package.json依赖里边
npm install webpack--save
// 添加build命令到package.json的scripts配置项
"scripts": {
"build": "webpack --config webpack.config.prod.js -p",
...
}
// 用法:
"npm run build"
这是一个基于 Express.js 框架开发的 web server,默认监听 8080 端口.server 内部调用 Webpack,这样做的好处是提供了额外的功能如热更新 "Live Reload" 以及热替换 "Hot Module Replacement"(即 HMR) 用法:
方式1 :
$ webpack - dev - server--inline--hot用法2 :
// 全局安装
npm install webpack - dev - server--save
// 终端输入
webpack VS Webpack-dev-server 选项
// 添加到package.json scripts
"scripts": {
"start": "webpack-dev-server --inline --hot",
...
}
// 运行:
$ npm start
// 浏览器预览:
http: //localhost:8080
注意像 inline 和 hot 这些选项是 Webpack-dev-server 特有的,而另外的如 hide-modules 则是 CLI 模式特有的选项.
webpack-dev-server CLI 选项和配置项
另外值得注意的是你可以通过以下两种方式向 webpack-dev-server 传入参数:
通过 webpack.config.js 文件的 "devServer" 对象
通过 CLI 选项
我发现有时devServer配置项(hot: true 和inline: true)不生效,我更偏向使用如下的方式向CLI传递参数
// 通过CLI传参
webpack - dev - server--hot--inline
// 通过webpack.config.js传参
devServer: {
inline: true,
hot: true
}
注意:确定你没有同时传入hot:true和-hot
// package.json
{
"scripts": "webpack-dev-server --hot --inline"
}
webpack-dev-server 的 "hot" 和 "inline" 选项
"inline" 选项会为入口页面添加 "热加载" 功能,"hot" 选项则开启 "热替换(Hot Module Reloading)",即尝试重新加载组件改变的部分(而不是重新加载整个页面).如果两个参数都传入,当资源改变时,webpack-dev-server 将会先尝试 HRM(即热替换),如果失败则重新加载整个入口页面.
3. "entry":值分别是字符串,数组和对象的情况
// 当资源发生改变,以下三种方式都会生成新的bundle,但是又有区别:
// 1. 不会刷新浏览器
$ webpack - dev - server
//2. 刷新浏览器
$ webpack - dev - server--inline
//3. 重新加载改变的部分,HRM失败则刷新页面
$ webpack - dev - server--inline--hot
Enter 配置项告诉 Webpack 应用的根模块或起始点在哪里,它的值可以是字符串,数组或对象.这看起来可能令人困惑,因为不同类型的值有着不同的目的.
像绝大多数 app 一样,倘若你的应用只有一个单一的入口,enter 项的值你可以使用任意类型,最终输出的结果都是一样的.
enter:数组类型
但是,如果你想添加多个彼此不互相依赖的文件,你可以使用数组格式的值.
例如,你可能在 html 文件里引用了 "googleAnalytics.js" 文件,可以告诉 Webpack 将其加到 bundle.js 的最后.
用法:
注意:文件名取自 "entry" 对象的键名.
//profile.html
<script src="dist/profileEntry.js"></script>
//index.html
<script src="dist/indexEntry.js"></script>
enter:混合类型
你也可以在 enter 对象里使用数组类型,例如下面的配置将会生成 3 个文件:vender.js(包含三个文件),index.js 和 profile.js 文件.
4. output:"path" 项和 "publicPath" 项
output 项告诉 webpack 怎样存储输出结果以及存储到哪里.output 的两个配置项 "path" 和 "publicPath" 可能会造成困惑.
"path" 仅仅告诉 Webpack 结果存储在哪里,然而 "publicPath" 项则被许多 Webpack 的插件用于在生产模式下更新内嵌到 css,html 文件里的 url 值.
例如,在 localhost(译者注:即本地开发模式)里的 css 文件中边你可能用 "./test.png" 这样的 url 来加载图片,但是在生产模式下 "test.png" 文件可能会定位到 CDN 上并且你的 Node.js 服务器可能是运行在 HeroKu 上边的.这就意味着在生产环境你必须手动更新所有文件里的 url 为 CDN 的路径.
然而你也可以使用 Webpack 的 "publicPath" 选项和一些插件来在生产模式下编译输出文件时自动更新这些 url.
5. 模块加载和链式模块加载
模块加载器是可自由添加的 Node 模块,用于将不同类型的文件 "load" 或 "import" 并转换成浏览器可以识别的类型,如 js,Stylesheet 等.更高级的模块加载器甚至可以支持使用 ES6 里边的 "require" 或 "import" 引入模块.
例如,你可以使用 babel-loader 来将使用 ES6 语法写成的文件转换成 ES5:
链式(管道式)的加载器(从右往左执行)
module: {
loaders: [{
test: /\.js$/, // 匹配.js文件,如果通过则使用下面的loader
exclude: /node_modules/, // 排除node_modules文件夹
loader: 'babel' // 使用babel(babel-loader的简写)作为loader
}]
多个 loader 可以用在同一个文件上并且被链式调用.链式调用时从右到左执行且 loader 之间用 "!" 来分割.
例如,假设我们有一个名为 "myCssFile.css" 的 css 文件,然后我们想将它的内容使用 style 标签内联到最终输出的 html 里边.我们可以使用 css-loader 和 style-loader 两个 loader 来达到目的.
这里展示它是如何工作的:
module: {
loaders: [{
test: /\.css$/,
loader: 'style!css' //(short for style-loader!css-loader)
}]
Webpack 在模块颞部搜索在 css 的依赖项,即 Webpack 检查 js 文件是否有 "require('myCssFile.css')" 的引用,如果它发现有 css 的依赖,Webpack 将 css 文件交给 "css-loader" 去处理
css-loader 加载所有的 css 文件以及 css 自身的依赖(如,@import 其他 css)到 JSON 对象里,Webpack 然后将处理结果传给 "style-loader"
style-loader 接受 JSON 值然后添加一个 style 标签并将其内嵌到 html 文件里
6. loader 自身可以配置
模块加载器(loader)自身可以根据传入不同的参数进行配置.
在下面的例子中,我们可以配置 url-loader 来将小于 1024 字节的图片使用 DataUrl 替换而大于 1024 字节的图片使用 url,我们可以用如下两种方式通过传入 "limit" 参数来实现这一目的
7. babelrc 文件
babal-loader 使用 "presets" 配置项来标识如何将 ES6 语法转成 ES5 以及如何转换 React 的 JSX 成 js 文件.我们可以用如下的方式使用 "query" 参数传入配置:
8. 插件
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}
]
}
插件一般都是用于输出 bundle 的 node 模块.
例如,uglifyJSPlugin 获取 bundle.js 然后压缩和混淆内容以减小文件体积.
类似的 extract-text-webpack-plugin 内部使用 css-loader 和 style-loader 来收集所有的 css 到一个地方最终将结果提取结果到一个独立的 "styles.css" 文件,并且在 html 里边引用 style.css 文件.
注意:如果你只是想把 css 使用 style 标签内联到 html 里,你不必使用 extract-text-webpack-plugin,仅仅使用 css loader 和 style loader 即可
//webpack.config.js
// 获取所有的.css文件,合并它们的内容然后提取css内容到一个独立的"styles.css"里
var ETP = require("extract-text-webpack-plugin");
module: {
loaders: [{
test: /\.css$/,
loader: ETP.extract("style-loader", "css-loader")
}]
},
plugins: [new ExtractTextPlugin("styles.css") //Extract to styles.css file
]
}
9. 加载器(loader)和插件
你可能已经意识到了,Loader 处理单独的文件级别并且通常作用于包生成之前或生成的过程中.
而插件则是处理包(bundle)或者 chunk 级别,且通常是 bundle 生成的最后阶段.一些插件如 commonschunkplugin 甚至更直接修改 bundle 的生成方式.
10. 处理文件的扩展名
很多 Webpack 的配置文件都有一个 resolve 属性,然后就像下面代码所示有一个空字符串的值.空字符串在此是为了 resolve 一些在 import 文件时不带文件扩展名的表达式,如 require('./myJSFile') 或者 import myJSFile from './myJSFile'(译者注:实际就是自动添加后缀,默认是当成 js 文件来查找路径)
就这么多.
{
resolve: {
extensions: ['', '.js', '.jsx']
}
}
转载自:https://segmentfault.com/a/1190000005089993?utm_source=tuicool&utm_medium=referral
来源: https://juejin.im/post/5a545ab4f265da3e484bb02f