画布 1
Vue和React和Angular
  持有HTML元素对象
    angular
      <input #input />
      @ViewChildren('input')
    react
      1.1 this.input = React.createRef()
      1.2 <input ref={this.input} />
      父想持有子元素，通过 prop
      2.1 <input ref="myInput" />
      2.2 this.refs.myInput
    vue
      ref="xxx"
        通过 this.$ref.xxx 来持有该组件
  元素分组
    angular
      <ng-container></ng-container>
    react
      <React.Fragment></React.Fragment>
      <></>
        语法糖
    vue
      <template></template>
  条件&列表渲染
    angular
      *ngFor
        *ngFor="let item of items; let i = index; trackBy: id;"
        可通过管道来显示过滤、处理后的数组
      *ngIf, *ngSwitch
      因为 angular 是将视图 html 和 js 分离开的，因此实现条件或列表渲染需要借助指令
    react
      因为 react 不像 angular 一样，将 js 和 html 分离开，而是扩展了语法糖：jsx
      jsx 允许在 js 代码中插入类似 html 代码，所以直接用 js 语法来实现条件或列表渲染即可
        function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
        function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
    vue
      v-if
        v-else-if
        v-else
        条件渲染
      v-show
        display: none 的封装
      v-for
        v-for="(item, index) in items" v-bind:key="id"
          因为 vue 将被侦听的数组做了包装处理，即使不替换原数组对象，调用下面的方法也会触发更新
            push()，pop()，shift()，unshift()，splice()，sort()，reverse()
          但直接通过索引，或 length 对数组操作不会触发更新
            items[1]="xxx"，items.length=1;
              非得使用这种操作时，可用 Vue.set() 和 items.splice() 替代
              对象属性也一样
        v-for="(value, key, index) in object"
          也可以遍历对象，使用 Object.keys()，所以不保证所有 js 引擎的遍历顺序一致
        v-for="n in 10"
          重复10次，等同于 for 循环，n 从 1 开始
        可通过计算属性来使用过滤、处理后的数据源
        当 v-for 和 v-if 同时作用于一个元素上时，v-for 优先级更高
      key
        key 不是指令，是元素属性，放到指令这里来讲
        因为 Vue 会尽可能高效的渲染元素，尤其在 v-if，v-for 的场景
          <template v-if="loginType === 'username'">
