开始
我们通过一个简单的例子来理解webpack的工作,首先,我们初始化一个项目,
npm init
复制代码
然后,安装webpack和webpack-cli
npm install webpack webpack-cli --save-dev
复制代码
然后,建立两个文件
src/component.js
export default (text = "Hello world") => {
const element = document.createElement("div");
element.innerHTML = text;
return element;
};
复制代码
src/index.js
import component from "./component";
document.body.appendChild(component());
复制代码
终端输入:
node_modules/.bin/webpack
复制代码
终端输出:
Hash: 24db01e09460251a2e2d
Version: webpack 4.20.2
Time: 99ms
Built at: 2018-10-04 16:43:58
Asset Size Chunks Chunk Names
main.js 1.04 KiB 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 236 bytes {0} [built]
| ./src/index.js 78 bytes [built]
| ./src/component.js 158 bytes [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to'production' for
this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each
environment.You can also set it to 'none' to disable any default behavior. Learn
more: https://webpack.js.org/concepts/mode/
复制代码
这时候终端的信息告诉我们打包已经成功,hash、version、time、build日期、静态资源明细等,然后就是一个warning表示需要一个mode,是开发环境还是生产环境。
如果终端输入:
node_modules/.bin/webpack --mode development
复制代码
终端输出:
Hash: fbb6656fe339f2629619
Version: webpack 4.20.2
Time: 98ms
Built at: 2018-10-04 21:33:53
Asset Size Chunks Chunk Names
main.js 4.64 KiB main [emitted] main
Entrypoint main = main.js
[./src/component.js] 158 bytes {main} [built]
[./src/index.js] 78 bytes {main} [built]
复制代码
这时候因为传入了设置了mode所以没有了warning。
当然webapp是需要html的,同时需要在浏览器中调试的。
安装html-webpack-plugin
npm install html-webpack-plugin --save-dev
复制代码
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: "Webpack demo",
}),
],
};
复制代码
终端输入:
Hash: 0b26f05922ba70d8a92d
Version: webpack 4.20.2
Time: 392ms
Built at: 2018-10-04 22:28:30
Asset Size Chunks Chunk Names
main.js 1.04 KiB 0 [emitted] main
index.html 181 bytes [emitted]
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 236 bytes {0} [built]
| ./src/index.js 78 bytes [built]
| ./src/component.js 158 bytes [built]
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 519 bytes {0} [built]
+ 2 hidden modules
复制代码
与之前的相比多了html的信息,dist文件夹也多了index,html文件。
构建快捷方式:
package.json
"scripts": {
"build": "webpack --mode production"
},
复制代码
通过终端输入:
npm run build
复制代码
其输出结果和上面的一样。
结论:
-
打包一个简单的文件,需要的准备。
-
写稍微复杂一点的app都需要单独建立,webpack.config.js来管理webpack的配置
-
HtmlWebpackPlugin可以生成HTML作为你app的入口
-
用package.json scripts来管理webpack。你可以用它作为一个轻量级的发任务启动器和使用webpack之外的功能等。
-
通过webpack-cli webpack提供了命令行,在没有配置的情况下你也可以使用webpack,但是还有很多高级的配置需要你配置。
webpack-dev-server
webpack的watch模式与webpack-dev-server
watch模式
通过命令行:
npm run build -- --watch
复制代码
这时候会处于watch状态下,当源文件发生修改的,dist中相应的文件也会发生改变,这与之前没修改一次就需要重新打包不一样。
webpack-dev-server(WDS)
与watch模式相比,webpack-dev-server不仅有监视模式而且功能更加强大。
webpack-dev-server根据字面意思就可以理解,这里既有dev又有server,我们可以理解它是运行在‘在开发环境下内存中的服务器’,我们可以进一步理解,当我们的代码发生变化时,这些改变不会编译到打包文件中,这个过程有利于我们开发和调试。而且webpack-dev-server更是支持了Hot Module Replacement (HMR)这个功能使得我们在改变代码的时候无须刷新整个页面。
WDS start
安装WDS
npm install webpack-dev-server --save-dev
复制代码
WDS与项目绑定
要将WDS集成到项目中,首先,我们需要将写好我们的npm 脚本,我们一般约定为npm start:
"scripts": {
"start": "webpack-dev-server --mode development"
"build": "webpack --mode production"
},
复制代码
我们通过npm start 开启服务
> one@1.0.0 start /Users/jiangyiling/study/webpack-demo/one
> webpack-dev-server --mode development
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wdm」: Hash: 7b54b098d32e9acb0067
Version: webpack 4.20.2
Time: 569ms
Built at: 2018-10-04 23:19:55
Asset Size Chunks Chunk Names
main.js 342 KiB main [emitted] main
index.html 181 bytes [emitted]
Entrypoint main = main.js
[./node_modules/_ansi-html@0.0.7@ansi-html/index.js] 4.16 KiB {main} [built]
[./node_modules/_ansi-regex@2.1.1@ansi-regex/index.js] 135 bytes {main} [built]
[./node_modules/_events@1.1.1@events/events.js] 8.13 KiB {main} [built]
[./node_modules/_loglevel@1.6.1@loglevel/lib/loglevel.js] 7.68 KiB {main} [built]
[./node_modules/_strip-ansi@3.0.1@strip-ansi/index.js] 161 bytes {main} [built]
[0] multi ./node_modules/_webpack-dev-server@3.1.9@webpack-dev-server/client?http://localhost:8080 ./src 40 bytes {main} [built]
[./node_modules/_querystring-es3@0.2.1@querystring-es3/index.js] 127 bytes {main} [built]
[./node_modules/_url@0.11.0@url/url.js] 22.8 KiB {main} [built]
[./node_modules/_webpack-dev-server@3.1.9@webpack-dev-server/client/index.js?http://localhost:8080] ./node_modules/_webpack-dev-server@3.1.9@webpack-dev-server/client?http://localhost:8080 7.78 KiB {main} [built]
[./node_modules/_webpack-dev-server@3.1.9@webpack-dev-server/client/overlay.js] 3.58 KiB {main} [built]
[./node_modules/_webpack-dev-server@3.1.9@webpack-dev-server/client/socket.js] 1.05 KiB {main} [built]
[./node_modules/_webpack@4.20.2@webpack/hot/emitter.js] (webpack)/hot/emitter.js 77 bytes {main} [built]
[./node_modules/webpack/hot sync ^\.\/log$] ./node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
[./src/component.js] 160 bytes {main} [built]
[./src/index.js] 78 bytes {main} [built]
+ 12 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[./node_modules/_html-webpack-plugin@3.2.0@html-webpack-plugin/lib/loader.js!./node_modules/_html-webpack-plugin@3.2.0@html-webpack-plugin/default_index.ejs] 392 bytes {0} [built]
[./node_modules/_lodash@4.17.11@lodash/lodash.js] 527 KiB {0} [built]
[./node_modules/_webpack@4.20.2@webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {0} [built]
[./node_modules/_webpack@4.20.2@webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {0} [built]
ℹ 「wdm」: Compiled successfully.
复制代码
通过终端的输出,我们可以看到一些信息,这个项目的地址在
localhost:8080
复制代码
这时你修改代码会发现,终端输出信息会增加,页面的内容也会发生改变,这边是热更新了。
WDS配置文件
在我们的实际项目中,WDS在其中起着重要的作用,所以它的配置也并不是我们上面的demo那么简单,我们需要给它配置各种参数,虽然你可以通过脚手架工具来设置,但是这显然是不方便的,所以我们继续通过例子来学习吧。
webpack.config.js
...
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// WDS配置内容
devServer: {
// Display only errors to reduce the amount of output.
stats: "errors-only",
// Parse host and port from env to allow customization.
//
// If you use Docker, Vagrant or Cloud9, set
// host: options.host || "0.0.0.0";
//
// 0.0.0.0 is available to all network devices
// unlike default `localhost`.
host: process.env.HOST, // Defaults to `localhost`
port: process.env.PORT, // Defaults to 8080
open: true, // Open the page in browser
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack demo",
}),
],
};
复制代码
通过上面的配置文件我们可以发现,与上一章相比我们多了一个devServer对象。这个对象的内容我们逐一解释,
-
stats
"errors-only"表示只有在错误时才展示错误内容
-
host
host的默认值是localhost
-
port
port的默认值是8080
-
open
表示运行了WDS便自动在浏览器中打开
我们可以根据需要设置这些参数,例如:
我们通过命令行工具修改port
PORT=3000 npm start
复制代码
我们也可以在webpack.config.js中直接指定端口号
'''
port:3000
'''
复制代码
这时,我们打开页面就是localhost:3000了。
WDS其他属性
overlay
当然WDS还有其他的一些属性,我们回忆一下,我们在使用vue和react的我们是不是有遇到过这种情况呢。
webpack.config.js
module.exports = {
devServer: {
...
overlay: true,
},
...
}
复制代码
更快的开发环境配置
我们在开发的时候,修改项目中的资源会实现刷新的功能,但是我们修改webpack的配置呢,结果自然是页面并不会刷新。怎样做可以让我们在修改webpack配置时也实现刷新呢。
npm install nodemon --save-dev
复制代码
package.json
"scripts": {
"start": "nodemon --watch webpack.config.js --exec \"webpack-dev-server --mode development\"",
"build": "webpack --mode production"
},
复制代码
轮询监听模式
这主要是在低版本的操作系统中使用,这里不表。
其它
WDS还有一些实用的功能,比如:代理等功能。
结论
- 与WDS具有更好的监听体验。
- WDS集成了很多有利于开发环境的功能,比如:设置PORT,代理,热更新等功能,这些功能对开发者都很友好。
文件配置
我们查看vue、react等脚手架工具生成的项目会发现,它们并没有把所有功能模块写在同一个文件中,我们在配置一个webpack时主要是按功能和不同的环境进行配置的,比如开发环境和生产环境,各自都有一些特别的设置,所以我们需要将webpack文件进行配置,做到模块化,这样不仅让文件结构更清晰,也更有利于维护。
文件的配置方法
- 可以通过 --config 来配置参数
- 正如前端中有很多的库,webpack也有相应的库,可以在webpack中使用这些库,然后使用这些库便可以简写很多的东西,例如:hjs-webpack, Neutrino, webpack-blocks。
- 利用工具,如: create-react-app、vue-cli等
如同前端的各种框架一样,有一个重要思想是把项目模块化,颗粒度更细。webpack也一样。我们将webpack配置颗粒化了,我们怎么将他们连接起来呢。
通过Merging将配置组合
Object.assign 和 Array.concat分别可以将对象和数组组合,Object.assign的使用会存在覆盖的情况,所以webpack-merge将这两个方法给组合了起来,并且对这两种方法进行了一定的优化。具体的感受,如下图:
> merge = require("webpack-merge")
...
> merge(
... { a: [1], b: 5, c: 20 },
... { a: [2], b: 10, d: 421 }
... )
{ a: [ 1, 2 ], b: 10, c: 20, d: 421 }
复制代码
设置webpack-merge
将webpack-merge放到项目中
npm install webpack-merge --save-dev
复制代码
根据之前的内容我们将,webpack.config.js进一步的抽象
webpack.parts.js
exports.devServer = ({ host, port } = {}) => ({
devServer: {
stats: "errors-only",
host, // Defaults to `localhost`
port, // Defaults to 8080
open: true,
overlay: true,
},
});
复制代码
webpack.config.js
const merge = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const parts = require("./webpack.parts");
const commonConfig = merge([
{
plugins: [
new HtmlWebpackPlugin({
title: "Webpack demo",
}),
],
},
]);
const productionConfig = merge([]);
const developmentConfig = merge([
parts.devServer({
// Customize host/port here if needed
host: process.env.HOST,
port: process.env.PORT,
}),
]);
module.exports = mode => {
if (mode === "production") {
return merge(commonConfig, productionConfig, { mode });
}
return merge(commonConfig, developmentConfig, { mode });
};
复制代码
从上面的配置我们可以看出我们将webpack分为了两个环境,production和development。并且把development环境需要使用的devServer抽离出来做了一个单独的文件,还有公用的html插件那一部分也做成了一个单独的对象。
我们怎样来识别什么时候使用不同的环境呢,当然我们需要继续修改npm脚本。
package.json
"scripts": {
"start": "webpack-dev-server --env development",
"build": "webpack --env production"
// "start": "webpack-dev-server --mode development",
// "build": "webpack --mode production"
},
复制代码
理解 --env
--env既可以给传字符串也可以传对象,下面例子很简洁的说明这个问题:
package.json
"scripts": {
"start": "webpack-dev-server --env development",
"build": "webpack --env.target production"
},
复制代码
这时候配置接收到参数字符串development和对象{target:production}。
结论
- webpack是js实现,因此在前端领域有很多地方可以用到。
- 用户可以选择一个最适合自己方式来实现自己的webpack配置,webpack-merge的出现提供了一个捷径,其他方式也可以实现。
- webpack允许用户通过命令行工具来传 -–env参数。可通过函数接口来接收env参数
样式
这一章主要讲述的是css样式的加载,从js代码中抽离css已结剥离没有使用过得css
加载样式
加载css
加载样式需要css-loader和style-loader。
css-loader可以通过@import和url()去查找匹配的文件,类似于es2015的import,将css样式引入到js中。
style-loader通过标签将css样式注入到html中。
开始,安装css-loader和style-loader:
npm install css-loader style-loader --save-dev
复制代码
然后在webpack配置文件夹中配置
webpack.parts.js
exports.loadCSS = ({ include, exclude } = {}) => ({
module: {
rules: [
{
test: /\.css$/,
include,
exclude,
use: ["style-loader", "css-loader"],
},
],
},
});
复制代码
webpack.config.js
const commonConfig = merge([
...
parts.loadCSS(),
]);
复制代码
从上面中的代码可以清楚的认识到css-loader和style-loader的用法,loader是将源文件加载然后输出新的文件,而且他们是从右至左加载,
use: ["style-loader", "css-loader"],
复制代码
可以理解是
styeLoader(cssLoader(input))
复制代码
css预处理--less
首先安装less-loader
npm install less-loader less --save-d
复制代码
webpack.parts.js
exports.loadCSS = ({ include, exclude } = {}) => ({
module: {
rules: [
{
test: /\.less$/,
include,
exclude,
use: ["style-loader","css-loader","less-loader"],
},
],
},
});
复制代码
处理方式类似于我们上面的css文件,类似的预处理器还有sass,stylus等。
结论
-
了解css-loader和style-loader的作用.
-
webpack可以处理各种css预处理器,例如:less、sass和stylus等
ps:这一章还有很多内容可以进一步探讨 复制代码
css分离
在我们上一章提到的css样式都是注入到js代码中的,这显然不满足我们的要求,我们肯定是希望把css分离出来。所以,我们需要用MiniCssExtractPlugin来解决这个问题,由于它会带来编译时的开销,所以,这个需要我们把它用在生产环境中。
配置MiniCssExtractPlugin
npm install mini-css-extract-plugin --save-dev
复制代码
webpack.parts.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
exports.extractCSS = ({ include, exclude, use = [] }) => {
// Output extracted CSS to a file
const plugin = new MiniCssExtractPlugin({
filename: "[name].css",
});
return {
module: {
rules: [
{
test: /\.less$/,
include,
exclude,
use: [
MiniCssExtractPlugin.loader,
].concat(use),
},
],
},
plugins: [plugin],
};
};
复制代码
将MiniCssExtractPlugin运用到生产环境中
webpack.config.js
const merge = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const parts = require("./webpack.parts");
const commonConfig = merge([
{
plugins: [
new HtmlWebpackPlugin({
title: "Webpack demo",
}),
],
},
]);
const productionConfig = merge([
parts.extractCSS({use:[ "css-loader"]})
]);
const developmentConfig = merge([
parts.devServer({
// Customize host/port here if needed
host: process.env.HOST,
port: process.env.PORT,
}),
parts.loadCSS()
]);
module.exports = mode => {
console.log('mode',mode)
if (mode === "production") {
return merge(commonConfig, productionConfig, { mode });
}
return merge(commonConfig, developmentConfig, { mode });
};
复制代码
通过这样设置我们便可以在开发环境中使用HMR在生产环境中使用css分离,在使用了css分离加载过后,css文件会自动注入html中。
结论
- 分离css在生产环境中是很有必要的,我们可以通过我们刚才所做的来分离css样式。
删除未使用的css
像以前我们使用的Bootstrap库,在使用它的时候有很多多余的样式并没有用上,打包的时候能删除没有用的样式,对打包的体积会有明显的缩小。
PurifyCSS是一个通过分析文件来实现这一目标的工具。它遍历代码并确定正在使用的CSS类,有足够的信息可以从项目中删除未使用的CSS。
启用PurifyCSS
安装purify-css及相关:
npm install glob purifycss-webpack purify-css --save-dev
复制代码
webpack配置:
webpack.parts.js
const PurifyCSSPlugin = require("purifycss-webpack");
exports.purifyCSS = ({ paths }) => ({
plugins: [new PurifyCSSPlugin({ paths })],
});
复制代码
将PurifyCSSPlugin插件与webpack配置连接,PurifyCSSPlugin需要在MiniCssExtractPlugin后面使用:
webpack.config.js
...
const path = require("path");
const glob = require("glob");
const parts = require("./webpack.parts");
const PATHS = {
app: path.join(__dirname, "src"),
};
...
const productionConfig = merge([
...
parts.purifyCSS({
paths: glob.sync(`${PATHS.app}/**/*.js`, { nodir: true }),
}),
]);
复制代码
glob.sync(${PATHS.app}/**/*.js
, { nodir: true }) 返回组件js文件的绝对路径数组。
打包过后的css样式是删除了没有用的css,体积也变得小了。
结论
- 使用PurifyCSS可以消除未使用的CSS。
- purifycss-webpack应该在MiniCssExtractPlugin之后应用该插件。
- purifyCSS可以消除大多数未使用的CSS规则。
Autoprefixing
不同的浏览器对一些特殊的css属性需要特定的前缀,为了兼容所有的浏览器,我们需要给这些特殊的css属性加上前缀,但是,一个一个的属性加前缀是一个很繁琐的过程,所以我们需要在编译过程用插件自动加上这些前缀。
设置Autoprefixing
实现Autoprefixing需要安装postcss-loader和autoprefixer
npm install postcss-loader autoprefixer --save-dev
复制代码
设置webpack:
webpack.parts.js
exports.autoprefix = () => ({
loader: "postcss-loader",
options: {
plugins: () => [require("autoprefixer")()],
},
});
复制代码
然后连接webpack配置:
webpack.config.js
const productionConfig = merge([
parts.extractCSS({
use: ["css-loader", parts.autoprefix()],
}),
...
]);
复制代码
我们还需要选择我们想要支持的浏览器,所以我们需要建一个文件.browserslistrc
,在这个文件中定义需要支持的浏览器。比如:
.browserslistrc
> 1% # Browser usage over 1%
Last 2 versions # Or last two versions
IE 8 # Or IE 8
复制代码
autoprefix不仅可以添加前缀而且可以根据browserslistrc来删除前缀。
结论
autoprefix的作用不言而喻,这大大的减少了我们的工作量。
- 可以通过autoprefixer PostCSS插件启用自动添加前缀。
- .browserslistrc是一个标准文件,适用于autoprefixer之外的工具。
加载静态资源
loader的定义
webpack提供了很多方法来设置模块loader,webpack 2 引进了use字段简化了操作,并且在loader中使用的是绝对路径,这有利于webpack在不同的环境下使用。
loader剖析
webpack支持各种各样的加载器,它也支持一些开箱即用的js库,你需要设置loader来读取你文件的结构。
下面是一个babel的例子:
webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
include: path.join(__dirname, "app"),
exclude(path) {
// You can perform more complicated checks as well.
return path.match(/node_modules/);
},
use: "babel-loader",
},
],
},
};
复制代码
loader的执行顺序
loader执行的顺序是从左到右,从下至上的。
强制执行
可以设置特定的loader在上面提到的常规规则的前面或者后面执行,可以通过pre
或者post
来规定设置的loader在其他的loader的前面或者后面执行。
给loader传参
{
test: /\.js$/,
include: PATHS.app,
use: "babel-loader?presets[]=env",
},
复制代码
USE
使用function时
{
test: /\.js$/,
include: PATHS.app,
use: (data)=>{
return {
loader:"babel-loader"
}
},
},
复制代码
其中参数包括resource
、realResource
、resourceQuery
、issuer
、compiler
。
内联定义
虽然这种方式并不受推崇,但是它确实也是一种方式: src/index.js
import "style-loader!css-loader!./pure.css";
复制代码
这种把loader直接写到import里,并且不同的loader之间用!
隔开。
匹配文件的方式
test
通过include
和exclude
来匹配文件,下面详细讨论:
-
test
可以是一个正则表达式、函数、字符串、对象和数组。 -
include
同上 -
exclude
同上 -
resource
匹配条件是路径中含有规定的查询的资源,例如:`resource:/inline/` 所匹配的文件的路径中需要像这样 /path/foo.inline.js, /path/bar.png?inline 复制代码
-
issuer
匹配条件是路在规定的文件中引用的资源,例如:`issuer: /bar.js/` 如果/path/foo.png这个资源在/path/bar.js中被引用了,那么这个文件将会被匹配上。 复制代码
-
resourcePath
匹配条件是上面提到的resource
的子集,例如:`resource:/inline/` 所匹配的文件的路径中需要像这样 /path/foo.inline.js 复制代码
-
resourceQuery
匹配条件是上面提到的resource
的子集,例如:`resource:/inline/` 所匹配的文件的路径中需要像这样 /path/bar.png?inline 复制代码
Boolean值也可以进一步限制匹配条件:
not
and
or
这三个条件就如同字面意思自行理解。
resourceQuery&resourcePath
oneOf
字段可以用于loader匹配具体的静态资源,例如:
{
test: /\.png$/,
oneOf: [
{
resourceQuery: /inline/,
use: "url-loader",
},
{
resourceQuery: /external/,
use: "file-loader",
},
],
},
复制代码
如果上下文信息是存在于文件中的而不是存在query(查询)的,则使用resourcePath
而不是resourceQuery
。
issuer
issuer可用于资源引入到规定的文件中的匹配,例如:
{
test: /\.css$/,
rules: [
{
issuer: /\.js$/,
use: "style-loader",
},
{
use: "css-loader",
},
],
},
复制代码
这里是指,只匹配在js文件中引用的css资源。
混合issuer
和not
{
test: /\.css$/,
rules: [
{
issuer: { not: /\.css$/ },
use: "style-loader",
},
{
use: "css-loader",
},
],
}
复制代码
这里不匹配在css中引用的css资源。
理解loader的行为
loader-runner允许您在没有webpack的情况下单独运行它们,inspect-loader
允许检查loader之间内容的传递,你可以通过这个插件了解到资源流的详情。
结论
- Loaders允许设置加载资源的规则。
- webpack提供和了多种资源加载规则
Loading Images
webpack可以通过url-loader加载行内资源。这个加载器可以将图片转成base64
的格式,这个过程将比较大的静态资源变小了,而且可以减少服务器请求次数。
当静态资源通过url-loader时,如果文件大小大于了url-loader设置的大小则调用file-loader,返回路径,url-loader内置file-loader。
设置url-loader
因为url-loader内置file-loader,所以,url-loader是一个很好的选择,可以不用考虑文件的大小。如下:
{
test: /\.(jpg|png)$/,
use: {
loader: "url-loader",
options: {
limit: 25000,
name: "[path][name].[hash].[ext]"
//name:"./static/image/[name].[hash].[ext]"
},
},
},
复制代码
从代码中可以知道对静态资源限制是25k,但是当文件的体积大于25K会调用file-loader,而且,将原文件按照[path][name].[hash].[ext]
的格式打包,正如注释的我们也可以按照自己想要的样子输出。
Loading SVGs
webpack允许几种方式加载svg,但还是类似引入图片的方式还是最方便的,如下:
{
test: /\.svg$/,
use: "file-loader",
},
复制代码
当然还有很多方式,这里不一一赘述。
优化Images
可以通过image-webpack-loader
、svgo-loader
和imagemin-webpack-plugin
这些loader应该第一时间处理数据,因此这些Loader在use字段值的末尾。压缩文件是很有价值的至少能减少带宽。
利用srcset
resize-image-loader
和responsive-loader
可以设置图片的srcset属性。
动态加载图片
Loading Sprites
这个技术可以让多个小图片合成一张图片,这个技术可以用于生成动画,同时还避免了请求的开销。
webpack-spritesmith
可以将提供的图片转化为sprite sheet
和Sass/Less/Stylus mixins
。至于细节,在这里不赘述了。
使用占位符
image-trace-loader
在加载图片并将结果暴露出来作image/svg+xml
URL编码数据,它可以与url-loader
和file-loader
一起使用,在图片正在加载的时候,显示占位符。
lqip-loader
也有相同的作用,与tracing不同的是,这个loader是提供一个模糊的图片。
获取图片的尺寸
image-size-loader
可以提供image的尺寸、类型、体积等。
图片的引用
在样式文件中,可以使用@import
和url()
引入,css-loader
一样,也可以在代码中引入。
image和css-loader的Source Map陷阱
如果使用Image和css-loader都选择了sourceMap
,那么output.publicPath
设置为绝对路径指向服务器非常重要。
结论
url-loader
可以设置option参数将把指定大小图片写成base64编码的,体积超过了就调用file-loader
。file-loader
打包图片可返回其路径并写入代码中,并对其进行相关的命名。- 对图片进行优化。
- 将多张图片放在一个请求中。
- 动态加载图片资源。
- 使用了
sourceMap
需要设置绝对路径output.publicPath
。
字体加载
字体加载和图片加载很相似,加载字体的方式有很多,可以据需使用url-loader
和file-loader
像图片一样。
选择一种格式
.woff2 被现代服务器广为采用,Loader设置和加载图像一致,如下:
{
test: /\.woff$/,
use: {
loader: "url-loader",
options: {
limit: 50000,
},
},
},
复制代码
在世纪的开发过程中肯定需要更多的设置,如下:
{
test:/\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use:{
loader:'url-loader',
options: {
limit: 5000,
mimetype: "application/font-woff",
name: "./static/fonts/[name].[ext]",
}
}
}
复制代码
多种格式字体的支持
考虑到对所有服务器的支持,我们一般会选择传入多种格式的字体,所以webpack需要对不同格式的文件进行解析。
{
test: /\.(ttf|eot|woff|woff2)$/,
use: {
loader: "file-loader",
options: {
name: "fonts/[name].[ext]",
},
},
},
复制代码
在css文件中:
@font-face {
font-family: "myfontfamily";
src: url("./fonts/myfontfile.woff2") format("woff2"),
url("./fonts/myfontfile.woff") format("woff"),
url("./fonts/myfontfile.eot") format("embedded-opentype"),
url("./fonts/myfontfile.ttf") format("truetype");
/* Add other formats as you see fit */
}
复制代码
操作file-loader的Output Path和publicPath
file-loader允许修改具体的输出,这和图片输出类似,此外,操作publicPath
并覆盖默认的loader设置,下面的设置可以解决这个问题。 (待续...)
{
// Match woff2 and patterns like .woff?v=1.1.1.
test: /\.woff2?(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: "url-loader",
options: {
limit: 50000,
mimetype: "application/font-woff",
name: "./fonts/[name].[ext]", // Output below ./fonts
publicPath: "../", // Take the directory into account
},
},
},
复制代码
基于svg生成字体文件
使用Google字体
使用icon字体
结论
- 当加载fonts时和images一样,可以选择内联生成base64,比较大的文件也可以分离出来。
加载JavaScript
我们知道为了兼容低版本的浏览器我们需要把高版本的es6代码转成较低版本的代码,例如:es5里就没有const声明,这个代码就不能在低版本浏览器中运行。这时候就需要用babel来处理了。
babel在现阶段几乎是必须的,开发者可以通过babel-loader
使用babel
和webpack
。
设置babel-loader
安装:
npm install babel-loader @babel/core --save-dev
复制代码
webpack.parts.js
exports.babelloader = () =>{
return {
module: {
rules: [
{
test: /\.js$/,
include: path.join(__dirname, "./src"),
exclude(path) {
// You can perform more complicated checks as well.
return path.match(/node_modules/);
},
use:{
loader: "babel-loader",
options: {
presets: ['latest']
}
}
},
],
},
}
}
复制代码
webpack.config.js
const commonConfig = merge([
...
parts.loadJavaScript({ include: PATHS.app }),
]);
复制代码
做完这些配置还需要配置一个.babelrc
文件,这个文件是用来。
设置.babelrc
.babelrc
这个文件是用来配置babel转码的规则和插件的,例如我们配置的:
{
"presets": ['latest']
}
复制代码
这时候再调用
npm run build -- --devtool false --mode development
复制代码
之前的es6都会被转码成es5
总结
- babel的作用就是将高版本的js转码成更具兼容性的低版本,而且它还可以使用TypeScript。
Source Maps
当代码被打包过后,调试会变得比较麻烦。如何定位抛出错误的位置,Source maps
的出现就是来解决这个问题的。
内联Source Maps
和分离Source Maps
webpack可以生成两种形式的一种是内联Source Maps
另一种是分离Source Maps
,内联和分离相比之下,内联主要是用在开发环境下,它具有更好的性能,而分离的则更适合生产环境,因为它的体积更小。
启用Source Maps
webpack提供了两种方式使用Source Maps
,可以使用devtool
字段,再者使用plugins
这个在后面会讨论到。
在webpack中使用Source Maps
直接使用webpack的devtool
字段,如下:
webpack.parts.js
exports.generateSourceMaps = ({ type }) => ({
devtool: type,
});
复制代码
webpack支持很多source map
类型,这些不同主要是基于其质量和不同的构建速度。现在,可以测试一下默认设置下的开发,如下:
webpack.config.js
const productionConfig = merge([
parts.generateSourceMaps({ type: "source-map" }),
...
]);
复制代码
Webpack支持的Source Map类型
webpack支持的Source map
类型可以分为两种策略:
- Inline 打包生成的map数据作为DataURI嵌入到代码中,不会生成.map文件。
- Separate 在生成的map数据会分离出单独的文件。
inline主要是用于开发环境下,而Separate用于生产环境下,如果不在乎性能开销在开发环境下也可以使用Separate。
Inline Source Map 类型
webpack提供多种inline source map类型,通常eval
是起点,当在chrome和firefox中时,推荐cheap-module-eval-source-map
作为速度和质量的妥协。source-map
的类型很多,在这里不多做介绍了。
(这一章内容太繁琐了,后面再更新)
Bundle Splitting
生产环境下的js文件是一个单文件,如果应用发生了变化,那么客户端需要把文件重新下载下来。
只下载改变的那一部分是最好的,Bundle splitting
可以通过optimization.splitChunks.cacheGroups
实现。
Bundle Splitting思想
增加点东西来split
npm install react react-dom --save
复制代码
src/index.js
import "react";
import "react-dom";
...
复制代码
打包过后会发现,打包出来的体积比较大。
设置依赖包
在
(待续。。。)