Vue从入门到实战Day04

news2024/11/17 4:38:30

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

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.coloradmin.cn/o/1662880.html

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

相关文章

电脑开机显示器不亮?3大原因及解决办法

电脑开机后&#xff0c;显示器不亮是一个常见的问题&#xff0c;可能会给用户带来困扰。然而&#xff0c;这个问题通常并不复杂&#xff0c;可以通过一些简单的方法来解决。在本文中&#xff0c;我们将介绍三种常见的方法&#xff0c;帮助您解决电脑开机显示器不亮的情况。这些…

AI作画涉及的深度学习算法

AI作画中使用的深度学习算法多种多样&#xff0c;这些算法主要基于神经网络的结构和训练方式&#xff0c;以生成和改进艺术作品。以下是一些在AI作画中常用的深度学习算法&#xff1a; 生成对抗网络&#xff08;GANs, Generative Adversarial Networks&#xff09;&#xff1a…

vue3 antd-vue 超简单方式实现a-table跨页勾选

一、效果如下&#xff1a; 第一页勾选了2&#xff0c; 3&#xff0c; 4 翻到第三页勾选24&#xff0c; 25 回显&#xff0c;如比返回第一页的时候触发分页改变&#xff0c; 在映射中的第一页的数据给到a-table绑定的state.selectedRowKeys即可&#xff0c;如下方法 二、勾选思路…

【深度学习】Diffusion扩散模型原理解析1

1、前言 diffusion&#xff0c;这几年一直很火的模型&#xff0c;比如这段时间在网上的文生图大模型——Stable diffusion。就是以diffusion作为基底模型&#xff0c;但由于该模型与VAE那边&#xff0c;都涉及了较多了概率论知识&#xff0c;实在让人望而却步。所以&#xff0…

idea-自我快捷键-2

1. 书签 创建书签&#xff1a; 创建书签&#xff1a;F11创建特色标记书签&#xff1a;Ctrl F11快速添加助记符书签&#xff1a;ctrl shift 数字键 查看书签&#xff1a; shift F11快速定位到助记符书签&#xff1a;Ctrl 数字键 删除书签&#xff1a; delete 2. 自动…

Android使用Chaquo来运行Python的librosa的相关代码【有详细案例教程】

在某些情况下&#xff0c;我们可能需要在android上运行python的代码&#xff0c;那么常见的解释器有很多&#xff0c;目前比较成熟的就是chaquo&#xff0c;它适配的第三方机器学习的库很多&#xff0c;下面是它的简单使用教程 1.环境的搭建 1.1 在Android studio中新建安卓工…

SpringSecurity + JWT实现登录认证

前置基础请参考&#xff1a;SpringSecurity入门-CSDN博客 配置&#xff1a; pom.xml <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.5</version></p…

网络1--通信过程的理解

1.封装与解包 通信的过程就是不断的封装和解包的过程 封装即就是按照“应用”“传输” “网络” “链路” 层&#xff0c;封装给每一层都加上相应的包头&#xff08;每一层都有协议&#xff0c;&#xff09;解包就是接受到的包文被一层层去掉相对应的包头。 任何一层的协议都…

【Qt】界面定制艺术:光标(cursor)、字体(font)、提示(toolTip)、焦点(focusPolicy)与样式表(styleSheet)的深度探索

文章目录 前言&#xff1a;1. cursor: 设置按钮的光标2. front&#xff1a;设置字体3. toolTip: 鼠标悬停提示4. focusPolicy&#xff1a;设置控件获取到焦点的策略5. styleSheet : 样式表总结&#xff1a; 前言&#xff1a; 在现代软件开发中&#xff0c;用户界面(UI)的设计和…

记录一些常用的网站

开发者搜索-Beta-让技术搜索更简单高效 (baidu.com)https://kaifa.baidu.com/JSON在线 | JSON解析格式化—SO JSON在线工具https://www.sojson.com/DCloud 插件市场https://ext.dcloud.net.cn/uni-app官网 (dcloud.net.cn)https://uniapp.dcloud.net.cn/在线学习书籍https://gi…

