【Vue篇】组件的武林绝学:状态风暴下的乾坤挪移术

引言

🔍 Vue组件迷雾重重?
✧ Scoped如何实现样式隔离?
✧ Data为何必须是函数?
✧ 父子组件如何跨域通信?
✧ Props单向数据流如何破局?

🚀 本文直击组件化七大核心:
▸ 样式隔离原理 → 数据独立法则 → 父子通信协议
▸ Props校验黑盒 → 非父子通信方案 → 双向绑定内核
▸ $nextTick异步陷阱破解

💼 以「小黑记事本」组件化重构为实战样本,揭秘企业级开发规范。文末附事件总线+跨层通信秘籍,3分钟助你打通组件任督二脉!

🌟 若有所获,❤️⭐️📦三连助力,组件开发从此如臂使指!

一、Scoped样式隔离机制

1. 默认情况:

写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

  1. 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响

  2. 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件

2. 代码演示

BaseOne.vue

javascript 复制代码
<template>
  <div class="base-one">
    BaseOne
  </div>
</template>

<script>
export default {

}
</script>
<style scoped>
</style>

BaseTwo.vue

javascript 复制代码
<template>
  <div class="base-one">
    BaseTwo
  </div>
</template>

<script>
export default {

}
</script>

<style scoped>
</style>

App.vue

javascript 复制代码
<template>
  <div id="app">
    <BaseOne></BaseOne>
    <BaseTwo></BaseTwo>
  </div>
</template>

<script>
import BaseOne from './components/BaseOne'
import BaseTwo from './components/BaseTwo'
export default {
  name: 'App',
  components: {
    BaseOne,
    BaseTwo
  }
}
</script>

3.scoped原理

  1. 当前组件内标签都被添加data-v-hash值 的属性
  2. css选择器都被添加 **data-v-hash值** 的属性选择器

最终效果 : 必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到

二、data必须是一个函数

1. data为什么要写成函数

一个组件的 data 选项必须是一个函数。

目的是为了 :保证每个组件实例,维护独立的一份数据对象。

每次创建新的组件实例 ,都会新执行一次data 函数,得到一个新对象。

2. 代码演示

BaseCount.vue

javascript 复制代码
<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      count: 100,
    }
  },
}
</script>

<style>
.base-count {
  margin: 20px;
}
</style>

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseCount></BaseCount>
  </div>
</template>

<script>
import BaseCount from './components/BaseCount'
export default {
  components: {
    BaseCount,
  },
}
</script>

<style>
</style>

三、组件通信

1. 什么是组件通信?

组件通信,就是指组件与组件之间的数据传递

  • 组件的数据是独 立的,无法直接访问其他组件的数据。
  • 想使用其他组件的数据,就需要组件通信

2. 组件之间如何通信

3. 组件关系分类

  1. 父子关系
  2. 非父子关系

4. 通信解决方案

5. 父子通信流程

  1. 父组件通过 props 将数据传递给子组件
  2. 子组件利用 $emit 通知父组件修改更新

6. 父向子通信代码示例

父组件通过props将数据传递给子组件

父组件App.vue

javascript 复制代码
<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件 
    <Son></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '学前端,就来黑马程序员',
    }
  },
  components: {
    Son,
  },
}
</script>

<style>
</style>

子组件Son.vue

javascript 复制代码
<template>
  <div class="son" style="border:3px solid #000;margin:10px">
    我是Son组件
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
}
</script>

<style>

</style>

父向子传值步骤

  1. 给子组件以添加属性的方式传值
  2. 子组件内部通过props接收
  3. 模板中直接使用props接收的值

7. 子向父通信代码示例

子组件利用**$emit**通知父组件,进行修改更新

子向父传值步骤

  1. $emit触发事件,给父组件发送消息通知
  2. 父组件监听**$emit**触发的事件
  3. 提供处理函数,在函数的性参中获取传过来的参数

四、​​Props vs Data:单向数据流原则​

