Vue从入门到实战Day04

一、组件的三大组成部分(结构/样式/逻辑)

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>

效果:点击编辑后,显示输入框,并获取焦点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/691070.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

权益资本成本-CAPM模型、MPEG模型、OJ模型、PEG模型原始数据及其代码(2000-2021年)

01、数据介绍 权益资本成本是指企业的所有者投入企业资金的成本&#xff0c;具体来说&#xff0c;它代表了股东对投资风险的补偿要求&#xff0c;也可以理解为股东对于企业未来收益的预期。 权益资本成本是筹集普通股资金所需的成本&#xff0c;它也是企业进行投资决策的重要…

璩静是为了薅百度羊毛

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 百度副总裁璩静离职了&#xff0c;网传她的年薪是1500万&#xff0c;而璩静在4月24日注册了一个文化传媒公司&#xff0c;大家都认为璩静是在为离职做准备。但松松我认为不是。 我认为&#xff1a;璩静成立新公司是…

前端 | 易混词卡片切换

文章目录 &#x1f4da;实现效果&#x1f4da;模块实现解析&#x1f407;html&#x1f407;css&#x1f407;javascript &#x1f4da;实现效果 绘制单词卡片效果&#xff0c;实现点击左半部分上翻&#xff0c;点击右半部分下翻。 &#x1f4da;模块实现解析 &#x1f407;…

0基础理解ECC并做题-攻防世界easy-ECC理解

基点p就是最初选定的那个点 1和2都是整数集合&#xff0c;但是1/20.5就不属于整数集合 一直加&#xff0c;一直乘&#xff0c;还能保证有限个数字&#xff1f;这是因为采用了取模的运算&#xff0c;让元素始终都在有限的范围内。 如何计算分数求模&#xff1f; 设n1/2mod23,那么…

示例九、红外接收模块

通过以下几个示例来具体展开学习,了解红外接收模块原理及特性&#xff0c;学习红外接收模块的应用&#xff08;干货版&#xff09;&#xff1a; 示例九、红外接收模块 ino文件源码&#xff1a; //Arduino C demoIRrecv irrecv(4); decode_results results; unsigned long key…

Virtualbox7.0.10+Ubuntu20.04网络配置

虚拟机部署在服务器上时&#xff0c;需要进行网络配置&#xff0c;使虚拟机和服务器在同网段下&#xff0c;以保证内网的终端可以访问到虚拟机 1. 设置虚拟机 打开虚拟机设置&#xff0c;选择“网络”&#xff0c;将网卡设为桥接网卡 注&#xff1a;设置前&#xff0c;需要先…

【JVM】Class文件的格式

目录 概述 Class文件的格式 概述 Class文件是JVM的输入&#xff0c;Java虚拟机规范中定义了Class文件的结构。Class文件是JVM实现平台无关、技术无关的基础。 1:Class文件是一组以8字节为单位的字节流&#xff0c;各个数据项目按顺序紧凑排列 2:对于占用空间大于8字节的数据…

【Qt 学习笔记】Qt常用控件 | 多元素控件 | Tree Widget的说明及介绍

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 多元素控件 | Tree Widget的说明及介绍 文章编号&#x…

直播预告-如何快乐学习亚马逊云科技AWS,玩游戏备考亚马逊云科技云从业者认证?

一边玩一边学习亚马逊云科技云技能&#xff0c;这么好的事尊的假的&#xff1f;本周六&#xff08;5约11日&#xff09;晚20点&#xff0c;亚马逊云科技UG云端夜话Night Talk活动精彩回归&#xff5e; 本次亚马逊云科技UG云端夜话直播是什么&#xff1f; 小李哥这次将在多平台…

计算机毕业设计Python地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Flink Hadoop 深度学习 机器学习 人工智能 知识图谱

学生信息 姓名&#xff1a;  祁浩 题目&#xff1a; 基于Python的中国地震数据分析与可视化系统的设计与实现 学号&#xff1a; 2020135211 班级&#xff1a; 20大数据本科2班 指导教师&#xff1a; 刘思思 答辩过程 学生开题陈述 为了让学习者更好的了解了解地震…

最新巨量X-Bogus、_signature参数逆向分析与算法还原

文章目录 1. 写在前面2. 接口分析3. 断点分析4. 扣代码补环境5. 数据解密 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路…

Github 2024-05-12 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目5Python项目2非开发语言项目2Vue项目1Rust项目1AFFiNE: 下一代知识库 创建周期:649 天开发语言:TypeScript协议类型:OtherSta…