一、组件的三大组成部分(结构/样式/逻辑)
1. scoped样式冲突
默认情况:写在组件中的样式会全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。
1. 全局样式:默认组件中的样式会作用到全局
2. 局部样式:可以给组件加上scoped属性,可以让样式只作用于当前组件
scoped的原理:
1. 当前组件内部标签都被添加data-v-hash值 的属性
2. CSS选择器都被添加[data-v-hash值] 的属性选择器
最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到
示例:
components/BaseOne.vue
components/BaseTwo.vue
<template><div class="base-one">BaseTwo</div>
</template><script>
export default {}
</script><style scoped>
div {border: 3px solid red;margin: 30px;
}
</style>
App.vue
<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>
效果:
2. data是一个函数
一个组件的data选项必须是一个函数。 -> 保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会执行一次data函数得到一个新对象
data() {return {count: 100}
},
示例:
components/BaseCount.vue
<template><div class="base-count"><button @click="count--">-</button><span>{{ count }}</span><button @click="count++">+</button></div>
</template><script>
export default {data() {return {count: 100,}},
}
</script><style>
.base-count {margin: 20px;
}
</style>
App.vue
<template><div class="app"><baseCount></baseCount><baseCount></baseCount><baseCount></baseCount></div>
</template><script>
import baseCount from './components/BaseCount'
export default {components: {baseCount,},
}
</script><style>
</style>
效果:
二、组件通信
组件通信,就是指组件与组件之间的数据传递。
- 组件的数据是独立的,无法直接访问其他组件的数据。
- 想用其他组件的数据 -> 组件通信。
不同的组件关系 和 组件通信方案分类
组件关系分类:
1. 父子关系
2. 非父子关系
1. 组件通信语法
2. 父传子
父组件通过 props 将数据传递给子组件
- ①父组件:给组件添加标签,添加属性的方式,传值
- ②子组件:通过props进行接收
- ③子组件:渲染使用
示例:
效果:
什么是 prop
Prop定义:组件上 注册的一些自定义属性
Prop作用:向子组件传递数据
特点:
- 可以传递任意数量的prop
- 可以传递任意类型的prop
示例:
App.vue
<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>
components/UserInfo.vue
<template><div class="userinfo"><h3>我是个人信息组件</h3><div>姓名:{{ username }}</div><div>年龄:{{ age }}</div><div>是否单身:{{ isSingle ? '是' : '否'}}</div><div>座驾:{{ car.brand }}</div><div>兴趣爱好: {{ hobby.join('、') }}</div></div>
</template><script>
export default {props: ['username', 'age', 'isSingle', 'car', 'hobby']
}
</script><style>
.userinfo {width: 300px;border: 3px solid #000;padding: 20px;
}
.userinfo > div {margin: 20px 10px;
}
</style>
效果:
props校验
思考:组件的prop可以乱传吗?
作用:为组件的prop指定验证要求,不符合要求,控制台就会有错误提示 -> 帮助开发者,快速发现错误
语法:
①类型校验
props: {校验的属性名:类型 // Number String Boolean···
},
②非空校验
③默认值
④自定义校验
props: {校验的属性名: {type: 类型, // Number String Boolean···required: true, // 是否必填default: 默认值, // 默认值validator (value) {// 自定义校验逻辑return 是否通过校验}}
},
示例:
App.vue
<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>
components/BaseProgress.vue
<template><div class="base-progress"><div class="inner" :style="{ width: w + '%' }"><span>{{ w }}%</span></div></div>
</template><script>
export default {// props: ["w"],// 1.基础写法(类型校验)// props: {// // 类型校验// w: Number// }// 2.完整写法(类型、是否必填、默认值、自定义校验)props: {w: {type: Number,required: true,default: 0,validator (value) {// console.log(value)if(value >= 0 && value <= 100) {return true} else {console.error('传入的prop w必须是0-100的数字')return false}}}}}
</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>
效果:
prop & data 单向数据流
共同点: 都可以给组件提供数据
区别:
- data的数据是自己的 -> 随便改
- prop的数据是外部的 -> 不能直接改,要遵循单向数据流
单向数据流:父组件的prop的数据更新,会单向的向下流动,影响到子组件。这个数据流动是单向的。
示例:
App.vue
<template><div class="app"><BaseCount :count="count"@changeCount="handleChange"></BaseCount></div>
</template><script>
import BaseCount from './components/BaseCount.vue'
export default {components:{BaseCount},data(){return {count:100}},methods:{handleChange(newCount) {this.count = newCount}}
}
</script><style></style>
components/BaseCount.vue
<template><div class="base-count"><button @click="handleSub">-</button><span>{{ count }}</span><button @click="handleAdd">+</button></div>
</template><script>
export default {// 1.自己的数据随便修改 (谁的数据 谁负责)// data () {// return {// count: 100,// }// },// 2.外部传过来的数据 不能随便修改// 单向数据流:父组件的prop更新,会单向的向下流动,影响到子组件props: {count: Number },methods: {handleAdd() {this.$emit('changeCount', this.count + 1)},handleSub() {this.$emit('changeCount', this.count - 1)}}}
</script><style>
.base-count {margin: 20px;
}
</style>
3. 子传父
子组件利用 $emit 通知父组件,进行修改更新
- ①子组件:$emit发送消息;
- ②父组件:给子组件添加消息监听;
- ③父组件:实现处理函数。
示例:
components/Son.vue
<template><div class="son" style="border:3px solid #000;margin:10px">我是Son组件 {{ title }}<button @click="changeFn">修改title</button></div>
</template><script>
export default {name: 'Son-Child',props: ['title'],methods: {changeFn() {// 1. 通过$emit,向父组件发送消息通知this.$emit('changeTitle', '传智教育')}}
}
</script><style></style>
App.vue
<template><div class="app" style="border: 3px solid #000; margin: 10px">我是APP组件<!-- 2. 父组件,对消息进行监听 --><Son :title="myTitle" @changeTitle="handleChange"></Son></div>
</template><script>
import Son from "./components/Son.vue"
export default {name: "App",components: {Son,},data() {return {myTitle: "学前端,就来黑马程序员",}},methods: {// 提供对应的处理函数,提供逻辑handleChange(newTitle) {// console.log(newTitle)this.myTitle = newTitle}}
}
</script><style>
</style>
效果:
4. 非父子(拓展)— event bus 事件总线
作用:非父子组件之间,进行简易消息传递。(复杂场景 -> Vuex)
1. 创建一个都能访问到的事件总线(空Vue实例) -> utils/EventBus.js
import Vue from 'vue'
const Bus = new Vue()
export default Bus
2. A组件(接收方),监听Bus实例的事件
created() {Bus.$on('sendMsg', (msg) => {this.msg = msg})
}
3. B组件(发送方),触发Bus实例的事件
Bus.$emit('sendMsg', '这是一个消息')
示例:
创建一个都能访问到的事件总线(空Vue示例) -> src/utils/EventBus.js
import Vue from 'vue'// 1. 创建一个都能访问到的事件总线(空的Vue实例)
const Bus = new Vue()export default Bus
src/components/BaseA.vue:接收方
<template><div class="base-a">我是A组件(接收方)<p>{{msg}}</p> </div>
</template><script>
import Bus from '../utils/EventBus'
export default {data() {return {msg: '',}},created() {// 在A组件(接收方),进行监听Bus的事件(订阅消息)Bus.$on('sendMsg', (msg) => {// console.log(msg)this.msg = msg})},
}
</script><style scoped>
.base-a {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>
src/components/BaseB.vue:发送方
<template><div class="base-b"><div>我是B组件(发布方)</div><button @click="sendMsgFn">发送消息</button></div>
</template><script>
import Bus from '../utils/EventBus'
export default {methods: {sendMsgFn() {// 3. B组件(发送方),触发事件的方式传递参数// 任何监听了这个消息的组件都可以接收到对应的消息Bus.$emit('sendMsg', '今天天气不错,适合旅游')},},
}
</script><style scoped>
.base-b {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>
src/components/BaseC.vue:接收方
<template><div class="base-c">我是C组件(接收方)<p>{{msg}}</p> </div>
</template><script>
import Bus from '../utils/EventBus'
export default {data() {return {msg: '',}},created() {Bus.$on('sendMsg', (msg) => {// console.log(msg)this.msg = msg})},
}
</script><style scoped>
.base-c {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>
App.vue
<template><div class="app"><BaseA></BaseA><BaseB></BaseB><BaseC></BaseC></div>
</template><script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue'
import BaseC from './components/BaseC.vue'
export default {components:{BaseA,BaseB,BaseC}
}
</script><style></style>
5. 非父子通信(拓展) — provide & inject
provide & inject作用:跨层级共享数据。
1. 父组件provide提供数据
export default {provide() {return {// 普通类型【非响应式】color: this.color,// 复杂类型 【响应式】userInfo: this.userInfo,}}
}
2. 子 / 孙组件 inject 取值使用
export default {inject: ['color', 'userInfo'],created() {console.log(this.color, this.userInfo)}
}
示例:
App.vue
<template><div class="app">我是APP组件<button @click="change">修改数据</button><SonA></SonA><SonB></SonB></div>
</template><script>
import SonA from './components/SonA.vue'
import SonB from './components/SonB.vue'
export default {provide() {return {// 简单类型 是非响应式的color: this.color,// 复杂类型 是响应式的userInfo: this.userInfo,}},data() {return {color: 'pink',userInfo: {name: 'zs',age: 18,},}},methods: {change() {this.color = 'red'this.userInfo.name = 'ls'},},components: {SonA,SonB,},
}
</script><style>
.app {border: 3px solid #000;border-radius: 6px;margin: 10px;
}
</style>
components/SonA.vue
<template><div class="SonA">我是SonA组件<GrandSon></GrandSon></div>
</template><script>
import GrandSon from '../components/GrandSon.vue'
export default {components:{GrandSon}
}
</script><style>
.SonA {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
</style>
components/SonB.vue
<template><div class="SonB">我是SonB组件</div>
</template><script>
export default {}
</script><style>
.SonB {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
</style>
components/GrandSon.vue
<template><div class="grandSon">我是GrandSon{{ color }} -{{ userInfo.name }} -{{ userInfo.age }}</div>
</template><script>
export default {inject: ['color', 'userInfo'],
}
</script><style>
.grandSon {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 100px;
}
</style>
效果:
三、综合案例:小黑记事本(组件版)
需求说明:
①拆分基础组件
新建组件 -> 拆分存放结构 -> 导入注册使用
②渲染待办任务
提供数据(公共父组件) -> 父传子传递list -> v-for渲染
③添加任务
收集数据 v-model -> 监听事件 -> 子传父传递任务 -> 父组件unshift
④删除任务
监听删除携带id -> 子传父传递id -> 父组件filter删除
⑤底部合计 和 清空功能
底部合计:父传子传递list -> 合计展示
清空功能:监听点击 -> 子传父通知父组件 -> 父组件清空
⑥持久化存储
watch监视数据变化,持久化到本地
TodoHeader.vue
<template><!-- 输入框 --><header class="header"><h1>小黑记事本</h1><input@keyup.enter="handleAdd" placeholder="请输入任务" class="new-todo" v-model="todoName"/><button class="add" @click="handleAdd">添加任务</button></header>
</template><script>
export default {data() {return {todoName: ''}},methods: {handleAdd() {// console.log(this.todoName)// 非空判断if(this.todoName.trim() === '') {alert('任务名称不能为空!')return}this.$emit('add', this.todoName)this.todoName = ''}}
};
</script><style>
</style>
TodoMain.vue
<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: Array,},methods: {handleDel(id) {this.$emit('delete', id)}}
};
</script><style>
</style>
TodoFooter.vue
<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: Array},methods: {clear() {this.$emit('clear')}}
};
</script><style>
</style>
App.vue
<template><!-- 主体区域 --><section id="app"><TodoHeader @add="handleAdd"></TodoHeader><TodoMain :list="list" @delete="handleDel"></TodoMain><TodoFooter :list="list" @clear="handleClear"></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(自己的数据自己负责)// 删除功能
// 1. 监听事件(监听删除的点击) 携带id
// 2. 子传父,将任务id传递给父组件App.vue
// 3. 进行删除 filter(自己的数据自己负责)// 底部合计
// 1. 父传子传list
// 2. 数据统计// 清空功能
// 1. 子传父,通知到父组件
// 2. 由父组件进行清空// 持久化存储
// 1. watch深度监视list的变化,往本地存储,进入页面优先读取本地export default {data() {return {list: JSON.parse(localStorage.getItem("list")) || [{ id: 1, name: "打篮球" },{ id: 2, name: "跳绳" },{ id: 3, name: "打羽毛球" },{ id: 4, name: "游泳" },],};},components: {TodoHeader,TodoMain,TodoFooter,},watch: {// 对list进行监视list: {deep: true,handler(newValue) {localStorage.setItem("list", JSON.stringify(newValue));}},},methods: {// 删除handleDel(id) {this.list = this.list.filter((res) => res.id !== id);},// 添加handleAdd(todoName) {// console.log(todoName)this.list.unshift({id: +new Date(),name: todoName,});},// 清空handleClear() {this.list = [];},},
};
</script><style>
</style>
效果:
四、进阶语法
1. v-model原理
原理:v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件的合写。
作用:提供数据的双向绑定
①数据变化,视图跟着变 :value
②视图变,数据跟着变 @input
注意:$event用于在模板中,获取事件的形参
<template><div id="app"><input v-model="msg" type="text"><input :value="msg" @input="msg = $event.target.value" type="text"></div>
</template>
示例:
App.vue
<template><div class="app"><input type="text" v-model="msg1"/><br /><!-- 模板中获取事件的形参 -> $event 获取 --><input type="text" :value="msg2" @input="msg = $event.target.value"></div>
</template><script>
export default {data() {return {msg1: '',msg2: ''}},
}
</script><style>
</style>
效果:
2. v-model应用于组件
表单类组件封装 & v-model简化代码
1. 表单类组件 封装
①父传子:数据应该是父组件 props 传递过来的,v-model 拆解 绑定数据
②子传父:监听输入,子传父传值给父组件修改
App.vue
<template><div class="app"><BaseSelect :cityId="selectId"@changeId="selectId = $event"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data() {return {selectId: '102',}},components: {BaseSelect,},methods: {handleChange(e) {console.log(e);this.selectId = e}}
}
</script><style>
</style>
components/BaseSelect.vue
<template><div><select :value="cityId" @change="handleChange" ><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 {props: {cityId: String},methods: {handleChange(e) {// console.log(e.target.value)this.$emit('changeId', e.target.value)}}
}
</script><style>
</style>
效果:
2. 父组件v-model简化代码,实现子组件和父组件数据双向绑定
①子组件中:props通过value接收,事件触发input
②父组件中:v-model给组件直接绑定数据(:value + @input)
示例:
BaseSelect.vue
<template><div><select :value="value" @change="handleChange" ><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 {props: {value: String},methods: {handleChange(e) {// console.log(e.target.value)this.$emit('input', e.target.value)}}
}
</script><style>
</style>
App.vue
<template><div class="app"><BaseSelect v-model="selectId"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data() {return {selectId: '103',}},components: {BaseSelect,},methods: {handleChange(e) {console.log(e);this.selectId = e}}
}
</script><style>
</style>
效果:
3. .sync修饰符
作用:可以实现子组件和父组件数据的双向绑定,简化代码
特点:prop属性名,可以自定义,非固定为value
场景:封装弹框类的基础组件,visible属性:true显示,false因此
本质:就是 :属性名 和 @update:属性名 合写
示例:
App.vue
<template><div class="app"><button class="logout"@click="isShow = true">退出按钮</button><!-- :visible.sync 等价于 :visible + @update:visible --><BaseDialog :visible.sync="isShow"></BaseDialog></div>
</template><script>
import BaseDialog from "./components/BaseDialog.vue"
export default {data() {return {isShow: false}},methods: {},components: {BaseDialog,},
}
</script><style>
</style>
BaseDialog.vue
<template><div class="base-dialog-wrap" v-show="visible"><div class="base-dialog"><div class="title"><h3>温馨提示:</h3><button class="close" @click="close">x</button></div><div class="content"><p>你确认要退出本系统么?</p></div><div class="footer"><button @click="close">确认</button><button @click="close">取消</button></div></div></div>
</template><script>
export default {props: {visible: Boolean},methods: {close() {this.$emit('update:visible', false)}}
}
</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>
效果:
4. ref 和 $refs / $nextTick
作用:利用ref 和 $refs可以用于获取dom元素,或 组件实例
特点:查找范围 -> 当前组件内(更精确稳定)
①获取dom:
1. 目标标签 — 添加ref属性
<div ref="chartRef">我是渲染图表的容器</div>
2. 恰当时机,通过this.$refs.xxx,获取目标标签
mounted() {console.log(this.$refs.chartRef)
),
示例:
App.vue
<template><div class="app"><div class="base-chart-box">这是一个捣乱的盒子</div><BaseChart></BaseChart></div>
</template><script>
import BaseChart from './components/BaseChart.vue'
export default {components:{BaseChart}
}
</script><style>
.base-chart-box {width: 200px;height: 100px;
}
</style>
BaseChart.vue
<template><div ref="mychart" class="base-chart-box">子组件</div>
</template><script>
import * as echarts from 'echarts'export default {mounted() {// 基于准备好的dom,初始化echarts实例// const myChart = echarts.init(document.querySelector('.base-chart-box'))const myChart = echarts.init(this.$refs.mychart)// console.log(this.$refs.mychart)// 指定图表的配置项和数据const option = {title: {text: 'ECharts 入门示例',},tooltip: {},xAxis: {data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],},yAxis: {},series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20],},],}// 使用刚指定的配置项和数据显示图表myChart.setOption(option)},
}
</script><style scoped>
.base-chart-box {width: 400px;height: 300px;border: 3px solid #000;border-radius: 6px;
}
</style>
效果:
②获取组件:
1.目标组件 — 添加ref属性
<BaseForm ref="baseForm"></BaseForm>
2. 恰当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
示例:
BaseForm.vue
<template><div class="app"><div>账号: <input v-model="username" type="text"></div><div>密码: <input v-model="password" type="text"></div></div>
</template><script>
export default {data() {return {username: 'admin',password: '123456',}},methods: {// 收集表单数据,返回一个对象getValues() {return {username: this.username,password: this.password}},// 重置表单resetValues() {this.username = ''this.password = ''}}
}
</script><style scoped>
.app {border: 2px solid #ccc;padding: 10px;
}
.app div{margin: 10px 0;
}
.app div button{margin-right: 8px;
}
</style>
App.vue
<template><div class="app"><BaseForm ref="baseForm"></BaseForm><button @click="handleGet">获取数据</button><button @click="handleReset">重置数据</button></div>
</template><script>
import BaseForm from './components/BaseForm.vue'
export default {data() {return {}},components: {BaseForm,},methods: {handleGet() {console.log(this.$refs.baseForm.getValues())},handleReset() {this.$refs.baseForm.resetValues()}}
}
</script><style>
</style>
效果:
5. Vue异步更新、$nextTick
$nextTick:等DOM更新后,才会触发执行此方法里的函数体
语法:this.$nextTick(函数体)
this.$nextTick(() => {// 业务逻辑
})
需求:编辑标题,编辑框自动聚焦
1. 点击编辑,显示编辑框
2. 让编辑框,立刻获取焦点
this.isShowEdit = true // 显示输入框
this.$refs.inp.focus() // 获取焦点
问题:”显示之后“,立刻获取焦点是不能成功的!
原因:Vue是异步更新DOM(提升性能)
this.$nextTick(() => {this.$refs.inp.focus()
})
示例代码:
App.vue
<template><div class="app"><div v-if="isShowEdit"><input type="text" v-model="editValue" ref="inp" /><button>确认</button></div><div v-else><span>{{ title }}</span><button @click="handleEdit">编辑</button></div></div>
</template><script>
export default {data() {return {title: '大标题',isShowEdit: false,editValue: '',}},methods: {handleEdit() {// 1. 显示输入框(异步DOM更新)this.isShowEdit = true// 2. 让输入框 获取焦点($nextTick等DOM更新完, 立刻去执行指定的函数体)this.$nextTick(() => {this.$refs.inp.focus()})}},
}
</script><style>
</style>
效果:点击编辑后,显示输入框,并获取焦点