1. 什么是props

(1)Props 定义

组件上 注册的一些 自定义属性

(2)Props 作用

向子组件传递数据

(3)特点

  1. 可以 传递 任意数量 的prop
  2. 可以 传递 任意类型 的prop

(4)代码演示

父组件App.vue

javascript 复制代码
<template>
  <div class="app">
    <UserInfo
      :username="username"
      :age="age"
      :isSingle="isSingle"
      :car="car"
      :hobby="hobby"
    ></UserInfo>
  </div>
</template>

<script>
import UserInfo from './components/UserInfo.vue'
export default {
  data() {
    return {
      username: '小帅',
      age: 28,
      isSingle: true,
      car: {
        brand: '宝马',
      },
      hobby: ['篮球', '足球', '羽毛球'],
    }
  },
  components: {
    UserInfo,
  },
}
</script>

<style>
</style>

子组件UserInfo.vue

javascript 复制代码
<template>
  <div class="userinfo">
    <h3>我是个人信息组件</h3>
    <div>姓名:</div>
    <div>年龄:</div>
    <div>是否单身:</div>
    <div>座驾:</div>
    <div>兴趣爱好:</div>
  </div>
</template>

<script>
export default {
  
}
</script>

<style>
.userinfo {
  width: 300px;
  border: 3px solid #000;
  padding: 20px;
}
.userinfo > div {
  margin: 20px 10px;
}
</style>

2. props校验

(1)思考

组件的props可以乱传吗?

(2)作用

为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误

(3)语法

  • 类型校验
  • 非空校验
  • 默认值
  • 自定义校验

(4)代码演示

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseProgress :w="width"></BaseProgress>
  </div>
</template>

<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
  data() {
    return {
      width: 30,
    }
  },
  components: {
    BaseProgress,
  },
}
</script>

<style>
</style>

BaseProgress.vue

javascript 复制代码
<template>
  <div class="base-progress">
    <div class="inner" :style="{ width: w + '%' }">
      <span>{{ w }}%</span>
    </div>
  </div>
</template>

<script>
export default {
  props: ['w'],
}
</script>

<style scoped>
.base-progress {
  height: 26px;
  width: 400px;
  border-radius: 15px;
  background-color: #272425;
  border: 3px solid #272425;
  box-sizing: border-box;
  margin-bottom: 30px;
}
.inner {
  position: relative;
  background: #379bff;
  border-radius: 15px;
  height: 25px;
  box-sizing: border-box;
  left: -3px;
  top: -2px;
}
.inner span {
  position: absolute;
  right: 0;
  top: 26px;
}
</style>

3. props校验完整写法

(1)语法

javascript 复制代码
props: {
  校验的属性名: {
    type: 类型,  // Number String Boolean ...
    required: true, // 是否必填
    default: 默认值, // 默认值
    validator (value) {
      // 自定义校验逻辑
      return 是否通过校验
    }
  }
},

(2)代码实例

javascript 复制代码
<script>
export default {
  // 完整写法(类型、默认值、非空、自定义校验)
  props: {
    w: {
      type: Number,
      //required: true,
      default: 0,
      validator(val) {
        // console.log(val)
        if (val >= 100 || val <= 0) {
          console.error('传入的范围必须是0-100之间')
          return false
        } else {
          return true
        }
      },
    },
  },
}
</script>

(3)注意

  1. default和required一般不同时写(因为当时必填项时,肯定是有值的)
  2. default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值

4. props&data:单向数据流

(1)共同点

都可以给组件提供数据

(2)区别

  • data 的数据是自己的 → 随便改
  • prop 的数据是外部的 → 不能直接改,要遵循 单向数据流

(3)单向数据流:

父级props 的数据更新,会向下流动,影响子组件。这个数据流动是单向的

(4)代码演示

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseCount></BaseCount>
  </div>
</template>

<script>
import BaseCount from './components/BaseCount.vue'
export default {
  components:{
    BaseCount
  },
  data(){
  },
}
</script>

