see:https://github.com/hi-cooper/vue-learning.git

途径:

  • **Element Text:**双大括号模板{{ <变量/函数/简单js表达式>}}

  • Element Attribute:[v-bind]:attribute

  • **Element Event:**v-on:event(简写@event)

  • 支持简单表达式(即有唯一的return值)
  • 响应式:不监听普通js变量,仅监听refreactive变更,当ref/reactive变量值变化时,会重新渲染组件。

ref、reactive、toRef、toRefs区别:

  • **ref:**支持任何类型。包含基本数据类型、对象类型(如深层嵌套对象、简单对象、数组等)。非基本类型将通过 reactive() 转换为响应式代理。基本数据类型时推荐使用
  • **reactive:**仅支持对象类型,不支持基本数据类型。对象类型时推荐使用
  • **toRef():**可以为传入对象的某个属性新创建一个响应式引用 ref。这个 ref 可以被传递,它会保持对其源 property 的响应式连接。修改响应式数据会影响以前的数据;数据改变,界面不自动更新
  • toRefs():(1)接收一个对象作为参数,它会遍历对象身上所有属性,然后调用单个toRef
    (2)将对象的多个属性变成响应式数据,并且要求响应式数据和原始数据关联,且更新响应式数据的时候不会更新界面,用于批量设置多个数据为响应式

1 Element text 绑定 JS变量/函数:双大括号模板

格式:{{ <变量/函数/简单js表达式> }}

限制:不允许在HTML attributes 中使用

<script setup lang="ts">
import { reactive, ref } from 'vue';

const refState1 = ref(1); // OK
const refState2 = ref({ count: 2 }); // OK
// const reactiveState1 = reactive(3); // NOT OK
const reactiveState2 = reactive({ count: 4 }); // OK

console.log(refState1.value); // 1
console.log(refState2.value.count); // 2
console.log(reactiveState2.count); // 4

function sayHelloRefState1(msg: string): string {
  return msg + refState1.value;
}
</script>

<template>
  refState1 = {{ refState1 }}<br />
  refState2 = {{ refState2 }}<br />
  refState2.count = {{ refState2.count }}<br />
  reactiveState2 = {{ reactiveState2 }}<br />
  reactiveState2.count = {{ reactiveState2.count }}<br />
  {{ sayHelloRefState1('hello, ') }}<br />
</template>

结果

2 Element attribute 绑定 JS变量/函数:v-bind/v-model

v-bind:单向绑定。用于绑定数据、属性、表达式。简写:<attribute>
v-mode:双向绑定。用于form表单中,在form外使用不起作用

2.1 带attribute的v-bind

<script setup lang="ts">
import { reactive } from 'vue';

const reactiveState2 = reactive({ count: 4 }); // OK

function sayHello(msg: string): string {
  return msg + reactiveState2.count;
}
</script>

<template>
  v-bind方式:<input v-bind:value="reactiveState2.count" /><br />
  简写方式:<input :value="reactiveState2.count" /><br />
  绑定函数:<input :value="sayHello('hello, ')" />
</template>

结果

2.2 不带attribute的v-bind

<script setup lang="ts">
const objectOfAttrs = {
  id: 'container',
  class: 'wrapper'
}
</script>

<template>
<div v-bind="objectOfAttrs"></div>
</template>

结果

<div id="container" class="wrapper"></div>

3 Element event 绑定 JS函数

<script setup lang="ts">
import { reactive } from 'vue';

const reactiveState2 = reactive({ count: 4 }); // OK

function count(): void {
  reactiveState2.count++;
}

function countWith(val: number): void {
  reactiveState2.count += val;
}

function countWithEvent(val: number, event: MouseEvent): void {
  reactiveState2.count += val;
  const target = event.target as HTMLButtonElement;
  console.log(target.id);
}
</script>

<template>
  <button v-on:click="count">使用v-on</button>
  <button @click="count">使用@</button>
  <button @click="countWith(4)">带参数</button>
  <button id="ID_A" @click="countWithEvent(5, $event)">带参数和事件1</button>
  <button id="ID_B" @click="countWithEvent(100, $event)">带参数和事件2</button><br />

  {{ reactiveState2.count }}
</template>

4 综合示例

