Webpack 是当下最热门的前端资源模块化管理和打包工具。css
Webpack 是当下最热门的前端资源模块化管理和打包工具。它能够将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还能够将按需加载的模块进行代码分隔,等到实际须要的时候再异步加载。经过 loader 的转换,任何形式的资源均可以视做模块,好比 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。html
Webpack 和其余模块化工具备什么区别呢?前端
首先要安装 Node.js, Node.js 自带了软件包管理器 npm,Webpack能够经过npm去安装。node
用 npm 安装 Webpack:react
$ npm install webpack -g
此时 Webpack 已经安装到了全局环境下,能够经过命令行 webpack -h 试试。jquery
一般咱们会将 Webpack 安装到项目的依赖中,这样就可使用项目本地版本的 Webpack。webpack
# 进入项目目录 # 肯定已经有 package.json,没有就经过 npm init 建立 # 安装 webpack 依赖 $ npm install webpack --save-dev
查看webpackgit
# 查看 webpack 版本信息 $ npm info webpack
安装指定版本的 webpackes6
$ npm install webpack@1.12.x --save-dev
若是须要使用 Webpack 开发工具,要单独安装:github
$ npm install webpack-dev-server --save-dev
首先建立一个静态页面 index.html 和一个 JS 入口文件 entry.js:
<!-- index.html --> <html> <head> <meta charset="utf-8"> </head> <body> <script src="bundle.js"></script> </body> </html>
// entry.js document.write('It works.')
而后编译 entry.js 并打包到 bundle.js:
$ webpack entry.js bundle.js
打包过程会显示日志:
Hash: e964f90ec65eb2c29bb9 Version: webpack 1.12.2 Time: 54ms Asset Size Chunks Chunk Names bundle.js 1.42 kB 0 [emitted] main [0] ./entry.js 27 bytes {0} [built]
用浏览器打开 index.html 将会看到 It works. 。 接下来添加一个模块 module.js 并修改入口 entry.js:
// module.js module.exports = 'It works from module.js.'
// entry.js document.write('It works.') document.write(require('./module.js')) // 添加模块
从新打包 webpack entry.js bundle.js 后刷新页面看到变化
It works.It works from module.js. Hash: 279c7601d5d08396e751 Version: webpack 1.12.2 Time: 63ms Asset Size Chunks Chunk Names bundle.js 1.57 kB 0 [emitted] main [0] ./entry.js 66 bytes {0} [built] [1] ./module.js 43 bytes {0} [built]
Webpack 会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到 bundle.js 。Webpack 会给每一个模块分配一个惟一的 id 并经过这个 id 索引和访问模块。在页面启动时,会先执行 entry.js 中的代码,其它模块会在运行 require 的时候再执行。
Webpack 自己只能处理 JavaScript 模块,若是要处理其余类型的文件,就须要使用 loader 进行转换。
Loader 能够理解为是模块和资源的转换器,它自己是一个函数,接受源文件做为参数,返回转换的结果。这样,咱们就能够经过 require 来加载任何类型的模块或文件,好比 CoffeeScript、 JSX、 LESS 或图片。
先来看看 loader 有哪些特性?
loader 通常以 xxx-loader
的方式命名,xxx 表明了这个 loader 要作的转换功能,好比 json-loader
。
Loader 能够在 require() 引用模块的时候添加,也能够在 webpack 全局配置中进行绑定,还能够经过命令行的方式使用。
接上一节的例子,咱们要在页面中引入一个 CSS 文件 style.css,首页将 style.css 也当作是一个模块,而后用 css-loader 来读取它,再用 style-loader 把它插入到页面中。
/* style.css */ body { background: yellow; }
修改 entry.js:
require("!style-loader!css-loader!./style.css") // 载入 style.css document.write('It works.') document.write(require('./module.js'))
安装 loader:
npm install css-loader style-loader
从新编译打包,刷新页面,就能够看到黄色的页面背景了。
若是每次 require CSS 文件的时候都要写 loader 前缀,是一件很繁琐的事情。咱们能够根据模块类型(扩展名)来自动绑定须要的 loader。
将 entry.js 中的 require("!style-loader!css-loader!./style.css")
修改成 require("./style.css")
,而后执行:
$ webpack entry.js bundle.js --module-bind 'css=style!css'
# 有些环境下可能须要使用双引号 $ webpack entry.js bundle.js --module-bind "css=style!css"
显然,这两种使用 loader 的方式,效果是同样的。
Webpack 在执行的时候,除了在命令行传入参数,还能够经过指定的配置文件来执行。默认状况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者经过 --config 选项来指定配置文件。
建立一个配置文件 webpack.config.js:
var webpack = require('webpack') module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ {test: /\.css$/,loader: 'style-loader!css-loader'}] } }
webpack.config.js文件一般放在项目的根目录中,它自己也是一个标准的Commonjs规范的模块。在导出的配置对象中有几个关键的参数:
entry参数定义了打包后的入口文件,有三种写法,每一个入口称为一个chunk:
类型 | 示例 | 解释 |
---|---|---|
字符串 | entry: "./index/index.js" | 配置模块会被解析为模块,并在启动时加载。 chunk名为默认为main, 具体打包文件名视output配置而定。 |
数组 | entry: ['./src/mod1.js', [...,] './src/index.js'] | 全部的模块会在启动时 按照配置顺序 加载,合并到最后一个模块会被导出。chunk名默认为main |
对象 | entry:{index: '...', login : [...]} | 若是传入Object ,则会生成多个入口打包文件key 是chunk 名,value 能够是字符串,也但是数组。 |
{ entry: { page1: "./page1", //支持数组形式,将加载数组中的全部模块,但以最后一个模块做为输出 page2: ["./entry1", "./entry2"] }, output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" } }
该段代码最终会生成一个 page1.bundle.js 和 page2.bundle.js,并存放到 ./dist/js/page 文件夹下
output参数是个对象,定义了输出文件的位置及名字:
output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" }
当咱们在entry中定义构建多个文件时,filename能够对应的更改成[name].js用于定义不一样文件构建后的名字。
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不一样模块的加载是经过模块加载器(webpack-loader)来统一管理的。loaders之间是能够串联的,一个加载器的输出能够做为下一个加载器的输入,最终返回到JavaScript上: loader使用须要先安装再加入到配置下中。
Loaders须要单独安装而且须要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括如下几方面:
module: { //加载器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 来处理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.less 文件使用 less-loader、cssloader来编译 { test:/\.less$/, loader: 'style!less'} //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, ] }
babel-loader babel官网
// npm一次性安装多个依赖模块,模块之间用空格隔开 npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
module: { loaders: [ { test:/\.jsx?$/, exclude:/node_modules/, loader:'babel', query:{presets:['react','es2015']} } ] }
npm install babel-loader --save-dev npm install babel-preset-es2015 --save-dev
{ "presets": ["es2015"] }
npm install --save-dev url-loadr
module: { loaders: [ { test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=10000&name=build/images/[name].[ext]' } ] }
对于上面的配置,若是图片资源小于10kb就会转化成 base64 格式的 dataUrl,其余的图片会存放在build/images文件夹下。
npm install --save-dev file-loader
module: { loaders: [ { test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/, loader: 'file' }, ] }
module: { //加载器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 来处理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, //图片文件使用 url-loader 来处理,小于8kb的直接转为base64 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ] }
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程当中生效,执行相关的任务。
Webpack有不少内置插件,同时也有不少第三方插件,可让咱们完成更加丰富的功能。
要使用某个插件,咱们须要经过npm安装它,而后要作的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)继续看例子,咱们添加了一个实现版权声明的插件。
module.exports = { plugins: [ new webpack.BannerPlugin("Copyright Nico inc.") //在这个数组中new一个就能够了 ] }
这个插件的做用是依据一个简单的模板,帮你生成最终的HTML5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不一样的哈希值。
安装
npm install --save-dev html-webpack-plugin
这个插件自动完成了咱们以前手动作的一些事情,在正式使用以前须要对一直以来的项目结构作一些改变:
在app目录下,建立一个Html文件模板,这个模板包含title等其它你须要的元素,在编译过程当中,本插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,在本例中咱们命名模板文件名称为index.tmpl.html,模板源代码以下
index.tmpl.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Webpack</title> </head> <body> <div id='box'> </div> </body> </html>
webpack.config.js
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数 }) ] }
分离CSS和JS文件
安装
npm install --save-dev extract-text-webpack-plugin
webpack.config.js
var webpack = require('webpack'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); ... plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html" }), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("style.css") ] }
项目中,对于一些经常使用的组件,站点公用模块常常须要与其余逻辑分开,而后合并到同一个文件,以便于长时间的缓存。要实现这一功能,配置参照:
var webpack = require('webpack'); var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; ··· entry: { a: './index/a.js', b: './idnex/b.js', c: './index/c.js', d: './index/d.js' }, ··· plugins: [ new CommonsChunkPlugin('part.js', ['a', 'b']), new CommonsChunkPlugin('common.js', ['part1', 'c']) ] ···
webpack 自带了一个压缩插件 UglifyJsPlugin,只须要在配置文件中引入便可。
{ plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }
加入了这个插件以后,编译的速度会明显变慢,因此通常只在生产环境启用。
缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变) webpack能够把一个哈希值添加到打包的文件名中,使用方法以下,添加特殊的字符串混合体([name], [id] and [hash])到输出文件名前
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { entry: __dirname + "/app/main.js", output: { path: __dirname + "/build", filename: "[name]-[hash].js" }, module: {}, plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html" }), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("[name]-[hash].css") ] }
当咱们常用React、jQuery等外部第三方库的时候,一般在每一个业务逻辑JS中都会遇到这些库。 如咱们须要在各个文件中都是有jQuery的$对象,所以咱们须要在每一个用到jQuery的JS文件的头部经过require('jquery')来依赖jQuery。 这样作很是繁琐且重复,所以webpack提供了咱们一种比较高效的方法,咱们能够经过在配置文件中配置使用到的变量名,那么webpack会自动分析,而且在编译时帮咱们完成这些依赖的引入。
webpack.config.js中
var webpack = require('webpack'); ... plugins: [ new webpack.ProvidePlugin({ 'Moment': 'moment', "$": "jquery", "jQuery": "jquery", "window.jQuery": "jquery", "React": "react" }) ] ...
要告诉webpack咱们但愿当前是什么环境,只须要在命令中写入 BUILD_DEV=1 webpck 那么webpack经过配置,就会将全部咱们引用到的__DEV__变量设置为true。
咱们能够在package.json中事先定义好命令:
"scripts": { "dev": "BUILD_DEV=1 webpack-dev-server --progress --colors", "build": "BUILD_PRERELEASE=1 webpack -p" }
那么就能够避免输入冗长的命令了:
开发时输入:
npm run dev
发布时输入:
npm run build
webpack的使用和browserify有些相似,下面列举几个经常使用命令:
webpack 最基本的启动webpack命令 webpack -w 提供watch方法,实时进行打包更新 webpack -p 对打包后的文件进行压缩 webpack -d 提供SourceMaps,方便调试 webpack --colors 输出结果带彩色,好比:会用红色显示耗时较长的步骤 webpack --profile 输出性能数据,能够看到每一步的耗时 webpack --display-modules 默认状况下 node_modules 下的模块会被隐藏,加上这个参数能够显示这些被隐藏的模块
/ ├── shell/ 脚本 │ ├── conf/ 工程配置 │ ├── src/ 开发目录 │ ├── components/ 组件 │ ├── pages/ 页面 │ ├── ├── dist/ 自动生成 │ ├── test/ 测试 │ ├── node_modules/ 自动生成,包含.Node 依赖以及开发依赖 │ ├── static/ 库文件等,不会被webpack的loader处理,手动管理 │ └── etc
完整的目录结构:
projectTemplate/ ├── shell/ node脚本 │ ├── │ ├── dev-server.js 本地开发服务器 │ ├── build.js 打包脚本 │ ├── utils.js 工具函数 │ ├── ├── conf/ 工程配置 │ ├── │ ├── index.js 基础配置文件,在此可简单的修改webpack相关配置 │ ├── webpack.base.js webpack的基础配置,主要是loader、resolve的配置 │ ├── webpack.dev.js webpack开发配置,主要是eslint、livereload、hot module replacement及相关的插件 │ ├── webpack.prod.js webpack生产配置,主要是代码的压缩混淆,图片压缩,加hash │ ├── karma.conf.js 测试配置 │ ├── ├── src/ 开发目录 │ ├── components/ 组件 │ ├── pages/ 页面(页面下的项目目录须要遵循必定的规范以便建立webpack的入口文件,不过这些规范是能够调整的;如下只是推荐) │ ├── index/ 首页 │ ├── images/ 图片资源 │ ├── page.css 样式文件,文件名称能够按照本身意愿命名 │ ├── page.js 脚本文件及webpack的入口文件,文件名称能够在/conf/index.js配置 │ ├── template.html 模板文件及要撰写的html文件,文件名称能够在/conf/index.js配置 │ ├── │ ├── dist/ 自动生成 │ ├── test/ 测试(目录能够意愿来建立,可是测试文件名称必须遵循*_test.js的命名规范,可在/conf/karma.conf.js修改配置) │ ├── node_modules/ 自动生成,包含node依赖以及开发依赖 │ ├── static/ 库文件等,不会被webpack的loader处理,手动管理 │ └── etc
想不想让你的浏览器监测你都代码的修改,并自动刷新修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,能够实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置以前须要单独安装它做为项目依赖
npm install --save-dev webpack-dev-server
devserver做为webpack配置选项中的一项,具备如下配置选项
devserver配置选项 | 功能描述 |
---|---|
contentBase | 默认webpack-dev-server会为根文件夹提供本地服务器, 若是想为另一个目录下的文件提供本地服务器, 应该在这里设置其所在目录(本例设置到“public"目录) |
port | 设置默认监听端口,若是省略,默认为”8080“ |
inline | 设置为true,当源文件改变时会自动刷新页面 |
colors | 设置为true,使终端输出的文件为彩色的 |
historyApiFallback | 在开发单页应用时很是有用,它依赖于HTML5 history API, 若是设置为true,全部的跳转将指向index.html |
以下配置:
module.exports = { ... devServer: { contentBase: "./public",//本地服务器所加载的页面所在的目录 colors: true,//终端中输出结果为彩色 historyApiFallback: true,//不跳转 inline: true//实时刷新 } }