讲课内容

一、 上传本地图片显示发生了unsage

  1. iframe 的 src 属性是资源 URL 安全上下文,因为不可信源可以在用户不知情的情况下执行某些不安全的操作。
  2. DomSanitizer 安全URL脚本访问
  3. XSS :是网站 Angular 2 如何保护我们免受 XSS 攻击 Angular 2 中默认将所有输入值视为不受信任。当我们通过 property,attribute,样式,类绑定或插值等方式,将一个值从模板中插入到DOM中时,Angular 2 会自帮我们清除和转义不受信任的值。我们来看一下具体示例: 报错:unsafe value used in a resource URL context

在Angular4中,通过input:file上传选择图片本地预览的时候,通过window.URL.createObjectURL获取的url赋值给image的src出现错误:

WARNING: sanitizing unsafe URL value 下面介绍一下解决方法: html代码: 1 2 <input type="file" (change)="fileChange($event)" > <img [src]="imgUrl" alt=""> 其中,change方法会在每次选择图片后调用,image的src必须通过属性绑定的形式,使用插值表达式同样会出错

import { Component, OnInit } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'

@Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit {

imgUrl;

constructor( private sanitizer: DomSanitizer ){}

ngOnInit() { }

fileChange(event){ let file = event.target.files[0]; let imgUrl = window.URL.createObjectURL(file); let sanitizerUrl = this.sanitizer.bypassSecurityTrustUrl(imgUrl);

this.imgUrl = sanitizerUrl;
复制代码

} } 首先,引入DomSanitizer,然后在构造器里面注入, 最重要的就是把window.URL.createObjectURL生成的imgUrl通过santizer的bypassSecurityTrustUrl方法,将它转换成安全路径。 最后将生成的安全的url赋值给imgUrl,此时就没有问题了~ https://blog.csdn.net/Neokekeke/article/details/78739035

template ng-template templateUrl

。模板元素是一种机制,允许包含加载页面时不渲染,但又可以随后通过 JavaScript 进行实例化的客户端内容。我们可以将模板视作为存储在页面上稍后使用的一小段内容。 映射可以兄弟之间,也可以父子之间,可以通过select完成映射。 如果没有内容投影,将会出现两种现象 组件标签不能嵌套使用。 不能优雅地包装原生的 HTML 标签。 因为没有“内容投影”机制,my-comp-1 无法感知到 my-comp-2 的存在,也无法和它进行交互。 因为没有“内容投影”机制,my-comp-1 无法感知到 my-comp-2 的存在,也无法和它进行交互。这明显有违 HTML 设计的初衷,因为 HTML 的本质是一种 XML 格式,标签能嵌套是最基本的特性,原生的 HTML 本身就有很多嵌套的情况:

  • 神族
  • 人族
  • 虫族
在真实的业务开发里面,另一个典型的嵌套组件就是 Tab 页,以下代码是很常见的: 如果没有内容投影机制,想要这样嵌套地使用自定义标签也是不可能的。

内容投影存在的第二个意义与组件的封装有关。

虽然 Angular 提供了 @Component 装饰器让开发者可以自定义标签,但是请不要忘记,自定义标签毕竟与 HTML 原生标签不一样,原生 HTML 标签上面默认带有很多属性、事件,而你自己定义标签是没有的。原生 HTML 标签上面暴露的属性和事件列表请参见 W3C 的规范。

从宏观的角度看,所有的自定义标签都只不过是一层“虚拟的壳子”,浏览器并不认识自定义标签,真正渲染出来的还是 div、form、input 之类的原生标签。所以,自定义标签只不过是一层逻辑上的抽象和包装,让人类更容易理解和组织自己的代码而已。

既然如此,自定义标签和 HTML 原生标签之间的关系是什么呢?本质上说,这是“装饰模式”的一种应用,而内容投影存在的意义就是可以让这个“装饰”的过程做得更加省力、更加优雅一些。 @ContentChildren(ChildTwoComponent) childrenTwo:QueryList;
// @ContentChild(ChildTwoComponent) // childTwo:ChildTwoComponent; /在ngAfterContentInit钩子里面访问被投影进来的组件 @ViewChild 与 @ViewChildren
我们可以利用 @ViewChild 这个装饰器来操控直属的子组件。 在ngAfterViewInit这个钩子里面可以直接访问子组件

生命周期

  1. https://segmentfault.com/a/1190000010928087
  2. onPush策略变更检测
  3. 变更检测的策略 第2-5课:组件:生命周期钩子 我不打算在这里罗列 API,在官方网站上面有更详细的描述和例子。

