VUE:单向绑定和双向绑定

三个绑定之间

关系

绑定
├── 单向绑定(数据 → 视图)
│    ├── 插值表达式:{{ data }}
│    └── 动态绑定::attr="data"  ← 你的 scroll-top 在这里
└── 双向绑定(数据 ⇄ 视图)
     └── v-model="data"

差异

类型 方向 语法 用途
单向绑定 数据 → 视图 动态绑定::属性="数据"v-bind:属性="数据" 任何HTML属性
数据 → 视图 插值表达式:{{ 数据 }}:value="数据" 显示数据
双向绑定 数据 ⇄ 视图 v-model="数据" 表单输入

案例对比

<template>
  <view style="padding: 30rpx">
    
    <!-- ================= 1. 单向绑定 ================= -->
    <view style="font-weight: bold">① 单向绑定(手动处理 input)</view>
	<!-- @input 就是事件监听语法。监听用户输入(视图 → 数据) -->
	<!-- :value="name" → 把数据给输入框(数据 → 视图) -->
	<!-- @input="updateName" → 监听用户输入(视图 → 数据) -->
    <input
      type="text"
      :value="name"
      @input="updateName"
      style="border:1px solid #333;height:40rpx;padding:0 10rpx;margin:10rpx 0;"
    />
	<input
	  type="text"
	  :value="name"
	  style="border:1px solid #333;height:40rpx;padding:0 10rpx;margin:10rpx 0;"
	/>
    <view>数据值:{{ name }}</view>

    <view style="margin:20rpx 0;border-top:1px solid #ddd;"></view>

    <!-- ================= 2. 双向绑定 ================= -->
    <view style="font-weight: bold">② 双向绑定(v-model 自动)</view>
    <input
      v-model="name2"
      placeholder="请输入内容"
      style="border:1px solid red;height:40rpx;padding:0 10rpx;margin:10rpx 0;"
    />
    <view>数据值:{{ name2 }}</view>
    <view style="margin:20rpx 0;border-top:1px solid #ddd;"></view>

    <!-- ================= 3. 纯单向展示 ================= -->
    <view style="font-weight: bold">③ 纯单向展示</view>
    <view
      style="border:1px dashed #999;height:40rpx;line-height:40rpx;padding:0 10rpx;margin:10rpx 0;"
    >
      {{ name3 }}
    </view>
    <view style="margin:30rpx 0;border-top:2px solid #000;"></view>
    <!-- ================= 控制按钮 ================= -->
    <button @click="changeAll" type="primary">
      点击修改所有数据
    </button>

  </view>
</template>

<script setup>
import { ref } from 'vue'

const name = ref('张三')
const name2 = ref('李四')
const name3 = ref('王五')

// 单向绑定需要手动更新
const updateName = (e) => {
  name.value = e.detail.value
}

// 统一修改数据
const changeAll = () => {
  name.value = '新张三'
  name2.value = '新李四'
  name3.value = '新王五'
}
</script>

动态绑定

什么是动态绑定?

动态绑定(Dynamic Binding)是 Vue.js 提供的一种机制,允许你将 HTML 属性、元素内容、样式、事件等JavaScript 数据(响应式状态) 关联起来。当数据发生变化时,视图会自动更新,无需手动操作 DOM。

✅ 核心思想:声明式地描述“视图如何依赖数据”,由 Vue 自动完成同步。

动态绑定的语法:v-bind

Vue 使用指令 v-bind 实现动态绑定。为方便书写,提供了缩写语法::(冒号)。

<!-- 完整写法 -->
<a v-bind:href="url">链接</a>

<!-- 缩写写法(推荐) -->
<a :href="url">链接</a>

其中 url 是组件实例中的一个 响应式数据属性(如 datarefreactive 等定义的变量)。

动态绑定的常见应用场景

绑定 HTML 属性(Attribute)

将元素的原生属性(如 srchrefdisabledtitle 等)绑定到 JS 表达式。

<!-- Template -->
<template>
  <div>
    <img :src="imageUrl" alt="动态图片" />
    <button :disabled="isDisabled">提交</button>
    <input type="text" :placeholder="placeholderText" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 响应式数据
const imageUrl = ref('https://example.com/photo.jpg')
const isDisabled = ref(true)
const placeholderText = ref('请输入内容...')

