Angular学习笔记(day4)

前言

无论是Vue、React还是Angular,双向绑定一定是个重要且必须掌握的知识点。双向绑定,即
视图(UI)⇌数据模型state(同步)。当后台数据变化时,UI会自动更新,当用户在UI中输入内容时,后台数据也会自动更新。下面我将讲一下Vue、React、Angular框架是怎么实现数据的双向绑定的。

一、双向绑定

Vue的双向绑定(v-model)

Vue的双向绑定是最直接简单的,代码如下:

<input v-model="username">
// v-model是一个语法糖,等价于下面的写法
<input :value="msg" @input="msg = $event.target.value">
data(){
	return{
		username:'',
		msg:''
	}	
}

Vue使用Object.defineProperty(Vue2)或Proxy(Vue3)实现对数据的拦截,并通过监听DOM事件来更新数据。它的使用很简单利用v-model就可以实现双向绑定。

React的双向绑定(value+onChange)

相比于Vue,由于React为单向数据流,所以默认只有单向绑定,需要开发者手动实现双向同步:

import React, { useState } from 'react';

function UsernameInput() {
  // 创建一个状态变量 username,用来存储输入框的值
  const [username, setUsername] = useState('');

  // 监听输入变化,更新状态
  const handleChange = (event) => {
    setUsername(event.target.value);
  };

  return (
    <div>
      <input
        type="text"
        value={username}         // 把状态绑定到输入框
        onChange={handleChange} // 用户输入时更新状态
        placeholder="请输入用户名"
      />
      <p>你输入的用户名是:{username}</p>
    </div>
  );
}

export default UsernameInput;

可以看出来,React是显示控制的,开发者必须手动监听输入并更新数据。这种更偏向函数式编程的方式,会比Vue麻烦一点,不过数据流会更清晰,且便于调试。

Angular双向绑定([(NgModel)])

Angular是支持双向绑定的,主要是通过NgModel[()]语法实现。下面这个例子实现了在页面上动态更新属性firstName:

import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
  imports: [FormsModule],
  template: `
    <main>
      <h2>Hello {{ firstName }}!</h2>
      <input type="text" [(ngModel)]="firstName" />
    </main>
  `
})
export class AppComponent {
  firstName:string = 'Ada';
}

[(ngModel)]是[value]和(input)的语法糖。在使用的时候记得先导入FormsModule。
除了在表单上可以双向绑定,在父子组件之间也可以进行双向绑定,不过需要进行更多的配置:

// ./app.component.ts 父组件
import { Component } from '@angular/core';
import { CounterComponent } from './counter/counter.component';
@Component({
  selector: 'app-root',
  imports: [CounterComponent],
  template: `
    <main>
      <h1>Counter: {{ initialCount }}</h1>
      <app-counter [(count)]="initialCount"></app-counter>
    </main>
  `,
})
export class AppComponent {
  initialCount = 18;
}
// './counter/counter.component.ts'; 子组件
import { Component, model } from '@angular/core';
@Component({
  selector: 'app-counter',
  template: `
    <button (click)="updateCount(-1)">-</button>
    <span>{{ count() }}</span>
    <button (click)="updateCount(+1)">+</button>
  `,
})
export class CounterComponent {
  count = model<number>(0);
  updateCount(amount: number): void {
    this.count.update(currentCount => currentCount + amount);
  }
}

同样的,首先在父组件内,通过[(count)]="initialCount"将子组件上绑定count;然后子组件需要额外注意的是:子组件必须包含一个model属性,即count = model<number>(0);然后后续子组件内更新可以使用update方法实现。

二、条件渲染

同样是三个框架同时对比!

Vue的条件渲染(v-if/v-show)

vue主要通过v-if来实现:

<template>
  <div>
    <p v-if="loggedIn">欢迎回来!</p>
    <p v-else>请先登录</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      loggedIn: false
    }
  }
}
</script>

实际上,v-show也是条件渲染,它是通过display:none控制,不会销毁DOM元素。

React的条件渲染(函数式)

React的写法更加灵活,也是我最喜欢的,他有多种方法可以实现:

//写法一:三元表达式
function App() {
  const [loggedIn, setLoggedIn] = useState(false);

  return (
    <div>
      {loggedIn ? <p>欢迎回来!</p> : <p>请先登录</p>}
    </div>
  );
}

//写法二:逻辑与(适合只渲染一个分支)
{loggedIn && <p>欢迎回来!</p>}
//写法三:函数里面写if/return,这个可以适合多个分支
function App() {
  const [loggedIn, setLoggedIn] = useState(false);

  if (!loggedIn) {
    return <p>请先登录</p>;
  }

  return <p>欢迎回来!</p>;
}