<input placeholder="Enter your username">
</template>
<template v-else>
<input placeholder="Enter your email address">
</template>
            实际上两个 input 是同一个实例，会被复用，复用就会出现残留用户的输入
            为 input 加上具有唯一值的 key 属性即可避免被复用
  组件的输入输出
    angular
      @Input 注解的变量
        基本类型时，相当于值拷贝，接收者对输入的数据做变更不影响数据源
        对象类型时，相当于引用拷贝，修改对象内部数据会影响数据源，但对引用重新赋值不没有影响
        父组件使用子组件时，通过 [xxx]="xxx" 语法输入数据，此时 "xxx" 是一个表达式（变量或方法）
          xxx="ooo"
            当不使用 [] 时，ooo 作为字符串传递给子组件的 xxx
        类似于 Java 的参数原理
      @Output 注解，通过 EmitEvent.emit() 发送事件
        父组件使用子组件时，通过 (xxx)="xxx" 接收子组件的输出
    react
      输入用 props，单向传入数据源，接收者只能读取，这是一个不可变对象
        语法跟原生 HTML 语法没啥区别，直接在子组件上通过 xxx={ooo} 方式输入 props 数据
          此时 ooo 是一个表达式（变量）
          xxx="ooo"
            当不使用 {} 语法时，ooo作为字符串传递给子组件的 xxx
      输出也用 props，但需要传入回调函数，由持有者在合适时机回调通知即可
    vue
      输入用 props，数据是单向传递
        父组件使用子组件时，通过 v-bind:xxx="xxx" 来向 props 中声明的接口输入数据
          也可用 :xxx="xxx" 简写语法
            此时 "xxx" 是一个表达式（变量）
            xxx="ooo"
              当不使用 v-bind: 前缀时，ooo 作为字符串传递给子组件的 xxx
        当父组件使用子组件时，通过 v-bind="obj"，此时会将对象 obj 的所有属性传递给子组件
          obj = {a:1, b:2}
          相当于 v-bind:a="obj.a"，v-bind:b="obj.b" 的语法糖
        当父组件使用子组件时，在子组件标签上添加的特性，会自动添加到子组件的根元素上
          这对于外部使用时，定制化传入 class 或 style 非常方便
          如果不需要该功能，可在注册组件时，通过 inheritAttrs: false 来关闭，注意，关闭后，仍旧不影响 class 和 style 这两个的继承处理
      输出用 $emit
        $emit('xxx', value)
          第一个参数指定事件名
            父组件使用子组件时，通过这个事件名，使用 v-on:xxx="xxx($event)" 语法来接收事件
              也可使用 @xxx="xxx($event)" 简写语法
              事件绑定时有修饰符方便操作
                .stop
                  阻止单击事件继续传播
                .prevent
                  提交事件不再重载页面
                .once
                  只响应一次
                .capture .self .passive
          第二个参数指定向外发送的值
      双向绑定
        v-model
          实际上是 v-bind:value 和 v-on:input 的语法糖
          所以自定义组件需要提供这两项
          语义不明确，推荐使用下面方式
        v-bind:xxx.sync="ooo"
          实际上是 v-bind:xxx="ooo" 和 v-on:update:xxx="ooo = $event" 的语法糖
  跨层通信
    1. 三者都可以通过层层传递输入和输出数据
    angular
      通过持有同个单例服务来通信
    react
      通过 React.Context
    vue
      $root，$parent
        但层级过多时繁琐
      依赖注入
        provide 和 inject
          provide: function() {
return {
getXXX: this.xxx
}
}
            父组件通过 provide 声明给子组件使用的方法
          inject: ['getXXX']
            后代组件通过 inject 来注入父组件提供的方法
  组件生命周期
    angular
      ngOnChanges(changeRecord)
      ngOnInit()
        只调用一次
      ngDoCheck()
      ngAfterContentInit()
        只调用一次
      ngAfterContentChecked()
      ngAfterViewInit()
        只调用一次
      ngAfterViewChecked()
      ngOnDestroy()
        只调用一次
    react
      componentWillMount()
        只调用一次
      componentDidMount()
        只调用一次
      componentWillReceiveProps(newProps)
        首次渲染不触发
      shouldComponentUpdate(newProps, newState)
        首次渲染和 forceUpdate() 不触发
      componentWillUpdate(nextProps, nextState)
        首次渲染不触发
      componentDidUpdate(prevProps, prevState)
        首次渲染不触发
      componentWillUnmount()
        只调用一次
    vue
      beforeCreate()
      created()
      beforeMount()
      mounted()
      beforeUpdate()
      updated()
      beforeDestroy()
      destroyed()
  表单
    angular
      响应式表单
        formGroup, formControlName
        FormBuilder 创建 FormControl 等表单项，创建时添加校验器方法
      模板驱动表单
        ngModel, ngModelChange
        指令实现校验器，模板变量 #name="ngModel" 导出表单对象，查看校验状态
    react
      与原生表单元素的使用差别不大
      value={this.state.value}
        数据从 js 流向表单元素
      onChange={this.handleChange}
        数据从表单元素流向 js
        this.handleChange 中调用 this.setState()
    vue
      v-model
        表单控件的 v-bind 和 v-on 的语法糖，双向绑定
        修饰符
          v-model.lazy
            默认在 input 事件进行数据同步，.lazy 可设置成在 change 事件同步
          v-model.number
            自动将用户输入值转为数值类型，因为即使用户输入数字，默认也是字符串
          v-model.trim
            自动过滤用户输入的首尾空白字符
  父组件动态插入渲染内容到子组件
    angular
      父组件
        <MyView><p>插入子组件中</p></MyView>
      子组件
        <ng-content></ng-content>
        使用 ng-content 来接收父组件在子组件标签内插入的元素内容
        当需要多个插槽时，可通过 select 配合 CSS 选择器规则
          <ng-content select="[name=xxx]"></ng-content>
          <MyView><p name="xxx">插入子组件中</p></MyView>
    react
      1. props.children
        父组件：<MyView><p>插入子组件中</p></MyView>
        子组件使用 this.props.children 来获取父组件在标签内传入的元素内容
          <div>{this.props.children}</div>
      2. render props
        父组件：<MyView render={data => (<p>插入子组件中</p>)}></MyView>
          传入一个值类型为函数的 prop
        子组件使用 this.props.render(this.state)动态调用父组件传入的 render 函数
        与方式1的区别在于，这种方式可以复用一些逻辑，方式1用途很简单，单纯传入渲染的元素内容而已
    vue
      父组件
        <MyView>
