注意:本文描述的配置只适用webpack1.x;因为webpack已经推出2.x并有大量更改,特此申明javascript
Webpack是一款用户打包前端模块的工具。主要是用来打包在浏览器端使用的javascript的。同时也能转换、捆绑、打包其余的静态资源,包括css、image、font file、template等。这里就尽可能详细的来介绍下一些基本功能的使用。css
npm install webpack -g
webpack须要编写一个config文件,而后根据这个文件来执行须要的打包功能。咱们如今来编写一个最简单的config。新建一个文件,命名为webpack-config.js。config文件实际上就是一个Commonjs的模块。内容以下:html
var path = require('path'); var buildPath = path.resolve(__dirname,"build"); var nodemodulesPath = path.resolve(__dirname,'node_modules'); var config = { //入口文件配置 entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"]//当requrie的模块找不到时,添加这些后缀 }, //文件导出的配置 output:{ path:buildPath, filename:"app.js" } } module.exports = config;
个人目录结构是这样的:前端
webpack |---index.html |---webpack-config.js |---src |---main.js |---js |---a.js
main.js文件内容以下:java
var a = require('./js/a'); a(); console.log('hello world'); document.getElementById("container").innerHTML = "<p>hello world</p>";
a.js文件内容以下:node
module.exports = function(){ console.log('it is a '); }
而后咱们执行以下的命令:react
webpack --config webpack-config.js --colors
这样咱们就能在目录里面看到一个新生成的目录build,目录结构以下:webpack
webpack |---index.html |---webpack-config.js |---build |---app.js
而后引用app.js就Ok啦。main.js和模块a.js的内容就都打包到app.js中了。这就演示了一个最简单的把模块的js打包到一个文件的过程了。git
webpack是根据config里面描述的内容对一个项目进行打包的。接着咱们来解释下config文件中的节点分别表明什么意思。一个config文件,基本都是由如下几个配置项组成的。es6
配置要打包的文件的入口;能够配置多个入口文件,下面会有介绍。
配置文件后缀名(extensions),除了js,还有jsx、coffee等等。
alias配置项,能够为经常使用模块配置改属性,能够节省编译的搜索时间。例如:
resolve:{ extensions:['.js','.jsx'], alias:{ 'react':path.join(nodeModulesPath,'react/react.js') } }
除了这个功能还能够配置其余有用的功能,因为我还不彻底了解,有知道的朋友欢迎指教。
配置输出文件的路径,文件名等。
配置要使用的loader。把资源文件(css、图片、html等非js模块)处理成相应的js模块,而后其它的plugins才能对这些资源进行下一步处理。好比babel-loader能够把es6的文件转换成es5。
大部分的对文件的处理的功能都是经过loader实现的。loader能够用来处理在入口文件中require的和其余方式引用进来的文件。loader通常是一个独立的node模块,要单独安装。
loader配置项:
test: /\.(js|jsx)$/, //注意是正则表达式,不要加引号,匹配要处理的文件 loader: 'eslint-loader', //要使用的loader,"-loader"能够省略 include: [path.resolve(__dirname, "src/app")], //把要处理的目录包括进来 exclude: [nodeModulesPath] //排除不处理的目录
目前已有的loader列表:https://webpack.github.io/docs/list-of-loaders.html
一个module的例子:
module: { preLoaders: [ { test: /\.(js|jsx)$/, loader: 'eslint-loader', include: [path.resolve(__dirname, "src/app")], exclude: [nodeModulesPath] }, ], loaders: [ { test: /\.(js|jsx)$/, //正则表达式匹配 .js 和 .jsx 文件 loader: 'babel-loader?optional=runtime&stage=0',//对匹配的文件进行处理的loader exclude: [nodeModulesPath]//排除node module中的文件 } ] }
顾名思义,就是配置要使用的插件。plugin是比loader功能更强大的插件,能使用更多的wepack api。来看一个使用plugin的例子:
plugins: [ //压缩打包的文件 new webpack.optimize.UglifyJsPlugin({ compress: { //supresses warnings, usually from module minification warnings: false } }), //容许错误不打断程序 new webpack.NoErrorsPlugin(), //把指定文件夹xia的文件复制到指定的目录 new TransferWebpackPlugin([ {from: 'www'} ], path.resolve(__dirname,"src")) ]
目前已有的plugins列表:http://webpack.github.io/docs/list-of-plugins.html
plugins: [ //压缩打包的文件 new webpack.optimize.UglifyJsPlugin({ compress: { //supresses warnings, usually from module minification warnings: false } })]
copy文件须要经过插件"transfer-webpack-plugin"来完成。
安装:
npm install transfer-webpack-plugin -save
配置:
var TransferWebpackPlugin = require('transfer-webpack-plugin'); //其余节点省略 plugins: [ //把指定文件夹下的文件复制到指定的目录 new TransferWebpackPlugin([ {from: 'www'} ], path.resolve(__dirname,"src")) ]
支持的js模块化方案包括:
ES6 模块
import MyModule from './MyModule.js';
CommonJS
var MyModule = require('./MyModule.js');
AMD
define(['./MyModule.js'], function (MyModule) { });
上面已经演示了打包js模块,这里再也不重复。ES6的模块须要配置babel-loader来先把处理一下js文件。
下面展现下打包ES模块的配置文件:
var webpack = require('webpack'); var path = require('path'); var buildPath = path.resolve(__dirname, 'build'); var nodeModulesPath = path.resolve(__dirname, 'node_modules'); var TransferWebpackPlugin = require('transfer-webpack-plugin'); var config = { entry: [path.join(__dirname, 'src/main.js')], resolve: { extensions: ["", ".js", ".jsx"] //node_modules: ["web_modules", "node_modules"] (Default Settings) }, output: { path: buildPath, filename: 'app.js' }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.NoErrorsPlugin(), new TransferWebpackPlugin([ {from: 'www'} ], path.resolve(__dirname,"src")) ], module: { preLoaders: [ { test: /\.(js|jsx)$/, loader: 'eslint-loader', include: [path.resolve(__dirname, "src/app")], exclude: [nodeModulesPath] }, ], loaders: [ { test: /\.js$/, //注意是正则表达式,不要加引号 loader: 'babel-loader?optional=runtime&stage=0',//babel模块相关的功能请自查,这里不作介绍 exclude: [nodeModulesPath] } ] }, //Eslint config eslint: { configFile: '.eslintrc' //Rules for eslint }, }; module.exports = config;
安装css-loader和style-loader
npm install css-loader --save -dev npm install style-loader --save -dev
config配置:
var config = { entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js" }, module:{ loaders:[{ test:/\.css$/, loader:'style!css',//sass配置:style!css!sass 执行顺序:左<--右 exclude:nodemodulesPath }] } }
style-loader会把css文件嵌入到html的style标签里,css-loader会把css按字符串导出,这两个基本都是组合使用的。打包完成的文件,引用执行后,会发现css的内容都插入到了head里的一个style标签里。
若是是sass或less配置方式与上面相似。
能够经过url-loader把较小的图片转换成base64的字符串内嵌在生成的文件里。
安装:
npm install url-loader --save -dev
config配置:
var config = { entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js" }, module:{ loaders:[{ test:/\.css$/, loader:'style!css',// exclude:nodemodulesPath }, { test:/\.png$/,loader:'url-loader?limit=10000'}//限制大小小于10k的 ] } }
css文件内容:
#container{ color: #f00; background:url(images/logo-201305.png); /*生成完图片会被处理成base64的字符串 注意:不要写'/images/logo-201305.png',不然图片不被处理*/ }
内嵌iconfont的使用方法其实和上述处理png图片的方法一致。经过url-loader来处理。
config配置:
var config = { entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js" }, module:{ loaders:[{ test:/\.css$/, loader:'style!css',// exclude:nodemodulesPath }, { test:/\.(png|woff|svg|ttf|eot)$/,loader:'url-loader?limit=10000'}//限制大小小于10k的 ] } }
css文件内容:
@font-face {font-family: 'iconfont'; src: url('fonts/iconfont.eot'); /* IE9*/ src: url('fonts/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('fonts/iconfont.woff') format('woff'), /* chrome、firefox */ url('fonts/iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('fonts/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ }
执行打包后会把字体文件都转换成base64字符串内容到文件里.
这里有个头疼的问题,就是每一个浏览器支持的字体格式不同,因为把所有格式的字体打包进去,形成没必要要的资源浪费。
我以打包handlebars的模块为例,来演示下打包模块的过程。有的模板对应的loader,有可能没有现成的,恐怕要本身实现loader。
先安装必须的node模块
npm install handlebars-loader --save -dev npm install handlebars -save//是必须的
config配置:
var config = { entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js" }, module:{ loaders:[ { test: /\.html$/, loader: "handlebars-loader" } ] } }
新建一个模板文件tb.html,目录结构:
webpack |---index.html |---webpack-config.js |---src |---template | |---tb.html |---main.js
main.js中调用模块的代码以下:
var template = require("./template/tp.html"); var data={say_hello:"it is handlebars"}; var html = template(data); document.getElementById('tmpl_container').innerHTML = html;
这须要经过插件“CommonsChunkPlugin”来实现。这个插件不须要安装,由于webpack已经把他包含进去了。
接着咱们来看配置文件:
var config = { entry:{app:path.resolve(__dirname,'src/main.js'), vendor: ["./src/js/common"]},//【1】注意这里 resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js" }, module:{ loaders:[{ test:/\.css$/, loader:'style!css', exclude:nodemodulesPath } ] }, plugins:[ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), //【2】注意这里 这两个地方市用来配置common.js模块单独打包的 new webpack.optimize.CommonsChunkPlugin({ name: "vendor",//和上面配置的入口对应 filename: "vendor.js"//导出的文件的名称 }) ] }
目录结构如今是这样的:
webpack |---index.html |---webpack-config.js |---src |---main.js |---js |---a.js //a里面require了common |---common.js
执行webpack会生成app.js和common.js两个文件.
有些场景,咱们可能但愿模块在须要的时候再加载,而不是一古脑儿打包到一块儿。从而加速首屏的加载速度。举个例子,在作单页应用的时候,每一个场景对应一个模块。若是场景不少,把模块打包到一块儿,最后的bundle文件必然很臃肿,加载很慢。那么只要在每一个场景须要展现的时候,再加载对应的js模块。就能够优化这个问题了。webpack支持模块按需加载,这个功能叫code split。下面介绍怎么使用这个功能。
目录结构:
webpack |---index.html |---webpack-config.js |---src |---main.js |---js |---codeSplit.js
codeSplit.js:
//就是普通的模块 没什么特殊的 console.log('code split'); module.exports = { name:'cplll' }
main.js:
var cp = function(resolve){ require.ensure(['./js/codeSplit.js'],function(){//注意这里哦,就是用require.ensure来按需加载的,这是webpack特有的 resolve(require('./js/codeSplit.js'));//加载好 require模块 }); } var getModule = function(){ return new Promise((resolve,reject)=>{ cp(resolve); }); } getModule().then((cl)=>{ console.log(cl.name); });
config配置:
//...省略 var buildPath = path.resolve(__dirname,"build"); var config = { entry:{ m1:path.resolve(__dirname,'src/main.js') },//注意在这里添加文件的入口 resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"app.js", publicPath:'build/' //注意这里哦,分离出来的模块会按这个路径来加载 } }
执行命令:
webpack --config webpack-config.js --colors
生成结果:
webpack |---index.html |---webpack-config.js |---build //生成结果 | |---app.js | |---1.app.js |---src |---main.js |---js |---codeSplit.js
页面里引用
<script type="text/javascript" src="./build/app.js"></script>
打开页面就是发现,app.js和1.app.js(在cp函数调用的时候加载)分开加载了。
最后须要特别注意,配置output里的publicPath。这里容易有坑。由于不配置加载路径是这样的:
http://localhost:9527/1.app.js
配置之后(publicPath:'build/'):
http://localhost:9527/build/1.app.js
config配置:
var config = { entry:{ m1:path.resolve(__dirname,'src/main.js'), m2:path.resolve(__dirname,'src/main1.js') },//注意在这里添加文件的入口 resolve:{ extentions:["","js"] }, output:{ path:buildPath, filename:"[name].js"//注意这里使用了name变量 } }
在开发的过程当中个,咱们确定不但愿,每次修改完都手动执行webpack命令来调试程序。因此咱们能够用webpack-dev-server这个模块来取代烦人的执行命令。它会监听文件,在文件修改后,自动编译、刷新浏览器的页面。另外,编译的结果是保存在内存中的,而不是实体的文件,因此是看不到的,由于这样会编译的更快。它就想到与一个轻量的express服务器。
安装:
npm install webpack-dev-server --save -dev
config配置:
var config = { entry:path.resolve(__dirname,'src/main.js'), resolve:{ extentions:["","js"] }, //Server Configuration options devServer:{ contentBase: '', //静态资源的目录 相对路径,相对于当前路径 默认为当前config所在的目录 devtool: 'eval', hot: true, //自动刷新 inline: true, port: 3005 }, devtool: 'eval', output:{ path:buildPath, filename:"app.js" }, plugins: [ new webpack.HotModuleReplacementPlugin(),//开启热替换插件 new webpack.NoErrorsPlugin() ] }
个人目录结构:
webpack |---index.html |---webpack-config.js//我把静态资源目录配置在了这里 |---src |---main.js |---js |---a.js |---common.js
执行命令:
webpack-dev-server --config webpack-dev-config.js --inline --colors
默认访问地址:
http://localhost:3000/index.html(根据配置会不同)
有一点须要声明,在index.html(引用导出结果的html文件)里直接引用“app.js”,不要加父级目录,由于此时app.js在内存里与output配置的目录无关:
<script type="text/javascript" src="app.js"></script>
详细文档在这里查看:http://webpack.github.io/docs/webpack-dev-server.html
热替换是指在应用运行时候替换、添加、移除某个模块而不须要所有模块从新编译、整个页面从新加载。在web应用变的愈来愈复杂的今天,webpack的编译速度会愈来愈慢。使用热替换能大大提升webpack的编译速度,提高开发效率。下面介绍如何基于webpack-dev-server配置热替换。
config配置:
var config = { entry:[ 'webpack/hot/dev-server',//注意点1:热替换配置点1 path.resolve(__dirname,'src/main1.js') ], // entry:{m1:path.resolve(__dirname,'src/main.js'), // m2:path.resolve(__dirname,'src/main1.js')}, resolve:{ extentions:["","js"] }, // target: 'node', //Server Configuration options devServer:{ contentBase: '', //Relative directory for base of server devtool: 'eval', hot: true, //注意点2:热替换配置点2 inline: true, port: 3005 //Port Number }, devtool: 'eval', output:{ path:buildPath, filename:"app.js" }, plugins: [ //Enables Hot Modules Replacement new webpack.HotModuleReplacementPlugin(),//注意点3:热替换配置点3 //Allows error warnings but does not stop compiling. Will remove when eslint is added new webpack.NoErrorsPlugin() ], }
配置文件里添加3个配置点
entry 节点里添加 'webpack/hot/dev-server'
devServer节点里添加 hot: true
plugins 节点里 new webpack.HotModuleReplacementPlugin()
这样配置文件就配置好了。接下来在代码文件里添加热替换要监听的模块。代码以下:
var h1 = require('./hot1'); if(module.hot){//判断是否开启了热替换 module.hot.accept('./hot1',function(){//在hot1模块更新时,执行替换 h1 = require('./hot1'); }); }
更多信息参考:webpack-dev-server和热替换介绍
在webpack.config.js使用process.env.NODE_ENV进行判断
在package.json里面的script设置环境变量,注意mac与windows的设置方式不同
"scripts": { "publish-mac": "export NODE_ENV=prod&&webpack -p --progress --colors", "publish-win": "set NODE_ENV=prod&&webpack -p --progress --colors", "dev-mac": "export NODE_ENV=dev&&webpack-dev-server", "dev-win": "set NODE_ENV=dev&&webpack-dev-server }
config配置:
//其余代码省略... var NODE_ENV = process.env.NODE_ENV; var config = { //入口文件配置 entry: path.resolve(__dirname, 'src/main.js'), resolve: { extentions: ["", "js"]//当requrie的模块找不到时,添加这些后缀 }, devtool: 'eval', //文件导出的配置 output: { path: buildPath, filename: "app.js" }, module: { loaders: [ { test: /\.html$/, loader: 'tmodjs',//对匹配的文件进行处理的loader exclude: [nodemodulesPath]//排除node module中的文件 } ] } } if(NODE_ENV === "prod"){//判断是生产环境执行生产配置 delete config.devtool; config.plugins = [ //压缩打包的文件 new webpack.optimize.UglifyJsPlugin({ compress: { //supresses warnings, usually from module minification warnings: false } })]; }
以后dev环境执行命令:npm run dev-win
生产环境执行命令:npm run publish-win
代码热替换, HotModuleReplacementPlugin
将css成生文件,而非内联,ExtractTextPlugin
报错但不退出webpack进程,NoErrorsPlugin
多个 html共用一个js文件(chunk),可用CommonsChunkPlugin
清理文件夹,Clean
调用模块的别名ProvidePlugin,例如想在js中用$,若是经过webpack加载,须要将$与jQuery对应起来