Open DarkFlame opened 6 years ago
AngularJs 单页应用框架的一些实现策略
所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面, 所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。 它是AJAX技术的进一步升华,把AJAX的无刷新机制发挥到极致, 因此能造就与桌面程序媲美的流畅用户体验。(一句话说就是改变URL不刷新页面)
因为我们只有一个页面(对浏览器),页面上的各种功能模块是动态生成的。 一般的做法就是把产品功能划分为若干状态,每个状态映射到相应的路由, 当触发状态的改变的时候,动态解析路由,加载与状态对应的页面功能模块 。(基本是集中式路由配置区别与angular2的动态路由) demo
目的是为了代码分层,单独维护测试
angular中代码复用和抽象的主要形式是directive(UI控件,业务型组件,类似与vue中components + directive的结合体),ng-controller (路由层面)
appModule.directive('ngBind',()=>{ return { template:'<span><span>',//模板 controller:function(){},//scope link(scope,element,attr,ctrl){//link函数 scope.$watch(attr.ngBind,(newV)=>{ element.html(newV) }) } //scope:{}//创建独立scope } })
angular的做法是提出service的概念,绝大部分的service都是一个单例(场景复用调用api的方法,mixins?) 依赖注入是一种面向对象的设计原则,用来降低代码之间的耦合度。简单说把一个实例变量做为一个参数传入到目标函数内。 直接调用明了,只会第一次被调用时才会被初始(导致的缺陷?)
//let $http = require('axios') ? //传统的做法mixins?plugins function getData ($http) { $http({ ... }); } appModule.service('appService',function($q){ this.method1 = ()=>{} this.method2 = ()=>{} }) //factory?$provider/constans/value
测试用例的编写
appModule.directive('calculatorDirective',()=>{ return { template:' <div >{{$ctrl.ngModel || $ctrl.version}}</div>', controller:function(version){ this.version = version; }, controllerAs :'$ctrl', link(scope,element,attr,ctrl){ } } }) describe('appModule',function () { beforeEach(module('appModule')); describe('directive',function () { it('should print current version',function () { module(function ($provide) { $provide.value('version','TEST_VER'); }); inject(function ($compile,$rootScope,version) { let element = $compile('<span calculator-directive ng-model="name" ng-click="name = \'hello world\'"></span>')($rootScope); $rootScope.test = () => $rootScope.name = 'nice to meet' $rootScope.$digest(); expect(element.text().trim()).toEqual('TEST_VER'); browserTrigger(element,'click'); expect(element.text().trim()).toEqual('hello world'); }); }); }); });
angular的做法是内部实现双向绑定,主要依赖$scope上$digest
class Scope(){ constructor(){ this.$watchers =[] this.$listeners =[] } $watch(watchFn, listenerFn,ops){ this.$watchers.push({ watchFn,listenerFn }) }
$digest(){ this.$watchers.forEach( function(watch) { let dirty ; do{ let newValue = watch.watchFn(self) let oldValue = watch.last
if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self) dirty = true } watch.last = newValue // 在作用域上添加数据本身并不会有性能折扣。如果没有监听器在监控某个属性, //它在不在作用域上都无所谓。Angular的脏检查,遍历的是监听器。 //关键是监控函数watchFn //场景: 10000个checkbox,如果都用ng-model去绑定,遍历10000个监听器,页面会卡死 //优化策略? }while(dirty)
}) }
$apply(){ try { return this.$eval(expr); } finally { this.$digest(); } }
$eval(){}
//scope上绑定event的监听函数 $on(name, listener) { let namedListeners = this.$listeners[name] if (!namedListeners) { this.$$listeners[name] = namedListeners = [] } namedListeners.push(listener) }
//向上冒泡传递event $emit(name,...args){ let scope = this,namedListeners do{ let namedListeners = this.$listeners[name] || [] namedListeners.forEach(listener=>{ listener.apply(null,args) }) // 向上访问父作用域 scope = scope.$parent }while(scope) } $broacast(){ //实现与emit类似,区别在于遍历子作用域时候是 深度优先 } }
## 加载策略 - 不支持原生的按需加载 ## 单页优缺点 - 提高开发效率,用户体验,部署方便(静态文件化) - 部分加载 - 不利于SEO
AngularJs 单页应用框架的一些实现策略
导言
所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面, 所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。 它是AJAX技术的进一步升华,把AJAX的无刷新机制发挥到极致, 因此能造就与桌面程序媲美的流畅用户体验。(一句话说就是改变URL不刷新页面)
特点
路由与状态的管理
因为我们只有一个页面(对浏览器),页面上的各种功能模块是动态生成的。 一般的做法就是把产品功能划分为若干状态,每个状态映射到相应的路由, 当触发状态的改变的时候,动态解析路由,加载与状态对应的页面功能模块 。(基本是集中式路由配置区别与angular2的动态路由) demo
组件化 demo
目的是为了代码分层,单独维护测试
angular中代码复用和抽象的主要形式是directive(UI控件,业务型组件,类似与vue中components + directive的结合体),ng-controller (路由层面)
JavaScript的模块化
angular的做法是提出service的概念,绝大部分的service都是一个单例(场景复用调用api的方法,mixins?) 依赖注入是一种面向对象的设计原则,用来降低代码之间的耦合度。简单说把一个实例变量做为一个参数传入到目标函数内。 直接调用明了,只会第一次被调用时才会被初始(导致的缺陷?)
测试用例的编写
视图(view)和模型(model)的关联桥梁->$scope
angular的做法是内部实现双向绑定,主要依赖$scope上$digest
$digest(){ this.$watchers.forEach( function(watch) { let dirty ; do{ let newValue = watch.watchFn(self) let oldValue = watch.last
}) }
$apply(){ try { return this.$eval(expr); } finally { this.$digest(); } }
$eval(){}
//scope上绑定event的监听函数 $on(name, listener) { let namedListeners = this.$listeners[name] if (!namedListeners) { this.$$listeners[name] = namedListeners = [] } namedListeners.push(listener) }
//向上冒泡传递event $emit(name,...args){ let scope = this,namedListeners do{ let namedListeners = this.$listeners[name] || [] namedListeners.forEach(listener=>{ listener.apply(null,args) }) // 向上访问父作用域 scope = scope.$parent }while(scope) } $broacast(){ //实现与emit类似,区别在于遍历子作用域时候是 深度优先 } }