<p>插入子组件中</p>
<template v-slot="xxx"><p>插入到指定的 slot 中{{xxx.value}}</p></template>
</MyView>
          使用时，可通过 v-slot 指令来指定具名插槽
      子组件
        <slot v-bind:value="value" name="xxx"></slot>
        使用 slot 来接收父组件在子组件标签内插入的元素内容
        如果子组件的 slot 上有通过 v-bind 绑定属性，那么该属性可在父组件中，通过 v-slot 来访问
  模板语法
    angular
      <p title={{xxx}}>{{xxx}}</p>
        双大括号的插值语法
        双大括号内可以是表达式
        可用于标签的文本内容，也可用于给标签属性赋值
        当需要复杂的逻辑处理时，xxx 可抽离成某个方法或 getter
    react
      <p title={xxx}>{xxx}</p>
        单个大括号
        其余跟 angular 的双大括号的插值语法一致
    vue
      <p>{{xxx}}</p>
        双大括号的插值语法
        但只能用于标签的文本内容，不能使用双大括号给标签属性赋值
        结合内置指定工作
          v-once
            <p v-once>{{xxx}}</p>
            只执行一次插值，后续数据变化不会触发更新视图
        当需要复杂的逻辑处理时， xxx 应该使用计算属性实现
          computed: {
xxx: function() {//...}
}
          当然类似angular使用方法也不会异常，但由于 vue 的视图响应式更新原理跟 angular 不同
          vue 如果像 angular 一样，使用方法来处理复杂逻辑绑定到双大括号内时，会导致每次页面重新渲染都会重新执行复杂逻辑
          使用计算属性能保证，只有当计算属性依赖的声明在 data 内的数据变更时，才会重新计算，可以大大节省性能
  指令
    angular
      属性型指令
        改变元素、组件或其他指令的外观或行为的指令
        ngClass，ngStyle
          原生的 [class] 只支持字符串拼接，ngClass，ngStyle 做了增强处理，可以接收对象或数组类型数据
      结构型指令
        通过添加和移除DOM元素改变DOM布局的指令
        内置的结构型指令
          *ngIf
            本质上新建一个 <ng-template> 标签来挂载 ngIf 指令
          *ngFor
            本质上新建一个 <ng-template> 来挂载ngFor 指令
            同时，解析=后边的字符串，分别对 ngForOf 和 ngForTrackBy 等输入属性赋值
            另外，使用 let-xxx 来定义模板输入变量
          *ngSwitchCase/*ngDefaultCase
    react
      没有指令，由于指令是用封装好的 js 逻辑代码来更好的服务组件的模板
      react 的模板是 jsx 语法，可直接嵌套在 js 代码中，所以无需指令的功能了
    vue
      v-if
        v-else-if
        v-else
        条件渲染
      v-show
        display: none 的封装
      v-html
        插值表达式是一段 HTML 代码时，默认会当做字符串处理，该指令能让 HTML 正常被解析
      v-bind
        绑定组件的输入
        可简写成 :xxx="xxx"
        作用于元素 HTML 标签时，即绑定某个属性
        作用于自定义组件时，可用于对组件声明的 props 变量进行输入
        v-bind:class，v-bind:style
          两个指令被增强，绑定的值不止是原生的字符串类型，还可以是对象或数组
        v-bind:is
          用于替换当前的标签
          <component v-bind:is="curCustomComponent"></component>
            比如有类似多 tab 的标签页，点击不同 tab 显示不同内容
            这时，内容区域就可以使用这种方式，通过 is 绑定的 curCustomComponent 指向的具体组件来渲染当前内容区域
          <table><tr v-bind:is="customTr"></tr></table>
            在某些场景中，特定标签内使用非指定的标签时，会被忽略，这时候自定义组件就使用不了了，此时可通过这种方式来解决
            但在使用 template 或 vue 书写模板代码的场景中没有该问题，所以了解一下即可
      v-on
        绑定组件的事件输出
        可简写成 @xxx="xxx"
        可绑定标签的原生事件如 @click="xxx"，也可绑定组件通过 $emit("xxx") 自定义的事件输出
      v-model
        表单控件的 v-bind 和 v-on 的语法糖，双向绑定
        当在自定义组件上使用 v-model 时，等价于
          v-bind:value="xxx"
          v-on:input="xxx"
          所以自定义组件内部必须有 value 的 prop，和 $emit('input') 的输出事件
      v-once
        插值表达式只第一次生效
  数据变更监听
    angular
      setter
      ngOnChange()
    react
      没有监听说法，因为 react 视图的更新必须调用 render() 通知更新，内部会自行去将需要更新的视图进行更新处理
      this.setState() 或 [xxx, setXXX] = useState() 方式来更新视图的本质也是内部会去触发 render() 的调用
      对于 props 来说，在生命周期函数，如 componentWillReceiveProps() 等可以获取父组件传递数据的变化
    vue
      侦听器
        watch: {
// 声明在 data/props 中的 xxx 变量变更时回调
xxx: function(newValue, oldValue) {}
}
        当只针对单个数据源变更监听时，可用 watch
      计算属性
        computed: {
xxx: function() {
// data/props 中的 a, b 变更时自动回调
return this.a + this.b;
}
}
        当多个数据源需要联合处理时，需要监听到每个数据时，可使用计算属性统一监听