// 模拟数据变化(例如 2 秒后启用按钮)
setTimeout(() => {
  isDisabled.value = false
  placeholderText.value = '现在可以输入了!'
}, 2000)
</script>
  • imageUrl 变化 → <img>src 自动更新。
  • isDisabledtrue → 按钮自动禁用。

⚠️ 注意:绑定布尔属性(如 disabledchecked)时,Vue 会自动处理真假值。

绑定内联样式(Style)

使用 :style 动态设置元素的 CSS 内联样式。

对象语法(最常用)

<template>
  <div :style="{ 
    color: textColor, 
    fontSize: fontSize + 'px',
    backgroundColor: bgColor 
  }">
    这是一段动态样式的文字
  </div>
</template>

<script setup>
import { ref } from 'vue'

const textColor = ref('#45C2D3')
const fontSize = ref(24)
const bgColor = ref('#ECFBFD')
</script>
  • 键(key)是 CSS 属性名(推荐驼峰式,如 fontSize)。
  • 值(value)是 JS 表达式(需包含单位,如 'px''rpx')。

数组语法(组合多个样式对象)

<template>
  <div :style="[baseStyle, overrideStyle]">
    使用数组合并样式
  </div>
</template>

<script setup>
import { reactive } from 'vue'

const baseStyle = reactive({
  color: 'blue',
  padding: '10px',
  borderRadius: '4px'
})

const overrideStyle = reactive({
  color: 'red', // 覆盖 baseStyle 的 color
  fontWeight: 'bold'
})
</script>
  • 后面的对象可覆盖前面的同名属性。

绑定 CSS 类名(Class)

使用 :class 动态切换或组合 CSS 类。

对象语法(控制类的有无)

<template>
  <div :class="{ active: isActive, error: hasError }">
    状态指示区域
  </div>
  <button @click="toggleActive">切换 active</button>
  <button @click="toggleError">切换 error</button>
</template>

<script setup>
import { ref } from 'vue'

// 初始状态:<div class="error">状态指示区域</div>
// 因为 hasError==true
const isActive = ref(false)
const hasError = ref(true)

// 点击“切换 active”后:<div class="active error">状态指示区域</div>
// 因为 isActive = true 且 hasError = true
function toggleActive() {
  isActive.value = !isActive.value
}

function toggleError() {
  hasError.value = !hasError.value
}
</script>

<style scoped>
.active {
  background-color: lightgreen;
  border: 2px solid green;
}
.error {
  color: red;
  font-weight: bold;
}
</style>

它的规则是:只有当对应的值(如 isActivehasError)为真值(truthy)时,对应的 class 名(如 'active''error')才会被添加到元素上;否则,该 class 不会出现。

数组语法(组合多个类)

<template>
  <div :class="[baseClass, isSpecial ? specialClass : '', { disabled: isDisabled }]">
    多种类组合
  </div>
</template>

<script setup>
import { ref } from 'vue'

const baseClass = ref('default-style')
const isSpecial = ref(true)
const specialClass = ref('highlight')
const isDisabled = ref(false)
</script>

<style scoped>
.default-style {
  padding: 8px;
  border: 1px solid #ccc;
}
.highlight {
  background-color: yellow;
}
.disabled {
  opacity: 0.5;
  pointer-events: none;
}
</style>

数组中不同类型的处理规则

  • 字符串(String)

    • 直接添加(只要不是空字符串)

    • 空字符串 ''nullundefined 会被忽略

      :class="['always', '', null, undefined]"
      // → class="always"
      
  • 对象(Object)

    • 按对象语法规则处理:只添加值为真(truthy)的 key

    • 不是“直接加整个对象”

      :class="[{ active: false, error: true }]"
      // → class="error" (active 被忽略)
      
  • 三元表达式 / 变量

    • 表达式先求值,再按结果的类型处理

    • 如果结果是字符串 → 按字符串规则处理

    • 如果结果是对象 → 按对象规则处理

      // 示例1:三元返回字符串
      isSpecial ? 'premium' : '' 
      // → 'premium'(添加) 或 ''(忽略)
      
      // 示例2:三元返回对象
      isSpecial ? { premium: true } : {}
      // → { premium: true }(添加 premium) 或 {}(无效果)
      

:class="[baseClass, isSpecial ? specialClass : '', { disabled: isDisabled }]"

  • 始终加上 baseClass;如果 isSpecial 为真,再加上 specialClass;如果 isDisabled 为真,再加上 disabled 类。

