说明
玩转webpack
课程学习笔记。
什么是资源内联
资源内联(
inline resource
),就是将一个资源以内联的方式嵌入进另一个资源里面.
1、HTML 内联 CSS (内联 CSS 或者 行内 CSS)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
background: #fff;
}
ul, ol, li {
list-style-type: none;
}
</style>
</head>
<body>
</body>
</html>
2、CSS 内联图片
通常将小图片通过 base64 的方式内嵌进 CSS 里面
.search {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABJ0lEQVQ4T6XSsUoEMRAG4H/ClZaLmbSW1pZ6+gAnFrK+gZXoK6jvIILgE6gIcnYWgmJno6AgYp1Z2EcIGQnsHbuaQ9abMkO+TGaGMGfQnPfxC3DOrajqPoB1AArgnohOvffPucc6ADMfAjgCUMYYH9MFY8wagEsAxyKScp2YAtbaERGNRST7LWZWVd2squq2LbSBMyK6E5GrXKnW2i1jzMh7v5sFmPkzhDCs69rngKIo3GAweBKRpVnAVwhh9Q/gRUQWs4Bz7jzGeFNV1ThXATOXAA5EJDV1Gr2aSETb3vvrLJAOmTmNKY2yVNUHVSVjzBDABYA3ADsi8j4TSIlmkfYAbABYUNUPACdE9NpAHaTXKjPz8k+kF9B8s4P0BibIpBf/AtpN/AYx54AR58WxmQAAAABJRU5ErkJggg==) no-repeat;
}
资源内联的意义
1、代码层面
- 页面框架的初始化脚本
- 上报相关打点
- css 内联避免页面闪动
2、请求层面
- 减少 HTTP ⽹网络请求数
- 小图片或者字体内联 (url-loader)
HTML 和 JS 内联
安装
0.5.1
的版本npm i raw-loader@0.5.1 -D
0.5.1 版本的 raw-loader 的代码:
module.exports = function(content) {
this.cacheable && this.cacheable();
this.value = content;
return "module.exports = " + JSON.stringify(content);
}
注意:需要使用 raw-loader
的 0.5.1
版本,最新的版本的 raw-loader
使用了导出模块的时候使用了 export default
语法, html
里面用的话有问题.
1、raw-loader 内联 html
<%= require('raw-loader!./meta.html') %>
2、raw-loader 内联 JS
<script><%= require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') %></script>
3、添加meta.html
<meta charset="UTF-8">
<meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="keywords" content="now,now直播,直播,腾讯直播,QQ直播,美女直播,附近直播,才艺直播,小视频,个人直播,美女视频,在线直播,手机直播">
<meta name="name" itemprop="name" content="NOW直播—腾讯旗下全民视频社交直播平台"><meta name="description" itemprop="description" content="NOW直播,腾讯旗下全民高清视频直播平台,汇集中外大咖,最in网红,草根偶像,明星艺人,校花,小鲜肉,逗逼段子手,各类美食、音乐、旅游、时尚、健身达人与你24小时不间断互动直播,各种奇葩刺激的直播玩法,让你跃跃欲试,你会发现,原来人人都可以当主播赚钱!">
<meta name="image" itemprop="image" content="https://pub.idqqimg.com/pc/misc/files/20170831/60b60446e34b40b98fa26afcc62a5f74.jpg"><meta name="baidu-site-verification" content="G4ovcyX25V">
<meta name="apple-mobile-web-app-capable" content="no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="dns-prefetch" href="//11.url.cn/">
<link rel="dns-prefetch" href="//open.mobile.qq.com/">
4、修改search.html
<!DOCTYPE html>
<html lang="en">
<head>
<%= require('raw-loader!./meta.html') %>
<title>Document</title>
<script><%= require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') %></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
4、结果
HTML内联增强版
1、实现一个 loader 去解析 HTML 里面的?__inline 语法,inline-html-loader
const fs = require('fs');
const path = require('path');
const getContent = (matched, reg, resourcePath) => {
const result = matched.match(reg);
const relativePath = result && result[1];
const absolutePath = path.join(path.dirname(resourcePath), relativePath);
return fs.readFileSync(absolutePath, 'utf-8');
};
module.exports = function(content) {
const htmlReg = /<link.*?href=".*?\__inline">/gmi;
const jsReg = /<script.*?src=".*?\?__inline".*?>.*?<\/script>/gmi;
content = content.replace(jsReg, (matched) => {
const jsContent = getContent(matched, /src="(.*)\?__inline/, this.resourcePath);
return `<script type="text/javascript">${jsContent}</script>`;
}).replace(htmlReg, (matched) => {
const htmlContent = getContent(matched, /href="(.*)\?__inline/, this.resourcePath);
return htmlContent;
});
return `module.exports = ${JSON.stringify(content)}`;
}
2、使用:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="./meta.html?__inline">
<title>Document</title>
<script type="text/javascript" src="../node_modules/lib-flexible/flexible.js?__inline"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
3、效果
CSS 内联
1、方案一:借助 style-loader
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top', // 样式插入到<head>
singleton: true, //将所有的style标签合并成一个
}
},
"css-loader",
"sass-loader"
],
},
]
},
};
2、方案二:html-inline-css-webpack-plugin
npm i html-inline-css-webpack-plugin -D
将<link rel="stylesheet" />
=> <style>...<style/>
注意:html-inline-css-webpack-plugin
需要放在 html-webpack-plugin
后面。
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new HtmlWebpackPlugin(),
new HTMLInlineCSSWebpackPlugin()
]
}
3、效果
CSS 内联的思路
1、style-loader
插入样式是一个动态的过程,查看打包后的 html 源码并不会看到 html 有 style 样式的。style-loader
是代码运行时动态的创建 style 标签,然后将 css style 插入到 style 标签里面去。
2、css-loader
的作用是将 css 转换成 commonjs 对象,也就是样式代码会被放到 js 里面去了。
4、CSS 内联的思路是:
将页面打包过程的产生的所有 CSS 提取成一个独立的文件,然后将这个 CSS 文件内联进 HTML head 里面。这里需要借助
mini-css-extract-plugin
和html-inline-css-webpack-plugin
来实现 CSS 的内联功能。
- 先将 css 提取打包成一个独立的 css 文件(使用MiniCssExtractPlugin.loader)
- 然后读取提取出的 css 内容注入到页面的 style 里面去。这个过程在构建阶段完成。
图片、字体内联
1、图片和字体的内联可以借助 url-loader
,通过修改 webpack 配置让小于 10k 的图片或者字体文件在构建阶段自动 base64。
2、增强版
url-loader
做资源内联最大的缺陷就是:不能个性化的去设置某张图片自动编码。
借鉴下 FIS
的语法糖,实现 ?__inline
的语法糖,引用某个图片的时候看到这个后缀则自动的将这张图片进行 base64 编码
export default function loader(content) {
const options = loaderUtils.getOptions(this) || {};
validateOptions(schema, options, {
name: 'File Loader',
baseDataPath: 'options',
});
const hasInlineFlag = /\?__inline$/.test(this.resource);
if (hasInlineFlag) {
const file = this.resourcePath;
// Get MIME type
const mimetype = options.mimetype || mime.getType(file);
if (typeof content === 'string') {
content = Buffer.from(content);
}
return `module.exports = ${JSON.stringify(
`data:${mimetype || ''};base64,${content.toString('base64')}`
)}`;
}
}
4、使用
.search {
background: url(./search-icon.png?__inline) no-repeat;
}