随着nodejs的发展,对于前端来说是一个福音,随之而来的前端开发们共享了非常多优秀的工具,前端的打包合并一直在前端基础构建中起了很重要的作用,
对于前端的开发模式以及线上的运行优化有这非常重要的影响. 一直用fis在做这方面的构建,为了够好的开发构建vue,那么webpack也应该好好熟悉一下.

一, webpack的安装

在命令行运行命令,这里说明一下 -g 是什么含义,就是安装到全局环境中,如果不加-g,那么它只会安装到你本地目录的node_modules文件夹下面,也就意味着,在其他文件中没办法用webpack

1
npm install webpack -g

安装完成后,我们需要为项目生成一个package.json的文件,主要用于标记webpack的依赖.回头其他人开发项目时,可以直接 npm install就可以了.

1
2
npm init
npm install webpack --save-dev

npm init 先生成packge文件,
npm install webpack –save-dev 新建webpack并且保存到package.json文件中.

二, webpack的配置
每个项目下都必须配置有一个 webpack.config.js,类似于Gruntfile.js,其实是一个配置文件,webpack会读取文件中的配置来处理你要执行的那些配置.
三, 写一个简单的demo

创建test.js

1
console.log('一个简单的demo');

创建index.html

1
2
3
4
5
6
7
8
9
<html>
<head>
<meta charset="utf-8">
<title>webpack demo</title>
</head>
<body>
<script type="text/javascript" src="testbuild.js" charset="utf-8"></script>
</body>
</html>

由于我们没写webpack.config.js,我们可以运行命令,生成testbuild.js文件

1
webpack ./test.js testbuild.js

在浏览器中打开index.html文件

打开控制台,已经输入了我们想要的结果了.

在创建文件test1.js

1
2
3
4
5
6
var test1 = {
init: function(){
console.dir('我是来自于test1.js');
}
}
module.exports = test1;

修改test.js文件

1
2
var test1 = require("./test1.js");
test1.init();

运行命令

1
webpack ./test.js testbuild.js

查看index.html

我们来看一下webpack都干了什么,其实给我们加了一段代码,会生成一个module数组,并且把依赖的文件自动打包加载进来.也就是说原来我们通过seajs和requirejs在线加载的模块文件,已经全部打包到webpack中了.
有点就是不用在线上加载那么多modulejs了.通过webpack配置,我们可以按需打包.这个功能其实fis也有.

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
/******/
(function(modules) {
// webpackBootstrap
/******/
// The module cache
/******/
var installedModules = {};
/******/
// The require function
/******/
function __webpack_require__(moduleId) {
/******/
// Check if module is in cache
/******/
if (installedModules[moduleId])
/******/
return installedModules[moduleId].exports;
/******/
// Create a new module (and put it into the cache)
/******/
var module = installedModules[moduleId] = {
/******/
exports: {},
/******/
id: moduleId,
/******/
loaded: false/******/
};
/******/
// Execute the module function
/******/
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
// Flag the module as loaded
/******/
module.loaded = true;
/******/
// Return the exports of the module
/******/
return module.exports;
/******/
}
/******/
// expose the modules object (__webpack_modules__)
/******/
__webpack_require__.m = modules;
/******/
// expose the module cache
/******/
__webpack_require__.c = installedModules;
/******/
// __webpack_public_path__
/******/
__webpack_require__.p = "";
/******/
// Load entry module and return exports
/******/
return __webpack_require__(0);
/******/
})/************************************************************************/
/******/
([/* 0 */
/***/
function(module, exports, __webpack_require__) {
var test1 = __webpack_require__(1);
test1.init();
/***/
}
, /* 1 */
/***/
function(module, exports) {
var test1 = {
init: function() {
console.dir('我是来自于test1.js');
}
}
module.exports = test1;
/***/
}
/******/
]);
使用配置文件
1
2
3
4
5
6
7
8
9
10
module.exports = {
entry: "./test.js",
output: {
path: ./,
filename: "testbuild.js"
},
module: {
loaders: []
}
};

运行 webpack试试,也会出现我们出现的结果.

0) entry 和 output
1
2
3
4
5
6
7
8
9
entry: {
page1: "./test.js",
page2: "./index.js",
page3: "./index1.js"
},
output: {
path: './',
filename: "[name].bundle.js"
},