绑定组件 Props(在组件间传递数据)

在使用子组件时,通过 :propName 将父组件的数据传递给子组件。

父组件(传递数据)

<!-- ParentComponent.vue -->
<template>
  <div>
    <UserInfoCard :title="pageTitle" :count="userCount" :loading="isLoading" />
    <button @click="updateData">更新数据</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import UserInfoCard from './UserInfoCard.vue'

const pageTitle = ref('用户中心')
const userCount = ref(1024)
const isLoading = ref(false)

function updateData() {
  userCount.value += 100
  isLoading.value = true
  setTimeout(() => {
    isLoading.value = false
  }, 1000)
}
</script>

子组件(接收 Props)

<!-- UserInfoCard.vue -->
<template>
  <div class="card">
    <h3>{{ title }}</h3>
    <p>当前用户数:{{ count }}</p>
    <p v-if="loading">加载中...</p>
  </div>
</template>

<script setup>
// 定义接收的 props
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  },
  loading: {
    type: Boolean,
    default: false
  }
})
</script>

<style scoped>
.card {
  border: 1px solid #ddd;
  padding: 16px;
  margin: 10px 0;
}
</style>
  • 子组件通过 defineProps({ title: String, count: Number }) 接收。
  • pageTitleuserCount 变化,子组件自动重新渲染。

绑定其他特殊属性

  • 绑定 key:用于列表渲染中提示 Vue 元素的唯一性。

    <template>
      <ul>
        <li v-for="item in list" :key="item.id">
          {{ item.name }}
        </li>
      </ul>
      <button @click="addItem">添加新项</button>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    let id = 3
    const list = ref([
      { id: 1, name: '苹果' },
      { id: 2, name: '香蕉' }
    ])
    
    function addItem() {
      list.value.push({ id: ++id, name: '新水果' + id })
    }
    </script>
    
  • 绑定 ref:获取 DOM 元素或子组件实例(Vue 3 中常用 ref())。

    <template>
      <input ref="inputRef" placeholder="聚焦我" />
      <button @click="focusInput">聚焦输入框</button>
    </template>
    
    <script setup>
    import { ref, nextTick } from 'vue'
    
    const inputRef = ref(null)
    
    function focusInput() {
      // 确保 DOM 已更新
      nextTick(() => {
        inputRef.value?.focus()
      })
    }
    </script>
    
  • 绑定 ARIA 属性、自定义属性等

    <template>
      <button 
        :aria-label="buttonLabel"
        :data-role="userRole"
        @click="handleClick"
      >
        操作按钮
      </button>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    const buttonLabel = ref('点击以保存当前表单')
    const userRole = ref('admin')
    
    function handleClick() {
      console.log('当前角色:', userRole.value)
    }
    </script>
    

动态绑定的本质

  • 不是字符串拼接,而是 JavaScript 表达式求值

    <!-- ❌ 错误:这是字符串,不会执行 JS -->
    <div style="color: textColor"></div>
    
    <!-- ✅ 正确:表达式会被求值 -->
    <div :style="{ color: textColor }"></div>
    
  • 支持任意合法 JS 表达式(但不支持语句,如 iffor):

    <div :title="message.split(' ').reverse().join(' ')"></div>
    
  • 响应式驱动:只有绑定的数据是响应式的(如 refreactivedata),视图才会自动更新。

注意事项

  1. 属性名大小写
    HTML 属性不区分大小写,但 Vue 会智能处理:

    • 在模板中建议始终使用 kebab-case(短横线命名) 绑定原生属性:

      <video :poster-image="posterUrl"> <!-- ❌ 不推荐 -->
      <video :poster="posterUrl">       <!-- ✅ 正确 -->
      
  2. 避免复杂逻辑写在模板中
    复杂计算应放在 computed 或方法中:

    <!-- 不推荐 -->
    <div :class="{ red: items.length > 10 && !loading }">
    
    <!-- 推荐 -->
    <div :class="{ red: shouldHighlight }">
    
    computed: {
      shouldHighlight() {
        return this.items.length > 10 && !this.loading;
      }
    }
    
  3. 安全转义
    Vue 会对绑定的内容自动进行 HTML 转义(防止 XSS),除非使用 v-html(需谨慎)。

×

喜欢就点赞,疼爱就打赏