webpack 是一个模块打包工具。它使得模块相互依赖而且可构建等价于这些模块的静态资源。相比于已经存在的模块打包器(module bundler),webpack的开发动机是实现代码分包(Code Splitting )和经过模块化完成代码的无缝集成。webpack能够根据项目需求合并代码,而且支持按需加载。javascript
webpack入门,能够参看:petehunt的Webpack howtocss
webpack的实现目标是:html
安装webpack以前,请确认node已经安装完毕,且npm包管理器可用。vue
1
2
|
npm install webpack -g
|
1
2
|
npm install webpack-dev-server -g
|
在项目的根目录下运行:java
1
2
|
npm install webpack
|
在项目根目录下新建webpack.config.js。咱们经过这个文件来处理控制webpack,给出咱们想要的输出。node
webpack.config.js
的简单的配置以下:react
1
2
3
4
5
6
7
8
9
|
module.exports = {
context: __dirname + '/src',
entry: './index/index.js',
output: {
path: __dirname + '/build',
filename: 'bundle.js'
}
};
|
项目根目录运行:jquery
1
2
|
webpack-dev-server --progress --colors
|
此时,访问: http://localhost:8080/index.bundle.js 便可访问到编译以后的js了。webpack
经过webpack打包发布,运行:git
1
2
|
webpack -p
|
按照咱们上面的webpack.config.js
文件的配置,打包成功以后会生成一个build文件夹,里面会包含打包好的js文件,集成了全部的依赖库和业务逻辑代码,咱们只需将此build文件夹发布到线上便可。
至此一个完整流程的webpack运行流程梳理完毕,固然webpack还有不少功能来实现咱们开发中所遇到的各类变态需求,在第三章中我抽出了一些项目中经常使用的功能。
配置那些js须要处理,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能够是字符串,也但是数组。
例如
1
2
3
4
5
|
entry: {
index: './index/index.js',
login: ['./mod/mod1.js', './index/login.js']
}
|
设置入口配置的文件的输出规则,经过output
对象实现,经常使用设置:
1
2
3
4
5
6
|
output: {
path: __dirname + '/build',
filename: '[name]-[id].js',
publicPath: '/asstes/'
}
|
其中:
output.path
:指定输出文件路径,一般设置为__dirname + ‘/build’,output.filename
: 输出文件名称,有下面列出的四种可选的变量。 filename项的配置能够是这几种的任意一种或多种的组合。 如 output.filename = ‘[name]-[id].js’, 则输出就是 index-1.js、 login-2.js。
[id]
, chunk的id[name]
,chunk名[hash]
, 编译哈希值[chunkhash]
, chunk的hash值output.publicPath
:设置为想要的资源访问路径。访问时,则须要经过相似http://localhost:8080/asstes/index-1.js
来访问资源,若是没有设置,则默认从站点根目录加载。
loader是webpack中比较重要的部分,她是处理各种资源的执行者。它们是一系列的函数(运行在node.js中),将资源中的代码做为参数,而后返回新的代码。你能够用loader告诉webpack能够加载哪些文件,或者不加载哪些文件。
Loader的特色
安装loader
1
2
|
npm install xxx-loader --save
|
或者
1
2
|
npm install xxx-loader --save-dev
|
其中,XXX为webpack支持的loader名,经常使用的有:html、css、jsx、coffee、jade、less、sass、style等。
你能够经过webpack loader 列表 查看全部支持的loader。固然你能够本身根据需求建立并发布loader。
配置loader
1
2
3
4
5
6
7
8
9
10
11
12
|
modules: {
loaders: [
{
test: /\.js$/, //匹配但愿处理文件的路径
exclude: /node_modules/, // 匹配不但愿处理文件的路径
loaders: 'xxx-loader?a=x&b=y' //此处xxx-loader 能够简写成xxx , ?后以query方式传递给loader参数
},
...
]
}
|
多loader调用示例:
在js中,若是要直接解析某个文件,你能够采用:
1
2
|
require('jade!./index.jade')
|
若是要解析css,并内联之,须要使用到分隔符 !
1
2
|
require(!style!css!./style.css)
|
同理,若是要解析less, 转换成css以后,再内联之,写法如:
1
2
|
require('!style!css!less!./style.less!') ; // 此语句的含义是,先调用less-loader解析style.less文件,输出结果会被css-loader处理, 而后再被style-loader处理
|
同理,在webpack.config.jsp配置文件中,只须要制定处理的loader序列:
即:
1
2
3
4
5
6
7
8
9
|
...
loaders: [
{
test: /\.less$/,
loader: "style!css!less"
}
]
...
|
本章会介绍比较经常使用的loader的配置方法。
在webpack中css默认方案是,将css编译并经过内联的方式在html页面中插入<style>
样式标签。固然这远远不能知足咱们的要求,webpack提供css-loader模块用于编译css文件,而且提供了插件extract-text-webpack-plugin
将css从js代码中抽出并合并。你能够访问此处,查看文档和例子。
这样你能够在模块中,尽情使用 require(style.css)
, webpack会帮你作解析,合并entry中定义js及其依赖中所用到的全部css,而后生成一个指定的css文件。
配置以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<br />var ExtractTextPlugin = require('extract-text-webpack-plugin');
...
module: {
loaders: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
"style-loader",
"css-loader?sourceMap"
)
}, {
test: /\.less$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
}
]
},
plugins: [
new ExtractTextPlugin("css/[name].css?[hash]-[chunkhash]-[contenthash]-[name]", {
disable: false,
allChunks: true
})
]
...
|
在css中或者js逻辑中,都会涉及到require图片的状况,webpack能够内联图片地址到打包js中而且经过require()
返回图片路径。固然,不仅是图片,还有css中用到的iconfont,特殊状况用到的flash等,均可以类似处理。这里,咱们须要用到url-loader 或 file-loader。
file-loader
: 将匹配到的文件复制到输出文件夹,并根据output.publicPath的设置返回文件路径url-loader
: 相似file-loader ,可是它能够返回一个DataUrl (base 64)若是文件小于设置的限制值limit
。一样,这以前,你须要实现配置相关loader。
安装url-loader
和 file-loader
:
1
2
|
npm install url-loader file-loader --save
|
配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
module:{
loaders:[
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192' // <= 8kb的图片base64内联
},
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10000&minetype=application/font-woff'
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=application/font-woff'
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=application/octet-stream'
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file'
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=image/svg+xml'
}
]
}
|
经过向url-loader传递参数,若是图片小于8kb,则base64内联,大于8kb,则经过output.publishPath配置的前缀将图片路径写入代码,并提取图片到输出目录。
在React项目中,须要解析JSX和相关JavaScript文件,须要下载loader:
1
2
|
npm install react-hot-loader jsx-loader --save
|
一样,配置loader:
1
2
3
4
5
6
7
8
9
10
|
...
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'react-hot!jsx-loader?harmony'
}
]
...
|
和React项目相似,若是要解析VUE框架编写的.vue文件,须要下载loader:
1
2
|
npm install vue-loader --save
|
配置loader:
1
2
3
4
5
6
7
|
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
|
但须要注意的是,若是你的代码中用到了如jade,less等其余语法,可能须要提早下载相应loader到本地。vue-loader的介绍能够查看: vue-loader
babel
可让咱们在编写代码的时候,使用更高级的ECMAScript6
的语法。而后咱们编写的JS文件能够被编译成可被低版本浏览器处理的常规代码。
使用方法
安装loader:
1
2
|
npm install babel-loader --save
|
配置loader:
1
2
3
4
5
6
7
8
|
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
}
]
|
例如:
1
2
3
4
5
6
7
8
9
10
11
|
const a = 1;
console.log(a);
if (true) {
let a = 3;
console.log(a);
}
console.log(a);
|
解析为:
1
2
3
4
5
6
7
8
9
10
11
|
var a = 1;
console.log(a);
if (true) {
var _a = 3;
console.log(_a);
}
console.log(a);
|
####3.4.1 借助web_modules引用外部库
有些时候,咱们用到的第三方库并无采用CommonJS或AMD规范,也没有提交到npm。这样的话,咱们没法经过npm来下载,并经过require()
来引用这些库。
webpack给咱们提供了一个很好的实现方式。咱们能够在项目根目录下,建立一个叫作web_modules的文件夹,而后将须要用到的第三方库存放在此处。那么以后,不须要作任何设置,能够在咱们的逻辑代码中使用require(
‘xx-lib.js’)而且使用了。
文件组织以下:
此时,咱们就能够在业务逻辑中,大胆地使用web_modules中配置的库了,打包的时候,webpack会自动将web_modules中被用到的库封装。
**例如: **
1
2
3
4
|
var director = require('director')
var Router = director.Router();
...
|
当咱们常用React、jQuery等外部第三方库的时候,一般在每一个业务逻辑JS中都会遇到这些库。
如咱们须要在各个文件中都是有jQuery的$
对象,所以咱们须要在每一个用到jQuery的JS文件的头部经过require('jquery')
来依赖jQuery。 这样作很是繁琐且重复,所以webpack提供了咱们一种比较高效的方法,咱们能够经过在配置文件中配置使用到的变量名,那么webpack会自动分析,而且在编译时帮咱们完成这些依赖的引入。
webpack.config.js中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var webpack = require('webpack');
...
plugins: [
new webpack.ProvidePlugin({
'Moment': 'moment',
"$": "jquery",
"jQuery": "jquery",
"window.jQuery": "jquery",
"React": "react"
})
]
...
|
这样,咱们在JS中,就不须要引入jQuery等经常使用模块了,直接使用配置的这些变量,webpack就会自动引入配置的库。
某些状况,咱们须要在页面中输出开发调试内容,可是又不想让这些调试内容在发布的时候泄露出去,那么咱们能够采用魔力变量(magic globals)来处理。
配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var webpack = require('webpack');
var definePlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'false')),
__PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'true'))
});
...
plugins: [
definePlugin
]
...
|
业务逻辑代码中写入
按照下面的代码写入,咱们就能够在咱们本身设定的环境下进行更具针对性的调试。好比咱们但愿在开发环境下能够AJAX能够调试本地mock数据,而后在发布的时候,能够正常访问服务端数据。那么经过此种方式能够彻底实现。
1
2
3
4
5
6
7
8
9
10
11
12
|
if (__DEV__) {
console.warn('Extra logging');
//开发环境须要进行的处理
//...
}
if (__PRERELEASE__) {
console.log('prerelease');
//预发环境须要进行的处理
//...
}
|
设置环境命令
要告诉webpack咱们但愿当前是什么环境,只须要在命令中写入 BUILD_DEV=1 webpck
那么webpack经过配置,就会将全部咱们引用到的__DEV__
变量设置为true。
咱们能够在package.json中事先定义好命令:
1
2
3
4
5
|
"scripts": {
"dev": "BUILD_DEV=1 webpack-dev-server --progress --colors",
"build": "BUILD_PRERELEASE=1 webpack -p"
}
|
那么就能够避免输入冗长的命令了:
开发时输入:
1
2
|
npm run dev
|
发布时输入:
1
2
|
npm run build
|
项目中,对于一些经常使用的组件,站点公用模块常常须要与其余逻辑分开,而后合并到同一个文件,以便于长时间的缓存。要实现这一功能,配置参照:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<br />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('part1.js', ['a', 'b']),
new CommonsChunkPlugin('common.js', ['part1', 'c'])
]
...
|
##4 使用devtool调试
能够经过在配置中加入devtool
项,选择预设调试工具来提升代码调试质量和效率:
eval
– 每一个模块采用eval
和 //@ sourceURL
来执行source-map
– sourceMap是发散的,和output.sourceMapFilename
协调使用hidden-source-map
– 和source-map相似,可是不会添加一个打包文件的尾部添加引用注释inline-source-map
– SourceMap以DataUrl的方式插入打包文件的尾部eval-source-map
– 每一个模块以eval方式执行而且SourceMap以DataUrl的方式添加进evalcheap-source-map
– 去除column-mappings
的SourceMap, 来自于loader中的内容不会被使用。cheap-module-source-map
– 去除column-mappings
的SourceMap, 来自于loader中的SourceMaps被简化为单个mapping文件各类模式的对比:
devtool | 构建速度 | 再次构建速度 | 支持发布版 | 质量 |
---|---|---|---|---|
eval | +++ | +++ | no | 生成代码 |
cheap-eval-source-map | + | ++ | no | 转换代码(lines only) |
cheap-source-map | + | o | yes | 转换代码(lines only) |
cheap-module-eval-source-map | o | ++ | no | 源代码 (lines only) |
cheap-module-source-map | o | – | yes | 源代码(lines only) |
eval-source-map | — | + | no | 源代码 |
source-map | — | — | yes | 源代码 |
为了方便你们摘取,和补全文章中用于示例的代码片断,特将配置文件整理以下,做参考:
配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
var webpack = require('webpack');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var ExtractTextPlugin = require('extract-text-webpack-plugin');
//自定义"魔力"变量
var definePlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'false')),
__PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
});
module.exports = {
//上下文
context: __dirname + '/src',
//配置入口
entry: {
a: './view/index/index.js',
b: './view/index/b.js',
vender: ['./view/index/c.js', './view/index/d.js']
},
//配置输出
output: {
path: __dirname + '/build/',
filename: '[name].js?[hash]',
publicPath: '/assets/',
sourceMapFilename: '[file].map'
},
devtool: '#source-map',
//模块
module: {
loaders: [
{
//处理javascript
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract(
"style-loader",
"css-loader?sourceMap"
)
}, {
test: /\.less$/,
loader: ExtractTextPlugin.extract(
"style-loader",
"css-loader!less-loader"
)
}, {
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=1024'
}, {
//处理vue
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10000&minetype=application/font-woff'
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=application/font-woff'
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=application/octet-stream'
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file'
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=image/svg+xml'
}
]
},
plugins: [
//公用模块
new CommonsChunkPlugin('common.js', ['a', 'b']),
//设置抽出css文件名
new ExtractTextPlugin("css/[name].css?[hash]-[chunkhash]-[contenthash]-[name]", {
disable: false,
allChunks: true
}),
//定义全局变量
definePlugin,
//设置此处,则在JS中不用相似require('./base')引入基础模块, 只要直接使用Base变量便可
//此处一般可用作,对经常使用组件,库的提早设置
new webpack.ProvidePlugin({
Moment: 'moment', //直接从node_modules中获取
Base: '../../base/index.js' //从文件中获取
})
],
//添加了此项,则代表从外部引入,内部不会打包合并进去
externals: {
jquery: 'window.jQuery',
react: 'window.React',
//...
}
};
|