丁香花免费观看电视剧高清

Heero.Luo发(fa)表于1年前,已(yi)被查看363次

噼里啪啦免费视频看

一般来说(shuo),SaaS 服务商提供(gong)的是标准化(hua)的产品服务(wu),体现的是所(suo)有客户的共性需求(qiu)。然而,部分客(ke)户(尤其是大(da)客户),会提出(chu)功能、UI 等方面的定制需(xu)求。针(zhen)对这些定制(zhi)需求,大体上有两个解(jie)决方案。

第一(yi)个方案是提(ti)供应用程序(xu) SDK,由客户的开(kai)发团队完成(cheng)整个定制应(ying)用的开发和(he)部署,SaaS 服(fu)务商提供必(bi)要的技术支(zhi)持即可。此方(fang)案要求客户(hu)的开发团队(dui)具备较强的(de) IT 专业能力。

第(di)二个方(fang)案则是由 SaaS 服(fu)务商的开发团(tuan)队在 SaaS 应用的(de)基础上进行(xing)二次开发,并(bing)部署。此方案(an)主要面向 IT 专(zhuan)业能力(li)较弱,或者仅(jin)需在 SaaS 应用的(de)基础上进行(xing)少量定制的(de)客户。然而,要支(zhi)持这种定(ding)制方式,相当(dang)于要求(qiu) SaaS 服务商在同(tong)一个应用中(zhong),针对不同的(de)客(ke)户运行不同(tong)分支的代码(ma)。要达到这个(ge)目的,应用程(cheng)序的架(jia)构也要进行(xing)相应的改造(zao)。本文主要讲(jiang)述改造的方(fang)案及其代码(ma)实现。

方案概(gai)览

对于前后(hou)端分离的项目来说(shuo),经过构建,最(zui)终会生成 html、js、css 三(san)种代码文件(jian)。以基于 Vue.js 框架的(de)项目为例,其(qi)构建出来的(de) index.html,内容与下面的代码(ma)相似:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <link href="https://cdn.my-app.net/sample/assets/css/chunk-0c7134a2.11fa7980.css" rel="prefetch">
  <link href="https://cdn.my-app.net/sample/assets/js/chunk-0c7134a2.02a43289.js" rel="prefetch">
  <link href="https://cdn.my-app.net/sample/assets/css/app.2dd9bc59.css" rel="preload" as="style">
  <link href="https://cdn.my-app.net/sample/assets/js/vendors~app.f1dba939.js" rel="preload" as="script">
  <link href="https://cdn.my-app.net/sample/assets/js/app.f7eb55ca.js" rel="preload" as="script">
  <link href="https://cdn.my-app.net/sample/assets/css/app.2dd9bc59.css" rel="stylesheet">
 </head>
 <body>
   <div id="app"></div>
   <script src="https://cdn.my-app.net/sample/assets/js/vendors~app.f1dba939.js"></script>
   <script src="https://cdn.my-app.net/sample/assets/js/app.f7eb55ca.js"></script>
 </body>
 </html>

实(shi)际上,index.html 只是访问入(ru)口,主要作用(yong)就是加载 css 和(he) js 资源。换句话(hua)说:任何的 html 页(ye)面,只要加载(zai)了上述(shu) css 和 js 资源,都可(ke)以运行这个(ge)应用

既然如(ru)此,只要做一个应用(yong)入口页,并根据客户配(pei)置加载相应(ying)代码分(fen)支构建出来(lai)的 css 和 js 资源即(ji)可。整体流程(cheng)如下图所示(shi):

整体流程

构(gou)建方案

入口(kou)页要加载对(dui)应分支(zhi)的 css 和 js 资源,首(shou)先需要一个(ge)资源列表。我(wo)们可以在构(gou)建流程增加(jia)一个步骤,把(ba) js 和 css 的引用提(ti)取(qu)到一个(ge)资源目录文(wen)件(index-assets.json)中:

const fs = require('fs');
const content = fs.readFileSync('./dist/index.html', 'utf-8');

