webpack入坑记 01

似水的流年 为何不去邂逅一场繁华的悸动

Grunt/Gulp流程

构建&执行过程

要完全理解webpack整个构建过程是有难度的,这需要你去学习webpack的源码,这里简单地说一下webpack的执行过程:

  • webpack先识别我们写的配置表选项,进行一系列的初始化
  • webpack从入口文件开始进行编译,递归解析对应依赖的文件,需要进行预处理的文件会根据配置的loader去转换
  • 根据不同块之间的依赖进行分组,分成不同的chunk
  • 将不同的chunk转成对应的输出文件
  • plugin会贯穿这个编译的过程,会在适当的时机调用适当的插件

webpack工作流程

把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件(Dependency Graph,从入口文件开始进行编译,递归解析对应依赖的文件),使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。

package.json配置

自定义命令

1
2
3
4
"scripts": {  
"dev": "webpack-dev-server --open --colors",
"build": "cross-env NODE_ENV=production webpack -p"
},

总结一下写阮一峰的15个demo遇到的问题:

webpack加载器解析顺序

无论是字符串语法style-loader!css-loader,亦或是数组语法['style-loader', 'css-loader'],webpack解析规则都是从右至左,依次解析并执行加载器处理文件,前一加载器处理的输出就是下一加载器处理的输入,直到最后加载器处理完成;此处即webpack先调用css-loader加载器处理css文件,然后将处理结果传递给style-loader加载器,style-loader接受该输入继续处理。

(1)注意babel-loader的rule一定要在url-loader下面

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
main.jsx
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
<React.Fragment>
<p>halo demo5 图片资源</p>
<img src={require('./4853ca667a2b8b8844eb2693ac1b2578.png')} />
<img src={require('./big.png')} />
</React.Fragment>
, document.getElementById('root'));


wbpack.config.js[https://thinkerhope.github.io/2019/09/10/webpack%E5%85%A5%E5%9D%91%E8%AE%B0/webpack03.jpg](
module: {
rules: [
{
test: /\.(png|jpg)$/,
//没有url-loader require('././.(png|jpg)') webpack打包会报错。
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
// loader: 'url-loader?limit=8192'
]
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
]
}
]
}

解释:好吧,我才刚学,等我会了再更新。

(2)让webpack.config.js支持 es6写法

不喜欢commonJS的写法,还是习惯es6的import,但是直接import发现并不支持 import,报语法错误了。

以下为解决方法

1
2
3
4
5
第一、把webpack.config.js改名为webpack.config.babel.js(前提是目录下装了babel-loader 和 babel-core)

第二、把增加一个.babelrc的文件,里面写上{ "presets": ["es2015"]}。

第三、在package.json文件中加上
1
2
3
4
5
"devDependencies": {
"babel-core": "^6.3.26",
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.24.1"
}

最后 npm install 或者 npm i Nice!可以webpack -p了

重点来了,之所以不支持es6,是因为我写的demo是webpack 1.00版本了,上个世纪的版本,新的webpack版本好像已经支持es6,而且webpack2.0以上,不能这样实现在webpack.config.js中书写es6语法。

插件系统

UglifyJs Plugin

配置
1
2
3
4
5
6
import UglifyJsPlugin from 'uglifyjs-webpack-plugin';
//注意看自己的webpack版本是否支持es6

plugins: [
new UglifyJsPlugin()
]
作用

压缩output(bundle.js)Js.code

HTML Webpack Plugin & Open Browser Webpack Plugin

配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import HtmlwebpackPlugin from 'html-webpack-plugin';
import OpenBrowserPlugin from 'open-browser-webpack-plugin';

module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlwebpackPlugin({
title: 'Webpack-demos',
filename: 'index.html'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080'
})
]
};
作用

HTML 为你创建一个index.html

OpenBrowser 当webpack加载的时候打开一个新标签页

代码拆分

webpack使用 require.ensure定义split point 。

1
2
3
4
5
6
7
// main.js
require.ensure(['./a'], function (require) {
var content = require('./a');
document.open();
document.write('<h1>' + content + '</h1>');
document.close();
});

上面的代码告诉webpack ./a.js 应该从 bundle.js 被拆分开,并创建一个新的chunk file

1
2
// a.js
module.exports = 'Hello World';

此时,你不用在index.html中手动引入chunk.js,也不用在webpack.config.js的output里配置chunk。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>

webpack.config.js
module.exports = {
entry: './mplugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
// (the commons chunk name)

filename: "commons.js",
// (the filename of the commons chunk)
})
]ain.js',
output: {
filename: 'bundle.js'
}
};

bundle-loader

代码拆分的另一种方式。

1
2
3
4
5
6
7
8
9
var load = require('bundle-loader!./a.js');

// To wait until a.js is available (and get the exports)
// you need to async wait for it.
load(function(file) {
document.open();
document.write('<h1>' + file + '</h1>');
document.close();
});

Q 利用bundle-loader实现按需加载

源码阅读,代码简短,也是通过require.ensure()实现。

CommonsChunkPlugin

有利于浏览器缓存和节省带宽

配置
1
2
3
4
5
6
7
8
9
10
11
var webpack = require('webpack');
...
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
// (the commons chunk name)

filename: "commons.js",
// (the filename of the commons chunk)
})
]

用CommonsChunkPlugin拆分第三方库

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
main.js
var $ = require('jquery');
$('h1').text('Hello World');

index.html
<html>
<body>
<h1></h1>
<script src="vendor.js"></script>
<script src="bundle.js"></script>
</body>
</html>

webpack.config.js
var webpack = require('webpack');

module.exports = {
entry: {
app: './main.js',
vendor: ['jquery'],
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js'
})
]
};

注意 entry.vendor: [‘jquery’]告诉webpack jquery要被包含在commmon chunk (vendor.js中),所以CommonsChunkPlugin中的name属性和entry中的vendor键必须相同,也可以是其它名字。

从上面也可以发现每个模块中要使用jquery都要require(‘jquery’)。

如果希望$作为全局变量,而不使用require(‘jquery’),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// main.js
$('h1').text('Hello World');


// webpack.config.js
var webpack = require('webpack');

module.exports = {
entry: {
app: './main.js'
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};

使用ProvidePlugin可以自动载入模块,而不需要import或者require()模块。

暴露全局变量 externals

当我们需要使用一些全局变量,又不希望变量包含在bundle文件中,不需要webpack处理,编译进文件中,在我们需要,使用它的时候可以通过CMD、AMD、或者window全局方式访问。

假设我们自己有个工具库,tools.js,它并没有提供给我们UMD的那些功能,只是使用window或者global的方式把工具的对象tools暴露出来

1
2
3
4
5
6
utils.js
window.tools = {
add: function(num1, num2) {
return num1 + num2
}
}

引入

1
<script src="http://xxx/tools.min.js"></script>

可能我们会直接引用

1
const res = tools.add(1,2)

既然我们是模块化开发,当然要杜绝一切全局变量了,我们要用require的方式。

1
`const tools = require(``'mathTools'``)``const res = tools.add(1,2)`

  

这时我们再来配置一些externals即可

1
2
3
4
5
6
7
8
9
10
module.exports = {
...
output: {
...
libraryTarget: "umd"
},
externals: {
mathTools: "tools"
},
...

打包后会有这一段

1
module.exports = mathTools;

要注意的点

externals: { mathTools: "tools" }const tools = require('mathTools') tools和mathTools是对应的。并且 utils.js中的tools 变量要和 const tools = require('mathTools')tools 变量对应。

皮毛的东西,继续填坑吧,哎。

-------------要说再见啦感谢大佬的光临~-------------