在这一节里面我们只讨论以下4件事:

什么是 UI 组件的生命周期? Angular 组件的生命周期有什么特别的地方? OnPush 策略的使用方式。 简要介绍脏检查的实现原理。 UI 组件的生命周期 enter image description here

无论使用什么样的前端框架,只要编写 UI 组件,生命周期都是必须要考虑的重要内容。请展开你的想象,如果让你来设计 UI 系统,组件有几个重要的阶段一定是绕不开的,比如:

初始化( init )阶段:在这个阶段你需要把组件 new 出来,把一些属性设置上去,等等这些操作。 渲染( render )阶段:在这个阶段需你要把组件的模板和数据结合起来,生成 HTML 标签结构,并且要整合到现有的 DOM 树里面去。 存活阶段:既然带有 UI,那么在组件的存活期内就一定会和用户进行交互。一般来说,带有 UI 的系统都是通过事件机制进行用户交互的。也就是说,这个阶段将会处理大量的用户事件:鼠标点击、键盘按键、手指触摸。 销毁( destory )阶段:最后,组件使用完了,需要把一些资源释放掉。最典型的操作:需要把组件上的所有事件全部清理干净,避免造成内存泄漏。 在组件生命的不同阶段,框架一般会暴露出一些“接口”,开发者可以利用这些接口来实现一些自己的业务逻辑。这种接口在有些框架里面叫做“事件”,在 Angular 里面叫做“钩子”,但其底层的本质都是一样的。

Angular 组件的生命周期钩子 enter image description here

Angular 一共暴露了8个“钩子”,构造函数不算。 并没有组件或者指令会实现全部钩子。 绿色的4个钩子可能会被执行很多次,紫色的只会执行一次。 Content 和 View 相关的4个钩子只对组件有效,指令上不能使用。因为在新版本的 Angular 里面,指令不能带有 HTML 模板。指令没有自己的 UI,当然就没有 View 和 Content 相关的“钩子”了。 请不要在生命周期钩子里面实现复杂的业务逻辑,尤其是那4个会被反复执行的钩子,否则一定会造成界面卡顿。 OnPush 策略 在真实的业务系统中,组件会构成 Tree 型结构,就像这样:

enter image description here

当某个叶子组件上的数据模型发生变化之后,就像这样:

enter image description here

这时候,Angular 将会从根组件开始,遍历整颗组件树,把所有组件上的 ngDoCheck() 方法都调用一遍:

enter image description here

请注意,默认情况下,无论哪个叶子组件上发生了变化,都会把整个组件树遍历一遍。如果组件树非常庞大,嵌套非常深,很明显会有效率问题。在绝大部分时间里面,并不会出现每个组件都需要刷新的情况,根本没有必要每次都去全部遍历。所以 Angular 提供了一种叫做 OnPush 的策略,只要把某个组件上的检测策略设置为 OnPush,就可以忽略整个子树了,就像这样:

enter image description here

很明显,使用了 OnPush 策略之后,检查效率将会获得大幅度的提升,尤其在组件的数量非常多的情况下:

enter image description here

Angular 内置的两种变更检测策略:

Default:无论哪个组件发生了变化,从根组件开始全局遍历,调用每个组件上的 ngDoCheck() 钩子。 OnPush:只有当组件的 @Input 属性发生变化的时候才调用本组件的 ngDoCheck() 钩子。 有一些开发者建议 Angular 项目组把 OnPush 作为默认策略,但是目前还没有得到官方支持,或许在未来的某个版本里面会进行修改。

了解一点点原理 如果你不想看到扯原理的内容,可以跳过这一小段。

enter image description here

大家都知道,AngularJS 是第一个把“双向数据绑定”这种设计带到前端领域来的框架,“双向数据绑定”最典型的场景就是对表单的处理。

双向数据绑定的目标很明确:数据模型发生变化之后,界面可以自动刷新;用户修改了界面上的内容之后,数据模型也会发生自动修改。

很明显,这里需要一种同步机制,在 Angular 里面这种同步机制叫做“变更检测”。

在老版本 AgnularJS 里面,变更检测机制实现得不太完善,经常会出现检测不到变更的情况,所以才有了让大家很厌烦的 $apply() 调用。

在新版本的 Angular 里面不再存在这个问题了,因为新版本的 Angular 使用 Zone.js 这个库,它会把所有可能导致数据模型发生变更的情况全部拦截掉,从而在数据发生变化的时候去通知 Angular 进行刷新。