// 匹配 html 中(zhong)的 js 或 css 引用标(biao)签
const assetTags = content.match(/<(?:link|script).*?>/gi) || [];

let result = [];
assetTags.forEach((assetTag) => {
  const asset = {
    tagName: '',
    attrs: {}
  };

  // 解析标签(qian)名
  if (/<(\w+)/.test(assetTag)) { asset.tagName = RegExp.$1; }

  // 解析属性(xing)
  const reAttrs = /\s(\w+)=["']?([^\s<>'"]+)/gi;
  let attr;
  while ((attr = reAttrs.exec(assetTag)) !== null) {
    asset.attrs[attr[1]] = attr[2];
  }

  result.push(asset);
});

// 移除 preload 的资源(yuan),并把 prefetch 的资源(yuan)放到 result 的最后面
const prefetches = [];
for (let i = 0, item; i < result.length;) {
  item = result[i];
  if (item.tagName === 'link') {
    if (item.attrs.rel === 'preload') {
      result.splice(i, 1);
      continue;
    } else if (item.attrs.rel === 'prefetch') {
      prefetches.push(result.splice(i, 1)[0]);
      continue;
    }
  }
  i++;
}
result = result.concat(prefetches);

fs.writeFileSync(
  './dist/index-assets.json',
  JSON.stringify({ list: result }),
  'utf-8'
);

执行脚(jiao)本(ben)后,就会(hui)生成资源目(mu)录文件,其内(nei)容为:

{
  "list": [
    {
      "attrs": {
        "href": "https://cdn.my-app.net/sample/assets/css/app.2dd9bc59.css",
        "rel": "stylesheet"
      },
      "tagName": "link"
    },
    {
      "attrs": {
        "src": "https://cdn.my-app.net/sample/assets/js/vendors~app.f1dba939.js"
      },
      "tagName": "script"
    },
    {
      "attrs": {
        "src": "https://cdn.my-app.net/sample/assets/js/app.f7eb55ca.js"
      },
      "tagName": "script"
    },
    {
      "attrs": {
        "href": "https://cdn.my-app.net/sample/assets/css/chunk-0c7134a2.11fa7980.css",
        "rel": "prefetch"
      },
      "tagName": "link"
    },
    {
      "attrs": {
        "href": "https://cdn.my-app.net/sample/assets/js/chunk-0c7134a2.02a43289.js",
        "rel": "prefetch"
      },
      "tagName": "link"
    }
  ]
}

在提取(qu)资源的过程(cheng)中,移除了通(tong)过 link 标签 preload 的资源,并把(ba) prefetch 的(de)资源放到了(le)资源列表的(de)末尾。具体原(yuan)因会在后文(wen)说明。

此外,因(yin)为多个分支(zhi)构建出来的代码都(dou)要上传到 OSS,为(wei)了避免放在(zai)同一个目录(lu)下(xia)互(hu)相覆盖,就得再(zai)加一层(ceng)分支目录。

https://cdn.my-app.net/sample/${branch}/

所(suo)以,代码分支(zhi)对应的(de)资源目录文(wen)件路径就是:

https://cdn.my-app.net/sample/${branch}/index-assets.json

加(jia)载方案

资源(yuan)加载时序图(tu)

加载流程如(ru)上图所示,接(jie)下来针对每(mei)一步详(xiang)述。

国产午夜亚洲精品午夜鲁丝片

进入(ru)页面后,携带(dai)客户信息(客(ke)户标识、内容标(biao)识(shi)等)请求(qiu)后端接口,该(gai)接口会(hui)返回代码分(fen)支名。实现如(ru)下:

// id 为客户信(xin)息
function getBranch(id) {
  // 如果请求后(hou)端接口超时(shi)(10s),就加载主分(fen)支
  const TIME_OUT = 10000;
  setTimeout(() => {
    loadAssetIndex('main');
  }, TIME_OUT);

  let branch;
  try {
    const response = await fetch(`/api/branch?id=${id}`);
    branch = (await response.json()).branch;
  } catch (e) {
    // 如果后端(duan)接口异(yi)常,就加载主(zhu)分支
    branch = 'main';
  }

  // 加载资(zi)源目录
  loadIndexAssets(branch);
}