<style>

</style>

BaseCount.vue

javascript 复制代码
<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  // 1.自己的数据随便修改  (谁的数据 谁负责)
   data () {
     return {
       count: 100,
     }
   },
  // 2.外部传过来的数据 不能随便修改
  //props: {
  //  count: {
  //    type: Number,
  //  }, 
  //}
}
</script>

<style>
.base-count {
  margin: 20px;
}
</style>

(4)口诀

谁的数据谁负责

五、综合案例-小黑记事本(组件版)

1. 需求说明

  • 拆分基础组件
  • 渲染待办任务
  • 添加任务
  • 删除任务
  • 底部合计 和 清空功能
  • 持久化存储

2. 拆分基础组件

咱们可以把小黑记事本原有的结构拆成三部分内容:头部(TodoHeader)、列表(TodoMain)、底部(TodoFooter)

3. 列表渲染

思路分析:

  1. 提供数据:提供在公共的父组件App.vue
  2. 通过父传子,将数据传递给TodoMain
  3. 利用v-for进行渲染

4. 添加功能

思路分析:

  1. 收集表单数据v-model
  2. 监听时间 (回车+点击 都要进行添加)
  3. 子传父,将任务名称传递给父组件App.vue
  4. 父组件接受到数据后 进行添加 unshift(自己的数据自己负责)

5. 删除功能

思路分析:

  1. 监听时间(监听删除的点击)携带id
  2. 子传父,将删除的id传递给父组件App.vue
  3. 进行删除filter(自己的数据自己负责)

6. 底部功能及持久化存储

思路分析:

  1. 底部合计:父组件传递list到底部组件 --->展示合计
  2. 清空功能:监听事件 ---> 子组件通知父组件 --->父组件清空
  3. 持久化存储:watch监听数据变化,持久化到本地

TodoHeader.vue

javascript 复制代码
<template>
  <!-- 统计和清空 -->
  <footer class="footer">
    <!-- 统计 -->
    <span class="todo-count"
      >合 计:<strong> {{ list.length }} </strong></span
    >
    <!-- 清空 -->
    <button class="clear-completed" @click="clear">清空任务</button>
  </footer>
</template>

<script>
export default {
  props: {
    list: {
      type: Array,
    },
  },
  methods:{
    clear(){
      this.$emit('clear')
    }
  }
}
</script>

<style>
</style>

TodoMain.vue

javascript 复制代码
<template>
   <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</h1>
    <input placeholder="请输入任务" class="new-todo" v-model="todoName" @keyup.enter="handleAdd"/>
    <button class="add" @click="handleAdd">添加任务</button>
  </header>
</template>

<script>
export default {
  data(){
    return {
      todoName:''
    }
  },
  methods:{
    handleAdd(){
      // console.log(this.todoName)
      this.$emit('add',this.todoName)
      this.todoName = ''
    }
  }
}
</script>

<style>

</style>

TodoFooter.vue

javascript 复制代码
<template>
  <!-- 列表区域 -->
  <section class="main">
    <ul class="todo-list">
      <li class="todo" v-for="(item, index) in list" :key="item.id">
        <div class="view">
          <span class="index">{{ index + 1 }}.</span>
          <label>{{ item.name }}</label>
          <button class="destroy" @click="handleDel(item.id)"></button>
        </div>
      </li>
    </ul>
  </section>
</template>

<script>
export default {
  props: {
    list: {
      type: Array,
    },
  },
  methods: {
    handleDel(id) {
      this.$emit('del', id)
    },
  },
}
</script>

<style>
</style>

App.vue

javascript 复制代码
<template>
  <!-- 主体区域 -->
  <section id="app">
    <TodoHeader @add="handleAdd"></TodoHeader>
    <TodoMain :list="list" @del="handelDel"></TodoMain>
    <TodoFooter :list="list" @clear="clear"></TodoFooter>
  </section>
