利用Heroku部署第一个Nodejs API

这次尝试的重点是去理解web app架构应用部署上线的方法,以及解决API的跨域访问问题,所以具体的API的功能并不是重点。这里展示的API非常简单,返回一个包含我名字的json对象。

上线部署用到的工具,主要是heroku这个平台。

1应用的大体结构和功能

利用express来开发这个API,代码的内容也是很简单的:

1

2

2 利用heroku的部署步骤和原理

关于heroku可以分为两个部分:创建项目和上线部署。

关于创建项目

在本地创建git仓库,如下:

3

在heroku的平台上,实际上是把本地的代码推到了heroku host的远程仓库,并且这个远程仓库跟最终上线的app相绑定。(user deploy the app’s code  by  pushing it to a special Heroku-hosted remote that’s associated with your app)

通过安装heroku CLI,可以使用heroku create来创建应用和相绑定的远程git仓库。

4

上面这个截图的最后一行很明显给出了一个app的URL和一个同名的git

可以通过git remote -v指令来确认下:

5

这样在开发完成或者做了修改之后,只需要push到heroku这个远程仓库上就行了

heroku的打包和部署过程还是挺有意思的,这里涉及了很多它自己定义的术语,比如: Procfiles, slug,  Dynos等等,非常有意思。

7

对于nodejs的应用来说,相应的东西都配置在package.json里面。比较关键的就是main入口程序和npm start的启动程序。

关于heroku的工作流可以看它的官网介绍:

https://devcenter.heroku.com/articles/how-heroku-works#building-applications

3 解决API的跨域问题

就像所有的API一样,如果不做特殊的设定,在发送HTTP请求的时候,是有跨域的问题的

所以需要在node servcie里做CORS,也就是cross origin resource share,允许跨域的资源分享。关于这个东西,express的文档里有专门的指导,如下:

https://enable-cors.org/server_expressjs.html

8

 

mock数据的好工具

对于web前端开发来讲,在UI有了基本的模样之后就需要考虑添加数据的问题了,没有数据很多UI的交互效果都不好开发。

Mock数据的工具也有很多很多。这里介绍一个商业化的产品,当然对于大部分的简单应用来讲免费的功能已经足够了。

这款工具叫做Mockaroo,这里主要介绍两个方面的内容,一是数据生成器的一些技巧,用来生成一些比较复杂的mock数据;另一个是它提供的REST API服务,让开发变得更容易更接近最终的产品。

数据生成器

 

1

默认状态下,mockarro会为用户生成一个新的schema,在进行了设置之后,用户可以保存这个schema

Schema描述一个table的结构,每一个元素是一个field,可以定义基本的name,另外是type,这是最基本。

关于Type,这个平台提供了很多默认的type,如下:

 

2

urlip地址这些类型就非常方便,其他的type选项也都挺有帮助的,能加速你mock data的速度,而且mock出来的data语义化也更好,比如你需要name,那么它生成的就真的是各种实际的name,而不是杂乱的字符串,让下一步的开发体验更好一点。

当然build-intype不可能面面俱到,总需要自定义很多东西,在我的使用中碰到了下面几种情况与技巧,可以生成出关系更复杂的数据。

fx函数处理

 

3

这里面可以对随机生成的数据做各种处理,比如此处 ‘Service’ + this, this指向的就是当前的随机数据,处理之后会在前面加上Service_的前缀。其他的函数处理估计还有更加复杂的功能

Regular express

 

4

type设置成regular expression. 就可以按照你的需要生成具有一些约束的数据。比如我的case里,serviceType需要是0,1,2中的一个,这个时候就可以用正则表达 ((0|1|2){1})来实现。

Formula

如果一个field的数据依赖于另一个field的数据,那么就可以通过formula在两个数据之间建立联系。比如希望一个新fieldapiDescribe,等于现有的一个fieldserviceDescribe,就可以如下设置:

 

5

嵌套的对象数据