有一些朋友可能会觉得奇怪,Zone.js 怎么这么牛叉?它内部到底是怎么玩的呢?

实际上要做到这一点并不复杂,因为在浏览器环境下,有可能导致数据模型发生变化的情况只有3种典型的回调:

事件回调:鼠标、键盘、触摸 定时器回调:setTimeout 和 setInterval Ajax 回调 Zone.js 覆盖了所有原生实现,当开发者在调用这些函数的时候,并不是调用的原生方法,而是调用的 Zone.js 自己的实现,所以 Zone.js 就可以做一些自己的处理了。

变更检测

概述 简单来说变化检测就是Angular用来检测视图与模型之间绑定的值是否发生了改变,当检测到模型中绑定的值发生改变时,则同步到视图上,反之,当检测到视图上绑定的值发生改变时,则回调对应的绑定函数。

什么情况下会引起变化检测? 总结起来, 主要有如下几种情况可能也改变数据:

用户输入操作,比如点击,提交等 请求服务端数据(XHR) 定时事件,比如setTimeout,setInterval 上述三种情况都有一个共同点,即这些导致绑定值发生改变的事件都是异步发生的。如果这些异步的事件在发生时能够通知到Angular框架,那么Angular框架就能及时的检测到变化。

图片描述

左边表示将要运行的代码,这里的stack表示Javascript的运行栈,而webApi则是浏览器中提供的一些Javascript的API,TaskQueue表示Javascript中任务队列,因为Javascript是单线程的,异步任务在任务队列中执行。

具体来说,异步执行的运行机制如下:

所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之 中放置一个事件。 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 主线程不断重复上面的第三步。 当上述代码在Javascript中执行时,首先func1 进入运行栈,func1执行完毕后,setTimeout进入运行栈,执行setTimeout过程中将回调函数cb 加入到任务队列,然后setTimeout出栈,接着执行func2函数,func2函数执行完毕时,运行栈为空,接着任务队列中cb 进入运行栈得到执行。可以看出异步任务首先会进入任务队列,当运行栈中的同步任务都执行完毕时,异步任务进入运行栈得到执行。如果这些异步的任务执行前与执行后能提供一些钩子函数,通过这些钩子函数,Angular便能获知异步任务的执行。

angular2 获取变化通知 那么问题来了,angular2是如何知道数据发生了改变?又是如何知道需要修改DOM的位置,准确的最小范围的修改DOM呢?没错,尽可能小的范围修改DOM,因为操作DOM对于性能来说可是一件奢侈品。

在AngularJS中是由代码scope.apply()或者scope.digest触发,而Angular接入了ZoneJS,由它监听了Angular所有的异步事件。

ZoneJS是怎么做到的呢?

实际上Zone有一个叫猴子补丁的东西。在Zone.js运行时,就会为这些异步事件做一层代理包裹,也就是说Zone.js运行后,调用setTimeout、addEventListener等浏览器异步事件时,不再是调用原生的方法,而是被猴子补丁包装过后的代理方法。代理里setup了钩子函数, 通过这些钩子函数, 可以方便的进入异步任务执行的上下文.

//以下是Zone.js启动时执行逻辑的抽象代码片段 function zoneAwareAddEventListener() {...} function zoneAwareRemoveEventListener() {...} function zoneAwarePromise() {...} function patchTimeout() {...} window.prototype.addEventListener=zoneAwareAddEventListener; window.prototype.removeEventListener=zoneAwareRemoveEventListener; window.prototype.promise = zoneAwarePromise; window.prototype.setTimeout = patchTimeout; 变化检测的过程 Angular的核心是组件化,组件的嵌套会使得最终形成一棵组件树。Angular的变化检测可以分组件进行,每一个Component都对应有一个changeDetector,我们可以在Component中通过依赖注入来获取到changeDetector。而我们的多个Component是一个树状结构的组织,由于一个Component对应一个changeDetector,那么changeDetector之间同样是一个树状结构的组织.

另外,Angular的数据流是自顶而下,从父组件到子组件单向流动。单向数据流向保证了高效、可预测的变化检测。尽管检查了父组件之后,子组件可能会改变父组件的数据使得父组件需要再次被检查,这是不被推荐的数据处理方式。在开发模式下,Angular会进行二次检查,如果出现上述情况,二次检查就会报错:Expression Changed After It Has Been Checked Error。而在生产环境中,脏检查只会执行一次。

