说明
玩转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 编码
3、实现inline-file-loader
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; }