Web应用中有很多时候都会用到嵌套的对象数据,对于这点,这个平台支持的也不错。比如我当前的schema中有一个数据inputParams,它是本身就是一个对象,包含id, serviceid,paramName等属性。这种情况可以如下设置:

 

6

inputParams设置为JSON Array,然后它的下层属性名按照: inputParams.xxx的方式去写。所有的相关数据就形成了一个嵌套的对象数据。

REST API

这个功能还是很不错的,当然它支持把数据download下来,但是如果能直接接受http请求不是更好吗?这个平台就提供了这个功能,不过貌似只能接受get请求。使用的方法也挺简单,把api_key加到url里就行了。另外把刚刚创建并保存的schema加上就行了。

 

7

vuex初步解析

原文链接:https://metricloop.com/blog/how-to-use-vuex-in-a-laravel-spark-project

期初最开始学习Vuejs2.0版本的时候,就发现它并不支持build-in的消息机制,比如$broadcast$emit之类的,因为自己之前都是使用AngularJS的,对于这个消息机制还是觉得挺方便的,尤其是在兄弟组建之间传递参数的时候(不过貌似从Angular2开始,好像也不再支持这些消息api了)。

vue的框架下,一般有两种解决方案,一种是开发一个event hub或者event busvue实例,专门来处理各种消息。另外一种方法就是使用vuex状态管理器。

code前的一点儿思考

关于这两种方法的思考如下: 一个简单的event hub,会随着前端组件的复杂化而变得越来越不受控制。另外,使用event hub的方案其实也并不像vue这个框架本身处理问题的风格。其实观察下vue2.0相比vue1.0的变化就可以发现,设计者在极力的浓缩vue的核心功能,为此他们把额外的功能放到了其他的包里面,或者依赖第三方的包,甚至直接从这个系统中移出去了。所以还是使用vuex是正道。

开始动手code

第一件事儿就是安装Vuexnpm install vuex。安装完成之后,首先需要创建一个新的文件: store.js,这里定义的就是整个app的状态管理器。

我们还需要把这个状态管理器storeveu实例结合起来,需要到入口文件app.js (根据你的实际项目而定)中引入vuex,并在注册vue实例的时候,加入store做个饭属性,这样整个应用的所有子组件就都可以获取store中的状态值了,像下面这样:

1

定义store状态管理器

在正确的引用和设置了相应的vuex之后,下面就可以来定义store了。首先需要在stoer.js文件中引入vuevuex

2

现在就可以放心大胆的创建全局的vuex store了。关于vuex下面这个官方文档中的图,可以很好的解释其中的一些关键概念:

3

可以这样理解其中的几个部分之间的关系:vue组件在接受用户的一些操作之后,可以在相应函数中想vuex提交action(dispatch action). action中,一般可以做跟后台的异步操作,然后在操作完成之后,提交mutation(commit muation),另外一个action中还可以对多个mutation进行封装,这样比较省事,这是action的俩个主要的价值,因为其实在vue组件中是可以直接commit mutation的。而mutation是唯一能改变state值的操作。在state的值发生变化之后,组件的UI得到渲染,一般组件可以通过它的computed计算属性来获得state的值,但是通用的最佳实践是通过storegetter来封装一层,再跟组件进行交互。

4

上面这段代码采用了模块化的方法来定义store,而不是把所有的东西都放在store内部,这样其实是很好的代码风格。这里主要定义了四个const,每一个都是vuex的关键组成部分。

state常量里,定义了state的初始状态值,其实就和组件的data属性是一个样的。然后在getters常量里,对count值进行了加1的操作,getter能够让组件获取countPlusOne,其实getter就和组件的computed很类似。第三个常量是mutation, 里面只有INCREMENT一个操作,这样当它被commited的时候,count值就会加1。最后一个是actions常量,里面定义一个increment action,可以从组件中被dispatch,并进一步commit INCREMENTmutation

在组件中使用Vuex中的state

