当前位置:   article > 正文

使用Vue CLI,服务端构造index.html,如何引入hash之后的JS、CSS等资源文件?_主入口文件生成了hash值,如何引入到html中

主入口文件生成了hash值,如何引入到html中

目录

一、前端打包构造index.html

二、服务器端构造index.html,有什么问题呢?

1. 不生成 index

2. 生成 index.html

三、问题解决,webpack-manifest-plugin && assets-webpack-plugin

1. 获取打包产物资源 json 文件

2. 读取 json 文件,index.html动态引入资源

总结


一、前端打包构造index.html

一般情况下,当我们运行 vue-cli-service build 时,public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。

 资源包带有hash,打包后如下:

index.html:

部署时直接服务器访问这个index.html就可以正常访问了。

这样的好处在于:

  • 能实现高效率的缓存控制
  • 通过这个被注入资源链接的 index.html,能很好的进行 code-splitting (代码分段)
  • 可以在现代模式下工作

二、服务器端构造index.html,有什么问题呢?

如果是服务器端构造的视图模板,肯定需要引入css和js等相关资源文件,我们有两种方法解决问题

1. 不生成 index

最终你可能会使用这样的一个index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  6. <meta name="renderer" content="webkit" />
  7. <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
  8. <link rel="icon" href="XXXXXX/favicon.ico" />
  9. <title>XXXXXX</title>
  10. <link href="XXXXXX/css/chunk-libs.css" rel="stylesheet" />
  11. <link href="XXXXXX/css/app.css" rel="stylesheet" />
  12. </head>
  13. <body>
  14. <div id="app"></div>
  15. <script src="XXXXXX/js/runtime.js"></script>
  16. <script src="XXXXXX/js/chunk-elementUI.js"></script>
  17. <script src="XXXXXX/js/chunk-libs.js"></script>
  18. <script src="XXXXXX/js/app.js"></script>
  19. </body>
  20. </html>

2. 生成 index.html

但这样的话,生成的 index.html 内容是动态多变的(文件增减、文件名变化、hash变化)。 服务器端压根不知道 splitChunks 后会分成多少个chunk块,也不知道hash之后的JS文件名字和CSS名字,所以引入资源成为了一个棘手的问题。

三、问题解决,webpack-manifest-plugin && assets-webpack-plugin

(两个插件效果一致,都是生成编译结果的资源单,只是资源单的数据结构不一致而已)

使用生成 index.html方式,解决资源引入问题

1. 获取打包产物资源 json 文件

你可以先读一下webpack中文官网 manifest

通过 WebpackManifestPlugin 插件,可以将 manifest 数据提取为一个容易使用的 json 文件。

npm install webpack-nano webpack-manifest-plugin --save-dev
  1. // webpack.config.js
  2. const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
  3. module.exports = {
  4. // an example entry definition
  5. entry: [ 'app.js' ],
  6. ...
  7. plugins: [
  8. new WebpackManifestPlugin()
  9. ]
  10. };
  1. // vue.config.js
  2. const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
  3. module.exports = {
  4. configureWebpack: {
  5. plugins: [
  6. new WebpackManifestPlugin()
  7. ]
  8. }
  9. }

使用默认选项上面的示例将创建一个“清单”。json文件在生成的输出目录中。manfist文件将包含源文件名到相应构建输出文件的映射。如:

  1. {
  2. "dist/batman.js": "dist/batman.1234567890.js",
  3. "dist/joker.js": "dist/joker.0987654321.js"
  4. }

按照官网的示例,你可以成功得到这个json资源清单。

但是这里我用了assets-webpack-plugin 来得到了这个json资源清单。使用如下:

  1. // vue.config.js
  2. const AssetsPlugin = require('assets-webpack-plugin')
  3. module.exports = {
  4. configureWebpack: {
  5. plugins: [
  6. new AssetsPlugin({
  7. filename: 'assets.js', // 文件的名称
  8. path: path.join(__dirname, 'dist'), // 输出目录,默认为项目的最高级别目录
  9. prettyPrint: true, // 格式化
  10. // 自定义
  11. processOutput: function(assets) {
  12. return 'window.ASSETS_FILE_MAP = ' + JSON.stringify(assets)
  13. }
  14. })
  15. ]
  16. }
  17. }

这样你会在你的打包输出目录dist得到一个assets.js:

2. 读取 json 文件,index.html动态引入资源

问题又来了,得到这个资源清单,我们改怎么使用呢?

前端打包构造的index.html格式化后是这样的:

服务端构造的index.html可能是这样的:

