webpack简介
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。
写在前面
一开始主要是参考官方文档来学习,后来发现阮一峰老师在github上有webpack的demo可以结合学习,这篇文章主要是以这个demo为主线来记录。
先贴一下项目的地址 webpack-demos
有几个要注意的地方,大佬的demo是几年前写的了,语法和配置上可能会和新版本有冲突。
-
output 默认会新建一个 dist 文件夹把打包好的文件放在里面
-
package.json 里面的build指令 把
webpack -p改成webpack -
output.filename,注意要写相对路径,比如 ./dist/bundle.js 。如果这样写是错误的,dist/bundle.js,会被解析为绝对路径然后报错。
-
在引入插件的时候有可能会报错
TypeError: compiler.plugin is not a function at CommonsChunkPlugin.apply遇到类似于这样的报错,可能是webpack版本对应的插件版本的问题,需要根据实际情况调整。
webpack-demos
1. demo01 入口文件
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
一个简单的案例,定义入口和输出
2. demo02 多入口文件
// webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js'
},
output: {
filename: '[name].js'
}
};
允许多个入口文件,这里的entry变成了一个对象。
output 中 filename 的值为 [name].js,这里的 name 是一个变量,对应着 entry 中的key
3. demo03 Babel-loader
loader 是一个文件加载器
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
这个案例中需要 Babel-loader 来转换JSX文件。
最外层是 module 对象,在 module.rules 定义转换规则,
规则对象的属性包括
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/, // 检测匹配什么类型的文件
exclude: /node_modules/, // include和exclude来包含或者排除范围
use: { // 选用什么loader
loader: 'babel-loader', // loader的名称
options: { // loader 的 配置项
presets: ['es2015', 'react'] // babel 的Babel的预设插件Babel -preset-es2015和Babel -preset-react
}
}
}
]
}
};
4. demo04 CSS-loader
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
]
}
};
Webpack 允许您在 JS 文件中包含 CSS,然后使用CSS-loader预处理 CSS 文件。
注意这里引入了两个CSS-loader
css-loader 用来读取CSS文件
style-loader 将<style>标签插入HTML中。
5. demo05 图像加载器
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.(png|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
};
打包后发现打包的文件夹里只有一张图片,???打包前不是有两张图片吗
原来是小图片进行了转换。
url-loader会将图片进行转换。如果图片小于一定的大小,就会转换为Data URL;否则,转换为普通 URL。
这个例子中的小图片被转换成了Data URL,所以没有被打包成图片。
这个一定大小可以在 option.limit 中进行设置。
下面是官网的原文
After launching the server, small.png and big.png have the following URLs.
<img src="data:image/png;base64,iVBOR...uQmCC">
<img src="4853ca667a2b8b8844eb2693ac1b2578.png">
6. demo06 CSS 模块
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
};
<!-- index.html -->
<html>
<body>
<h1 class="h1">Hello World</h1>
<h2 class="h2">Hello Webpack</h2>
<div id="example"></div>
<script src="./bundle.js"></script>
</body>
</html>
// main.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var style = require('./app.css');
ReactDOM.render(
<div>
<h1 className={style.h1}>Hello World</h1>
<h2 className="h2">Hello Webpack</h2>
</div>,
document.getElementById('example')
);
/* app.css */
.h1 {
color:red;
}
:global(.h2) {
color: blue;
}
通过css-loader?modules(查询参数模块)启用CSS 模块,
通过:global(selector)将其关闭,也就是恢复为全局样式。
上面的 .h1 将作为局部样式, .h2 将作为全局样式。
这里的局部样式是指打包的模块内部。
demo的效果如图片
从demo的效果可以看到,
第一个h1为黑色,第二个h1变成了红色。
这是因为 .h1 是局部样式,只有第二个h1可以应用(处于局部样式的作用域内,即打包的模块中)。
第一个和第二个h2都是蓝色。
这是因为 .h2 是全局样式,全局作用域内的元素都可以应用上。
这个是怎么做到的呢?其实在浏览器里检查一下元素就知道了。
可以发现,打包后实际点的类名变了。
在打包的过程中,webpack自动将 .h1 的类名转换了,用以保证其只在特定作用域中生效。
而 .h2 是全局样式,没有进行转换。
但是这里要注意渲染部分的写法,
需要传入变量来定义类名,className={style.h1}
如果只是写 “h1” 是无法生效的,会渲染为普通的字符串。
// main.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var style = require('./app.css');
ReactDOM.render(
<div>
<h1 className={style.h1}>Hello World</h1>
<h2 className="h2">Hello Webpack</h2>
</div>,
document.getElementById('example')
);
7. demo07 UglifyJs 插件
// webpack.config.js
var webpack = require('webpack');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new UglifyJsPlugin()
]
};
主要是具体插件的使用。
这个插件的功能是简化js代码。
在plugins属性中配置要使用的插件,记得要new
8. demo08 入口文件
// webpack.config.js
var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('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-webpack-plugin 可以帮助创建 index.html
open-browser-webpack-plugin可以在 Webpack 加载时打开一个新的浏览器选项卡。
在新建插件实例的时候可能需要传递一些参数,具体可以参考插件文档。
这里运行时报错了,好像是要安装和webpack版本对应的插件版本。
9. demo09 环境标志
// webpack.config.js
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
// package.json
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
可以在带有环境标志的开发环境中启用某些代码。
在运行的时候将环境变量传递给webpack,就可以在不同的环境中做对应的操作
10. demo10 代码拆分
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
// main.js
require.ensure(['./a'], function(require) {
var content = require('./a');
document.open();
document.write('<h1>' + content + '</h1>');
document.close();
});
代码拆分的结构如图
Webpackrequire.ensure用来定义一个分割点
require.ensure告诉 Webpack./a.js应该bundle.js与单个块文件分离并构建到单个块文件中。
require.ensure是webpack所独有的,可以被es6的import取代:
感觉理解得不是特别好,建议参考这篇文章 zhuanlan.zhihu.com/p/82918552
11. demo11 使用 bundle-loader 进行代码拆分
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
// main.js
var load = require('bundle-loader!./a.js');
load(function(file) {
document.open();
document.write('<h1>' + file + '</h1>');
document.close();
});
代码拆分的结构如图
代码拆分的另一种方法。注意loader的串联。
require('bundle-loader!./a.js')告诉 Webpacka.js从另一个块加载。
现在的WebPack将建设main.js成bundle.js和a.js成0.bundle.js。
12. demo12 通用模块
// webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
bundle1: './main1.jsx',
bundle2: './main2.jsx'
},
output: {
filename: '[name].js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
// (the commons chunk name)
filename: "commons.js",
// (the filename of the commons chunk)
})
]
}
// main1.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('a')
);
// main2.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h2>Hello Webpack</h2>,
document.getElementById('b')
);
<!-- index.html -->
<html>
<body>
<div id="a"></div>
<div id="b"></div>
<script src="commons.js"></script>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>
当多个脚本有共同的块时,可以使用CommonsChunkPlugin将共同的部分提取到一个单独的文件中,这对于浏览器缓存和节省带宽很有用。
和上面的拆分相反,这里讲的是合并,把通用的逻辑从多个文件中抽离出来。
在这个例子中,main1.jsx和main2.jsx的公共逻辑是react和react-dom,被抽离出来保存在另外的文件中。
13. demo13 供应商区块
// 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'
})
]
};
// 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>
可以使用 CommonsChunkPlugin 从脚本中提取供应商库到一个单独的文件中。
这里用的是jQuery。 entry.vendor: ['jquery']告诉 Webpackjquery应该包含在公共块中vendor.js。
这样的话在引用之前需要require,如果需要某个模块在全局范围的每一个模块中可用,使用ProvidePlugin( Official doc ) 自动加载模块,而不必在任何地方 import or require 它们。
// 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'
})
]
};
在这种情况下,jquery.js需要全局加载。
14. demo14 入口文件
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
},
]
},
externals: {
// require('data') is external and available
// on the global var data
'data': 'data'
}
};
// main.jsx
var data = require('data');
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>{data}</h1>,
document.body
);
下面是官网的原文
Attention, Webpack will only build
bundle.js, but notdata.js.you require
dataas a module variable in your script. but it actually is a global variable.
如果你想使用一些全局变量,又不想将它们包含在Webpack bundle中,可以externals在webpack.config.js(官方文档)中启用字段。
externals中可以配置某些模块,webpack编译打包时不处理它,却可以引用到它。
可以将react和react-dom放入externals,这将大大减少bundle.js构建时间和构建大小。
15. demo15 React router
+---------------------------------------------------------+
| +---------+ +-------+ +--------+ |
| |Dashboard| | Inbox | |Calendar| Logged in as Jane |
| +---------+ +-------+ +--------+ |
+---------------------------------------------------------+
| |
| Dashboard |
| |
| |
| +---------------------+ +----------------------+ |
| | | | | |
| | + + | +---------> | |
| | | | | | | |
| | | + | | +-------------> | |
| | | | + | | | | |
| | | | | | | | | |
| +-+---+----+-----+----+ +----------------------+ |
| |
+---------------------------------------------------------+
对React router不是特别熟悉,先记录
总结
1. 理解一些核心概念
2. webpack简单配置
记得先安装webpack和webpack-cli
const path = require('path');
module.exports = {
entry: './src/index.js', //打包的入口文件
mode: 'development', //打包的环境
output: { //打包的输出文件
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: { //加载器,用来加载各种类型的文件
rules: [
{
test: /.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [ //插件,用来提供各种功能
new HtmlWebpackPlugin({
title: 'Development',
}),
],
};