本文同步自个人博客http://joeray61.comcss
本文是一个简单的webpack入门教程,但愿可以帮助webpack初学者快速上手。若有错误,敬请斧正。
本文全部的demo均可以在webpack-demo里找到,git clone
以后须要执行npm install
安装全部依赖包。html
webpack
是一个模块打包工具,出自德国开发者Tobias Koppers
之手,处理速度很快。在webpack
看来,一切资源均可以是一个模块,不只限于js文件。正是因为这些特色,使得webpack
能够替代Grunt
和Gulp
,成为了目前业内最火的前端构建工具。前端
webpack能够安装到全局,也能够做为项目的依赖工具安装到项目中node
// 全局安装 $ npm install -g webpack $ npm install -g webpack-dev-server
若是报没有权限的话,在命令前面加上sudo
就能够了react
// 局部安装 $ npm install --save-dev webpack $ npm install --save-dev webpack-dev-server
能够看到,咱们在安装webpack
的同时,也安装了webpack-dev-server
。那么webpack-dev-server
是什么呢?它是一个轻量级的基于express
的node.js
服务器,用来服务资源文件。不是必须的,可是建议一块儿安装。webpack
$ webpack main.js bundle.js
以上是直接使用webpack
命令行来进行打包的命令,把main.js
构建成bundle.js
,具体示例能够查看demo1,在文件夹中执行上方的命令便可。git
webpack
有一些参数是咱们应该要知道的github
webpack
: 构建一次开发版本web
webpack -p
: 构建一次产品版本,与开发版本的区别是会对文件进行压缩正则表达式
webpack -d
: 添加source map
webpack --watch
: 监听文件的改动,持续增量构建
webpack --colors
: 让命令行的输出更好看一点(实际使用zsh
的命令行时发现并无区别)
webpack
还有不少强大的功能,这些均可以在命令行使用,可是若是都写在命令行,既不方便使用,也不能直观地反映各个配置项,这时候,就须要配置文件登场了。
webpack
默认使用的配置文件名是webpack.config.js
,也能够经过--config
参数在命令行指定另外一个命名的配置文件。配置文件其实也是一个模块,全部的构建信息都放在module.exports
中。下面咱们就来说一讲各个重要的配置项。
如下各个示例请在各demo文件夹中执行
webpack-dev-server
进行查看
入口文件的配置项名称是entry
,表明了webpack
给整个项目进行打包的一个主入口,能够搭配output
属性指定的输出文件来使用
// webpack.config.js module.exports = { entry: 'main.js', output: { filename: 'bundle.js' } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo2</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello Webpack!</h1>');
示例见demo2
在多页的应用中,咱们须要有多个入口文件,这在webpack
中也是能够支持的。
// webpack.config.js module.exports = { entry: { profile: './profile.js', feed: './feed.js' }, output: { filename: '[name].bundle.js' } }; // feed.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo3-Feed</title> </head> <body> <script src="feed.bundle.js"></script> </body> </html> // feed.js document.write('<h1>Hello Feed!</h1>'); // profile.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo3-Profile</title> </head> <body> <script src="profile.bundle.js"></script> </body> </html> // profile.js document.write('<h1>Hello Profile!</h1>');
示例见demo3,在demo3文件夹中启动webpack-dev-server
,访问localhost:8080/profile.html
和localhost:8080/feed.html
查看效果。
Loaders
顾名思义就是加载器,用于加载各类格式的文件,例如React
使用的JSX
,ES6
和ES7
,图片、CSS
、JSON
文件等,可谓是webpack
中最核心的功能之一。官方文档中列出了全部可用的loaders。
loaders
配置在module.exports
中的module
字段下(提及来有点绕,待会儿看代码就懂了),是一个数组,表明loader
的集合,每个loader
有几个配置项:
test
: 正则表达式,用于匹配文件的路径,通常都直接用来匹配文件后缀,在复杂的场景下,能够针对同一类型的不一样文件作不一样处理
loader
: loader
的名称,实际使用时能够省略-loader
的后缀(例如css-loader
,能够写成css
)
include
: 必须包含的文件路径
exclude
: 不须要处理的文件路径
query
: loader
的额外设置选项
下面我介绍几个经常使用的loader。
先安装style-loader
和css-loader
。其中,style-loader
用来向 HTML 页面中插入<style>
,css-loader
用来读取 css 文件。
$ npm install --save-dev style-loader css-loader
而后在webpack.config.js
中配置loader
module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: 'style!css' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo4</title> </head> <body> <h1>Hello World!</h1> <script src="bundle.js"></script> </body> </html> // a.css h1 {color: red;} // main.js require('./a.css');
示例见demo4,能够看到,多个loader
之间用!
链接。在本地用webpack-dev-server
启动服务,访问localhost:8080
能够看到h1
中的字体颜色如a.css
定义的那样显示红色
这个 demo 中的 css 是对全局生效的,若是须要只对当前模块生效,就要用到咱们下面介绍的css module
。
近几年,模块化这个概念在前端被不断地被说起。模块化使得代码被分割成一个个更小的、独立的、可维护性更高的独立单元。相比于JS
,CSS
的模块化发展的相对慢一点。最近出现了一个叫作css modules
的技术,在css模块中,全部的类名和动画名默认只对向前模块生效。webpack
对css module
提供了不错的支持,只须要在css-loader
后面加上?module
便可使用。若是要对全局生效,可使用:global()
,把选择器做为参数传入。
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loader: 'style!css?module' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo5</title> </head> <body> <div class="foo">Foo</div> <div class="bar">Bar</div> <script src="bundle.js"></script> </body> </html> // a.css .foo {color: red;} :global(.bar) { color: green; } // main.js var style = require('./a.css'); document.write('<div class=' + style.foo + '>Module - Foo</div>'); document.write('<div class="bar">Module - Bar</div>');
须要注意的是,在main.js
中使用时,必须用变量style.foo
,直接写成class="foo"
是不会生效的,由于a.css
中的.foo
的类名被编译了,因此导出的style.foo
将不等于foo
。
示例代码见demo5。在 demo5 的目录下执行webpack-dev-server
,在浏览器中打开localhost:8080
,能够看到对应的效果。
先安装url-loader
和file-loader
$ npm install --save-dev url-loader file-loader
注意,
url-loader
对file-loader
有依赖,可是安装url-loader
时不会自动安装file-loader
,这里我暂时没有花时间去了解缘由,先都手动安装吧。若是不安装file-loader
,超过limit
大小的文件将没法加载。
url-loader
是一个文件加载器,咱们能够给他设定一个limit
,用来限定文件大小,文件小于这个大小时会被转换成一个Data Url
,反之转换成普通url表示资源路径。咱们经常使用url-loader
来加载图片。
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo6</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js var img1 = new Image(); img1.src = require('./webpack.png'); document.body.appendChild(img1); var img2 = new Image(); img2.src = require('./wade.jpg'); document.body.appendChild(img2);
示例代码见demo6。在demo6目录下运行webpack-dev-server
,在浏览器中访问localhost:8080
,审查元素能够看到,8k如下的图片使用了Data Url
,8k以上的图片使用的是资源地址。
现现在,ES6
、React
可谓是红透半边天,提及ES6
、React
,就不得不提Babel
了。Babel
是一个 Javascript 编译工具,可让你如今就能使用目前还未被浏览器彻底支持的下一代JS(ES6/ES7),或是基于 JS 进行扩展的 JSX 等。
咱们先安装须要的包
$ npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom
其中,babel-core
是babel
的核心功能,babel-loader
是webpack
使用的babel
加载器,babel-preset-es2015
和babel-preset-react
是babel
用来解析ES6
和JSX
的包,react
和react-dom
是开发react
的依赖包。
// webpack.config.js module.exports = { entry: './main.jsx', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] } } ] } }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo7</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html> // main.jsx import React from 'react'; import {render} from 'react-dom'; render( <h1>Hello world!</h1>, document.querySelector('#root') );
能够看到,在这个配置中,咱们把babel-loader
的配置写在query
字段中。其实也能够直接写在loader
字段中,loader: 'babel-loader?presets[]=es2015&presets[]=react'
,可是这样就不是太清晰了,建议配置比较长时拿出来放到query
字段中去。
示例代码见demo7,在demo7目录下执行webpack-dev-server
,在浏览器中访问localhost:8080
查看效果。
plugins
是webpack
中另外一个很是重要且又用的配置,它与loaders
的区别在于,loader
是用来加载文件的,会逐个文件处理,而plugins
是做为webpack
功能的补充,整个构建过程都在持续发挥做用。下面我来给你们介绍几个经常使用的插件。
这个插件估计不少人看名字就猜出来了,是用于压缩 JS 代码的。它是webpack
的内置插件,不须要安装就可使用。
直接上代码吧:
// webpack.config.js var webpack = require('webpack'); var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [ new UglifyJsPlugin() ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo8</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js var name = 'JoeRay61'; var age = 24; document.write('My name is ' + name + ', I\'m ' + age + ' years old.');
示例代码见demo8。在demo8目录下执行webpack-dev-server
,在浏览器中访问localhost:8080
,打开开发这工具,能够看到bundle.js的代码被压缩成了一行,变量名也被替换了,说明插件生效了。
!function(r){function e(t){if(o[t])return o[t].exports;var n=o[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(r,e){var o="JoeRay61",t=24;document.write("My name is "+o+", I'm "+t+" years old.")}]);
想象这样一种场景,咱们但愿在开发环境中在代码中输出一些调试信息,那么如何在webpack
中实现呢。这时候就须要用上Feature Flags
。它是借由webpack
的自定义插件机制实现的全局环境变量,咱们能够在代码中判这些全局变量的值来实现定制化的功能。
// webpack.config.js var webpack = require('webpack'); var flags = new webpack.DefinePlugin({ __PROD__: JSON.stringify(JSON.parse(process.env.PROD || 'false')) }); module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [flags] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo9</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello world!</h1>'); if (!__PROD__) { document.write('<p>it is dev version</p>'); }
示例代码见demo9。进入demo9目录,分别执行PROD=1 webpack-dev-server
和webpack-dev-server
,在浏览器中访问localhost:8080
,能够看到区别。
HMR
是webpack
中一个激动人心的功能,中文直译叫模块热替换
,是指配合webpack-dev-server
的服务器,在你修改了项目的模块以后,不须要手动刷新页面,便可以看到更新后的效果,有效地提高了开发效率。
配合webpack-dev-server
,咱们有2种方式能够启用该功能:
在命令中中webpack-dev-server
命令后面追加--hot
和--inline
参数
--hot
: 添加HMR插件,将服务器切换到 hot 模式
--inline
: 将webpack-dev-server
的 runtime 加入到打包后的文件中
--hot --inline
: 当这两个参数并存时,会额外添加一个webpack/hot/dev-server
的entry
配置webpack.config.js
添加new webpack.HotModuleReplacementPlugin()
插件
添加webpack/hot/dev-server
和webpack-dev-server/client?http://localhost:8080
这2个entry
针对第2中方式咱们来测试一下:
// webpack.config.js var webpack = require('webpack'); module.exports = { entry: [ 'webpack/hot/dev-server', 'webpack-dev-server/client?http://localhost:8080', './main.js' ], output: { filename: 'bundle.js' }, plugins: [ new webpack.HotModuleReplacementPlugin() ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo10</title> </head> <body> <script src="bundle.js"></script> </body> </html> // main.js document.write('<h1>Hello world!</h1>');
注意,测试过程当中我发现,若是原来是全局安装的
webpack-dev-server
,必需要在项目中局部安装webpack-dev-server
,不然启动服务器时会报错
安装命令$ npm install --save-dev webpack-dev-server
示例代码见demo10。在demo10目录下,执行webpack-dev-server
,在浏览器中查看localhost:8080
,发现输出Hello world!
,这时候不要关闭服务器,直接修改main.js
的代码,保存后查看浏览器效果,发现虽然没有手动刷新页面,可是效果已经出来了。
当几个不一样的脚本有公共的部分时,咱们能够把公共部分抽出来放到一个单独的文件中。
// webpack.config.js var webpack = require('webpack'); module.exports = { entry: { index: './index.js', main: './main.js' }, output: { filename: '[name].bundle.js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin('common.js') ] }; // index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo11</title> </head> <body> <script src="common.js"></script> <script src="index.bundle.js"></script> <script src="main.bundle.js"></script> </body> </html> // index.js var data = require('./data'); document.write('<p>foo is ' + data.foo + '</p>'); // main.js var data = require('./data'); document.write('<p>bar is ' + data.bar + '</p>'); // data.js var data = { foo: 123, bar: 456 }; module.exports = data;
具体示例见demo11。在demo11目录下执行webpack-dev-server
,在浏览器中访问localhost:8080
,打开开发者工具,发现data.js
被抽出单独放到了common.js
中。
本文是我本身学习webpack
的一个笔记,我把它记录下来,但愿能给其余的webpack
初学者提供一点帮助,谢谢观看,欢迎交流!