问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何在Laravel中使用AngularJS

发布网友 发布时间:2022-05-09 17:17

我来回答

3个回答

懂视网 时间:2022-05-14 20:17

angularjs属性绑定更新机制解释:

所有现代前端框架都是用组件来合成 UI,这样很自然就会产生父子组件层级,这就需要框架提供父子组件通信的机制。同样,Angular 也提供了两种方式来实现父子组件通信:输入输出绑定和共享服务。对于 stateless presentational components 我更喜欢输入输出绑定方式,然而对于 stateful container components 我使用共享服务方式。

本文主要介绍输入输出绑定方式,特别是当父组件输入绑定值变化时,Angular 如何更新子组件输入值。如果想了解 Angular 如何更新当前组件 DOM,可以查看 译 Angular DOM 更新机制,这篇文章也会有助于加深对本文的理解。由于我们将探索 Angular 如何更新 DOM 元素和组件的输入绑定属性,所以假定你知道 Angular 内部是如何表现组件和指令的,如果你不是很了解并且很感兴趣,可以查看 译 为何 Angular 内部没有发现组件, 这篇文章主要讲了 Angular 内部如何使用指令形式来表示组件。而本文对于组件和指令两个概念互换使用,因为 Angular 内部就是把组件当做指令。

模板绑定语法

你可能知道 Angular 提供了 属性绑定语法 —— [],这个语法很通用,它可以用在子组件上,也可以用在原生 DOM 元素上。如果你想从父组件把数据传给子组件 b-comp 或者原生 DOM 元素 span,你可以在父组件模板中这么写:

import { Component } from '@angular/core';

@Component({
 moduleId: module.id,
 selector: 'a-comp',
 template: `
 <b-comp [textContent]="AText"></b-comp>
 <span [textContent]="AText"></span>
 `
})
export class AComponent {
 AText = 'some';
}

你不必为原生 DOM 元素做些额外的工作,但是对于子组件 b-comp 你需要申明输入属性 textContent

@Component({
 selector: 'b-comp',
 template: 'Comes from parent: {{textContent}}'
})
export class BComponent {
 @Input() textContent;
}

这样当父组件 AComponent.AText 属性改变时,Angular 会自动更新子组件 BComponent.textContent 属性,和原生元素 span.textContent 属性。同时,还会调用子组件 BComponent 的生命周期钩子函数 ngOnChanges(注:实际上还有 ngDoCheck,见下文)。

你可能好奇 Angular 是怎么知道 BComponentspan 支持 textContent 绑定的。这是因为 Angular 编译器在解析模板时,如果遇到简单 DOM 元素如 span,就去查找这个元素是否定义在 dom_element_schema_registry,从而知道它是 HTMLElement 子类,textContent 是其中的一个属性(注:可以试试如果 span 绑定一个 [abc]=AText 就报错,没法识别 abc 属性);如果遇到了组件或指令,就去查看其装饰器 @Component/@Directive 的元数据 input 属性里是否有该绑定属性项,如果没有,编译器同样会抛出错误:

Can’t bind to ‘textContent’ since it isn’t a known property of …

这些知识都很好理解,现在让我们进一步看看其内部发生了什么。

组件工厂

尽管在子组件 BComponentspan 元素绑定了输入属性,但是输入绑定更新所需要的信息全部在父组件 AComponent 的组件工厂里。让我们看下 AComponent 的组件工厂代码:

function View_AComponent_0(_l) {
 return jit_viewDef1(0, [
 jit_elementDef_2(..., 'b-comp', ...),
 jit_directiveDef_5(..., jit_BComponent6, [], {
  textContent: [0, 'textContent']
 }, ...),
 jit_elementDef_2(..., 'span', [], [[8, 'textContent', 0]], ...)
 ], function (_ck, _v) {
 var _co = _v.component;
 var currVal_0 = _co.AText;
 var currVal_1 = 'd';
 _ck(_v, 1, 0, currVal_0, currVal_1);
 }, function (_ck, _v) {
 var _co = _v.component;
 var currVal_2 = _co.AText;
 _ck(_v, 2, 0, currVal_2);
 });
}

如果你读了 译 Angular DOM 更新机制 或 译 为何 Angular 内部没有发现组件,就会对上面代码中的各个视图节点比较熟悉了。前两个节点中,jit_elementDef_2 是元素节点,jit_directiveDef_5 是指令节点,这两个组成了子组件 BComponent;第三个节点 jit_elementDef_2 也是元素节点,组成了 span 元素。(想看更多就到PHP中文网AngularJS开发手册中学习)