<template>
  <!-- 绑定事件 -->
  <button
    @click="changePlainStateOnly"
    title="只更新普通变量,不会重新渲染组件(即界面上的普通变量不会更新)"
  >
    Change PlainState Only
  </button>
  <button
    @click="changeAllState"
    title="更新带ref/reactive变量,会重新渲染组件(所以普通变量也会在界面中刷新)"
  >
    Change All State
  </button>
  <button
    @click="changeToRefState"
    title="更新toRef变量,会重新渲染组件,同时影响toRef变量,以及它所指向的ref/reactive变量"
  >
    Change toRef State
  </button>
  <button @click="changeToRefsStateDemo1" title="同toRef">Change toRefs State (Demo1)</button>
  <button @click="changeToRefsStateDemo2" title="同toRef">Change toRefs State (Demo2)</button><br />
  <div class="demo ok-demo">正确示例</div>
  <div class="demo-child">
    <div class="category">
      <span class="title">Element text 绑定 JS变量</span>
      <ul>
        <li>refState1 = {{ refState1 }}</li>
        <li>toRefState1 (refState1) = {{ toRefState1 }}</li>
        <li>---</li>
        <li>refState2.count = {{ refState2.count }}</li>
        <li>toRefState2 (refState2.count) = {{ toRefState2 }}</li>
        <li>toRefsState2 (refState2) = {{ toRefsState2.count }}</li>
        <li>count (refState2.count) = {{ count }}</li>
        <li>name (refState2.name) = {{ name }}</li>
        <li>---</li>
        <li>reactiveState1.count = {{ reactiveState1.count }}</li>
        <li>toRefState3 (reactiveState1.count) = {{ toRefState3 }}</li>
        <li>toRefsState3 (reactiveState1) = {{ toRefsState3.count }}</li>
        <li>
          reactiveState1Count (reactiveState1.reactiveState1Count) = {{ reactiveState1Count }}
        </li>
        <li>reactiveState1Name (reactiveState1.reactiveState1Name) = {{ reactiveState1Name }}</li>
      </ul>
    </div>

    <div class="category">
      <span class="title">Element attribute 绑定 JS变量</span>
      <ul>
        <li title="使用:简写">refState1: <input :value="refState1" /></li>
        <li title="使用 v-bind">refState1 (v-bind): <input v-bind:value="refState1" /></li>
        <li>refState2.count: <input :value="refState2.count" /></li>
      </ul>
    </div>

    <div class="category">
      <span class="title">仅支持简单表达式</span>
      <ul>
        <li>
          <input :value="'count=' + refState1" />
        </li>
        <li><input :value="refState1 + 100" /></li>
      </ul>
    </div>

    <div class="category">
      <span class="title">Element text/attribute 绑定 JS函数</span>
      <div :title="getConcatCount('hello, ')">
        {{ getConcatCount('hello, this count =') }}
      </div>
    </div>

    <div class="category">
      <span class="title">绑定包含多个 attribute 的 JS变量</span>
      <div v-bind="objectOfAttrs"></div>
    </div>
  </div>

  <div class="demo error-demo">错误示例</div>
  <div class="demo-child">
    <!-- attribute中不支持{{  }},需使用v-bind -->
    <div class="category">
      <ul>
        <li>plainState1:{{ plainState1 }}</li>
        <li>plainState1: <input type="text" :value="plainState1" /></li>
        <li>plainState2.count: <input type="text" :value="plainState2.count" /></li>
        <li>
          <input value="{{ plainState1 }}" /><span class="red">无效:不允许绑定HTML attribute</span>
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, toRef, toRefs, onMounted, type Ref, type UnwrapNestedRefs } from 'vue';
import init from '@/global';

var plainState1 = 1000;
const plainState2 = {
  count: 2000,
  name: 'plainState2',
  plainState2Name: 'plainState2',
  plainState2Count: 2000
};

const refState1 = ref(3000);
const refState2 = ref({
  count: 4000,
  name: 'refState2'
});

const reactiveState1 = reactive({
  count: 5000,
  name: 'reactiveState1',
  reactiveState1Name: 'reactiveState1',
  reactiveState1Count: 5000
});

var toRefState1 = toRef(refState1);
var toRefState2 = toRef(refState2.value, 'count');
var toRefState3 = toRef(reactiveState1, 'count');

// const toRefsState1 = toRefs(refState1); // ERROR, could not read value
// const toRefsState1 = toRefs(refState2); // ERROR, could not read value
const toRefsState2 = toRefs(refState2.value);
const toRefsState3 = toRefs(reactiveState1);

var { name, count } = toRefs(refState2.value);
var { reactiveState1Name, reactiveState1Count } = toRefs(reactiveState1);

function changePlainStateOnly() {
  plainState1++; // 不会更新界面
  plainState2.count++; // 不会更新界面
}

function changeAllState() {
  plainState1++; // 会更新界面,因后面包含ref/reactive变量,会重新刷新组件
  plainState2.count++; // 会更新界面,因后面包含ref/reactive变量,会重新刷新组件
  refState1.value++; // 同时会更新界面(refState1、toRefState1、toRefsState1)
  refState2.value.count++; // 同时会更新界面(refState2、toRefState2、toRefsState2、name、 count)
  reactiveState1.count++; // 同时会更新界面(reactiveState1、toRefState3、toRefsState3)
}

function changeToRefState() {
  toRefState1.value++; // 同时会更新界面(refState1、toRefState1、toRefsState1)
  toRefState2.value++; // 同时会更新界面(refState2、toRefState2、toRefsState2、name、 count)
  toRefState3.value++; // 同时会更新界面(reactiveState1、toRefState3、toRefsState3)
}

function changeToRefsStateDemo1() {
  // toRefsState1++; // 同时会更新界面(refState1、toRefState1、toRefsState1)
  toRefsState2.count.value++; // 同时会更新界面(refState2、toRefState2、toRefsState2、name、 count)
  toRefsState3.count.value++; // 同时会更新界面(reactiveState1、toRefState3、toRefsState3)
}

function changeToRefsStateDemo2() {
  count.value++; // 同时会更新界面(refState2、toRefState2、toRefsState2、name、 count)
  reactiveState1Count.value++; // 同时会更新界面(reactiveState1.reactiveState1Count)
}

function getConcatCount(msg: string): string {
  return msg + ' ' + refState1.value;
}

onMounted(() => {
  console.log(`The initial refState1 is ${refState1.value}.`);
  init();
});

const objectOfAttrs = {
  id: 'container',
  class: 'wrapper',
  style: 'width: 500px; height: 50px; background-color: #00eeee;'
};
</script>