除了(le)实现基本的(de)流程,以上代码还(hai)做了(le)降级(ji)处理——如果后(hou)端接口超时或响应(ying)异常,就加载(zai)主分支,避免(mian)页面白屏

2. 加(jia)载资源目录(lu)

加(jia)载指定分支(zhi)名的资源目(mu)录。实现如下:

// 用于避(bi)免重复加载(zai)
let status = 0;

function loadIndexAssets(branch) {
  if (status) { return; }
  status = 1;

  let list;
  try {
    const response = await fetch(`https://cdn.my-app.net/sample/${branch}/index-assets.json`);
    list = (await response.json()).list;
  } catch (e) {
    if (branch !== 'main') {
      status = 0;
      loadAssetIndex('main');
    }
    return;
  }
  status = 2;
  loadFiles(list);
}

同样地,以上(shang)代码也做了(le)降级处理——如果特定分支(zhi)名的资源目(mu)录文件加载(zai)失败,就(jiu)会加载主分(fen)支的资源目(mu)录文件,避免(mian)页面白屏

3. 加(jia)载资源

遍历(li)资源列表,把 css 和(he) js 都加载到页(ye)面上。代(dai)码实现如下(xia):

function loadFiles(list) {
  list.forEach(function(item) {
    const elt = doc.createElement(item.tagName); 
    // 脚本有依赖(lai)关系,要(yao)按顺序加载
    if (item.tagName === 'script') { elt.async = false; }

    for (const name in item.attrs) {
      elt.setAttribute(name, item.attrs[name]);
    }
    doc.head.appendChild(elt);
  });
}

需要(yao)注意的是,对(dui)于动态创建(jian)的 script 节点来说(shuo),它的 async 属(shu)性默认为 true。也(ye)就是说,这些(xie) script 会被并行请(qing)求,并尽快解(jie)析和执行,执(zhi)行顺序是未(wei)知的。然而,资(zi)源目录(lu)中的 js 是有依赖关(guan)系的,后面的(de) js 依赖于前面(mian)的 js。因此,必须(xu)把 script 节点的 async 设(she)为 false,让其按顺(shun)序解析和执(zhi)行。

脚本(ben)顺利执行后(hou),应用就会初(chu)始化。

4. 入口页(ye)

为了让读者(zhe)更好地理解(jie)整个过程,上(shang)述加载分支(zhi)资源的代码是用 ES6 编(bian)写的,并且会用(yong)到如 fetch、Promise、async、await 等特性(xing)。从兼容性的(de)角度考虑,这(zhe)段代码需要(yao)经过 Babel 的转译(yi),转译的过程中会插(cha)入一些额外(wai)的代码。然而(er),这段代码会(hui)阻塞后续的(de)流程,应尽可(ke)能轻量化。因(yin)此,实际开发(fa)的时候(hou)是采用 ES5 编写(xie),fetch 也替换为 XMLHttpRequest。此(ci)外,由于代码量(liang)比较少,还可以通过 Webpack 的(de) inline-source-webpack-plugin,把构建后的 js 代码(ma)以行内脚本(ben)的形式(shi)输出到页面(mian)上,减少一个(ge) js 文件请求。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <div id="app"></div>
  <script inline inline-asset="main\.\w+\.js$" inline-asset-delete></script>
</body>
</html>

其(qi)他注意点

资(zi)源目录文件(jian)的过期时间(jian)

由于资源目(mu)录文件(jian)的路径是固(gu)定的,所以该(gai)文件要禁用(yong) HTTP 的强缓存,或者(zhe)仅配置短时(shi)间的强缓存(cun)。

否则,一旦用(yong)户使用(yong)的浏览器长(chang)时间缓存了(le)该文件,那么(me)在缓存期间(jian),不管更新了(le)多少个版本(ben),用户访问的(de)仍然是缓存下来的(de)那个版本。

