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.mzph.cn/diannao/10721.shtml

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

相关文章

LeetCode 138. 随机链表的复制

目录 1.原题链接&#xff1a; 2.结点拆分&#xff1a; 代码实现&#xff1a; 3.提交结果&#xff1a; 4.读书分享&#xff1a; 1.原题链接&#xff1a; 138. 随机链表的复制 2.结点拆分&#xff1a; ①.拷贝各个结点&#xff0c;连接在原结点后面&#xff1b; ②.处…

【MySQL】基本操作

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;MySQL 目录 &#x1f449;&#x1f3fb;创建和删除数据库&#x1f449;&#x1f3fb;数据库编码集和数据库校验集校验规则对数据库的影响 &#x1f449;&…

【1 bit 翻转+无任何保护】MidnightsunQuals 2021 BroHammer

前言 又是一道非常有意思的题目&#xff0c;其实笔者很喜欢这种跟页表、特权级等相关的题目&#xff08;&#xff1a;虽然大多都无法独立做出来&#xff0c;但是通过这些题目可以学到很多的东西 题目分析 内核版本&#xff1a;v4.17.0smap/smep/kpti/kaslr 全关 题目给了源…

laravel8 导入 excel常见问题

上传xls 或 xlsx 文件后&#xff0c;文件解析为 zip 格式&#xff0c;输入正常情况&#xff0c;不影响解析 里面的内容 遇到解析内容&#xff0c;解析为空的情况&#xff0c;可能是 因为excel 存在多个 Sheet1 造成&#xff0c;服务器不能解析一个 Sheet1 的情况&#xff0…

智慧停车场管理系统主要组成

智慧泊车场办理体系&#xff0c;完成了泊车办理过程中的车辆类型分类、出场时的车牌辨认、行进路线的引导、空余车位诱导&#xff0c;以及准备离场前的反向寻车和方便缴费等全部环节。这六个流程中&#xff0c;泊车场对车辆的办理&#xff0c;进步了泊车场的运行效率&#xff0…

数据分享—中国土壤有机质数据

土壤有机质数据是进行区域土地资源评价&#xff0c;开展自然地理研究常使用的数据&#xff0c;本期推文主要分享全国土壤有机质数据集。梧桐君会不定期分享地理信息数据&#xff0c;欢迎大家长期订阅。 数据来源 “万物土中生”&#xff0c;小编今天要分享的中国土壤有机质数…

牛客NC404 最接近的K个元素【中等 二分查找+双指针 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/b4d7edc45759453e9bc8ab71f0888e0f 知识点 二分查找&#xff1b;找到第一个大于等于x的数的位置idx;然后从idx开始往两边扩展Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、…

可观测性监控

1 目的 常见的监控&#xff0c;主要是以收集数据以识别异常系统效应为主&#xff0c;多是单个服务&#xff0c;相互独立的状态。 可观测性&#xff0c;希望调查异常系统效应的根本原因&#xff0c;能够把多个服务、中间件、容器等串联起来&#xff0c;同时柔和metrics、log、…

vue3.0(六) toRef,toValue,toRefs和toRow,markRaw

文章目录 toReftoValuetoRefstoRowmarkRawtoRef和toRefs的区别toRaw 和markRaw的用处 toRef toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。返回的是一个指向源对象属性的 ref 引用&#xff0c;任何对该引用的修改都会同步到源对象属性上。使用 toRef 时需…

2024年最新趋势跨境电商平台开发需了解的新技术

随着数字化技术的不断演进和全球市场的日益融合&#xff0c;跨境电商平台开发将面临前所未有的挑战和机遇。为了更好地适应并引领这一发展&#xff0c;开发者需要密切关注2024年最新的技术趋势&#xff0c;以确保他们的平台能够在竞争激烈的市场中脱颖而出。本文将对跨境电商平…

HTML/CSS2

1.前置说明 HTML/CSS1 2.img元素 格式&#xff1a; <img src"图片地址" alt"占位文字" width"图片宽度" height"图片高度">其中alt是当图片加载失败时显示的文字 而且不同内核的浏览器显示出来的占位文字的效果也是不尽相同…

网安面经之文件上传漏洞

一、文件上传漏洞 1、文件上传漏洞的原理&#xff1f;危害&#xff1f;修复&#xff1f; 原理&#xff1a;⽂件上传漏洞是发⽣在有上传功能的应⽤中&#xff0c;如果应⽤程序对⽤户上传的⽂件没有控制或者存在缺陷&#xff0c;攻击者可以利⽤应⽤上传功能存在的缺陷&#xff…

从文本日志到图形日志 图形化编程桌面产品的突破

在一个宽敞明亮的会议室里&#xff0c;阳光透过落地窗洒在会议桌上。卧龙和凤雏相对而坐&#xff0c;他们的面前摆放着一些关于图形化编程桌面产品的资料和测试报告。会议室里的气氛紧张而热烈&#xff0c;团队成员们围坐在一起&#xff0c;专注地倾听着卧龙和凤雏的讨论。 卧龙…

单位学校FM调频电台直放站系统

随着教育技术的不断发展&#xff0c;校园广播系统的建设已成为现代学校必不可少的一部分。作为传统有线广播的有效补充&#xff0c;基于无线电信号传输的 FM 调频电台在学校的使用日益广泛&#xff0c;尤其是在紧急通知、日常信息传播及教学辅助等方面发挥着重要作用。为了增强…

韩顺平0基础学Java——第9天

p169-201 数组&#xff08;第六章&#xff09; 数组扩容 此时原来的数组arr被销毁。 牛蛙&#xff1a; 最后再加一句 SYstem。out。println&#xff08;“是否添加&#xff1f;”&#xff09;&#xff1b; char key myscanner。netx&#xff08;&#xff09;。charAT&…

【Python超详细的学习笔记】Python超详细的学习笔记,涉及多个领域,是个很不错的笔记

获取笔记链接 Python超详细的学习笔记 一&#xff0c;逆向加密模块 1&#xff0c;Python中运行JS代码 1.1 解决中文乱码或者报错问题 import subprocess from functools import partial subprocess.Popen partial(subprocess.Popen, encodingutf-8) import execjs1.2 常用…

WordPress插件Plus WebP,可将jpg、png、bmp、gif图片转为WebP

现在很多浏览器和CDN都支持WebP格式的图片了&#xff0c;不过我们以前的WordPress网站使用的图片都是jpg、png、bmp、gif&#xff0c;那么应该如何将它们转换为WebP格式的图片呢&#xff1f;推荐安装这款Plus WebP插件&#xff0c;可以将上传到媒体库的图片转为WebP格式图片&am…

HIVE调优MapJoin

HIVE调优MapJoin 目录 HIVE调优MapJoin 1.mapjoin &#xff08;1.2以后自动默认启动mapjoin&#xff09; 2.创建表格 3.查询建表 4.通过 explain 展示执行计划 5.Map JOIN 相关设置&#xff1a; 1.mapjoin &#xff08;1.2以后自动默认启动mapjoin&#xff09;…

[机器学习-05] Scikit-Learn机器学习工具包进阶指南:协方差估计和交叉分解功能实战【2024最新】

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

设计模式-结构型-桥接模式-Bridge

桥接模式可以减少类的创建 矩阵类 public class Matrix {private String fileName;public Matrix(String fileName) {this.fileName fileName;}public String getFileName() {return fileName;} } 图片抽象类 public abstract class Image {protected ImageImp imp;public …