entry是只文件的入口,例如page1、page2也就相当于页面的入口文件

output 其中path是指输出的路径,filename只输出的文件名

1)插件项

plugins:

新建index.js

1
2
var test1 = require("./test1.js");
test1.init();

例如我们把配置文件写为两个入口文件,和两个出口文件。

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.exports = {
//插件项
entry: {
page1: "./test.js",
page2: "./index.js"
},
output: {
path: './',
filename: "[name].bundle.js"
},
module: {
loaders: [{
test: /\.json$/,
loader: "json"
}, {
test: /\.less$/,
loader: 'style-loader!css-loader!less-loader'
}, // use ! to chain loaders
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}, {
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'
} // inline base64 URLs for <=8k images, direct URLs for the rest
]
}
};

运行

1
webpack

此时我们发现生成两个page1.bundle.js以及page2.bundle.js
但是我们发现他们两个文件一模一样,对我们来说,他们两个有公用的部分,我们可以提取出来,这样我们就不用再前端加载相同的代码了。

接着我们在webpack.config.js中添加

1
2
3
4
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
plugins: [commonsPlugin]

完整的文件为

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
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
//插件项
plugins: [commonsPlugin],
entry: {
page1: "./test.js",
page2: "./index.js"
},
output: {
path: './',
filename: "[name].bundle.js"
},
module: {
loaders: [{
test: /\.json$/,
loader: "json"
}, {
test: /\.less$/,
loader: 'style-loader!css-loader!less-loader'
}, // use ! to chain loaders
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}, {
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'
} // inline base64 URLs for <=8k images, direct URLs for the rest
]
}
};

运行webpack之后,我们发现多了一个common.js文件

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
106
107
108
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ var parentJsonpFunction = window["webpackJsonp"];
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, callbacks = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId])
/******/ callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/ while(callbacks.length)
/******/ callbacks.shift().call(null, __webpack_require__);
/******/ if(moreModules[0]) {
/******/ installedModules[0] = 0;
/******/ return __webpack_require__(0);
/******/ }
/******/ };
/******/ // The module cache
/******/ var installedModules = {};
/******/ // object to store loaded and loading chunks
/******/ // "0" means "already loaded"
/******/ // Array means "loading", array contains callbacks
/******/ var installedChunks = {
/******/ 2:0
/******/ };
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = function requireEnsure(chunkId, callback) {
/******/ // "0" is the signal for "already loaded"
/******/ if(installedChunks[chunkId] === 0)
/******/ return callback.call(null, __webpack_require__);
/******/ // an array means "currently loading".
/******/ if(installedChunks[chunkId] !== undefined) {
/******/ installedChunks[chunkId].push(callback);
/******/ } else {
/******/ // start chunk loading
/******/ installedChunks[chunkId] = [callback];
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
/******/ script.type = 'text/javascript';
/******/ script.charset = 'utf-8';
/******/ script.async = true;
/******/ script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"page1","1":"page2"}[chunkId]||chunkId) + ".bundle.js";
/******/ head.appendChild(script);
/******/ }
/******/ };
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ })
/************************************************************************/
/******/ ([
/* 0 */,
/* 1 */
/***/ function(module, exports) {
var test1 = {
init: function() {
console.dir('我是来自于test1.js');
}
}
module.exports = test1;
/***/ }
/******/ ]);

page1.bundle.js

1
2
3
4
5
6
7
8
9
10
webpackJsonp([0],[
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var test1 = __webpack_require__(1);
test1.init();
/***/ }
]);

page2.bundle.js

1
2
3
4
5
6
7
8
9
webpackJsonp([1],[
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var test1 = __webpack_require__(1);
test1.init();
/***/ }
]);

我们发现公用的一部分代码被提取出来了。赞一个。

加入我们在加入两个文件:
test2.js index1.js
index1.js作为入口文件,引用test2.js里的方法

接着运行webpack
这是会后page3.bundle.js 只包含了test2.js里的代码

问题?? 1、在这里提出一个问题?就是现在对于我来说写代码还是比较麻烦的,因为我希望的时候这个入口文件不是由我来配置,而是自动遍历html中的入口文件。这是我觉得比较合理的地方。我觉得可以用插件来时实现,或者已经有了这样的插件

2)Loaders

