Webpack 指南(整理 草稿)

基础

安装

首先要安装 Node.js, Node.js 自带了软件包管理器 npm。用 npm 全局安装 Webpack:

$ npm install webpack -g

通常我们会将 Webpack 安装到项目的依赖中,这样就可以使用项目本地版本的 Webpack。

# 进入项目目录,初始化,创建 package.json。# 若存在 package.json 文件,则不运行。$ npm init# 确定已经有 package.json# 安装 webpack 依赖$ npm install webpack --save-dev

如果需要使用 Webpack 开发工具,要单独安装:

$ npm install webpack-dev-server --save-dev

使用

首先创建一个静态页面 index.html 和一个 JS 入口文件 entry.js:

// entry.js
document.write('It works.')
然后编译 entry.js 并打包到 bundle.js:

$ webpack entry.js bundle.js

用浏览器打开 index.html 将会看到

It works. 

最终目录结构如下:

.├── entry.js├── index.html├── package.json├── node_modules

接下来添加一个模块 module.js 并修改入口 entry.js:

// module.jsmodule.exports = 'It works from module.js.'
// entry.jsdocument.write('It works.')document.write(require('./module.js')) // 添加模块

重新打包 webpack entry.js bundle.js 后刷新页面看到变化

It works.It works from module.js.

最终目录结构如下:

.├── bundle.js├── entry.js├── index.html├── module.js├── package.json├── node_modules

进阶

使用 Loader

Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。
Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果。这样,我们就可以通过 require 来加载任何类型的模块或文件,比如 CoffeeScript、 JSX、 LESS 或图片。

先来看看 loader 有哪些特性?

  1. Loader 可以通过管道方式链式调用,每个 loader 可以把资源转换成任意格式并传递给下一个 loader ,但是最后一个 loader 必须返回 JavaScript。

  2. Loader 可以同步或异步执行。

  3. Loader 运行在 node.js 环境中,所以可以做任何可能的事情。

  4. Loader 可以接受参数,以此来传递配置项给 loader。

  5. Loader 可以通过文件扩展名(或正则表达式)绑定给不同类型的文件。

  6. Loader 可以通过 npm 发布和安装。

  7. 除了通过 package.json 的 main 指定,通常的模块也可以导出一个 loader 来使用。

  8. Loader 可以访问配置。

  9. 插件可以让 loader 拥有更多特性。

  10. 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的使用有三种方法,分别是:

  1. 在require中显式指定,如:

  2. 在命令行中指定,如:$ webpack entry.js output.js --module-bind 'css=style!css'

  3. 在配置项(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

entry参数定义了打包后的入口文件,可以是个字符串或数组或者是对象;如果是数组,数组中的所有文件会打包生成一个filename文件;如果是对象,可以将不同的文件构建成不同的文件:

{    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参数是个对象,定义了输出文件的位置及名字:

output: {    path: "dist/js/page",    publicPath: "/output/",    filename: "[name].bundle.js"}
  1. path: 打包文件存放的绝对路径

  2. publicPath: 网站运行时的访问路径

  3. filename:打包后的文件名

当我们在entry中定义构建多个文件时,filename可以对应的更改为[name].js用于定义不同文件构建后的名字。

module

在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配置项。

resolve

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 第一个是空字符串! 对应不需要后缀的情况.

plugin

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')]

externals

当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:

 externals: {     "jquery": "jQuery" }

这样我们就可以放心的在项目中使用这些API了:var jQuery = require(“jquery”);

context

当我们在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-dev-server/ 可以浏览项目中的页面和编译后的资源输出,并且通过一个 socket.io 服务实时监听它们的变化并自动刷新页面。

# 安装$ npm install webpack-dev-server -g# 运行$ webpack-dev-server --progress --colors

React 开发环境的配置

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()
]
};
参考资料:

  1. React Webpack Cookbook

  2. Babel 入门教程

  3. Webpack 中文指南

  4. Webpack 官方文档

webpack-dev-server

webpack-dev-server 是一个基于 Node.js Express 框架的开发服务器,它是一个静态资源 Web 服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。

简单来说,webpack-dev-server就是一个小型的静态文件服务器。使用它,可以为webpack打包生成的资源文件提供Web服务。

webpack-dev-server有两种模式支持自动刷新——iframe模式和inline模式。

iframe模式

在iframe模式下:页面是嵌套在一个iframe下的,在代码发生改动的时候,这个iframe会重新加载。使用iframe模式无需额外的配置,只需在浏览器输入:

http://localhost:8080/webpack-dev-server/index.html

inline模式

在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方式需要做三个配置:

  1. 把 webpack/hot/dev-server 加入到webpack配置文件的 entry 项;

  2. 把 new webpack.HotModuleReplacementPlugin() 加入到webpack配置文件的plugins项;

  3. 把 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文件中加入资源文件的绝对地址,例如:
第三 要使webpack-dev-server和它的运行时程序连接起来。这个简单,只需要使用iline模式即可。

参见:
WEBPACK DEV SERVER
webpack-dev-server 官方文档

关键字:webpack


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部