节点绑定

相同类型的节点使用相同的节点定义函数,但区别是接收的参数不同,比如 jit_directiveDef_5 节点定义函数参数如下:

jit_directiveDef_5(..., jit_BComponent6, [], {
 textContent: [0, 'textContent']
}, ...),

其中,参数 {textContent: [0, 'textContent']} 叫做 props,这点可以查看 directiveDef 函数的参数列表:

directiveDef(..., props?: {[name: string]: [number, string]}, ...)

props 参数是一个对象,每一个键为绑定属性名,对应的值为绑定索引和绑定属性名组成的数组,比如本例中只有一个绑定,textContent 对应的值为:

{textContent: [0, 'textContent']}

如果指令有多个绑定,比如:

<b-comp [textContent]="AText" [otherProp]="AProp">

props 参数值也包含两个属性:

jit_directiveDef5(49152, null, 0, jit_BComponent6, [], {
 textContent: [0, 'textContent'],
 otherProp: [1, 'otherProp']
}, null),

Angular 会使用这些值来生成当前指令节点的 binding,从而生成当前视图的指令节点。在变更检测时,每一个 binding 决定 Angular 使用哪种操作来更新节点和提供上下文信息,绑定类型是通过 BindingFlags 设置的(注:每一个绑定定义是 BindingDef,它的属性 flags: BindingFlags 决定 Angular 该采取什么操作,比如 Class 型绑定和 Style 型绑定都会调用对应的操作函数,见下文)。比如,如果是属性绑定,编译器会设置绑定标志位为:

export const enum BindingFlags {
 TypeProperty = 1 << 3,
注:上文说完了指令定义函数的参数,下面说说元素定义函数的参数。

本例中,因为 span 元素有属性绑定,编译器会设置绑定参数为 [[8, 'textContent', 0]]

jit_elementDef2(..., 'span', [], [[8, 'textContent', 0]], ...)

不同于指令节点,对元素节点来说,绑定参数结构是个二维数组,因为 span 元素只有一个绑定,所以它仅仅只有一个子数组。数组 [8, 'textContent', 0] 中第一个参数也同样是绑定标志位 BindingFlags,决定 Angular 应该采取什么类型操作(注:[8, 'textContent', 0] 中的 8 表示为 property 型绑定):

export const enum BindingFlags {
 TypeProperty = 1 << 3, // 8

其他类型标志位已经在文章 译 Angular DOM 更新机制 有所解释:

TypeElementAttribute = 1 << 0,
TypeElementClass = 1 << 1,
TypeElementStyle = 1 << 2,

编译器不会为指令定义提供绑定标志位,因为指令的绑定类型也只能是 BindingFlags.TypeProperty

注:节点绑定 这一节主要讲的是对于元素节点来说,每一个节点的 binding 类型是由 BindingFlags 决定的;对于指令节点来说,每一个节点的 binding 类型只能是 BindingFlags.TypeProperty

updateRenderer 和 updateDirectives

组件工厂代码里,编译器还为我们生成了两个函数:

function (_ck, _v) {
 var _co = _v.component;
 var currVal_0 = _co.AText;
 var currVal_1 = _co.AProp;
 _ck(_v, 1, 0, currVal_0, currVal_1);
},
function (_ck, _v) {
 var _co = _v.component;
 var currVal_2 = _co.AText;
 _ck(_v, 2, 0, currVal_2);
}

如果你读了 译 Angular DOM 更新机制,应该对第二个函数即 updateRenderer 有所熟悉。第一个函数叫做 updateDirectives。这两个函数都是 ViewUpdateFn 类型接口,两者都是视图定义的属性:

interface ViewDefinition {
 flags: ViewFlags;
 updateDirectives: ViewUpdateFn;
 updateRenderer: ViewUpdateFn;

有趣的是这两个函数的函数体基本相同,参数都是 _ck_v,并且两个函数的对应参数都指向同一个对象,所以为何需要两个函数?

因为在变更检测期间,这是不同阶段的两个不同行为:

  • 更新子组件的输入绑定属性

  • 更新当前组件的 DOM 元素

  • 这两个操作是在变更检测的不同阶段执行,所以 Angular 需要两个独立的函数分别在对应的阶段调用:

  • updateDirectives——变更检测的开始阶段被调用,来更新子组件的输入绑定属性

  • updateRenderer——变更检测的中间阶段被调用,来更新当前组件的 DOM 元素

  • 这两个函数都会在 Angular 每次的变更检测时 被调用,并且函数参数也是在这时被传入的。让我们看看函数内部做了哪些工作。

    _ck 就是 check 的缩写,其实就是函数 prodCheckAndUpdateNode,另一个参数就是 组件视图数据。函数的主要功能就是从组件对象里拿到绑定属性的当前值,然后和视图数据对象、视图节点索引等一起传入 prodCheckAndUpdateNode 函数。其中,因为 Angular 会更新每一个视图的 DOM,所以需要传入当前视图的索引。如果我们有两个 span 和两个组件:

    <b-comp [textContent]="AText"></b-comp>
    <b-comp [textContent]="AText"></b-comp>
    <span [textContent]="AText"></span>
    <span [textContent]="AText"></span>

    编译器生成的 updateRenderer 函数和 updateDirectives 函数如下:

    function(_ck, _v) {
     var _co = _v.component;
     var currVal_0 = _co.AText;
     
     // update first component
     _ck(_v, 1, 0, currVal_0);
     var currVal_1 = _co.AText;
     
     // update second component
     _ck(_v, 3, 0, currVal_1);
    }, 
    function(_ck, _v) {
     var _co = _v.component;
     var currVal_2 = _co.AText;
     
     // update first span
     _ck(_v, 4, 0, currVal_2);
     var currVal_3 = _co.AText;
    
     // update second span
     _ck(_v, 5, 0, currVal_3);
    }

    没有什么更复杂的东西,这两个函数还不是重点,重点是 _ck 函数,接着往下看。

    更新元素的属性

    从上文我们知道,编译器生成的 updateRenderer 函数会在每一次变更检测被调用,用来更新 DOM 元素的属性,并且其参数 _ck 就是函数 prodCheckAndUpdateNode。对于 DOM 元素的更新,该函数经过一系列的函数调用后,最终会调用函数 checkAndUpdateElementValue,这个函数会检查绑定标志位是 [attr.name, class.name, style.some] 其中的哪一个,又或者是属性绑定:

    case BindingFlags.TypeElementAttribute -> setElementAttribute
    case BindingFlags.TypeElementClass -> setElementClass
    case BindingFlags.TypeElementStyle -> setElementStyle
    case BindingFlags.TypeProperty  -> setElementProperty;

    上面代码就是刚刚说的几个绑定类型,当绑定标志位是 BindingFlags.TypeProperty,会调用函数 setElementProperty,该函数内部也是通过调用 DOM Renderer 的 setProperty 方法来更新 DOM。

    注:setElementProperty 函数里这行代码 view.renderer.setProperty(renderNode,name, renderValue);,renderer 就是 Renderer2 interface,它仅仅是一个接口,在浏览器平台下,它的实现就是 DefaultDomRenderer2。

    更新指令的属性

    上文中已经描述了 updateRenderer 函数是用来更新元素的属性,而 updateDirective 是用来更新子组件的输入绑定属性,并且变更检测期间传入的参数 _ck 就是函数 prodCheckAndUpdateNode。只是进过一系列函数调用后,最终调用的函数却是checkAndUpdateDirectiveInline,这是因为这次节点的标志位是 NodeFlags.TypeDirective

    checkAndUpdateDirectiveInline 函数主要功能如下:

    1. 从当前视图节点里获取组件/指令对象

    2. 检查组件/指令对象的绑定属性值是否发生改变

    3. 如果属性发生改变:

      a. 如果变更策略设置为 OnPush,设置视图状态为 checksEnabled

      b. 更新子组件的绑定属性值

      c. 准备 SimpleChange 数据和更新视图的 oldValues 属性,新值替换旧值

      d. 调用生命周期钩子 ngOnChanges

    4. 如果该视图是首次执行变更检测,则调用生命周期钩子 ngOnInit

    5. 调用生命周期钩子 ngDoCheck

    当然,只有在生命周期钩子在组件内定义了才被调用,Angular 使用 NodeDef 节点标志位来判断是否有生命周期钩子,如果查看源码你会发现类似如下代码:

    if (... && (def.flags & NodeFlags.OnInit)) {
     directive.ngOnInit();
    }
    if (def.flags & NodeFlags.DoCheck) {
     directive.ngDoCheck();
    }

    和更新元素节点一样,更新指令时也同样把上一次的值存储在视图数据的属性 oldValues 里(注:即上面的 3.c 步骤)。

    好了,本篇文章到这就结束了(想看更多就到PHP中文网AngularJS使用手册中学习),有问题的可以在下方留言提问。

    热心网友 时间:2022-05-14 17:25

    ReactJS通常也被称为"React",是一个刚刚在这场游戏中登场的新手。它由*创建,并在2013年首次发布。*认为React在处理SPA问题上可以成为Angular的替代品,因此如果你认为Angular和React这两种框架是竞争对手,那你的理解就对了。

    热心网友 时间:2022-05-14 18:43

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" ng-app="myApp">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Laravel-angularJS</title>
    <!-- <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> -->
    <script type="text/javascript" src="app/js/jquery.min.js"></script>
    <script src="app/js/jquery-ui.js" type="text/javascript"></script>
    </head>

    <body>

    <div class="main-container" ng-view></div>

    <script src="app/lib/angular/angular.js"></script>
    <script src="app/lib/angular/angular-resource.min.js"></script>
    <script src="app/js/app.js"></script>
    <script src="app/js/services.js"></script>
    <script src="app/js/controllers.js"></script>
    <script src="app/js/filters.js"></script>
    <script src="app/js/directives.js"></script>
    <script src="app/js/date.js"></script>
    <script src="app/lib/angular/ui_bootstrap.js"></script>
    <script src="app/lib/angular/ui-bootstrap-tpls-0.9.0.min.js"></script>
    <script src="app/js/underscore.js"></script>
    <script>
    angular.mole("myApp").constant("CSRF_TOKEN", '<?php echo csrf_token(); ?>');
    </script>
    </body>
    </html>
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    2024年欧洲杯赛程表 德国欧洲杯足球赛2024赛程时间表 勇者斗魔王勇者斗恶龙12Gameboy游戏1中最后魔王变身后怎么打2中什么武... 电脑开机十秒技巧怎样让电脑开机速度变快 完美世界手游熊猫哪里抓完美国际熊猫是怎么得的 ...每一关跳旗杆的时候怎么才能跳到满分我每次都只能跳到 ip11和ip11pro区别 请问;谁知道SJ-M里面有一个叫基_什么? 智齿疼一定要拔吗 大师们帮我算算我的命数!不胜感激~ 怎样选择评估公司 睡眠质量不好半夜总是醒来,有哪些办法能帮助睡眠呢? 盐醋泡黄瓜青椒吃了会胖吗? 青椒炒黄瓜的热量是多少? 晚上吃了一小碗米饭青椒炒蛋黄瓜炒蛋饭后吃了一根香蕉半个橘子会胖吗 qq仙灵万兽怎么点修 QQ仙灵帮派点修需要什么条件 黄瓜炒青椒的热量是多少? qq仙灵丹青阁点什么修好? 今天中午我没吃晚上只吃了半碗稀饭和一小盆凉拌黄瓜和一点炒青椒用菜油炒的晚上还要跑一个小时步会胖吗今 黄瓜沾辣椒吃了发胖吗 QQ仙灵什么职业升级最快 以前微信绑定银行卡,以前的不用了,请问怎么才能解除绑定? 我把D盘的文件全复制到了C盘然后格式化了D盘,为啥后面打开快捷方式显示指向的驱动器或网络连接不可用呢? 超过4G的文件怎么从C盘转移到D盘?复制文件或文件夹时出错 求救,。。。帮帮忙 圣詹姆斯公园球场的起源 疲劳的机理是什么,构件的疲劳破坏是什么造成的结果 联新钢结构问您知如何检测钢结构工程质量? 什么叫钢结构疲劳破坏 钢架雪车起源于哪里?有什么规则? adonisjs是什么?和laravel有什么关系 晚上吃凉调黄瓜和用辣椒油调的海带会胖么 在淘宝买东西,加客服微信发好评截图和订单编号就能领红包,会有安全隐患吗? 睡眠质量差半夜经常醒来之后睡不着怎么办? 大半夜睡不着怎么办 睡眠不好,半夜易醒。 我最近老是睡眠不好,半夜总是会醒来,怎样可以保持良好的睡眠呢 为什么半夜老是睡不着? - 信息提示 对了我的快递有商家的微信说扫描领红包,我没在意就发到群上,群的人能不能扫码领到红包,对商家有影响吗 青椒与黄瓜能否同炒如同炒吃后对身体能否有不好的影响? 街上扫二维码得礼物会不会被盗 青椒炒黄瓜的基本简介 word导出pdf后,pdf格式边距不对称 ,可以肯定在WORD中页边距是对称的 excel怎样设置条件格式 红米note5配置怎么样 比方说我一分外卖定价十元,你们需要加收多少钱? 红米note5什么时候出来 电脑已经连接无线网,但是浏览器不能上网 河北张家口开始2022的60岁的农村老人验证了吗