</template>

<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'

// 渲染功能:
// 1.提供数据: 提供在公共的父组件 App.vue
// 2.通过父传子,将数据传递给TodoMain
// 3.利用 v-for渲染

// 添加功能:
// 1.手机表单数据  v-model
// 2.监听事件(回车+点击都要添加)
// 3.子传父,讲任务名称传递给父组件 App.vue
// 4.进行添加 unshift(自己的数据自己负责)
// 5.清空文本框输入的内容
// 6.对输入的空数据 进行判断

// 删除功能
// 1.监听事件(监听删除的点击) 携带id
// 2.子传父,讲删除的id传递给父组件的App.vue
// 3.进行删除filter(自己的数据 自己负责)

// 底部合计:父传子  传list 渲染
// 清空功能:子传父  通知父组件 → 父组件进行更新
// 持久化存储:watch深度监视list的变化 -> 往本地存储 ->进入页面优先读取本地数据
export default {
  data() {
    return {
      list: JSON.parse(localStorage.getItem('list')) || [
        { id: 1, name: '打篮球' },
        { id: 2, name: '看电影' },
        { id: 3, name: '逛街' },
      ],
    }
  },
  components: {
    TodoHeader,
    TodoMain,
    TodoFooter,
  },
  watch: {
    list: {
      deep: true,
      handler(newVal) {
        localStorage.setItem('list', JSON.stringify(newVal))
      },
    },
  },
  methods: {
    handleAdd(todoName) {
      // console.log(todoName)
      this.list.unshift({
        id: +new Date(),
        name: todoName,
      })
    },
    handelDel(id) {
      // console.log(id);
      this.list = this.list.filter((item) => item.id !== id)
    },
    clear() {
      this.list = []
    },
  },
}
</script>

<style>
</style>

六、非父子通信-event bus 事件总线

1. 作用

非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)

2. 步骤

  1. 创建一个都能访问的事件总线 (空Vue实例)

    javascript 复制代码
    import Vue from 'vue'
    const Bus = new Vue()
    export default Bus
  2. A组件(接受方),监听Bus的 $on事件

    javascript 复制代码
    created () {
      Bus.$on('sendMsg', (msg) => {
        this.msg = msg
      })
    }
  3. B组件(发送方),触发Bus的**$emit**事件

    javascript 复制代码
    Bus.$emit('sendMsg', '这是一个消息')

3. 代码示例

EventBus.js

复制代码
import Vue from 'vue'
const Bus  =  new Vue()
export default Bus

BaseA.vue(接受方)

javascript 复制代码
<template>
  <div class="base-a">
    我是A组件(接收方)
    <p>{{msg}}</p>  
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
}
</script>

<style scoped>
.base-a {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

BaseB.vue(发送方)

javascript 复制代码
<template>
  <div class="base-b">
    <div>我是B组件(发布方)</div>
    <button>发送消息</button>
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
}
</script>

<style scoped>
.base-b {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseA></BaseA>
    <BaseB></BaseB> 
  </div>
</template>

<script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue' 
export default {
  components:{
    BaseA,
    BaseB
  }
}
</script>

<style>

</style>

七、非父子通信-provide&inject

1. 作用

跨层级共享数据

2. 场景

3. 语法

  1. 父组件 provide提供数据
javascript 复制代码
export default {
  provide () {
    return {
       // 普通类型【非响应式】
       color: this.color, 
       // 复杂类型【响应式】
       userInfo: this.userInfo, 
    }
  }
}

2.子/孙组件 inject获取数据

javascript 复制代码
export default {
  inject: ['color','userInfo'],
  created () {
    console.log(this.color, this.userInfo)
  }
}

4. 注意

  • provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改

八、v-model原理

1.原理:

v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件 的合写

javascript 复制代码
<template>
  <div id="app" >
    <input v-model="msg" type="text">

    <input :value="msg" @input="msg = $event.target.value" type="text">
  </div>
</template>

2.作用:

提供数据的双向绑定

  • 数据变,视图跟着变 :value
  • 视图变,数据跟着变 @input

3.注意

$event 用于在模板中,获取事件的形参

4.代码示例

javascript 复制代码
<template>
  <div class="app">
    <input type="text"  />
    <br /> 
    <input type="text" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg1: '',
      msg2: '',
    }
  },
}
</script> 
<style>
</style>

