index.js
抛出importEntry方法,记载入口模版
Paragraph Name
数据缓存
const styleCache = {};
const scriptCache = {};
const embedHTMLCache = {};
默认的fetch 和 获取模版函数
export function importEntry(entry, opts = {}) {
}
opts说明
const defaultFetch = window.fetch.bind(window);
function defaultGetTemplate(tpl) { return tpl; }
getPublicPath
getExternalStyleSheet 和 getExecutableScript,getExternalScripts
getExecutableScript 获取css文件内容,如果是inline格式直接返回,如果是链接,查看是否缓存放回文本
function getExecutableScript(scriptSrc, scriptText, proxy, strictGlobal) {
const sourceUrl = isInlineCode(scriptSrc) ? '' : `//# sourceURL=${scriptSrc}\n`;
window.proxy = proxy;
// TODO 通过 strictGlobal 方式切换切换 with 闭包,待 with 方式坑趟平后再合并
return strictGlobal
? `;(function(window, self){with(window){;${scriptText}\n${sourceUrl}}}).bind(window.proxy)(window.proxy, window.proxy);`
: `;(function(window, self){;${scriptText}\n${sourceUrl}}).bind(window.proxy)(window.proxy, window.proxy);`;
}
execScripts获得外部的js 区分inline和链接,如果是async模式,返回一个对象,content属性能异步加载js的文件
execScripts
const {
fetch = defaultFetch, strictGlobal = false, success, error = () => {
}, beforeExec = () => {
},
} = opts;
const geval = (code) => {
beforeExec();
(0, eval)(code);
};
用eval执行js代码,如果是strictGlobal就将所有的属性挂载在代理上,不是直接挂载在window上。
如果是入口文件:
通过util中的noteGlobalProps和getGlobalProp方法,来获取在执行代码后挂载到全局的属性,并将其包装成exports对象的形式抛出
其他文件
不是异步,执行,是异步加载并执行
getEmbedHTML 方法
请求所有外部的css并将其以style的方式嵌入html中
/**
* convert external css link to inline style for performance optimization
* @param template
* @param styles
* @param opts
* @return embedHTML
*/
function getEmbedHTML(template, styles, opts = {}) {
const { fetch = defaultFetch } = opts;
let embedHTML = template;
return getExternalStyleSheets(styles, fetch)
.then(styleSheets => {
embedHTML = styles.reduce((html, styleSrc, i) => {
html = html.replace(genLinkReplaceSymbol(styleSrc), `<style>/* ${styleSrc} */${styleSheets[i]}</style>`);
return html;
}, embedHTML);
return embedHTML;
});
}
importHTML 方法
如果入口是string,就调用importHTML方法
// 通过fetch获取的html文件,经过processTpl解析
.then(html => {
const assetPublicPath = getPublicPath(url);
const { template, scripts, entry, styles } = processTpl(getTemplate(html), assetPublicPath);
return getEmbedHTML(template, styles, { fetch }).then(embedHTML => ({
template: embedHTML,
assetPublicPath,
getExternalScripts: () => getExternalScripts(scripts, fetch),
getExternalStyleSheets: () => getExternalStyleSheets(styles, fetch),
execScripts: (proxy, strictGlobal) => {
if (!scripts.length) {
return Promise.resolve();
}
return execScripts(entry, scripts, proxy, { fetch, strictGlobal });
},
}));
}));
返回值
{
template: embedHTML,
assetPublicPath,
getExternalScripts: () => getExternalScripts(scripts, fetch),
getExternalStyleSheets: () => getExternalStyleSheets(styles, fetch),
execScripts: (proxy, strictGlobal) => {
if (!scripts.length) {
return Promise.resolve();
}
return execScripts(entry, scripts, proxy, { fetch, strictGlobal });
},
}
process-tpl.js
一些正则
const ALL_SCRIPT_REGEX = /(<script[\s\S]*?>)[\s\S]*?<\/script>/gi; // 所有script标签
const SCRIPT_TAG_REGEX = /<(script)\s+((?!type=('|")text\/ng-template\3).)*?>.*?<\/\1>/is; // // 不是 ng-template的script 正向否定查找
const SCRIPT_SRC_REGEX = /.*\ssrc=('|")?([^>'"\s]+)/; //script 上的 src
const SCRIPT_TYPE_REGEX = /.*\stype=('|")?([^>'"\s]+)/; // script 上的type
const SCRIPT_ENTRY_REGEX = /.*\sentry\s*.*/; //
const SCRIPT_ASYNC_REGEX = /.*\sasync\s*.*/;
const SCRIPT_NO_MODULE_REGEX = /.*\snomodule\s*.*/;
const SCRIPT_MODULE_REGEX = /.*\stype=('|")?module('|")?\s*.*/;
const LINK_TAG_REGEX = /<(link)\s+.*?>/isg;
const LINK_PRELOAD_OR_PREFETCH_REGEX = /\srel=('|")?(preload|prefetch)\1/;
const LINK_HREF_REGEX = /.*\shref=('|")?([^>'"\s]+)/;
const LINK_AS_FONT = /.*\sas=('|")?font\1.*/;
const STYLE_TAG_REGEX = /<style[^>]*>[\s\S]*?<\/style>/gi;
const STYLE_TYPE_REGEX = /\s+rel=('|")?stylesheet\1.*/;
const STYLE_HREF_REGEX = /.*\shref=('|")?([^>'"\s]+)/;
const HTML_COMMENT_REGEX = /<!--([\s\S]*?)-->/g;
const LINK_IGNORE_REGEX = /<link(\s+|\s+.+\s+)ignore(\s*|\s+.*|=.*)>/is;
const STYLE_IGNORE_REGEX = /<style(\s+|\s+.+\s+)ignore(\s*|\s+.*|=.*)>/is;
const SCRIPT_IGNORE_REGEX = /<script(\s+|\s+.+\s+)ignore(\s*|\s+.*|=.*)>/is;
一些方法
export const genLinkReplaceSymbol = (linkHref, preloadOrPrefetch = false) => `<!-- ${preloadOrPrefetch ? 'prefetch/preload' : ''} link ${linkHref} replaced by import-html-entry -->`;
export const genScriptReplaceSymbol = (scriptSrc, async = false) => `<!-- ${async ? 'async' : ''} script ${scriptSrc} replaced by import-html-entry -->`;
export const inlineScriptReplaceSymbol = `<!-- inline scripts replaced by import-html-entry -->`;
export const genIgnoreAssetReplaceSymbol = url => `<!-- ignore asset ${url || 'file'} replaced by import-html-entry -->`;
export const genModuleScriptReplaceSymbol = (scriptSrc, moduleSupport) => `<!-- ${moduleSupport ? 'nomodule' : 'module'} script ${scriptSrc} ignored by import-html-entry -->`;
生成html中资源的说明,例如加载了‘./a.css’就会生成
processTpl 方法
处理html中的js,css以,入口文件和html模版本身,将符合条件的css和js提取出来(加载css和js的标签)并将其替换掉
let scripts = [];
const styles = [];
let entry = null;
const template = tpl
- 去掉html的注释
- 处理html中<link>元素相关的逻辑
.replace(LINK_TAG_REGEX, match => {
/* change the css link */
code...
return match;
})
* 检查是否是样式文件,如果是尝试获取地址,有地址的如果是忽略直接替代成忽略标签,不是添加进styles数组,替代成样式标签。
* 如果不满足样式文件,查看是否是预加载(有地址并且不是字体文件),替代成预加载样式标签
* 都不符合,不做处理
- 检查<style>元素,如果是忽略,替代成忽略标签
- 检查script 元素,替代
.replace(ALL_SCRIPT_REGEX, (match, scriptTag) => {
// code ...
});
* 检查是否是符合js类型的type,不符合不作处理
* 检查是否含有src,若果有采取类似link的处理,不同的是js有可能是异步async的
* 检查是否是inline-code,如果是提取出来
* 将符合条件的推入scripts数组,其他的不作处理
util.js
util 分析
-
shouldSkipProperty 是否需要跳过该全局属性(变量)
-
getGlobalProp 得到一个控制的全局属性
-
noteGlobalProps 标记全局属性
-
getInlineCode 解析script中的代码
-
defaultGetPublicPath 获得资源的public path
-
isModuleScriptSupported 是否支持模块
-
requestIdleCallback requestIdleCallback polyfill
网友评论