定义好store中的state之后,下一步就是让vue组件和state进行交互。为此我们定义了一个很简单的组件,点击按钮实现加1效果,很简单的组件吧。

5

这是一个非常典型的Vue组件,通过.vue格式的单文件来定义。这里面有三个东西比较新鲜,这些新鲜玩意是让我们跟vuex state进行通信的。vuex提供很多方便的语法糖,这些语法糖可以让组件和vuex的交互更加方便。比如在这个组件模块中,我们引入了mapGettersmapActions,这就是组件和vuexgetteractions进行通信的映射语法糖。在computed计算属性中,我们用mapGettersstate映射到组件中。注意到这里采用了对象语法,{‘count’: ‘countPlusOne’}, 相当于是把countPlusOne重命名为count. 同样的方法,也可以用mapActionsincrement action映射到组件的method里。注意这里都使用了这种扩展运算符。

AngularJS几个简单的性能优化点

原文在这里https://www.binpress.com/tutorial/speeding-up-angular-js-with-simple-optimizations/135

依然来自todd大神,总结几点就是:

  • 对于那些不太会发生变化的属性值,使用一次绑定的方法
  • $scope.$apply$scope.$digest的区别
  • 避免使用ng-repeat,因为在列表更新的时候,这个指令会删除之前的DOM元素。解决的方法是使用track by,提高性能
  • 尽量使用$filter服务,而不是在DOM上直接使用inlinefilter

NG2: 变化检测机制解析

 

What’s change detection anyways?

变化检测的基本任务就是,对程序内部的状态值进行检测,让状态值跟UI的展示效果同步,这个状态值可以是对象、数组或者基本类型的变量,任何合法的JS数据结构都是可以的。

这些程序内部的状态值,在UI上可能是文章段落、表单或者按钮,对于web应用来说就是DOM。基本上说我们就是把这些数据结构作为输出,然后把DOM给用户输出到UI上,我们把这个过程称为渲染过程。

1

但是,如果这个数据的变化发生在程序的runtime,在DOM已经生成以后,情况就会有点儿复杂。我们如何能够得知数据模型发生了怎样的变化呢?访问DOM的成本总是很重,所以我们需要找一个比较简便的方法。

有很多的方法来解决这个问题,其实一个最笨的方法是,对整个DOM进行刷新,比如一旦触发http请求,就去整体的刷新整个页面。另一个方法是,对DOM的新状态值和老状态值进行diff, 然后只对变化的部分进行更新。

在不同的JS框架,有很多的方法来处理这个任务,可以参考这篇文章:

http://teropa.info/blog/2015/03/02/change-and-its-detection-in-javascript-frameworks.html

我们这篇文章主要分析NG2的变化检测机制。

What causes change?

我们已经知道了变化检测是什么了,下一个问题是,到底什么是时候变化检测会发生,需要去更新UI呢?Ok让我们看看下面的代码:

2

希望你对于NG2的组件已经足够的属性了,关于组件的实现不是本文的目的,所以不展开讲了。

这个上面的组件简单的展示两个属性和一个方法去改变属性值,这个方法会在模板中按钮元素被点击的时候触发。 在这个case中,按钮被点击的时刻也是程序中的状态值发生变化的时刻。也就是说我们需要更新UI了。

下面是另外一个情况:

3

上面这个组件在初始化的时候会生成一个联系人列表,起初是空列表,之后发出http请求,当请求返回的时候,列表要进行更新,也就是说状态值发生了变化,我们需要更新UI了。

所以NG2就对这些可能导致属性值发生变化的场景进行了总结,归结起来就是下面这三种情况:

  • 事件绑定: click, submit等等
  • XHR请求: 从server获取异步数据
  • Timer操作:setTimeout(),setInterval()

可以发现这些都是异步操作,所以我们可以得到一个结论,就是无论何时当异步操作发生的时候,应用的状态值就有可能发生了变化。也是需要进行UI更新的时候了。