5.v-model使用在其他表单元素上的原理

不同的表单元素, v-model在底层的处理机制是不一样的。比如给checkbox使用v-model

底层处理的是 checked属性和change事件。

不过咱们只需要掌握应用在文本框上的原理即可

九、表单类组件封装

1. 需求目标

实现子组件和父组件数据的双向绑定(实现App.vue中的selectId和子组件选中的数据进行双向绑定)

①父传子:数据应该是父组件props传递过来的,拆解v-model绑定数据

②子传父:监听输入,子传父传值给父组件修改

2. 代码演示

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseSelect></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
  data() {
    return {
      selectId: '102',
    }
  },
  components: {
    BaseSelect,
  },
}
</script>

<style>
</style>

BaseSelect.vue

javascript 复制代码
<template>
  <div>
    <select>
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
}
</script>

<style>
</style>

十、v-model简化代码

1. 目标:

父组件通过v-model 简化代码,实现子组件和父组件数据 双向绑定

2. 如何简化:

v-model其实就是 :value和**@input**事件的简写

  • 子组件:props通过value接收数据,事件触发input
  • 父组件:v-model直接绑定数据

3. 代码示例

子组件

javascript 复制代码
<select :value="value" @change="handleChange">...</select>
props: {
  value: String
},
methods: {
  handleChange (e) {
    this.$emit('input', e.target.value)
  }
}

父组件

javascript 复制代码
<BaseSelect v-model="selectId"></BaseSelect>

十一、.sync修饰符

1. 作用

可以实现 子组件 与 父组件数据 的 双向绑定,简化代码

简单理解 :子组件可以修改父组件传过来的props值

2. 场景

封装弹框类的基础组件,visible属性 true显示 false隐藏

3. 本质

.sync修饰符 就是 :属性名 和 @update:属性名 合写

4. 语法

父组件

javascript 复制代码
//.sync写法
<BaseDialog :visible.sync="isShow" />
--------------------------------------
//完整写法
<BaseDialog 
  :visible="isShow" 
  @update:visible="isShow = $event" 
/>

子组件

javascript 复制代码
props: {
  visible: Boolean
},

this.$emit('update:visible', false)

5. 代码示例

App.vue

javascript 复制代码
<template>
  <div class="app">
    <button @click="openDialog">退出按钮</button>
    <BaseDialog :isShow="isShow"></BaseDialog>
  </div>
</template>

<script>
import BaseDialog from './components/BaseDialog.vue'
export default {
  data() {
    return {
      isShow: false,
    }
  },
  components: {
    BaseDialog,
  },
}
</script>

<style>
</style>

BaseDialog.vue

javascript 复制代码
<template>
  <div class="base-dialog-wrap" v-show="isShow">
    <div class="base-dialog">
      <div class="title">
        <h3>温馨提示:</h3>
        <button class="close">x</button>
      </div>
      <div class="content">
        <p>你确认要退出本系统么?</p>
      </div>
      <div class="footer">
        <button>确认</button>
        <button>取消</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    isShow: Boolean,
  }
}
</script>

<style scoped>
.base-dialog-wrap {
  width: 300px;
  height: 200px;
  box-shadow: 2px 2px 2px 2px #ccc;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 0 10px;
}
.base-dialog .title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid #000;
}
.base-dialog .content {
  margin-top: 38px;
}
.base-dialog .title .close {
  width: 20px;
  height: 20px;
  cursor: pointer;
  line-height: 10px;
}
.footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 26px;
}
.footer button {
  width: 80px;
  height: 40px;
}
.footer button:nth-child(1) {
  margin-right: 10px;
  cursor: pointer;
}
</style>