loaders是非常重要的一块配置,它将告诉我们每一种文件需要使用什么加载器来加载处理。
例如最近比较火爆的vue.js为结尾的.vue文件。

需要单独安装到本地,并且配置到配置文件里

1、json-loader 处理json文件

安装

1
sudo npm install -g --save-dev json-loader

用法

1
2
var json = require("json!./file.json");
console.dir(json);

可以直接引用json文件中的对象包装成一个对象

2、Babel-Loader

label的应用,语言的转换

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
``
安装完成后,配置webpack.config.js
``` javascript
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',//在webpack的module部分的loaders里进行配置即可
query: {
presets: ['es2015','react']
}
}

新建Person.js,用es6语法书写代码

1
2
3
4
5
6
7
8
9
10
11
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
say(){
return `我是${this.name},我今年${this.age}岁了。`;
}
}
export default Person;

在index.js中添加

1
2
3
4
import Person from './Person.js';
let p = new Person('张三', 20);
document.write(p.say());

编译后生成的文件
page2.bundle.js

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
webpackJsonp([1, 3], [
/* 0 */
/***/
function(module, exports, __webpack_require__) {
"use strict";
var _Person = __webpack_require__(2);
var _Person2 = _interopRequireDefault(_Person);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj }; }
var test1 = __webpack_require__(1);
test1.init();
var json = __webpack_require__(3);
console.dir(json);
var p = new _Person2.default('张三', 20);
document.write(p.say());
/***/
},
/* 1 */
/***/
function(module, exports) {
'use strict';
var test1 = {
init: function init() {
console.dir('我是来自于test1.js');
}
};
module.exports = test1;
/***/
},
/* 2 */
/***/
function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor); } }
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor; }; }();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function"); } }
var Person = function() {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "say",
value: function say() {
return "我是" + this.name + ",我今年" + this.age + "岁了。";
}
}]);
return Person;
}();
exports.default = Person;
/***/
},
/* 3 */
/***/
function(module, exports) {
module.exports = {
"name": "test"
};
/***/
}
]);

可以看到 Person类被转换成了es5的写法。赞一个,不过同学们要小心了,es5现在目前支持的浏览器也只能到ie9,并且ie9不支持严格模式,需要支持ie8的同学注意了,es5只有某几个属性支持。ie6/7的同学们就更惨了。。。。

3) css

安装

1
npm install --save-dev css-loader style-loader sass-loader less-loader less postcss-loader

其中包括了sass和less,我们测试less
看看配置文件

1
2
3
4
5
6
7
8
{
test: /\.less$/,
loader: 'style-loader!css-loader!less-loader'
}, // use ! to chain loaders
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}

创建file.less文件

1
2
3
4
@bg: black;
body{
background: @bg;
}

index.js中加入

1
require("!style!css!less!./file.less");

运行webpack后,css已经被打包进入到js中,看图

问题?? 提出一个问题,如果是文件形式的,同样也有less路径的填写问题?

4) url-loader

安装url-loader

1
npm install --save-dev url-loader file-loader

配置文件

1
2
3
4
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'
}, // inline base64 URLs for <=8k images, direct URLs for the rest

在index.js中添加代码

1
2
var img = require('./23.png');
console.dir(img);

在file.less中添加

1
background: url(23.png) no-repeat;

运行webpack
可以看到
23.png转换成了1614a0070c3a3656c434ebca4d5f4000.png

问题?? 就是html中的图片怎么处理?

3、resolve

root告诉我们module从哪里查找

extensions 自动扩展文件后缀名,也就是说可以省略地址

alias 别名,可以代替js的路径
1
2
3
4
5
6
7
8
9
10
resolve: {
//查找module的话从这里开始查找
root: '~/test/src', //绝对路径
//自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['', '.js', '.json', '.scss'],
//模块别名定义,方便后续直接引用别名,无须多写长长的地址
alias: {
test : 'js/test1.js',//后续直接 require('test') 即可
}
}

webpack 命令

1
2
3
4
5
6
7
8
$ webpack --config XXX.js //使用另一份配置文件(比如webpack.config2.js)来打包
$ webpack --watch //监听变动并自动打包
$ webpack -p //压缩混淆脚本,这个非常非常重要!
$ webpack -d //生成map映射文件,告知哪些模块被最终打包到哪里了

执行也可以加参数,例如加

$ webpack –display-error-details 可以抛出更详细的错误。