赞
踩
目录
三、问题解决,webpack-manifest-plugin && assets-webpack-plugin
2. 读取 json 文件,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 (代码分段)
- 可以在现代模式下工作
如果是服务器端构造的视图模板,肯定需要引入css和js等相关资源文件,我们有两种方法解决问题
最终你可能会使用这样的一个index.html
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
- <meta name="renderer" content="webkit" />
- <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
- <link rel="icon" href="XXXXXX/favicon.ico" />
- <title>XXXXXX</title>
- <link href="XXXXXX/css/chunk-libs.css" rel="stylesheet" />
- <link href="XXXXXX/css/app.css" rel="stylesheet" />
- </head>
- <body>
- <div id="app"></div>
- <script src="XXXXXX/js/runtime.js"></script>
- <script src="XXXXXX/js/chunk-elementUI.js"></script>
- <script src="XXXXXX/js/chunk-libs.js"></script>
- <script src="XXXXXX/js/app.js"></script>
- </body>
- </html>
但这样的话,生成的 index.html 内容是动态多变的(文件增减、文件名变化、hash变化)。 服务器端压根不知道 splitChunks 后会分成多少个chunk块,也不知道hash之后的JS文件名字和CSS名字,所以引入资源成为了一个棘手的问题。
(两个插件效果一致,都是生成编译结果的资源单,只是资源单的数据结构不一致而已)
使用生成 index.html方式,解决资源引入问题
你可以先读一下webpack中文官网 manifest
通过 WebpackManifestPlugin 插件,可以将 manifest 数据提取为一个容易使用的 json 文件。
npm install webpack-nano webpack-manifest-plugin --save-dev
- // webpack.config.js
-
- const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
-
- module.exports = {
- // an example entry definition
- entry: [ 'app.js' ],
- ...
- plugins: [
- new WebpackManifestPlugin()
- ]
- };
- // vue.config.js
-
- const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
-
- module.exports = {
- configureWebpack: {
- plugins: [
- new WebpackManifestPlugin()
- ]
- }
- }
使用默认选项,
上面的示例将创建一个“清单”。json文件在生成的输出目录中。manfist文件将包含源文件名到相应构建输出文件的映射。如:
- {
- "dist/batman.js": "dist/batman.1234567890.js",
- "dist/joker.js": "dist/joker.0987654321.js"
- }
按照官网的示例,你可以成功得到这个json资源清单。
但是这里我用了assets-webpack-plugin 来得到了这个json资源清单。使用如下:
- // vue.config.js
-
- const AssetsPlugin = require('assets-webpack-plugin')
-
- module.exports = {
- configureWebpack: {
- plugins: [
- new AssetsPlugin({
- filename: 'assets.js', // 文件的名称
- path: path.join(__dirname, 'dist'), // 输出目录,默认为项目的最高级别目录
- prettyPrint: true, // 格式化
- // 自定义
- processOutput: function(assets) {
- return 'window.ASSETS_FILE_MAP = ' + JSON.stringify(assets)
- }
- })
- ]
- }
- }
这样你会在你的打包输出目录dist得到一个assets.js:
问题又来了,得到这个资源清单,我们改怎么使用呢?
前端打包构造的index.html格式化后是这样的:
服务端构造的index.html可能是这样的:
所以我们要在服务端构造的index.html中,在script内联脚本中动态引入这些 js 和 css 资源,以及那些chunk块,完整代码如下:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
- <meta name="renderer" content="webkit" />
- <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
- <link rel="icon" href="https://xxxxxxxxx/favicon.ico" />
- <title>demo</title>
- </head>
- <body>
- <div id="app"></div>
- <script crossorigin="anonymous" src="https://xxxxxxxxx/tlog/tlog.min.js"></script>
- <script src="https://xxxxxxxxx/assets.js"></script>
- <script>
- // 获取每个chunk
- const getChunks = (chunkCommonsJosn, josnArr, isCss) => {
- let chunks = {};
- let chunks1 = {};
-
- const lastIndex = chunkCommonsJosn.lastIndexOf('/');
- const str = chunkCommonsJosn.slice(lastIndex + 1, -3);
- const arr = str.split('.');
- chunks[arr[0]] = arr[1];
- if (isCss) {
- chunks1[arr[0]] = 1;
- }
- for(let i = 0; i < josnArr.length; i++) {
- const item = josnArr[i];
- const lastIndex = item.lastIndexOf('/');
- const str = item.slice(lastIndex + 1, -3);
- const arr = str.split('.');
- if (arr[1] === 'worker') {
- continue
- }
- chunks[arr[0]] = arr[1];
- if (isCss) {
- chunks1[arr[0]] = 1;
- }
- }
- return { chunks, chunks1 };
- }
- window.onload = function () {
- let version = 'v5'; // 定义输出的目录
- const json = window.ASSETS_FILE_MAP;
-
- // 获取数据
- const appJosn = json.app;
- const chunkCommonsJosn = json['chunk-commons'];
- const chunkElementUIJosn = json['chunk-elementUI'];
- const chunkLibsJosn = json['chunk-libs'];
- const jsJosn = json[''] && json[''].js;
- const cssJosn = json[''] && json[''].css;
-
- const chunkCommonsJs = getChunks(chunkCommonsJosn.js, jsJosn)
- const chunkCommonsCss = getChunks(chunkCommonsJosn.css, cssJosn, true)
-
- // 创建元素
- let appLink = document.createElement('link');
- let chunkLibsLink = document.createElement('link');
- let appScript = document.createElement('script');
- let chunkElementUIScript = document.createElement('script');
- let chunkLibsScript = document.createElement('script');
-
- // 元素属性编辑
- appLink.href = appJosn.css;
- chunkLibsLink.href = chunkLibsJosn.css;
- appLink.rel = 'stylesheet';
- chunkLibsLink.rel = 'stylesheet';
- appScript.src = appJosn.js;
- chunkElementUIScript.src = chunkElementUIJosn.js;
- chunkLibsScript.src = chunkLibsJosn.js;
- appScript.crossorigin = 'anonymous';
- chunkElementUIScript.crossorigin = 'anonymous';
- chunkLibsScript.crossorigin = 'anonymous';
-
- // 把元素追加到父级元素中
- document.head.appendChild(appLink);
- document.head.appendChild(chunkLibsLink);
- document.body.appendChild(appScript);
- document.body.appendChild(appScript);
- document.body.appendChild(chunkElementUIScript);
- document.body.appendChild(chunkLibsScript);
-
- (function(c) {
- function e(e) {
- for (var d, u, a = e[0], f = e[1], b = e[2], t = 0, r = []; t < a.length; t++) u = a[t],
- h[u] && r.push(h[u][0]),
- h[u] = 0;
- for (d in f) Object.prototype.hasOwnProperty.call(f, d) && (c[d] = f[d]);
- o && o(e);
- while (r.length) r.shift()();
- return k.push.apply(k, b || []),
- n()
- }
- function n() {
- for (var c, e = 0; e < k.length; e++) {
- for (var n = k[e], d = !0, u = 1; u < n.length; u++) {
- var a = n[u];
- 0 !== h[a] && (d = !1)
- }
- d && (k.splice(e--, 1), c = f(f.s = n[0]))
- }
- return c
- }
- var d = {},
- u = {
- runtime: 0
- },
- h = {
- runtime: 0
- },
- k = [];
- function a(c) {
- return f.p + version + "/js/" + ({
- "chunk-commons": "chunk-commons"
- } [c] || c) + "." + chunkCommonsJs.chunks [c] + ".js"
- }
- function f(e) {
- if (d[e]) return d[e].exports;
- var n = d[e] = {
- i: e,
- l: !1,
- exports: {}
- };
- return c[e].call(n.exports, n, n.exports, f),
- n.l = !0,
- n.exports
- }
- f.e = function(c) {
- var e = [],
- n = chunkCommonsCss.chunks1;
- u[c] ? e.push(u[c]) : 0 !== u[c] && n[c] && e.push(u[c] = new Promise((function(e, n) {
- for (var d = version + "/css/" + ({
- "chunk-commons": "chunk-commons"
- } [c] || c) + "." + chunkCommonsCss.chunks [c] + ".css", h = f.p + d, k = document.getElementsByTagName("link"), a = 0; a < k.length; a++) {
- var b = k[a],
- t = b.getAttribute("data-href") || b.getAttribute("href");
- if ("stylesheet" === b.rel && (t === d || t === h)) return e()
- }
- var r = document.getElementsByTagName("style");
- for (a = 0; a < r.length; a++) {
- b = r[a],
- t = b.getAttribute("data-href");
- if (t === d || t === h) return e()
- }
- var o = document.createElement("link");
- o.rel = "stylesheet",
- o.type = "text/css",
- o.onload = e,
- o.onerror = function(e) {
- var d = e && e.target && e.target.src || h,
- k = new Error("Loading CSS chunk " + c + " failed.\n(" + d + ")");
- k.request = d,
- delete u[c],
- o.parentNode.removeChild(o),
- n(k)
- },
- o.href = h;
- var i = document.getElementsByTagName("head")[0];
- i.appendChild(o)
- })).then((function() {
- u[c] = 0
- })));
- var d = h[c];
- if (0 !== d) if (d) e.push(d[2]);
- else {
- var k = new Promise((function(e, n) {
- d = h[c] = [e, n]
- }));
- e.push(d[2] = k);
- var b, t = document.createElement("script");
- t.charset = "utf-8",
- t.timeout = 120,
- f.nc && t.setAttribute("nonce", f.nc),
- t.src = a(c),
- b = function(e) {
- t.onerror = t.onload = null,
- clearTimeout(r);
- var n = h[c];
- if (0 !== n) {
- if (n) {
- var d = e && ("load" === e.type ? "missing": e.type),
- u = e && e.target && e.target.src,
- k = new Error("Loading chunk " + c + " failed.\n(" + d + ": " + u + ")");
- k.type = d,
- k.request = u,
- n[1](k)
- }
- h[c] = void 0
- }
- };
- var r = setTimeout((function() {
- b({
- type: "timeout",
- target: t
- })
- }), 12e4);
- t.onerror = t.onload = b,
- document.head.appendChild(t)
- }
- return Promise.all(e)
- },
- f.m = c,
- f.c = d,
- f.d = function(c, e, n) {
- f.o(c, e) || Object.defineProperty(c, e, {
- enumerable: !0,
- get: n
- })
- },
- f.r = function(c) {
- "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(c, Symbol.toStringTag, {
- value: "Module"
- }),
- Object.defineProperty(c, "__esModule", {
- value: !0
- })
- },
- f.t = function(c, e) {
- if (1 & e && (c = f(c)), 8 & e) return c;
- if (4 & e && "object" === typeof c && c && c.__esModule) return c;
- var n = Object.create(null);
- if (f.r(n), Object.defineProperty(n, "default", {
- enumerable: !0,
- value: c
- }), 2 & e && "string" != typeof c) for (var d in c) f.d(n, d,
- function(e) {
- return c[e]
- }.bind(null, d));
- return n
- },
- f.n = function(c) {
- var e = c && c.__esModule ?
- function() {
- return c["default"]
- }: function() {
- return c
- };
- return f.d(e, "a", e),
- e
- },
- f.o = function(c, e) {
- return Object.prototype.hasOwnProperty.call(c, e)
- },
- f.p = "https://xxxxxxxxxxxxx/" + version + '/',
- f.oe = function(c) {
- throw console.error(c),
- c
- };
- var b = window["webpackJsonp"] = window["webpackJsonp"] || [],
- t = b.push.bind(b);
- b.push = e,
- b = b.slice();
- for (var r = 0; r < b.length; r++) e(b[r]);
- var o = t;
- n()
- })([]);
- }
- </script>
- </body>
- </html>
部署,亲测有效无bug!!
- 服务端构造index.html,先使用 webpack-manifest-plugin 或 assets-webpack-plugin 生成一个json资源清单
- 读取json文件,动态创建<link />、<script />和加载 chunks 文件
如果有用,点个赞吧(*^▽^*)
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。