十二、ref和$refs

1. 作用

利用ref和**$refs**可以用于 获取 dom 元素 或 组件实例

2. 特点:

查找范围 → 当前组件内(更精确稳定)

3. 语法

1.给要获取的盒子添加ref属性

javascript 复制代码
<div ref="chartRef">我是渲染图表的容器</div>

2.获取时通过**refs**获取**this.refs.chartRef** 获取

javascript 复制代码
mounted () {
  console.log(this.$refs.chartRef)
}

4. 注意

之前只用**document.querySelect('.box')**获取的是整个页面中的盒子

5. 代码示例

App.vue

javascript 复制代码
<template>
  <div class="app">
    <BaseChart></BaseChart>
  </div>
</template>

<script>
import BaseChart from './components/BaseChart.vue'
export default {
  components:{
    BaseChart
  }
}
</script>

<style>
</style>

BaseChart.vue

javascript 复制代码
<template>
  <div class="base-chart-box" ref="baseChartBox">子组件</div>
</template>

<script>
// yarn add echarts 或者 npm i echarts
import * as echarts from 'echarts'

export default {
  mounted() {
    // 基于准备好的dom,初始化echarts实例
    var myChart = echarts.init(document.querySelect('.base-chart-box'))
    // 绘制图表
    myChart.setOption({
      title: {
        text: 'ECharts 入门示例',
      },
      tooltip: {},
      xAxis: {
        data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
      },
      yAxis: {},
      series: [
        {
          name: '销量',
          type: 'bar',
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    })
  },
}
</script>

<style scoped>
.base-chart-box {
  width: 400px;
  height: 300px;
  border: 3px solid #000;
  border-radius: 6px;
}
</style>

十三、异步更新 & $nextTick

1. 需求

编辑标题, 编辑框自动聚焦

  1. 点击编辑,显示编辑框
  2. 让编辑框,立刻获取焦点

2. 代码实现

javascript 复制代码
// 显示输入框
this.isShowEdit = true  
// 获取焦点
this.$refs.inp.focus()  

3. 问题

"显示之后",立刻获取焦点是不能成功的!

原因:Vue 是异步更新DOM (提升性能)

4. 解决方案

$nextTick:等 DOM更新后,才会触发执行此方法里的函数体

语法: this.$nextTick(函数体)

javascript 复制代码
this.$nextTick(() => {
  this.$refs.inp.focus()
})

注意: $nextTick 内的函数体 一定是箭头函数 ,这样才能让函数内部的this指向Vue实例

📜 以上就是 【Vue篇】组件的武林绝学:状态风暴下的乾坤挪移术 的核心奥义!

通过本文的修炼,你已掌握:
✨ 组件内功心法 :Scoped样式结界、Data函数化形、Props单向数据流
✨ 通信乾坤术 :父子组件传音入密、事件总线凌波微步、跨层Provide/Inject虚空引渡
✨ 绝杀实战 :小黑记事本组件涅槃、表单双向绑定秘术、$nextTick时空校准法则

🔍 招式存疑 ?文中表述欠妥之处,欢迎各位少侠在评论区「剑气留痕」,共同论剑切磋!
💡 进阶求索 ?若有组件化「奇经八脉」待打通,或遭遇「状态风暴」难题,速速留言共探玄机!

🗡️ 你的每一记 ❤️点赞|⭐收藏|📤分享 ,皆为吾辈持续精进「Vue真经」的绝世内力!

相关推荐
陈随易23 分钟前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
Dream-Y.ocean2 小时前
鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发
javascript·3d·harmonyos
Csvn3 小时前
性能监控
前端
Highcharts.js3 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
Csvn4 小时前
构建工具
前端
FRing034 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷5 小时前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码
kyriewen6 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
IT_陈寒7 小时前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫7 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库