Who notifies Angular?

Alright, 我们现在知道了导致应用状态发生变化的原因。但是Angular是如何知道的呢?Angular如何知道发生了异步事件呢?

原来angular利用了Zones来做这部分的工作。事实上,Angular在通用Zones的基础上做了一些扩展称为NgZone, 关于这个机制你可以看看下面这篇文章:

https://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html

长话短说的话,在ng2的源码中有一个东西叫做ApplicationRef, 它会对NgZones的onTurnDone事件进行监听,无论何时这个事件被触发,就会执行tick函数,进而执行变化检测。

Change detection

聊完了变化检测的触发机制,我们得具体说说变化检测的机制了。首先,我们需要注意的是,在ng2中每个组件都有它自己的变化检测器。

5

所以对应着组件树,我们其实也能得到一个变化检测器树,这是一个很重要的,它是后面做性能优化的基础,因为它允许我们单独的控制每个组件。

让我们假设组件树中有个时间被触发,比如一个按钮被点击。下一步会发生什么呢?ngzone会捕获这个异步事件,然后触发相应的变化检测。

6

在变化检测器树中,数据流是从上到下进行单向流动的。之所以数据流是单向流动的,是因为变化检测也是从上倒下,从根组件开始往下走的。这个单向的数据流要比ng1中的digest cycle要强的多,因为更具有可预期能力。

另外一个发现是,这种变化检测机制会在一次过滤之后达到稳定的状态。也就是说,如果在某组件触发变化检测之后,又发生了变化,ng2会抛出error.

Performance

Smarter change Detection

在默认情况下,如何有异步事件发生整个应用的变化检测器树都会进行检测。如何能够能够让angular只对发生变化的组件部分进行检测,性能不是能优化很多吗?

我们可以利用两种数据结构immutables和observables来实现这个优化的效果。

Understanding mutability

为了能够理解为什么immutable能够实现优化,我们首先需要了解mutability是什么意思。假设有下面这个组件:
7

这段代码没有太多新鲜的,VCardApp是父组件,<v-card>是子组件,子组件有一个属性绑定vData,通过父组件的vData值进行传递。另外,还有一个changeData方法。

关键的地方是,在changeData方法中,vData的name属性被改变了,但是要注意的是,它的引用地址并没有变。

这是JS对象天然的mutable属性决定的,因为这个原因NG2不得不采用保守的措施,对所有的组件的所有属性值进行深度的遍历。

所以需要immutable对象来发挥效用。

Immutable objects

Immutable对象保证不能操作并修改对象的属性。也就是说,如果我们使用immutable对象,又想修改这个对象的时候,总是得到一个新的引用地址,因为原始的对象是不可变的。

伪代码如下:

8

此处的someAPIForImmutables可以换成真的能够生成不可变对象的方法。

简单的讲就是:如果要改变,那末就生成一个新的引用地址。

Reducing the number of checks

首先来看下<v-card>组件:

9

我们发现这个组件完全依赖@input 输入值,也就是说,我们可以设置一种策略,如果输入属性没有变化的话,就可以跳过相应的变化检测。

而ng2的onpush策略恰恰就是为这个设计的。对于引用类型的变量,比如对象等,它是去check这个变量的引用地址是否发生了变化。对于一般的对象而言,容易出现上面叙述的那种情况,因为对象本身是mutable的。而immutable的价值就在这里,要想改变它就必须新建一个对象,导致引用地址发生变化,onPush策略就不会漏过需要检测的组件了。

onpush的使用还是很方便的,在对应的元数据中添加一个changeDetection属性就行了。

10

这样的话,我们就可以跳过某一部分的子组件树的变化检查,从而提高效率

11

Observable

像上面说的那样,observables这种数据结构也是一种能够准确地发现数据变化的方法,不过跟immutable不同,observable不会在变化发生的时候,给出一个全新的引用值。observable的处理方法是触发一个事件,我们通过subscribe这个事件来进行响应。