React的条件渲染本质上是JS的条件判断+JSX的渲染表达式,没有语法糖,但更加灵活。

Angular的条件渲染(ngIf和@if)

angular里面的条件渲染主要介绍两点,他们以Angular17为分界线。
在Angular17之前,我们通过ngIf来实现条件渲染,需要与ng-template配合使用

<div *ngIf="condition; else elseBlock">条件为真时的内容</div>
<ng-template #elseBlock>条件为假时的内容</ng-template>

else 里面写上锚点,然后在ng-template里面插入这个锚点,进行匹配。
而Angular17+就更加直观,使用类似编程语言的块语法,并支持直接嵌套和多条件分支:

@if (condition) {
  <div>条件为真时的内容</div>
} @else if (anotherCondition) {
  <div>另一个条件为真时的内容</div>
} @else {
  <div>所有条件都为假时的内容</div>
}

如果是新项目的话,推荐无脑选@if,语法更清晰,且性能更好;如果要考虑代码兼容性问题的话,那么可以使用过去的ngIf。
补充:实际上,官方文档里面也提供了另一种控制块,即@switch。实际上就和if以及switch的场景类似啦,这里贴一下:

@switch (userPermissions) {
  @case ('admin') {
    <app-admin-dashboard />
  }
  @case ('reviewer') {
    <app-reviewer-dashboard />
  }
  @case ('editor') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

三、循环渲染

我们同样将三个框架的循环渲染都放在这,对比着学习与记忆!

Vue的循环渲染(v-for)

vue主要通过v-for实现循环渲染,示例如下:

<template>
 <ul>
  <li v-for="(item, index) in items" :key="item.id">
    {{ index }} - {{ item.name }}
  </li>
</ul>
</template>

<script>
export default {
  data() {
    return {
      users: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
    }
  }
}
</script>

React的循环渲染(JSX+.map())

function UserList() {
  const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

  return (
    <ul>
      {users.map((user, index) => (
        <li key={user.id}>
          {index} - {user.name}
        </li>
      ))}
    </ul>
  );
}

在React中,可以使用原生的map方法,返回一个由标签元素组成的数组,JSX会自动进行处理并渲染。

Angular的循环渲染(*ngFor或@For)

同样以Angular17为分界线。旧语法使用*ngFor:

@Component({
  selector: 'app-example',
  template: `
    <!-- 基本用法 -->
    <div *ngFor="let item of items">{{item.name}}</div>
    
    <!-- 带索引和其他本地变量 -->
    <div *ngFor="let item of items; index as i; first as isFirst">
      {{i}}: {{item.name}} {{isFirst ? '(第一个)' : ''}}
    </div>
    
    <!-- 使用trackBy提高性能 -->
    <div *ngFor="let item of items; trackBy: trackById">{{item.name}}</div>
  `
})
export class ExampleComponent {
  items = [
    { id: 1, name: '项目1' },
    { id: 2, name: '项目2' },
    { id: 3, name: '项目3' }
  ];
  
  trackById(index: number, item: any): number {
    return item.id;
  }
}

注意:在旧版的*ngFor中,trackBy后面需要跟一个函数,函数内返回唯一标识符作为key

而Angular17+之后使用@For

@Component({
  selector: 'app-example',
  template: `
    <!-- 基本用法 -->
    @for (item of items; track item.id) {
      <div>{{item.name}}</div>
    }
    
    <!-- 带索引和其他本地变量 -->
    @for (item of items; track item.id; let i = $index; let isFirst = $first) {
      <div>{{i}}: {{item.name}} {{isFirst ? '(第一个)' : ''}}</div>
    }
    
    <!-- 处理空列表 -->
    @for (item of items; track item.id) {
      <div>{{item.name}}</div>
    } @empty {
      <div>列表为空</div>
    }
  `
})
export class ExampleComponent {
  items = [
    { id: 1, name: '项目1' },
    { id: 2, name: '项目2' },
    { id: 3, name: '项目3' }
  ];
}

注意:相比于*ngFor,新版的@for就更加简单,track后面可以直接跟需要追踪的key!

同时补充一下@for控制块中的隐式变量:
在这里插入图片描述
同时,当遍历的对象为空时,可以包含@empty来做一个保险

@for (item of items; track item.name) {
  <li> {{ item.name }}</li>
} @empty {
  <li aria-hidden="true"> There are no items. </li>
}

这么看起来,ngFor和Vue的比较像,@For和React的比较像哈哈,我个人还是更喜欢新的模式,大家也可以根据个人喜好以及项目适配度来选择开发方式。
今天先分享到这~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值