所以我们要在服务端构造的index.html中,在script内联脚本中动态引入这些 js 和 css 资源,以及那些chunk块,完整代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  6. <meta name="renderer" content="webkit" />
  7. <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
  8. <link rel="icon" href="https://xxxxxxxxx/favicon.ico" />
  9. <title>demo</title>
  10. </head>
  11. <body>
  12. <div id="app"></div>
  13. <script crossorigin="anonymous" src="https://xxxxxxxxx/tlog/tlog.min.js"></script>
  14. <script src="https://xxxxxxxxx/assets.js"></script>
  15. <script>
  16. // 获取每个chunk
  17. const getChunks = (chunkCommonsJosn, josnArr, isCss) => {
  18. let chunks = {};
  19. let chunks1 = {};
  20. const lastIndex = chunkCommonsJosn.lastIndexOf('/');
  21. const str = chunkCommonsJosn.slice(lastIndex + 1, -3);
  22. const arr = str.split('.');
  23. chunks[arr[0]] = arr[1];
  24. if (isCss) {
  25. chunks1[arr[0]] = 1;
  26. }
  27. for(let i = 0; i < josnArr.length; i++) {
  28. const item = josnArr[i];
  29. const lastIndex = item.lastIndexOf('/');
  30. const str = item.slice(lastIndex + 1, -3);
  31. const arr = str.split('.');
  32. if (arr[1] === 'worker') {
  33. continue
  34. }
  35. chunks[arr[0]] = arr[1];
  36. if (isCss) {
  37. chunks1[arr[0]] = 1;
  38. }
  39. }
  40. return { chunks, chunks1 };
  41. }
  42. window.onload = function () {
  43. let version = 'v5'; // 定义输出的目录
  44. const json = window.ASSETS_FILE_MAP;
  45. // 获取数据
  46. const appJosn = json.app;
  47. const chunkCommonsJosn = json['chunk-commons'];
  48. const chunkElementUIJosn = json['chunk-elementUI'];
  49. const chunkLibsJosn = json['chunk-libs'];
  50. const jsJosn = json[''] && json[''].js;
  51. const cssJosn = json[''] && json[''].css;
  52. const chunkCommonsJs = getChunks(chunkCommonsJosn.js, jsJosn)
  53. const chunkCommonsCss = getChunks(chunkCommonsJosn.css, cssJosn, true)
  54. // 创建元素
  55. let appLink = document.createElement('link');
  56. let chunkLibsLink = document.createElement('link');
  57. let appScript = document.createElement('script');
  58. let chunkElementUIScript = document.createElement('script');
  59. let chunkLibsScript = document.createElement('script');
  60. // 元素属性编辑
  61. appLink.href = appJosn.css;
  62. chunkLibsLink.href = chunkLibsJosn.css;
  63. appLink.rel = 'stylesheet';
  64. chunkLibsLink.rel = 'stylesheet';
  65. appScript.src = appJosn.js;
  66. chunkElementUIScript.src = chunkElementUIJosn.js;
  67. chunkLibsScript.src = chunkLibsJosn.js;
  68. appScript.crossorigin = 'anonymous';
  69. chunkElementUIScript.crossorigin = 'anonymous';
  70. chunkLibsScript.crossorigin = 'anonymous';
  71. // 把元素追加到父级元素中
  72. document.head.appendChild(appLink);
  73. document.head.appendChild(chunkLibsLink);
  74. document.body.appendChild(appScript);
  75. document.body.appendChild(appScript);
  76. document.body.appendChild(chunkElementUIScript);
  77. document.body.appendChild(chunkLibsScript);
  78. (function(c) {
  79. function e(e) {
  80. for (var d, u, a = e[0], f = e[1], b = e[2], t = 0, r = []; t < a.length; t++) u = a[t],
  81. h[u] && r.push(h[u][0]),
  82. h[u] = 0;
  83. for (d in f) Object.prototype.hasOwnProperty.call(f, d) && (c[d] = f[d]);
  84. o && o(e);
  85. while (r.length) r.shift()();
  86. return k.push.apply(k, b || []),
  87. n()
  88. }
  89. function n() {
  90. for (var c, e = 0; e < k.length; e++) {
  91. for (var n = k[e], d = !0, u = 1; u < n.length; u++) {
  92. var a = n[u];
  93. 0 !== h[a] && (d = !1)
  94. }
  95. d && (k.splice(e--, 1), c = f(f.s = n[0]))
  96. }
  97. return c
  98. }
  99. var d = {},
  100. u = {
  101. runtime: 0
  102. },
  103. h = {
  104. runtime: 0
  105. },
  106. k = [];
  107. function a(c) {
  108. return f.p + version + "/js/" + ({
  109. "chunk-commons": "chunk-commons"
  110. } [c] || c) + "." + chunkCommonsJs.chunks [c] + ".js"
  111. }
  112. function f(e) {
  113. if (d[e]) return d[e].exports;
  114. var n = d[e] = {
  115. i: e,
  116. l: !1,
  117. exports: {}
  118. };
  119. return c[e].call(n.exports, n, n.exports, f),
  120. n.l = !0,
  121. n.exports
  122. }
  123. f.e = function(c) {
  124. var e = [],
  125. n = chunkCommonsCss.chunks1;
  126. u[c] ? e.push(u[c]) : 0 !== u[c] && n[c] && e.push(u[c] = new Promise((function(e, n) {
  127. for (var d = version + "/css/" + ({
  128. "chunk-commons": "chunk-commons"
  129. } [c] || c) + "." + chunkCommonsCss.chunks [c] + ".css", h = f.p + d, k = document.getElementsByTagName("link"), a = 0; a < k.length; a++) {
  130. var b = k[a],
  131. t = b.getAttribute("data-href") || b.getAttribute("href");
  132. if ("stylesheet" === b.rel && (t === d || t === h)) return e()
  133. }
  134. var r = document.getElementsByTagName("style");
  135. for (a = 0; a < r.length; a++) {
  136. b = r[a],
  137. t = b.getAttribute("data-href");
  138. if (t === d || t === h) return e()
  139. }
  140. var o = document.createElement("link");
  141. o.rel = "stylesheet",
  142. o.type = "text/css",
  143. o.onload = e,
  144. o.onerror = function(e) {
  145. var d = e && e.target && e.target.src || h,
  146. k = new Error("Loading CSS chunk " + c + " failed.\n(" + d + ")");
  147. k.request = d,
  148. delete u[c],
  149. o.parentNode.removeChild(o),
  150. n(k)
  151. },
  152. o.href = h;
  153. var i = document.getElementsByTagName("head")[0];
  154. i.appendChild(o)
  155. })).then((function() {
  156. u[c] = 0
  157. })));
  158. var d = h[c];
  159. if (0 !== d) if (d) e.push(d[2]);
  160. else {
  161. var k = new Promise((function(e, n) {
  162. d = h[c] = [e, n]
  163. }));
  164. e.push(d[2] = k);
  165. var b, t = document.createElement("script");
  166. t.charset = "utf-8",
  167. t.timeout = 120,
  168. f.nc && t.setAttribute("nonce", f.nc),
  169. t.src = a(c),
  170. b = function(e) {
  171. t.onerror = t.onload = null,
  172. clearTimeout(r);
  173. var n = h[c];
  174. if (0 !== n) {
  175. if (n) {
  176. var d = e && ("load" === e.type ? "missing": e.type),
  177. u = e && e.target && e.target.src,
  178. k = new Error("Loading chunk " + c + " failed.\n(" + d + ": " + u + ")");
  179. k.type = d,
  180. k.request = u,
  181. n[1](k)
  182. }
  183. h[c] = void 0
  184. }
  185. };
  186. var r = setTimeout((function() {
  187. b({
  188. type: "timeout",
  189. target: t
  190. })
  191. }), 12e4);
  192. t.onerror = t.onload = b,
  193. document.head.appendChild(t)
  194. }
  195. return Promise.all(e)
  196. },
  197. f.m = c,
  198. f.c = d,
  199. f.d = function(c, e, n) {
  200. f.o(c, e) || Object.defineProperty(c, e, {
  201. enumerable: !0,
  202. get: n
  203. })
  204. },
  205. f.r = function(c) {
  206. "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(c, Symbol.toStringTag, {
  207. value: "Module"
  208. }),
  209. Object.defineProperty(c, "__esModule", {
  210. value: !0
  211. })
  212. },
  213. f.t = function(c, e) {
  214. if (1 & e && (c = f(c)), 8 & e) return c;
  215. if (4 & e && "object" === typeof c && c && c.__esModule) return c;
  216. var n = Object.create(null);
  217. if (f.r(n), Object.defineProperty(n, "default", {
  218. enumerable: !0,
  219. value: c
  220. }), 2 & e && "string" != typeof c) for (var d in c) f.d(n, d,
  221. function(e) {
  222. return c[e]
  223. }.bind(null, d));
  224. return n
  225. },
  226. f.n = function(c) {
  227. var e = c && c.__esModule ?
  228. function() {
  229. return c["default"]
  230. }: function() {
  231. return c
  232. };
  233. return f.d(e, "a", e),
  234. e
  235. },
  236. f.o = function(c, e) {
  237. return Object.prototype.hasOwnProperty.call(c, e)
  238. },
  239. f.p = "https://xxxxxxxxxxxxx/" + version + '/',
  240. f.oe = function(c) {
  241. throw console.error(c),
  242. c
  243. };
  244. var b = window["webpackJsonp"] = window["webpackJsonp"] || [],
  245. t = b.push.bind(b);
  246. b.push = e,
  247. b = b.slice();
  248. for (var r = 0; r < b.length; r++) e(b[r]);
  249. var o = t;
  250. n()
  251. })([]);
  252. }
  253. </script>
  254. </body>
  255. </html>

部署,亲测有效无bug!!

总结

  1. 服务端构造index.html,先使用 webpack-manifest-plugin 或 assets-webpack-plugin 生成一个json资源清单
  2. 读取json文件,动态创建<link />、<script />和加载 chunks 文件

 如果有用,点个赞吧(*^▽^*) 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/231721
推荐阅读
相关标签
  

闽ICP备14008679号