在使用observables的case下,我们也想通过OnPush策略去skip某个子组件树的变化检测,从而实现提升性能的目的,但是如果JS对象的引用永远不会发生变化,我们该如何处理呢?。ng2提供了一个非常smart的方法,能够让组件树结构中的某个路径在某些事件触发的时候进行变化检测,这应该恰恰是我们需要的。。

比如下面这个组件:

12

比如说,我们想开发一个电商应用的购物车模块,当用户向购物车中添加新商品的时候,希望在UI上出现一个计数器,这样用户可以看到购物车中的产品数量。

上面的CartBadgeCmp组件就是干这个的,它有一个counter属性,另外还有一个addItemSteam输入属性,可以看出它是一个observable。关于observable的具体细节会在另外的文章中进行更加全面的解析。

https://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

同样的,我们也将变化检测机制设为OnPush,所以只用当输入属性发生变化时,变化检测才会被触发。

但是如上面所说的那样,addItemStream的引用是不会变的,所以对于这个子组件树来说,变化检测也就不会触发。这样问题就来了,因为可以看到在组件的ngOnInit生命周期钩子中,我们subscribe了这个事件流,并且会把counter进行增值。这就出现了状态值变了,但是view并不会更新。

在这样的情况下,我们的变化检测树的样子应该是这样的:

13

就是没有任何检测实例被触发。

我们如何处理这样的问题呢?不用担心angular提供了相应的方法,像我们上面说的,变化检测是从组件树的顶部到底部的顺序执行的。所以我们做为app的开发者需要告诉angular一条路径,在这条路径上的组件是需要进行变化检测的。

angular给我们提供了ChangeDetectorRef类,通过它的markForCheck()方法,我们可以手动的触发变化检测。

具体的实现像下面这样:

14

15

利用observables,变化检测树的触发情况如下,正是我们需要的效果

16

原文链接:https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

Angular1面试题目总结(来自Ultimate Angular)

