此篇终结,请直接看 Webpack2 中文版!
此篇终结,请直接看 Webpack2 中文版!
此篇终结,请直接看 Webpack2 中文版!css
首先要安装 Node.js, Node.js 自带了软件包管理器 npm。用 npm 全局安装 Webpack:html
$ npm install webpack -g
一般咱们会将 Webpack 安装到项目的依赖中,这样就可使用项目本地版本的 Webpack。前端
# 进入项目目录,初始化,建立 package.json。 # 若存在 package.json 文件,则不运行。 $ npm init # 肯定已经有 package.json # 安装 webpack 依赖 $ npm install webpack --save-dev
若是须要使用 Webpack 开发工具,要单独安装:node
$ npm install webpack-dev-server --save-dev
首先建立一个静态页面 index.html
和一个 JS 入口文件 entry.js
:react
<!-- 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
:jquery
$ webpack entry.js bundle.js
用浏览器打开 index.html
将会看到webpack
It works.
最终目录结构以下:git
. ├── entry.js ├── index.html ├── package.json ├── node_modules
接下来添加一个模块 module.js
并修改入口 entry.js
:github
// 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
后刷新页面看到变化web
It works.It works from module.js.
最终目录结构以下:
. ├── bundle.js ├── entry.js ├── index.html ├── module.js ├── package.json ├── node_modules
Webpack 自己只能处理 JavaScript 模块,若是要处理其余类型的文件,就须要使用 loader 进行转换。
Loader 能够理解为是模块和资源的转换器,它自己是一个函数,接受源文件做为参数,返回转换的结果。这样,咱们就能够经过 require 来加载任何类型的模块或文件,好比 CoffeeScript、 JSX、 LESS 或图片。
先来看看 loader 有哪些特性?
Loader 能够经过管道方式链式调用,每一个 loader 能够把资源转换成任意格式并传递给下一个 loader ,可是最后一个 loader 必须返回 JavaScript。
Loader 能够同步或异步执行。
Loader 运行在 node.js 环境中,因此能够作任何可能的事情。
Loader 能够接受参数,以此来传递配置项给 loader。
Loader 能够经过文件扩展名(或正则表达式)绑定给不一样类型的文件。
Loader 能够经过 npm 发布和安装。
除了经过 package.json 的 main 指定,一般的模块也能够导出一个 loader 来使用。
Loader 能够访问配置。
插件可让 loader 拥有更多特性。
Loader 能够分发出附加的任意文件。
Loader 自己也是运行在 node.js 环境中的 JavaScript 模块,它一般会返回一个函数。大多数状况下,咱们经过 npm 来管理 loader,可是你也能够在项目中本身写 loader 模块。
按照惯例,而非必须,loader 通常以 xxx-loader
的方式命名,xxx
表明了这个 loader 要作的转换功能,好比 json-loader
。
除了npm安装模块的时候之外,在任何场景下,loader名字都是能够简写的。例如:安装时必须用全名,即:npm install json-loader
,而在引用 loader 的时候可使用全名 json-loader
,也可使用短名 json
。这个命名规则和搜索优先级顺序在 webpack 的 resolveLoader.moduleTemplates
api 中定义。
Default: ["*-webpack-loader", "*-web-loader", "*-loader", "*"]
Loader 能够在 require()
引用模块的时候添加,也能够在 webpack 全局配置中进行绑定,还能够经过命令行的方式使用。
loader是能够串联使用的,也就是说,一个文件能够先通过A-loader再通过B-loader最后再通过C-loader处理。而在通过全部的loader处理以前,webpack会先取到文件内容交给第一个loader。
接上一节的例子,咱们要在页面中引入一个 CSS 文件 style.css
,首先将 style.css
也当作是一个模块,而后用 css-loader
来读取处理(路径处理、import处理等),而后通过 style-loader
处理(包装成JS文件,运行的时候直接将样式插入DOM中)。
/* style.css */ body { background: yellow; }
修改 entry.js
:
// entry.js require("!style!css!./style.css") // 载入 style.css document.write('It works.') document.write(require('./module.js'))
安装 loader:
# css-loader:读取 css 文件 # style-loader:将 css 文件插入页面 $ npm install css-loader style-loader --save-dev
从新编译打包,刷新页面,就能够看到黄色的页面背景了。
若是每次 require CSS 文件的时候都要写 loader 前缀,是一件很繁琐的事情。咱们能够根据模块类型(扩展名)来自动绑定须要的 loader。
将 entry.js
中的 require("!style!css!./style.css")
修改成 require("./style.css")
,而后执行:
$ webpack entry.js bundle.js --module-bind 'css=style!css'
显然,这两种使用 loader 的方式,效果是同样的。
最终的目录结构以下:
. ├── bundle.js ├── entry.js ├── index.html ├── module.js ├── node_modules ├── package.json ├── style.css
loader还能够接受参数,不一样的参数可让loader有不一样的行为(前提是loader确实支持不一样的行为),具体每一个loader支持什么样的参数能够参考loader的文档。loader的使用有三种方法,分别是:
在require中显式指定,如:
在命令行中指定,如:$ webpack entry.js output.js --module-bind 'css=style!css'
在配置项(webpack.config.js)中指定,如:
第一种显式指定,即在 JS
文件中指定:
require('style!css!./style.css');`
第二种在命令行中指定参数的用法用得较少,能够这样写:
$ webpack --module-bind jade --module-bind 'css=style!css'
使用 --module-bind
指定loader,若是后缀和loader同样,直接写就行了,好比jade表示.jade文件用jade-loader处理,若是不同,则须要显示指定,如 css=style!css
表示分别使用 css-loader
和 style-loader
处理 .css
文件。
第三种在配置项中指定是最灵活的方式,它的指定方式是这样:
module: { // loaders是一个数组,每一个元素都用来指定loader loaders: [{ test: /\.jade$/, //test值为正则表达式,当文件路径匹配时启用 loader: 'jade', //指定使用什么loader,能够用字符串,也能够用数组 exclude: /regexp/, //可使用exclude来排除一部分文件 //可使用query来指定参数,也能够在loader中用和require同样的用法指定参数,如`jade?p1=1` query: { p1:'1' } }, { test: /\.css$/, loader: 'style!css' //loader能够和require用法同样串联 }, { test: /\.css$/, loaders: ['style', 'css'] //也能够用数组指定loader }] }
注意: 用数组指定串联loader时,配置文件中要写 loaders
,而非 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!css' } ] } }
同时简化 entry.js 中的 style.css 加载方式:
require('./style.css')
最后运行 webpack
,能够看到 webpack 经过配置文件执行的结果和上一节经过命令行 webpack entry.js bundle.js --module-bind 'css=style!css'
执行的结果是同样的。
插件能够完成更多 loader 不能完成的功能。
插件的使用通常是在 webpack 的配置信息 plugins 选项中指定。
Webpack 自己内置了一些经常使用的插件,还能够经过 npm 安装第三方插件。
接下来,咱们利用一个最简单的 BannerPlugin 内置插件来实践插件的配置和运行,这个插件的做用是给输出的文件头部添加注释信息。
修改 webpack.config.js,添加 plugins:
var webpack = require('webpack') module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ {test: /\.css$/, loader: 'style!css'} ] }, plugins: [ new webpack.BannerPlugin('This file is created by zhaoda') ] }
而后运行 webpack,打开 bundle.js,能够看到文件头部出现了咱们指定的注释信息:
/*! This file is created by zhaoda */ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; // 后面代码省略
entry参数定义了打包后的入口文件,能够是个字符串或数组或者是对象;若是是数组,数组中的全部文件会打包生成一个filename文件;若是是对象,能够将不一样的文件构建成不一样的文件:
{ entry: { page1: "./page1", //支持数组形式,将加载数组中的全部模块,但以最后一个模块做为输出 page2: ["./entry1", "./entry2"], // 若是想保持目录结构,则直接按照目录结构命名 'subapp1/page': './app/subapp1/page.js', 'subapp2/page': './app/subapp2/page.js', }, output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" } }
该段代码最终会在 ./dist/js/page
文件夹下生成以下结构:
│ page1.bundle.js │ page2.bundle.js │ ├─subapp1 │ page.bundle.js │ └─ssubapp2 page.bundle.js
保持目录结构命名的方式,在构架大型应用中很是有用。
output参数是个对象,定义了输出文件的位置及名字:
output: { path: path.resolve(__dirname, 'dist'), publicPath: 'http://localhost:3000/static/', filename: "js/[name].bundle.js" }
path
: 打包文件存放的绝对路径
publicPath
: 网站运行时的访问路径
filename
:打包后的文件名
当咱们在entry
中定义构建多个文件时,filename
能够对应的更改成[name].js
用于定义不一样文件构建后的名字。
以下 'http://localhost:3000/static/'
通常咱们作调试时的路径,若是咱们要在网页中引用 js 文件,html 文件中的路径写为:http://localhost:3000/static/js/<name>.bundle.js
,
即 <publicPath>
+<filename>
。
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不一样模块的加载是经过模块加载器(webpack-loader)来统一管理的。loaders之间是能够串联的,一个加载器的输出能够做为下一个加载器的输入,最终返回到JavaScript上:
module: { //加载器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 来处理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.js 文件使用 jsx-loader 来编译处理 { test: /\.js$/, loader: 'jsx-loader?harmony' }, //.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'} ] }
字段 | 说明 |
---|---|
test |
表示匹配的资源类型 |
loader 或 loaders |
表示用来加载这种类型的资源的loader |
! |
定义loader的串联关系,多个loader之间用“!”链接起来 |
此外,还能够添加用来定义png、jpg这样的图片资源在小于10k时自动处理为base64图片的加载器:
{ test: /\.(png|jpg)$/,loader: 'url-loader?limit=10000'}
给css和less还有图片添加了loader以后,咱们不只能够像在node中那样 require()
js文件了,咱们还能够 require()
css、less甚至图片文件:
require('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
注意,require()
还支持在资源path前面指定loader,即 require(![loaders list]![source path])
形式:
require("!style!css!less!bootstrap/less/bootstrap.less"); // “bootstrap.less”这个资源会先被"less-loader"处理, // 其结果又会被"css-loader"处理,接着是"style-loader" // 可类比pipe操做
require()
时指定的loader会覆盖配置文件里对应的loader配置项。
webpack在构建包的时候会按目录的进行文件的查找,resolve
属性中的 extensions
数组中用于配置程序能够自行补全哪些文件后缀:
resolve: { //查找module的话从这里开始查找 root: '/pomy/github/flux-example/src', //绝对路径 //自动扩展文件后缀名,意味着咱们require模块能够省略不写后缀名 extensions: ['', '.js', '.json', '.scss'], //模块别名定义,方便后续直接引用别名,无须多写长长的地址 alias: { AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 便可 ActionType : 'js/actions/ActionType.js', AppAction : 'js/actions/AppAction.js' } }
而后咱们想要加载一个js文件时,只要 require('common')
就能够加载 common.js
文件了。
注意一下, extensions
第一个是空字符串! 对应不须要后缀的状况.
webpack提供了[丰富的组件]用来知足不一样的需求,固然咱们也能够自行实现一个组件来知足本身的需求:
plugins: [ //your plugins list ]
在webpack中编写js文件时,能够经过require的方式引入其余的静态资源,可经过loader对文件自动解析并打包文件。一般会将js文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程当中咱们并不想将样式打在脚本中,最好能够独立生成css文件,之外链的形式加载。这时 extract-text-webpack-plugin
插件能够帮咱们达到想要的效果。须要使用npm的方式加载插件,而后参见下面的配置,就能够将js中的css文件提取,并以指定的文件名来进行加载。
npm install extract-text-webpack-plugin –-save-dev
plugins: [ new ExtractTextPlugin('styles.css') ]
当咱们想在项目中require一些其余的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中颇有必要。此时咱们就能够经过配置externals参数来解决这个问题:
externals: { "jquery": "jQuery" }
这样咱们就能够放心的在项目中使用这些API了:var jQuery = require(“jquery”)
;
当咱们在require一个模块的时候,若是在require中包含变量,像这样:
require("./mods/" + name + ".js");
那么在编译的时候咱们是不能知道具体的模块的。但这个时候,webpack也会为咱们作些分析工做:
1.分析目录:’./mods’;
2.提取正则表达式:’/^.*.js$/’;
因而这个时候为了更好地配合wenpack进行编译,咱们能够给它指明路径,像在cake-webpack-config中所作的那样(咱们在这里先忽略abcoption的做用):
var currentBase = process.cwd(); var context = abcOptions.options.context ? abcOptions.options.context : path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);
关于 webpack.config.js 更详尽的配置能够参考 Webpack Configuration。
当项目逐渐变大,webpack 的编译时间会变长,能够经过参数让编译的输出内容带有进度和颜色。
$ webpack --progress --colors
若是不想每次修改模块后都从新编译,那么能够启动监听模式。开启监听模式后,没有变化的模块会在编译后缓存到内存中,而不会每次都被从新编译,因此监听模式的总体速度是很快的。
$ webpack --progress --colors --watch
固然,使用 webpack-dev-server
开发服务是一个更好的选择。它将在 localhost:8080 启动一个 express 静态资源 web 服务器,而且会以监听模式自动运行 webpack,在浏览器打开 http://localhost:8080/ 或 http://localhost:8080/webpack... 能够浏览项目中的页面和编译后的资源输出,而且经过一个 socket.io 服务实时监听它们的变化并自动刷新页面。
# 安装 $ npm install webpack-dev-server -g # 运行 $ webpack-dev-server --progress --colors
Webpack相关:
$ npm install webpack -g $ npm install webpack-dev-server -g # 安装必要的 loader: ## 编译 JSX $ npm install --save-dev babel-loader ## CSS 文件处理 $ npm install --save-dev css-loader style-loader ## React $ npm install --save-dev react-hot-loader
Babel 相关:
$ npm install --save-dev babel-core # 添加 ES6 支持 $ npm install --save-dev babel-preset-es2015 $ npm install --save-dev babel-react
var webpack = require('webpack'); module.exports = { entry: [ 'webpack/hot/only-dev-server', "./js/app.js" ], output: { path: './build', filename: "bundle.js" }, module: { loaders: [ { test: /\.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, { test: /\.css$/, loader: "style!css" } ] }, resolve:{ extensions:['','.js','.json'] }, plugins: [ new webpack.NoErrorsPlugin() ] };
参考资料:
webpack-dev-server 是一个基于 Node.js Express 框架的开发服务器,它是一个静态资源 Web 服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,均可以直接使用这个开发服务器进行开发。在开发过程当中,开发服务器会监听每个文件的变化,进行实时打包,而且能够推送通知前端页面代码发生了变化,从而能够实现页面的自动刷新。
简单来讲,webpack-dev-server就是一个小型的静态文件服务器。使用它,能够为webpack打包生成的资源文件提供Web服务。
webpack-dev-server有两种模式支持自动刷新——iframe模式和inline模式。
在iframe模式下:页面是嵌套在一个iframe下的,在代码发生改动的时候,这个iframe会从新加载。使用iframe模式无需额外的配置,只需在浏览器输入:
http://localhost:8080/webpack-dev-server/index.html
在inline模式下:一个小型的webpack-dev-server客户端会做为入口文件打包,这个客户端会在后端代码改变的时候刷新页面。使用inline模式有两种方式:命令行方式和Node.js API。
命令行方式比较简单,只需加入--line选项便可。例如:
webpack-dev-server --inline
使用--inline
选项会自动把webpack-dev-server客户端加到webpack的入口文件配置中。
注意:默认配置文件名称为:webpack.config.js
,若要更改须要在命令行中指明。例如,
webpack-dev-server --inline --config webpack.config.dev.js。
若用Node.js API方式,由于webpack-dev-server没有inline:true这个配置项,因此须要手动把
webpack-dev-server/client?http://localhost:8080
加到配置文件的入口文件配置处。
webpac-dev-server 支持 Hot Module Replacement,即模块热替换,在前端代码变更的时候无需整个刷新页面,只把变化的部分替换掉。使用HMR功能也有两种方式:命令行方式和Node.js API。
命令行方式一样比较简单,只需加入--line --hot
选项。--hot
这个选项干了一件事情,它把webpack/hot/dev-server
入口点加入到了webpack配置文件中。这时访问浏览器,你会看见控制台的log信息:
[HMR] Waiting for update signal from WDS... [WDS] Hot Module Replacement enabled.
HMR前缀
的信息由webpack/hot/dev-server模块产生,WDS前缀
的信息由webpack-dev-server客户端产生。
Node.js API方式须要作三个配置:
把 webpack/hot/dev-server
加入到webpack配置文件的 entry
项;
把 new webpack.HotModuleReplacementPlugin()
加入到webpack配置文件的plugins
项;
把 hot:true
加入到 Webpack 配置文件的 webpack-dev-server
的配置项里面。
devServer:{ hot:true }
注意:要使HMR功能生效,还须要作一件事情,就是要在应用热替换的模块或者根模块里面加入容许热替换的代码。不然,热替换不会生效,仍是会重刷整个页面。
if(module.hot) module.hot.accept();
也可使用一些插件去完成这个工做,例如webpack-module-hot-accept插件。不过,webpack-dev-server HMR结合react-hot-loader使用的时候,react-hot-loader会去作这个工做。
综合上述,使用wepack-dev-server辅助开发,使得开发者在开发前端代码的过程当中无需频繁手动刷新页面,使用HMR甚至不用等待页面刷新,确实能够给开发者带来很好的体验。
可是,问题又来了。我要进行先后端联调的时候怎么办呢?毕竟webpack-dev-server只是一个静态文件服务器,不具有动态处理的能力。这个时候就须要将后端服务器与webpack-dev-server结合使用了。webpack-dev-server只用来为webpack打包生成的资源文件提供服务,好比js文件、图片文件、css文件等;后端服务器除提供API接口外,还提供入口HTML。
要将webpack-dev-server与后端服务器结合使用,须要作三件事情。
第一 首页HTML文件是从后端服务器发出的,这时页面的根地址变成了后端服务器地址,怎么使得webpack产生的资源文件在请求资源的时候是向web-dev-server请求而不是后端服务器请求?只需在webpack配置文件中的 output.publicPath
配置项写上绝对URL地址,例如output.publicPath = "http://localhost:8080/assets/"
。这时,webpack打包产生的资源文件里面的url地址都会是绝对地址,而不是相对地址。
第二 后端服务器产生的入口HTML文件要向webpack-dev-server请求资源文件,这个简单,只需在HTML文件中加入资源文件的绝对地址,例如:<script src="http://localhost:8080/assets/bundle.js">
第三 要使webpack-dev-server和它的运行时程序链接起来。这个简单,只须要使用iline模式便可。
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { ... ... // plugins 项配置中增长 plugins: [ ... ... // 提取公共代码 commonsPlugin, //压缩 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] }
参见:
WEBPACK DEV SERVER
webpack-dev-server 官方文档
前端模块加载工具——webpack(二)