相比之下,AngularJS采用的是双向数据流,错综复杂的数据流使得它不得不多次检查,使得数据最终趋向稳定。理论上,数据可能永远不稳定。AngularJS给出的策略是,脏检查超过10次,就认为程序有问题,不再进行检查。

图片描述

变化检测策略 Angular有两种变化检测策略。Default是Angular默认的变化检测策略,也就是上述提到的脏检查,只要有值发生变化,就全部从父组件到所有子组件进行检查,。另一种更加高效的变化检测方式:OnPush。OnPush策略,就是只有当输入数据(即@Input)的引用发生变化或者有事件触发时,组件才进行变化检测。

defalut 策略 main.component.ts

@Component({ selector: 'app-root', template: `

变更检测策略

{{ slogan }}

改变明星属性 改变明星对象 `, }) export class AppComponent { slogan: string = 'change detection'; title: string = 'default 策略'; star: Star = new Star('周', '杰伦'); changeStar() { this.star.firstName = '吴'; this.star.lastName = '彦祖'; } changeStarObject() { this.star = new Star('刘', '德华'); }

} movie.component.ts

@Component({ selector: 'movie', styles: ['div {border: 1px solid black}'], template: `

{{ title }}

Star: {{star.firstName}} {{star.lastName}}

`,

}) export class MovieComponent { @Input() title: string; @Input() star;

} 上面代码中, 当点击第一个按钮改变明星属性时,依次对slogan, title, star三个属性进行检测, 此时三个属性都没有变化, star没有发生变化,是因为实质上在对star检测时只检测star本身的引用值是否发生了改变,改变star的属性值并未改变star本身的引用,因此是没有发生变化。

而当我们点击第二个按钮改变明星对象时 ,重新new了一个 star ,这时变化检测才会检测到 star发生了改变。

然后变化检测进入到子组件中,检测到star.firstName和star.lastName发生了变化, 然后更新视图.

OnPush策略 与上面代码相比, 只在movie.component.ts中的@component中增加了一行代码:

changeDetection:ChangeDetectionStrategy.OnPush 此时, 当点击第一个按钮时, 检测到star没有发生变化, ok,变化检测到此结束, 不会进入到子组件中, 视图不会发生变化.

当点击第二个按钮时,检测到star发生了变化, 然后变化检测进入到子组件中,检测到star.firstName和star.lastName发生了变化, 然后更新视图.

所以,当你使用了OnPush检测机制时,在修改一个绑定值的属性时,要确保同时修改到了绑定值本身的引用。但是每次需要改变属性值的时候去new一个新的对象会很麻烦,immutable.js 你值得拥有!

变化检测对象引用 通过引用变化检测对象ChangeDetectorRef,可以手动去操作变化检测。我们可以在组件中的通过依赖注入的方式来获取该对象:

constructor( private changeRef:ChangeDetectorRef ){} 变化检测对象提供的方法有以下几种:

markForCheck() - 在组件的 metadata 中如果设置了 changeDetection:ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法, 该方法的意思是在变化监测时必须检测该组件。 detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。 reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测 detectChanges() - 从该组件到各个子组件执行一次变化检测 OnPush策略下手动发起变化检测 组件中添加事件改变输入属性

在上面代码movie.component.ts中修改如下

@Component({ selector: 'movie', styles: ['div {border: 1px solid black}'], template: `

{{ title }}

点击切换名字 Star: {{star.firstName}} {{star.lastName}}

`, changeDetection:ChangeDetectionStrategy.OnPush }) export class MovieComponent { constructor( private changeRef:ChangeDetectorRef ){} @Input() title: string; @Input() star;

changeStar(){ this.star.lastName = 'xjl'; } } 此时点击按钮切换名字时,star更改如下

![图片描述][3] 第二种就是上面讲到的使用变化检测对象中的 markForCheck()方法.

ngOnInit() { setInterval(() => { this.star.lastName = 'xjl'; this.changeRef.markForCheck(); }, 1000); } 输入属性为Observable 修改app.component.ts

@Component({ selector: 'app-root', template: `

变更检测策略

{{ slogan }}

改变明星属性 改变明星对象 `, }) export class AppComponent implements OnInit{ slogan: string = 'change detection'; title: string = 'OnPush 策略'; star: Star = new Star('周', '杰伦'); count:Observable

转载于:https://juejin.im/post/5af7d71df265da0b9265acf7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值