当前位置: 首页 > news >正文

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>

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

相关文章:

Vue从入门到实战Day04

一、组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09; 1. scoped样式冲突 默认情况&#xff1a;写在组件中的样式会全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。 1. 全局样式&#xff1a;默认组件中的样式会作用到全局 2. 局部样式&#xff1a;可以…...

Linux学习笔记:信号

信号 在Linux中什么是信号信号的产生方式硬件产生的信号软件产生的信号异常产生的信号 进程对信号的处理信号的保存信号方法更改函数signal信号处理的更改恢复默认信号忽略 信号的管理信号集 sigset_t对信号集的操作 信号的捕捉过程 在Linux中什么是信号 在 Linux 系统中&…...

C#中的隐式类型转换和显式类型转换

在C#中&#xff0c;类型转换分为隐式类型转换&#xff08;Implicit Type Conversion&#xff09;和显式类型转换&#xff08;Explicit Type Conversion&#xff09;&#xff0c;也称为隐式转换和强制转换。 隐式类型转换&#xff08;Implicit Type Conversion&#xff09; 隐…...

linux上如何排查JVM内存过高?

在Linux上排查JVM内存过高的问题&#xff0c;可以采用以下几种方法&#xff1a; 1. **使用top命令查看进程**&#xff1a;通过top命令可以观察到系统中资源占用情况&#xff0c;包括CPU和内存。当收到内存过高的报警时&#xff0c;可以使用top命令来查看是哪个进程的内存使用率…...

第四届上海理工大学程序设计全国挑战赛 J.上学 题解 DFS 容斥

上学 题目描述 usst 小学里有 n 名学生&#xff0c;他们分别居住在 n 个地点&#xff0c;第 i 名学生居住在第 i 个地点&#xff0c;这些地点由 n−1 条双向道路连接&#xff0c;保证任意两个地点之间可以通过若干条双向道路抵达。学校则位于另外的第 0 个地点&#xff0c;第…...

word-排版文本基本格式

1、文本的基本格式&#xff1a;字体格式、段落格式 2、段落&#xff1a;word排版的基本控制单位 3、每敲一次回车&#xff0c;为一个段落标记&#xff0c;注意区分换行符和段落标记&#xff0c;换行符为指向下的箭头&#xff0c;段落标记为带拐弯的箭头&#xff0c;换行符&…...

目标检测YOLO实战应用案例100讲-无监督领域自适应目标检测方法研究与应用(五)

目录 多源无监督领域自适应目标检测方法 4.1研究现状及问题形成 4.2相关工作详述...

通过python实现Google的精准搜索

问题背景&#xff1a; 我想通过Google或者其他网站通过精准搜索确认该产品是否存在&#xff0c;但是即使该产品不存在Google也会返回一些相关的url链接&#xff0c;现在想通过python实现搜索结果的精准匹配以确认该产品是否为正确的名称【可以通过google搜索到&#xff0c;如果…...

Nios-II编程入门实验

文章目录 一 Verilog实现流水灯二 Nios实现流水灯2.1 创建项目2.2 SOPC添加模块2.3 SOPC输入输出连接2.4 Generate2.5 软件部分2.6 运行结果 三 Verilog实现串口3.1 代码3.2 引脚3.3 效果 四 Nios2实现串口4.1 sopc硬件设计4.2 top文件4.3 软件代码4.4 实现效果 五 参考资料六 …...

从0开始学python(七)

目录 前言 1 break、continue和pass函数 1.1 break 1.2 continue 1.3 pass 2、序列的索引及切片操作 2.1字符串的索引和切片 2.1.1 字符串索引 2.1.2 字符串切片 总结 前言 上一篇文章我们介绍了python中的循环结构&#xff0c;包括for和while的使用。本章接着往下讲。…...

【二叉树算法题记录】404. 左叶子之和

题目描述 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 题目分析 其实这题无论是迭代法还是递归法&#xff0c;最重要的是要明确判断左叶子的条件&#xff1a;当前节点有左孩子&#xff0c;且这个左孩子没有它的左孩子和右孩子。 迭代法 感觉只要二叉树相关…...

面试集中营—Spring篇

Spring 框架的好处 1、轻量&#xff1a;spring是轻量的&#xff0c;基本的版本大约2MB&#xff1b; 2、IOC&#xff1a;控制反转&#xff0c;Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了&#xff0c;而是由容易来控制&#xff0c;一个字&#xff1a;爽&#xf…...

Lia 原理

训练阶段 论文流程&#xff1a; 具体实现&#xff1a; 通过latent space传递运动信息,实现分两部分。 1&#xff09;image space->latent space 将源图像映射到隐空间编码。X_s (source image )映射到编码Z_sr&#xff0c;通过W_rd方向上的变化&#xff0c;得到新的编码Z…...

文本批量操作技巧:内容查找不再繁琐,自动化批量移动至指定文件夹

在文本处理和信息管理的日常工作中&#xff0c;我们经常需要处理大量的文件和数据。面对这些海量的信息&#xff0c;如何快速而准确地查找特定的内容&#xff0c;并将它们批量移动至指定的文件夹&#xff0c;成为了一项关键的技能。本文将介绍办公提效工具一些实用的文本批量操…...

[数据结构]动画详解单链表

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到动画详解数据结构系列 用通俗易懂的动画的动画使数据结构可视化 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低…...

图片批量管理迈入智能新时代:一键输入关键词,自动生成并保存惊艳图片,轻松开启创意之旅!

在数字化时代&#xff0c;图片已成为我们表达创意、记录生活、传递信息的重要工具。然而&#xff0c;随着图片数量的不断增加&#xff0c;如何高效、便捷地管理这些图片&#xff0c;却成为了一个令人头疼的问题。 第一步&#xff0c;进入首助编辑高手主页面&#xff0c;在上方…...

【硬件模块】ESP-01SWiFi模块基于AT指令详解(WiFi,TCP/IP,MQTT)

ESP-01S ESP-01S是由安信可科技开发的一款Wi-Fi模块。其核心处理器是ESP8266&#xff0c;该处理器在较小尺寸的封装中集成了业界领先的Tensilica L106超低功耗32位微型MCU&#xff0c;带有16位精简模式&#xff0c;主频支持80MHz和160MHz&#xff0c;并集成了Wi-Fi MAC/BB/RF/P…...

数据结构之单单单——链表

目录 一.链表 1&#xff09;链表的概念 2&#xff09;链表的结构 二.单链表的实现 三.链表的分类 1&#xff09;单向或者双向 2&#xff09;带头或不带头 3&#xff09;循环或非循环 一.链表 1&#xff09;链表的概念 链表&#xff08;Linked List&#xff09;是一种…...

【Linux笔记】 基础指令(二)

风住尘香花已尽 日晚倦梳头 重命名、剪切指令 -- mv 简介&#xff1a; mv 命令是 move 的缩写&#xff0c;可以用来移动文件或者将文件改名&#xff0c;是 Linux 系统下常用的命令&#xff0c;经常用来备份文件或者目录 语法&#xff1a; mv [选项] 源文件或目录 目标文件或目录…...

软件全套资料梳理(需求、开发、实施、运维、安全、测试、交付、认证、评审、投标等)

软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...