小(xiao)小的加速

定(ding)制客户毕竟(jing)是少数,大部分客户(hu)用的(de)仍然(ran)是标准的 SaaS 应(ying)用。也就是说,大部分(fen)情况下加载(zai)的是主分支(zhi)的资源目录(lu)文件。因此,可(ke)以在入口页(ye)提前加载这(zhe)个资源:

<link href="https://cdn.my-app.net/sample/main/index-assets.json" rel="preload" as="fetch" />

关于预加载(zai)

link 标签支持两(liang)种方式的预(yu)加载:

  • preload 是提前(qian)加载,但(dan)是不阻塞 onload,主要用(yong)于预加载当(dang)前页面会用到的资源(yuan);
  • prefetch 是闲时加载(zai),主要用于加(jia)载将来可能(neng)会用到的资(zi)源。

以前文的(de) index.html 为例,app.2dd9bc59.css、vendors~app.f1dba939.js、app.f7eb55ca.js 这三个(ge)资源都在页(ye)面中通(tong)过 link 或 script 标签引(yin)用,所以会通过 preload 去提前加(jia)载。而其他资(zi)源则是将来(lai)可能会用到(dao)的资源(比如(ru)在某个(ge)时机才会动态(tai) import 的资源),所以(yi)是通过 prefetch 闲时(shi)加载。

然而,在(zai)前文讲到提(ti)取页面 css 和 js 资源的时候(hou),我们把 preload 的资(zi)源移除了,并(bing)且把 prefetch 的资源(yuan)移到了末尾(wei)。为什么要这(zhe)么做呢?我们(men)从入口页加(jia)载流程去分(fen)析这个问题。

入口页(ye)流程图

如上图所示:

  • 执行加(jia)载逻辑之后(hou),页面 onload 已经触(chu)发,提前加载(zai)的时机早已(yi)过去,所以 preload 已经没有(you)意义。
  • 加载资(zi)源目录文件(jian)之后,加载 css、js 资源(yuan)之前,页面(mian)没有其他的(de)加载任务,已(yi)经处在空闲状态。如(ru)果此时把 prefetch 的(de) link 元素插入到(dao)页面中,浏览器(qi)马上就会加(jia)载这部分资(zi)源。因此,在资(zi)源列表中,prefetch 的(de)资源要(yao)往后放,让那些应用初(chu)始化所需的资源(yuan)可以被优先(xian)加载进来。

总(zong)结

总地来说(shuo),本文所述的(de)方案有(you)以下优势:

  • 轻(qing)量化,无需依(yi)赖第三方库(ku)或框架。
  • 无需(xu)改动应用的逻(luo)辑,而是在进(jin)入应用之前(qian)增加了(le)一层入口页(ye),侵入性低。
  • 适(shi)配性广,无需(xu)变(bian)更(geng)应用的(de)技术栈。

然而(er),也具备一定(ding)的局限性:

  • 只(zhi)适用于(yu)前后端分离(li)的应用,并且(qie) html 文件中不能(neng)承载任何功(gong)能。
  • 入口页的(de)三个步骤——执行(xing)加载逻辑、加(jia)载资源(yuan)目录文件、加载资源(yuan),是串行执行的,页面(mian)的白屏时间(jian)会增加。有条(tiao)件的情况下(xia),可以把前两(liang)步放到后端去执行(xing)。
  • 未考虑后端(duan)的多分支管(guan)理方案。

评论 (1条)

发表评论

(必填)

(选填,不(bu)公开)

(选填,不公开)

(必(bi)填)

久久香综合精品久久伊人欧美日韩一区二区综合高清在线观看 网站地图
四虎国产精品永久在线观看高清视频,白领人妻系列视频在线观看免费高清视频,性直播视频在线观看免费 久久香综合精品久久伊人欧美日韩一区二区综合高清在线观看每日更新国产精品白丝AV网站观看免费高清版,欧美日韩一区二区综合观看在线下载播放等成年人看的在线视频