本文参考Ultimate Angular网站创始人Todd Motto的原文,总结了他认为的比较重要的,能够体现工程师技术能力的Angular1中的技术点。他的原文(https://toddmotto.com/modern-angular-interview-questions)里就没有提供答案,自寻答案吧。。。

Components 组件

      • What is a “component”?
      • When would you use a component over a directive?
      • What is component architecture?
      • What is the difference between a stateful and stateless component?
      • What are lifecycle hooks and why are they important?
      • When would you consider one-way dataflow over two-way?
      • Why is isolate scope an important concept?
      • How would you describe “MVC/MVVM”?
      • What is the difference between MVC/MVVM and component architecture?
      • What types of bindings can a component receive?
      • Can you describe immutable datastructures?
      • Give an example of an immutable operation in JavaScript

State management and dataflow 状态管理和数据流

      • How do you get data into a component?
      • How do you get data out of a component?
      • What benefits does unidirectional dataflow bring?
      • What are common problems with multidirectional dataflow?
      • Have you ever used $ngRedux or a similar implementation?
      • What benefits does a Redux approach with AngularJS bring?

Performance and debugging 性能与debug

      • What are key areas you can address for faster $digest cycles?
      • What are the benefits to using one-time binding expressions?
      • What can lead to memory leaks in AngularJS?
      • How would you speed up an ng-repeat?
      • How does track by work?
      • What are $evalAsync and $applyAsync?
      • What are the differences between $watch and $watchCollection?
      • Explain how you would attempt to debug an AngularJS performance issue
      • What debugging tools are you familiar with?
      • What is strict-di mode and how does it affect runtime performance?
      • What tools have you used to make Angular faster?
      • What is the $templateCache?

Modules and internals 模块

      • What are the key building blocks of an Angular application?
      • How would you describe a module?
      • What use case do sub-modules have?
      • What have you learned from studying the Angular source code?
      • How do you bootstrap Angular asynchronously?
      • How can you bootstrap multiple applications at once?
      • What is dependency injection (DI)?
      • Why is dependency injection useful in Angular?
      • How does the $digest cycle work?
      • What is $rootScope and how does it differ to $scope?
      • When have you used $scope.$apply, and why?

Directives指令

      • What is a directive?
      • What directive(s) implement true two-way databinding?
      • Why use ng-click over addEventListener?
      • When would you use addEventListener?
      • What is the link function and when should you use it?
      • How do you use the link function to communicate back to the controller?
      • What logic should live inside link, and what logic should live in the controller?
      • What is the compile function and what can it return?
      • What are the pre and post link lifecycle methods?
      • Why can the compile function be more effective than link?
      • When would you use a directive over a component?
      • What are event directives, and what are structural directives?
      • What problems have you faced when using directives?
      • What practices should you avoid when using directives?
      • What types of bindings can a directive receive?
      • When would you use require, and what effect does it have on link?
      • What is transclusion?
      • What directive properties would you recommend avoiding?
      • What directives do you tend to avoid and why?
      • What different types of scope are there?
      • What is JQLite and does it have any limitations?

Forms表单

      • How would you implement form validation using the form’s controller?
      • What do dirty, pristine, touched and untouched mean?
      • What limitations do AngularJS forms have?
      • What are some of the built-in validators?
      • _What are $parsers and $formatters, and when should you use them?
      • _What is the $validators pipeline, and when should you use it?
      • What is ngModelOptions and is it a good directive to implement?

Routing路由

      • What is routing?
      • What is component routing?
      • When would you use a template route, if ever?
      • What is a dynamic route and how do you implement it?
      • What is “HTML5 mode”?
      • How would you render a view only when the data has become available?
      • What are transition hooks and what role do they play in routing?
      • How do you create sibling views?

Controller控制器

      • What is the role of a controller?
      • How would you get data into a controller?
      • When would you use $scope.$watch? Should you? How do you unwatch?
      • Explain when you would use controllerAs and the effect it has
      • When should you use $scope inside a controller?
      • When would you consider using a nested controller? Is it good practice?

Filters过滤器

      • What is a filter?
      • How does a filter actually work?
      • What is the most performant approach to filtering data and why?
      • How do you use multiple filters at once in templates?
      • How do you use multiple filters at once inside a controller?
      • How do you pass arguments to a custom filter?

Service and HTTP 服务与HTTP

      • What is a service?
      • What is a factory?
      • What is a provider?
      • What design patterns do services and factories promote?
      • What is a service’s role in an Angular application?
      • What’s the difference between $http and $resource?
      • When could it make sense to use $resource over $http?
      • What is a Promise? Name some places Angular uses them.
      • What is $q and when would you use it?
      • What is an http interceptor and what are good use cases for it?
      • What different types of authentication have you implemented?

Event 事件机制

      • When should you use events in AngularJS?
      • What’s the difference between $emit and $broadcast?
      • What’s the difference between $scope.$emit and $rootScope.$emit?
      • How does event unbinding differ in $scope and $rootScope?

Testing and Tooling测试与工具

      • What is the difference between a unit and end-to-end (e2e) test?
      • What tools have you used for unit testing?
      • What tools have you used for end-to-end (e2e) testing?
      • What tooling are you familiar with?
      • Describe how lazy-loading works
      • What build step processes are effective for faster Angular applications?
      • Are you using ES6 transpilers or TypeScript?

前端优化总结贴

之前做过一个前端性能优化的项目,想想还是有不少东西可以总结下。其实从原理上说,主要是关于浏览器渲染页面的过程,因为这个过程直接决定可能优化的方式。

整体来说,在这个项目里面主要做了两方面的优化:

  • 页面加载速度的优化
  • 动画效果流畅无卡顿

现在去搜索前端性能优化,绝大部分讲的都是第一个部分,就是如何提高初次加载速度。而关于第二个部分讲到其实不多,所以还是有一些价值的。

页面加载速度的优化

首先是浏览器的关键渲染路径(CRP),它的过程是下面这样,这个部分的资料很多了,就不需要太多的分析了。

1

基于这个CRP路径,对于页面加载速度的优化方法可以分为下面这三大类:

2

  1. 减少请求资源的大小:压缩图片(这是最大头,占比重很高,之前看过一篇文章说,如果只能优化一个点应该优化哪个,结果所有专家给出的答案都是简单粗暴的图片压缩),最小化JS,HTML,CSS文件,利用缓存资源。
  2. 减少阻塞渲染的资源:阻塞渲染的话,指的就是CSS样式文件了,处理的方式有,为link标签添加媒体查询的条件,这样初次加载页面的时候,避免请求不必要的资源。还有一个大招是:在style标签里直接写内联样式,这招确实很有效。
  3. 减少阻塞解析的资源:阻塞解析的话,指的就是JS文件了,处理的方式有,用async和defer两个属性对JS脚本进行异步和后台加载。

动画效果流畅无卡顿

在每一帧里面,浏览器都要完成下面这些任务,所谓的像素管道:

3

有时候页面的卡顿可能并不明显,所以需要用分析工具来进行解析,比如chrome浏览器自带的timeline功能。Timeline的分析功能非常的强大,甚至可以直接定位到导致性能问题的JS脚本的具体行数上。

在我做的那个实际的case里面,主要优化了两个地方:一个是在滑动滚动条的时候会伴随着一些动画,这些动画不是非常流畅;另一个是,页面上的一些图片阵列是可以调整大小的,这个size变化的过程也可以更加的流畅。具体的细节记录在了github的项目里面。

这两个点其实本质上都是一个问题,这个问题也是最经常出现的,称为forced synchronous Layout,简称为FSL,timeline工具会给出非常明显的提示。

导致FSL的原因是:在for循环里先对DOM元素的几何尺寸相关属性进行了获取操作,然后又尝试去修改这些DOM元素的style。从上面那个像素管道图上看的话,相当于在style之前先进行了layout,然后又再进行style和layout,这个就是FSL。

解决的办法是,把几何尺寸相关属性的读取放到for循环之外,放到一个变量里。

下面这个篇文章给出了会触发layout的DOM属性和方法:

http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

Angular:$parse一个高阶的方法

感觉angular已经学到家了吗?No no no,马上来个高级的用法:$parse

如果你想在AngularJS的知识海洋里取得更深层次的突破,$parse是一个你必须要知道的服务,它几乎用在了所有的指令上,学习了它据说可以让你脑洞大开。

所以$parse到底是个什么鬼?让俺们从已经知道的ngClick开始。ngClick指令,会接收一个表达式,然后当这个指令元素被点击的时候,去执行这个表达式,那末它的内部机制是怎样的呢?恭喜你,猜对了,就是$parse

$parse会把接收的表达式转化为一个函数并返回。而之后当你调用这个函数的时候,会给这个函数传入一个参数,这个参数就是这个表达式运行的context。所以返回一个函数只是一个形式,关键是允许你可以在某个context下执行某些表达式。。。

It will execute the expression with the given context

JS的世界里,context可以是一个复杂的概念,但是对于$parse的使用来说,我们可以简单的把它理解为一个JS对象。表达式里的所有东西都要在这个JS对象上运行。

比如下面这个例子:

1

在上面这个例子里,我们可以在一个context环境里安全的去解析某个字符串的值。

利用这个功能,我们可以写一个简化版的myClick,像下面这样:

2

在这个case里,指令的域成了context

上面这个简化的ngClick指令比真正的ngClick还是有差别的,因为真正的ngClick允许向生成的函数中传入$event对象。这是怎么实现的呢?原来$parse返回的函数还能接收第二个参数,当作附加的context

而在click的回调函数中,我们恰好能得到event对象,这样我们就能把它传入了:

3

链接:https://blog.umur.io/2014/02/25/advanced-angular-parse/