C++学习第三十课:C++异常处理机制应用

一、引言 在编程过程中&#xff0c;我们经常会遇到一些无法预见或难以预料的特殊情况&#xff0c;这些情况统称为“异常”。异常可能是由用户输入错误、资源不足、硬件故障或程序逻辑错误等原因引起的。当这些异常情况发生时&#xff0c;如果程序没有适当的处理机制&#xff0…

MySQL数据库表的创建DDL语句(21-25)

21.用户反馈表 CREATE TABLE 7_feedback (feedbackId int(11) NOT NULL AUTO_INCREMENT COMMENT ID,feedbackType int(4) NOT NULL DEFAULT 0 COMMENT 反馈类型&#xff0c;内容来自源系统基础数据表,userId int(11) DEFAULT NULL COMMENT 反馈者ID,creatTime datetime NOT NU…

商场综合体能源监管平台,实现能源高效管理

商场作为大型综合体建筑&#xff0c;其能源消耗一直是备受关注的问题。为了有效管理商场能耗&#xff0c;提高商场能源效率&#xff0c;商场综合体能源监管平台应运而生。 商场综合体能源监管平台可通过软硬件一起进行节能监管&#xff0c;硬件设备包括各种传感器、监测仪表和…

ArcGIS10.2系列许可到期解决方案

本文手机码字&#xff0c;不排版了。 昨晚&#xff08;2021\12\17&#xff09;12点后&#xff0c;收到很多学员反馈 ArcGIS10.2系列软件突然崩溃。更有的&#xff0c;今天全单位崩溃。 ​ 提示许可15天内到期。之前大部分许可是到2021年1月1日的。 ​ 后续的版本许可都是永久的…

哪些企业需要用OV 证书?怎么获取OV证书?看这里

OV证书相当于DV证书而言&#xff0c;其安全等级高&#xff0c;兼容性强&#xff0c;稳定性好&#xff0c;那么哪些企业适用OV证书呢&#xff1f; 1 政府与公共服务网站 政府机构及提供公共服务的网站&#xff0c;必须确保数据的隐私和安全&#xff0c;有助于增强公众对在线服务…

台服dnf局域网搭建,学习用笔记

台服dnf局域网搭建 前置条件虚拟机初始化上传安装脚本以及其他文件至虚拟机密钥publickey.pem客户端配置如果IP地址填写有误&#xff0c;批量修改IP地址 前置条件 安装有vmvarecentos7.6镜像&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/centos-vault/7.6.1810/isos/x86…

拥有蝴蝶效应的爬虫如何进行防护

美国气象学家爱德华罗伦兹&#xff08;Edward N.Lorenz&#xff09;1963年在一篇提交纽约科学院的论文中分析了一个叫做蝴蝶效应的理论&#xff1a;“一个气象学家提及&#xff0c;如果这个理论被证明正确&#xff0c;一只海鸥扇动翅膀足以永远改变天气变化。”在以后的演讲和论…

输出正射图时,分辨率怎么填写整幅输出?

答&#xff1a;设置完输出路径、分辨率、坐标系后&#xff0c;会给图像宽高&#xff0c;根据最大值设置分幅尺寸就可以。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景…

红龙工业设备制造有限公司亮相2024杭州数字物流技术设备展

参展企业介绍 温州红龙工业设备制造有限公司成立于2015年11月。是中国先进的工业皮带设备研发制造和工业皮带整体解决方案运营服务商&#xff0c;现主营皮带接头机、皮带热压机、皮带接驳机、皮带打齿机、输送带打齿机、输送带分层级、输送带导条机、输送带裁切机、高频机等工业…

前端开发指导

前端开发指导 本文介绍了配置前端开发环境需要的软件、配置项等,指导如何开始进行UDM部门前端开发的全流程。本文以Windows系统下在Microsoft Virtual Studio Code中开发为基础。 一、综述 目标:零基础或者新员工依照此文档,能够完成开发环境的搭建及熟悉测试环境的搭建。…