下载 Angular 2 个组件和 运行 个脚本
Make Angular 2 component download and run a script
我正在尝试使用 MathJax 在我的 Angular 2
应用程序中显示方程式。我的应用程序只有一小部分实际上需要此功能,所以我不希望所有用户都必须下载所需的 3-400kb。
我最初的想法是 import "mathjax"
在我的组件中,这样只有在创建组件时才会加载库。但是,我了解到 MathJax 不能很好地与其他人一起使用,因为它使用自己的自定义模块加载器。
我当前的设置从我的 index.html
加载 script
但这意味着 每个人 都会在只有一小部分用户需要此组件时下载它
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=AM_SVG"></script>
Angular 2
中是否有一种干净的方法让我的组件在 ngOnInit()
挂钩中下载脚本并在它进入 DOM 后执行回调?诸如(伪代码)之类的东西:
ngOnInit(){
//download script, add to DOM
fetch('https://...MathJax.js').then(addToDom).then(this.onMathJaxLoaded);
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
我没有尝试过,但我认为这可能是可行的:
ngOnInit(){
//download script, add to DOM
var script = document.createElement('script');
document.body.appendChild(script)
script.onload = this.onMathJaxLoaded.bind(this);
script.src = 'https://...MathJax.js';
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
这是一个 fiddle,其中包含一个示例,说明它在普通 JavaScript 中如何工作; https://jsfiddle.net/5qu5h7bc/
基于@TryingToImprove 的解决方案,我决定在我的应用程序中添加一个 ScriptLoaderService
,其中包含一个 load()
函数,该函数获取远程脚本并通知调用者(使用 RxJS Observable)完成后。
使用方法如下:
this.scriptLoaderService.load('https://...js').subscribe(
()=>console.log('script has loaded');
);
这是服务:
/**
* A directory of scripts that have been or are being loaded
*/
private scripts = new Map<string,Observable<boolean>|boolean>();
/**
* Downloads script; returns Observable that emits TRUE once "load" event fires
*/
public load(src:string):Observable<boolean>{
if(this.scripts.has(src)){
// If script has already been fully loaded.
if(this.scripts.get(src)===true) return Observable.of(true);
// Else if download is already underway but load event hasn't fired yet
else return this.scripts.get(src);
}
//Add a script tag to the DOM
let script = document.createElement('script');
document.body.appendChild(script);
// upon subscription, listen for the load event. Once it arrives, emit TRUE
let obs:Observable<boolean> = Observable.fromEvent(script,'load')
// Map should hold TRUE once script is loaded
.do(()=> this.scripts.set(src,true))
.take(1).map(e=>true);
// set the src attribute (will cause browser to download)
script.src = src;
//add observable to the scripts Map
this.scripts.set(src,obs);
return obs;
}
测试于 Firefox 51
我正在尝试使用 MathJax 在我的 Angular 2
应用程序中显示方程式。我的应用程序只有一小部分实际上需要此功能,所以我不希望所有用户都必须下载所需的 3-400kb。
我最初的想法是 import "mathjax"
在我的组件中,这样只有在创建组件时才会加载库。但是,我了解到
我当前的设置从我的 index.html
加载 script
但这意味着 每个人 都会在只有一小部分用户需要此组件时下载它
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=AM_SVG"></script>
Angular 2
中是否有一种干净的方法让我的组件在 ngOnInit()
挂钩中下载脚本并在它进入 DOM 后执行回调?诸如(伪代码)之类的东西:
ngOnInit(){
//download script, add to DOM
fetch('https://...MathJax.js').then(addToDom).then(this.onMathJaxLoaded);
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
我没有尝试过,但我认为这可能是可行的:
ngOnInit(){
//download script, add to DOM
var script = document.createElement('script');
document.body.appendChild(script)
script.onload = this.onMathJaxLoaded.bind(this);
script.src = 'https://...MathJax.js';
}
onMathJaxLoaded(){
// Run math renderer
MathJax.Hub.Queue("Typeset",...);
}
这是一个 fiddle,其中包含一个示例,说明它在普通 JavaScript 中如何工作; https://jsfiddle.net/5qu5h7bc/
基于@TryingToImprove 的解决方案,我决定在我的应用程序中添加一个 ScriptLoaderService
,其中包含一个 load()
函数,该函数获取远程脚本并通知调用者(使用 RxJS Observable)完成后。
使用方法如下:
this.scriptLoaderService.load('https://...js').subscribe(
()=>console.log('script has loaded');
);
这是服务:
/**
* A directory of scripts that have been or are being loaded
*/
private scripts = new Map<string,Observable<boolean>|boolean>();
/**
* Downloads script; returns Observable that emits TRUE once "load" event fires
*/
public load(src:string):Observable<boolean>{
if(this.scripts.has(src)){
// If script has already been fully loaded.
if(this.scripts.get(src)===true) return Observable.of(true);
// Else if download is already underway but load event hasn't fired yet
else return this.scripts.get(src);
}
//Add a script tag to the DOM
let script = document.createElement('script');
document.body.appendChild(script);
// upon subscription, listen for the load event. Once it arrives, emit TRUE
let obs:Observable<boolean> = Observable.fromEvent(script,'load')
// Map should hold TRUE once script is loaded
.do(()=> this.scripts.set(src,true))
.take(1).map(e=>true);
// set the src attribute (will cause browser to download)
script.src = src;
//add observable to the scripts Map
this.scripts.set(src,obs);
return obs;
}
测试于 Firefox 51