【注】本文原发自此处,转载请注明出处。css
本文译自【Webpack-The Confusing Parts】原文html
本文已同步发表在个人博客node
webpack
是当前最受欢迎的模块管理器(module bundler
),对于使用React
开发的项目来讲堪称神器。固然,对于使用其余框架,好比Angular
或者Backbone
等的开发者来讲,webpack
也是种很好的工具。react
第一次配置webpack.config.js
时,有不少地方使我很困惑。在使用了webpack
一段时间后,我认识到正是这些地方让webpack
如此强大和迷人。jquery
webpack
核心理念webpack
中,不只js
文件能够做为一个模块,其余文件(css
,images
,html
)均可以做为模块。这就是说,你能够在其余文件中加载这些模块,require('myJSfile.js')
,或者require('myCSSfile.css')
。这意味着咱们将任意文件拆分红便于管理的小文件,而后经过在其余文件中加载这些小文件来达到重复利用的目的。Load only "what" you need and "when" you need
)—— 通常状况下打包工具会将咱们全部的模块打包生成一个最终的文件bundle.js
。可是在实际应用中,bundle.js
一般会很大(10M~15M),须要很长时间才能加载完成。webpack
提供了多种code splitting
的方法,会生成过个打包后的文件,且支持按需加载。这样咱们只有在须要用到某个模块的时候才会异步加载该模块。如今咱们来看下这些使人困惑的部分。webpack
首先要明确的一点是,webpack
有不少特性,有些只在开发环境使用,还有些只在生产环境使用,固然还有在生产环境和开发环境均可以使用的。以下图所示:es6
因此一般咱们会有两个
config
文件,以针对开发环境和生产环境做不一样配置。
在package.json
作以下配置:web
"scripts": { //npm run build to build production bundles "build": "webpack --config webpack.config.prod.js", //npm run dev to generate development bundles and run dev. server "dev": "webpack-dev-server" }
webpack CLI
Vs webpack-dev-server
须要知道webpack
提供了两个接口npm
webpack
命令行工具(webpack CLI tool
) —— 默认使用这种方式,无需单独安装,被集成在webpack
中。webpack-dev-server
—— node.js
服务器,须要单独安装能够经过命令行添加参数,也能够经过配置文件(默认为webpack.config.js
),webpack
打包时会读取这些配置。json
最初学习
webpack
时你可能用的就是命令行方式,以后大部分使用命令行的场景为生产环境打包。
使用方法
方法 1: //全局安装 npm install webpack --g //在命令行使用 $ webpack //<--Generates bundle using webpack.config.js 方法 2 : //本地安装并保存在package.json中 npm install webpack --save //在scripts中添加 "scripts": { "build": "webpack --config webpack.config.prod.js -p", ... } //按如下方式运行 "npm run build"
webpack-dev-server
(适用于开发环境构建)webpack-dev-server
是一个基于Express
的node
服务器,默认使用8080
端口。这个方式的优势是它提供了浏览器热加载(Hot Module Replacement
)。
使用方法
方法一: //全局安装 npm install webpack-dev-server -g //在命令行使用 $ webpack-dev-server --inline --hot 方法二: //添加到package.json中 "script": { "start": "webpack-dev-server --inline --hot", ... } //在命令行使用 $ npm start 在浏览器中打开 http://localhost:8080/
webpack
和webpack-dev-server
选项须要注意的一点是,像inline
和hot
这些选项,只有webpack-dev-server
有;而另外一些好比hide-modules
是单独为webpack
命令行方式提供的选项。
webpack-dev-server
参数为webpack-dev-server
提供参数有两种方式。
webpack.config.js
中的devServer
CLI
选项使用方法
//经过CLI $ webpack-dev-server --hot --inline //经过webpack.config.js devServer: { inline: true, hot: true }
我发现经过devServer
设置的配置项(hot: true, inline: true
)有时不起做用。因此我更喜欢使用CLI
的方式,在package.json
中添加以下代码:
package.json { "script": { "start": "webpack-dev-server --hot --inline" } }
注意不要
同时设置devServer
中hot: true
和CLI
中--hot
"hot"
Vs "inline"
inline
模式会触发页面的动态重载(live reloading
);hot
模式会触发页面的热加载(hot Module Replacement
),这种模式只重载页面中变化了的部分。若是同时设置了inline
和hot
,webpack-dev-server
会先尝试HMR
,若是HMR
失败了,则重载整个页面。
//当代码发生变化时,如下3种方式都会从新打包,可是: //1. 不会重载页面 $ webpack-dev-server //2. 会重载整个页面 $ webpack-dev-server --inline //3. HMR, 若失败则加载整个页面 $ webpack-dev-server --inline --hot
entry
(String
Vs Array
Vs Object
)entry
指出了打包入口文件,支持字符串
,数组
和对象
三种形式。这三种形式有何区别呢?
若是为单一入口文件,也就是说入口文件只有一个,那这三种方式会获得相同的结果。
entry
— Array
如有多个入口文件,且彼此独立,那么可使用数组方式。好比入口文件为a.js
,b.js
,使用数组方式会将b.js
的内容追加到bundle.js
的内容后。
一个很常见的场景就是在html
文件加入统计代码,好比googleAnalytics.js
,就能够用数组的方式告知webpack
将其打包到bundle.js
末尾,以下:
entry
— Object
这种方式主要针对多页面应用(指包含多个html
文件)。这种方式可使webpack
根据这个对象一次就打包出多个文件。
以下这种配置能够打包出两个js
文件:indexEntry.js
和profileEntry.js
,能够分别在index.html
和profile.html
中引入。
{ entry: { "indexEntry": "./public/src/index.js", "profileEntry": "./public/src/profile.js" }, output: { path: "/dist", filename: "[name].js" //indexEntry.js & profileEntry.js } }
使用方法
//profile.html <script src="dist/profileEntry.js"></script> //index.html <script src="dist/indexEntry.js"></script>
注: output
中name
对应的是entry
中的属性名。
entry
— 结合使用array
和object
能够在object
内部再使用array
方式。好比以下配置:
{ entry: { "vendor": ['jquery', 'analytics.js', 'optimizely.js'], "index": "./public/src/index.js", "profile": "./public/src/profile.js" }, output: { path: "/dist", filename: "[name].js" //vendor.js, index.js & profile.js } }
output
— path
和publicPath
output
设定了打包生成文件的路径。它有两个属性path
和publicPath
。path
告知webpack
将打包生成后的文件存储于什么路径,好比咱们但愿将文件打包到dist
文件夹下,只需设置path
为/dist
便可;publicPath
用于在生产环境打包时更新文件(包括css
、html
)中的url
。
以下配置:
//开发环境config entry: __dirname + "/app/main.js", output: { path: __dirname + "/public", //开发环境中不须要使用publicPath, 除非你的静态资源好比图片等没有存储在本地开发环境。 //publicPath: "http://mycdn.com", filename: "bundle.js" } //生产环境config entry: __dirname + "/app/main.js", output: { path: __dirname + "/public", //publicPath: 一些插件(url-loader, file-loader, HtmlWebpackPlugin等) //在生成图片,样式表等的url路径时会用到该配置 //好比: //.image { // background-image: url('./test/png'); //} //按以下配置打包后会变成: //.image { // background-image: url('http://mycdn.com/test.png'); //} publicPath: "http://mycdn.com/", filename: "bundle.js" }
举个例子,在你的css
文件中,用到了./test.png
这个url
去加载本地的图片。可是在生产环境中,这张图片test.png
会存储在cdn
服务器上。这样若是仍是用./test.png
就会访问不到该图片,必须把文件中全部的url
手动改为cdn
的路径才能在生产环境使用。
webpack
为咱们提供的publicPath
这个属性使咱们能够很方便地处理这类问题。只须要将publicPath
设置为生产环境的路径,这些识别publicPath
的插件,好比url-loader
,就会自动为咱们处理好url
。以下图所示:
//开发环境,server和image都在本地 .image { background-image: url('./test.png'); } //生产环境, server在HeroKu服务器上,而image在cdn上 .image { background-image: url('https://someCDN/test.png'); }
(译者注:
publicPath
还用于指定在使用webpack-dev-server
时,如何访问其暂存于内存中的打包后的文件。)
Loaders And Chaining Loaders
)加载器是一些node
模块,能够加载(load
)或者引入(import
)各类类型的文件使其转化成浏览器支持的文件格式,包括js
,css
等等。
好比:可使用babel-loader
将使用ES6
写的js
文件转换为浏览器支持的ES5
格式。以下:
module { loaders: [ { test: /\.js$/, //检测js文件,若是是,则对其使用loader处理 exclude: /node_modules/, //不对node_modules下文件处理 loader: 'babel' //使用babel (对babel-loader的简写) } ] }
对同一种类型的文件能够链式运用多个加载器。链式调用为从右向左,经过!
分割加载器。
举例:咱们有一个css
文件myCSSFile.css
,咱们想将这个文件中的内容转换成<style>CSS content</style>
的形式插入到咱们的html
页面中。可使用两个加载器css-loader
和style-loader
来达成以上目的:
modules: { loaders: [ { test: /\.css$/, loader: 'style!css' //style-loader!css-loader的简写 } ] }
以下展现了其原理:
一、webpack
查找模块中依赖的css
文件。也就是说,webpack
会检查js
文件中是否引用了myCSSFile.css
。若是找到了依赖,webpack
会先用css-loader
对其进行处理。
二、css-loader
会加载全部的css
和这个css
的依赖(好比@import otherCSS
),并将css
的内容处理为JSON
数据格式。而后将结果传给style-loader
进行处理。
三、style-loader
会对接收到的json
数据进行处理,并将其处理为style
标签——<style>CSS contents</style>
,而后插入到html
页面中。
能够向loaders
传递各类参数进行配置。
在如下这个例子中,咱们对url-loader
进行了配置:小于1024字节的图片将会被转为为base64
格式,而大于1024字节的图片仍是使用图片url
。有两种方式进行配置:
//方式1 使用'?' { test: /\.png$/, loader: "url-loader?limit=1024" } //方式2 使用'query'属性 { test: /\.png$/, loader: "url-loader", query: {limit: 1024} }
.babelrc
文件使用babel-loader
的话,须要配置presets
才能正确转化,包括将es6
转换为es5
,将JSX
转为js
。能够经过以下方式设置参数
module: { loaders: [ { test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel', query: { presets: ['react', 'es2015'] } } ] }
可是在不少项目中babel
的配置可能会比较大,因此能够单独在babel
的配置文件.babelrc
中配置。若是有.babelrc
,babel-loader
会自动加载该文件。
以下:
//webpack.config.js module: { loaders: [ { test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel' } ] } //.babelrc { "presets": ["react", "es2015"] }
Plugins
)插件是一些node
模块,能够对生成的打包文件进行处理。
好比,uglifyJSPlugin
插件能够对打包后获得的bundle.js
进行压缩处理,减少文件体积。extract-text-webpack-plugin
运用了css-loader
和style-loader
将全部的css
统一处理并根据结果生成一个单独的css
文件(style.css
),将文件连接插入到html
文件中。
//webpack.config.js //获取全部的css文件,并将其内容整合,生成一个单独的css文件'style.css' var ETP = require('extract-text-webpack-plugin'); module: { loaders: [ { test: /\.css$/, loader: ETP.extract('style-loader', 'css-loader') } ] }, plugins: [ new ExtractTextPlugin("style.css") ]
注:
若是你只是想使用内联css
样式,在html
页面中加入style
标签,能够只用css
和style
加载器。以下:
module: { loaders: [ { test: /\.css$/, loader: 'style!css' } ] }
Loaders Vs Plugins
)能够看到,加载器做用于单独的文件,在bundle
生成以前完成;
插件做用于bundle
或chunk
,一般是在bundle
生成过程的最后进行。一些插件好比commonsChunksPlugins
甚至会影响bundle
如何生成。(译者注
:该插件用于提取出各个模块中引用的相同模块,下篇文章code splitting
中会详细说明)
Resolving File Extensions
)不少webpack
配置文件中都包含一个resolve extensions
的属性,其中包含一个空字符串。这个空字符串就是用于正确加载不含后缀的文件的。好比:require('./myJSFile')
或 import myJSFile from './myJSFile'
。
{ resolve: { extensions: ['', '.js', '.jsx'] } }
注: 翻译水平有限,若有问题还但愿你们能不吝赐教,但愿和你们共同进步。
(本文完)