ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【前端Vue】day04 [打印本页]

作者: 守听    时间: 2025-1-2 04:18
标题: 【前端Vue】day04
一、学习目标

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

​ scoped办理样式冲突/data是一个函数
2.组件通讯

3.综合案例:小黑记事本(组件版)

4.进阶语法

二、scoped办理样式冲突

1.默认情况

写在组件中的样式会 全局生效 → 因此很轻易造成多个组件之间的样式冲突问题。
2.代码演示

BaseOne.vue
  1. <template>
  2.   <div class="base-one">
  3.     BaseOne
  4.   </div>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style scoped>
  11. </style>
复制代码
BaseTwo.vue
  1. <template>
  2.   <div class="base-one">
  3.     BaseTwo
  4.   </div>
  5. </template>
  6. <script>
  7. export default {
  8. }
  9. </script>
  10. <style scoped>
  11. </style>
复制代码
App.vue
  1. <template>
  2.   <div id="app">
  3.     <BaseOne></BaseOne>
  4.     <BaseTwo></BaseTwo>
  5.   </div>
  6. </template>
  7. <script>
  8. import BaseOne from './components/BaseOne'
  9. import BaseTwo from './components/BaseTwo'
  10. export default {
  11.   name: 'App',
  12.   components: {
  13.     BaseOne,
  14.     BaseTwo
  15.   }
  16. }
  17. </script>
复制代码
3.scoped原理

终极结果: 必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到

4.总结

三、data必须是一个函数

1、data为什么要写成函数

一个组件的 data 选项必须是一个函数。目标是为了:保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象。

2.代码演示

BaseCount.vue
  1. <template>
  2.   <div class="base-count">
  3.     <button @click="count--">-</button>
  4.     <span>{{ count }}</span>
  5.     <button @click="count++">+</button>
  6.   </div>
  7. </template>
  8. <script>
  9. export default {
  10.   data: function () {
  11.     return {
  12.       count: 100,
  13.     }
  14.   },
  15. }
  16. </script>
  17. <style>
  18. .base-count {
  19.   margin: 20px;
  20. }
  21. </style>
复制代码
App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseCount></BaseCount>
  4.   </div>
  5. </template>
  6. <script>
  7. import BaseCount from './components/BaseCount'
  8. export default {
  9.   components: {
  10.     BaseCount,
  11.   },
  12. }
  13. </script>
  14. <style>
  15. </style>
复制代码
3.总结

data写成函数的目标是什么?
四、组件通讯

1.什么是组件通讯?

组件通讯,就是指组件与组件之间的数据传递

2.组件之间如何通讯


思考:
3.组件关系分类


4.通讯办理方案


5.父子通讯流程


6.父向子通讯代码示例

父组件通过props将数据传递给子组件
父组件App.vue
  1. <template>
  2.   <div class="app" style="border: 3px solid #000; margin: 10px">
  3.     我是APP组件
  4.     <Son></Son>
  5.   </div>
  6. </template>
  7. <script>
  8. import Son from './components/Son.vue'
  9. export default {
  10.   name: 'App',
  11.   data() {
  12.     return {
  13.       myTitle: '学前端,就来黑马程序员',
  14.     }
  15.   },
  16.   components: {
  17.     Son,
  18.   },
  19. }
  20. </script>
  21. <style>
  22. </style>
复制代码
子组件Son.vue
  1. <template>
  2.   <div class="son" style="border:3px solid #000;margin:10px">
  3.     我是Son组件
  4.   </div>
  5. </template>
  6. <script>
  7. export default {
  8.   name: 'Son-Child',
  9. }
  10. </script>
  11. <style>
  12. </style>
复制代码

父向子传值步骤
7.子向父通讯代码示例

子组件使用 $emit 通知父组件,进行修改更新

子向父传值步骤
8.总结

五、什么是props

1.Props 定义

组件上 注册的一些 自定义属性
2.Props 作用

向子组件传递数据
3.特点


4.代码演示

父组件App.vue
  1. <template>
  2.   <div class="app">
  3.     <UserInfo
  4.       :username="username"
  5.       :age="age"
  6.       :isSingle="isSingle"
  7.       :car="car"
  8.       :hobby="hobby"
  9.     ></UserInfo>
  10.   </div>
  11. </template>
  12. <script>
  13. import UserInfo from './components/UserInfo.vue'
  14. export default {
  15.   data() {
  16.     return {
  17.       username: '小帅',
  18.       age: 28,
  19.       isSingle: true,
  20.       car: {
  21.         brand: '宝马',
  22.       },
  23.       hobby: ['篮球', '足球', '羽毛球'],
  24.     }
  25.   },
  26.   components: {
  27.     UserInfo,
  28.   },
  29. }
  30. </script>
  31. <style>
  32. </style>
复制代码
子组件UserInfo.vue
  1. <template>
  2.   <div class="userinfo">
  3.     <h3>我是个人信息组件</h3>
  4.     <div>姓名:</div>
  5.     <div>年龄:</div>
  6.     <div>是否单身:</div>
  7.     <div>座驾:</div>
  8.     <div>兴趣爱好:</div>
  9.   </div>
  10. </template>
  11. <script>
  12. export default {
  13.   
  14. }
  15. </script>
  16. <style>
  17. .userinfo {
  18.   width: 300px;
  19.   border: 3px solid #000;
  20.   padding: 20px;
  21. }
  22. .userinfo > div {
  23.   margin: 20px 10px;
  24. }
  25. </style>
复制代码
六、props校验

1.思考

组件的props可以乱传吗
2.作用

为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误
3.语法



4.代码演示

App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseProgress :w="width"></BaseProgress>
  4.   </div>
  5. </template>
  6. <script>
  7. import BaseProgress from './components/BaseProgress.vue'
  8. export default {
  9.   data() {
  10.     return {
  11.       width: 30,
  12.     }
  13.   },
  14.   components: {
  15.     BaseProgress,
  16.   },
  17. }
  18. </script>
  19. <style>
  20. </style>
复制代码
BaseProgress.vue
  1. <template>
  2.   <div class="base-progress">
  3.     <div class="inner" :style="{ width: w + '%' }">
  4.       <span>{{ w }}%</span>
  5.     </div>
  6.   </div>
  7. </template>
  8. <script>
  9. export default {
  10.   props: ['w'],
  11. }
  12. </script>
  13. <style scoped>
  14. .base-progress {
  15.   height: 26px;
  16.   width: 400px;
  17.   border-radius: 15px;
  18.   background-color: #272425;
  19.   border: 3px solid #272425;
  20.   box-sizing: border-box;
  21.   margin-bottom: 30px;
  22. }
  23. .inner {
  24.   position: relative;
  25.   background: #379bff;
  26.   border-radius: 15px;
  27.   height: 25px;
  28.   box-sizing: border-box;
  29.   left: -3px;
  30.   top: -2px;
  31. }
  32. .inner span {
  33.   position: absolute;
  34.   right: 0;
  35.   top: 26px;
  36. }
  37. </style>
复制代码
七、props校验完整写法

1.语法

  1. props: {
  2.   校验的属性名: {
  3.     type: 类型,  // Number String Boolean ...
  4.     required: true, // 是否必填
  5.     default: 默认值, // 默认值
  6.     validator (value) {
  7.       // 自定义校验逻辑
  8.       return 是否通过校验
  9.     }
  10.   }
  11. },
复制代码
2.代码实例

  1. <script>
  2. export default {
  3.   // 完整写法(类型、默认值、非空、自定义校验)
  4.   props: {
  5.     w: {
  6.       type: Number,
  7.       //required: true,
  8.       default: 0,
  9.       validator(val) {
  10.         // console.log(val)
  11.         if (val >= 100 || val <= 0) {
  12.           console.error('传入的范围必须是0-100之间')
  13.           return false
  14.         } else {
  15.           return true
  16.         }
  17.       },
  18.     },
  19.   },
  20. }
  21. </script>
复制代码
3.注意

1.default和required一般差别时写(因为当时必填项时,肯定是有值的)
2.default后面如果是简朴类型的值,可以直接写默认。如果是复杂类型的值,则须要以函数的形式return一个默认值
八、props&data、单向数据流

1.共同点

都可以给组件提供数据
2.区别


3.单向数据流:

父级props 的数据更新,会向卑鄙动,影响子组件。这个数据活动是单向的
4.代码演示

App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseCount></BaseCount>
  4.   </div>
  5. </template>
  6. <script>
  7. import BaseCount from './components/BaseCount.vue'
  8. export default {
  9.   components:{
  10.     BaseCount
  11.   },
  12.   data(){
  13.   },
  14. }
  15. </script>
  16. <style>
  17. </style>
复制代码
BaseCount.vue
  1. <template>
  2.   <div class="base-count">
  3.     <button @click="count--">-</button>
  4.     <span>{{ count }}</span>
  5.     <button @click="count++">+</button>
  6.   </div>
  7. </template>
  8. <script>
  9. export default {
  10.   // 1.自己的数据随便修改  (谁的数据 谁负责)
  11.    data () {
  12.      return {
  13.        count: 100,
  14.      }
  15.    },
  16.   // 2.外部传过来的数据 不能随便修改
  17.   //props: {
  18.   //  count: {
  19.   //    type: Number,
  20.   //  },
  21.   //}
  22. }
  23. </script>
  24. <style>
  25. .base-count {
  26.   margin: 20px;
  27. }
  28. </style>
复制代码

5.口诀

谁的数据谁负责
九、综合案例-组件拆分

1.需求说明


2.拆分基础组件

咱们可以把小黑记事本原有的结构拆成三部分内容:头部(TodoHeader)、列表(TodoMain)、底部(TodoFooter)

十、综合案例-列表渲染

思绪分析:
十一、综合案例-添加功能

思绪分析:
十二、综合案例-删除功能

思绪分析:
十三、综合案例-底部功能及长期化存储

思绪分析:
十四、非父子通讯-event bus 变乱总线

1.作用

非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)
2.步骤

3.代码示例

EventBus.js
  1. import Vue from 'vue'
  2. const Bus  =  new Vue()
  3. export default Bus
复制代码
BaseA.vue(接受方)
  1. <template>
  2.   <div class="base-a">
  3.     我是A组件(接收方)
  4.     <p>{{msg}}</p>  
  5.   </div>
  6. </template>
  7. <script>
  8. import Bus from '../utils/EventBus'
  9. export default {
  10.   data() {
  11.     return {
  12.       msg: '',
  13.     }
  14.   },
  15. }
  16. </script>
  17. <style scoped>
  18. .base-a {
  19.   width: 200px;
  20.   height: 200px;
  21.   border: 3px solid #000;
  22.   border-radius: 3px;
  23.   margin: 10px;
  24. }
  25. </style>
复制代码
BaseB.vue(发送方)
  1. <template>
  2.   <div class="base-b">
  3.     <div>我是B组件(发布方)</div>
  4.     <button>发送消息</button>
  5.   </div>
  6. </template>
  7. <script>
  8. import Bus from '../utils/EventBus'
  9. export default {
  10. }
  11. </script>
  12. <style scoped>
  13. .base-b {
  14.   width: 200px;
  15.   height: 200px;
  16.   border: 3px solid #000;
  17.   border-radius: 3px;
  18.   margin: 10px;
  19. }
  20. </style>
复制代码
App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseA></BaseA>
  4.     <BaseB></BaseB>
  5.   </div>
  6. </template>
  7. <script>
  8. import BaseA from './components/BaseA.vue'
  9. import BaseB from './components/BaseB.vue'
  10. export default {
  11.   components:{
  12.     BaseA,
  13.     BaseB
  14.   }
  15. }
  16. </script>
  17. <style>
  18. </style>
复制代码
4.总结

1.非父子组件传值借助什么?
2.什么是变乱总线
3.发送方应该调用变乱总线的哪个方法
4.接收方应该调用变乱总线的哪个方法
5.一个组件发送数据,可不可以被多个组件接收
十五、非父子通讯-provide&inject

1.作用

跨层级共享数据
2.场景


3.语法

  1. export default {
  2.   provide () {
  3.     return {
  4.        // 普通类型【非响应式】
  5.        color: this.color,
  6.        // 复杂类型【响应式】
  7.        userInfo: this.userInfo,
  8.     }
  9.   }
  10. }
复制代码
2.子/孙组件 inject获取数据
  1. export default {
  2.   inject: ['color','userInfo'],
  3.   created () {
  4.     console.log(this.color, this.userInfo)
  5.   }
  6. }
复制代码
4.注意


十六、v-model原理

1.原理:

v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input变乱 的合写
  1. <template>
  2.   <div id="app" >
  3.     <input v-model="msg" type="text">
  4.     <input :value="msg" @input="msg = $event.target.value" type="text">
  5.   </div>
  6. </template>
复制代码
2.作用:

提供数据的双向绑定

3.注意

$event 用于在模板中,获取变乱的形参
4.代码示例

  1. <template>
  2.   <div class="app">
  3.     <input type="text"  />
  4.     <br />
  5.     <input type="text" />
  6.   </div>
  7. </template>
  8. <script>
  9. export default {
  10.   data() {
  11.     return {
  12.       msg1: '',
  13.       msg2: '',
  14.     }
  15.   },
  16. }
  17. </script>
  18. <style>
  19. </style>
复制代码
5.v-model使用在其他表单元素上的原理

差别的表单元素, v-model在底层的处理机制是不一样的。比如给checkbox使用v-model
底层处理的是 checked属性和change变乱。
不过咱们只须要掌握应用在文本框上的原理即可
十七、表单类组件封装

1.需求目标

实现子组件和父组件数据的双向绑定 (实现App.vue中的selectId和子组件选中的数据进行双向绑定)
2.代码演示

App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseSelect></BaseSelect>
  4.   </div>
  5. </template>
  6. <script>
  7. import BaseSelect from './components/BaseSelect.vue'
  8. export default {
  9.   data() {
  10.     return {
  11.       selectId: '102',
  12.     }
  13.   },
  14.   components: {
  15.     BaseSelect,
  16.   },
  17. }
  18. </script>
  19. <style>
  20. </style>
复制代码
BaseSelect.vue
  1. <template>
  2.   <div>
  3.     <select>
  4.       <option value="101">北京</option>
  5.       <option value="102">上海</option>
  6.       <option value="103">武汉</option>
  7.       <option value="104">广州</option>
  8.       <option value="105">深圳</option>
  9.     </select>
  10.   </div>
  11. </template>
  12. <script>
  13. export default {
  14. }
  15. </script>
  16. <style>
  17. </style>
复制代码
十八、v-model简化代码

1.目标:

父组件通过v-model 简化代码,实现子组件和父组件数据 双向绑定
2.如何简化:

v-model其实就是 :value和@input变乱的简写

3.代码示例

子组件
  1. <select :value="value" @change="handleChange">...</select>
  2. props: {
  3.   value: String
  4. },
  5. methods: {
  6.   handleChange (e) {
  7.     this.$emit('input', e.target.value)
  8.   }
  9. }
复制代码
父组件
  1. <BaseSelect v-model="selectId"></BaseSelect>
复制代码
十九、.sync修饰符

1.作用

可以实现 子组件父组件数据双向绑定,简化代码
简朴明白:子组件可以修改父组件传过来的props值
2.场景

封装弹框类的基础组件, visible属性 true体现 false隐蔽
3.本质

.sync修饰符 就是 :属性名@update:属性名 合写
4.语法

父组件
  1. //.sync写法
  2. <BaseDialog :visible.sync="isShow" />
  3. --------------------------------------
  4. //完整写法
  5. <BaseDialog
  6.   :visible="isShow"
  7.   @update:visible="isShow = $event"
  8. />
复制代码
子组件
  1. props: {
  2.   visible: Boolean
  3. },
  4. this.$emit('update:visible', false)
复制代码
5.代码示例

App.vue
  1. <template>
  2.   <div class="app">
  3.     <button @click="openDialog">退出按钮</button>
  4.     <BaseDialog :isShow="isShow"></BaseDialog>
  5.   </div>
  6. </template>
  7. <script>
  8. import BaseDialog from './components/BaseDialog.vue'
  9. export default {
  10.   data() {
  11.     return {
  12.       isShow: false,
  13.     }
  14.   },
  15.   components: {
  16.     BaseDialog,
  17.   },
  18. }
  19. </script>
  20. <style>
  21. </style>
复制代码
BaseDialog.vue
  1. <template>
  2.   <div class="base-dialog-wrap" v-show="isShow">
  3.     <div class="base-dialog">
  4.       <div class="title">
  5.         <h3>温馨提示:</h3>
  6.         <button class="close">x</button>
  7.       </div>
  8.       <div class="content">
  9.         <p>你确认要退出本系统么?</p>
  10.       </div>
  11.       <div class="footer">
  12.         <button>确认</button>
  13.         <button>取消</button>
  14.       </div>
  15.     </div>
  16.   </div>
  17. </template>
  18. <script>
  19. export default {
  20.   props: {
  21.     isShow: Boolean,
  22.   }
  23. }
  24. </script>
  25. <style scoped>
  26. .base-dialog-wrap {
  27.   width: 300px;
  28.   height: 200px;
  29.   box-shadow: 2px 2px 2px 2px #ccc;
  30.   position: fixed;
  31.   left: 50%;
  32.   top: 50%;
  33.   transform: translate(-50%, -50%);
  34.   padding: 0 10px;
  35. }
  36. .base-dialog .title {
  37.   display: flex;
  38.   justify-content: space-between;
  39.   align-items: center;
  40.   border-bottom: 2px solid #000;
  41. }
  42. .base-dialog .content {
  43.   margin-top: 38px;
  44. }
  45. .base-dialog .title .close {
  46.   width: 20px;
  47.   height: 20px;
  48.   cursor: pointer;
  49.   line-height: 10px;
  50. }
  51. .footer {
  52.   display: flex;
  53.   justify-content: flex-end;
  54.   margin-top: 26px;
  55. }
  56. .footer button {
  57.   width: 80px;
  58.   height: 40px;
  59. }
  60. .footer button:nth-child(1) {
  61.   margin-right: 10px;
  62.   cursor: pointer;
  63. }
  64. </style>
复制代码
6.总结

1.父组件如果想让子组件修改传已往的值 必须加什么修饰符?
2.子组件要修改父组件的props值 必须使用什么语法?
二十、ref和$refs

1.作用

使用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例
2.特点:

查找范围 → 当前组件内(更精确稳定)
3.语法

1.给要获取的盒子添加ref属性
  1. <div ref="chartRef">我是渲染图表的容器</div>
复制代码
2.获取时通过 $refs获取 this.$refs.chartRef 获取
  1. mounted () {
  2.   console.log(this.$refs.chartRef)
  3. }
复制代码
4.注意

之前只用document.querySelect(‘.box’) 获取的是整个页面中的盒子
5.代码示例

App.vue
  1. <template>
  2.   <div class="app">
  3.     <BaseChart></BaseChart>
  4.   </div>
  5. </template>
  6. <script>
  7. import BaseChart from './components/BaseChart.vue'
  8. export default {
  9.   components:{
  10.     BaseChart
  11.   }
  12. }
  13. </script>
  14. <style>
  15. </style>
复制代码
BaseChart.vue
  1. <template>
  2.   <div class="base-chart-box" ref="baseChartBox">子组件</div>
  3. </template>
  4. <script>
  5. // yarn add echarts 或者 npm i echarts
  6. import * as echarts from 'echarts'
  7. export default {
  8.   mounted() {
  9.     // 基于准备好的dom,初始化echarts实例
  10.     var myChart = echarts.init(document.querySelect('.base-chart-box'))
  11.     // 绘制图表
  12.     myChart.setOption({
  13.       title: {
  14.         text: 'ECharts 入门示例',
  15.       },
  16.       tooltip: {},
  17.       xAxis: {
  18.         data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
  19.       },
  20.       yAxis: {},
  21.       series: [
  22.         {
  23.           name: '销量',
  24.           type: 'bar',
  25.           data: [5, 20, 36, 10, 10, 20],
  26.         },
  27.       ],
  28.     })
  29.   },
  30. }
  31. </script>
  32. <style scoped>
  33. .base-chart-box {
  34.   width: 400px;
  35.   height: 300px;
  36.   border: 3px solid #000;
  37.   border-radius: 6px;
  38. }
  39. </style>
复制代码
二十一、异步更新 & $nextTick

1.需求

编辑标题, 编辑框自动聚焦

2.代码实现

  1. <template>
  2.   <div class="app">
  3.     <div v-if="isShowEdit">
  4.       <input type="text" v-model="editValue" ref="inp" />
  5.       <button>确认</button>
  6.     </div>
  7.     <div v-else>
  8.       <span>{{ title }}</span>
  9.       <button @click="editFn">编辑</button>
  10.     </div>
  11.   </div>
  12. </template>
  13. <script>
  14. export default {
  15.   data() {
  16.     return {
  17.       title: '大标题',
  18.       isShowEdit: false,
  19.       editValue: '',
  20.     }
  21.   },
  22.   methods: {
  23.     editFn() {
  24.         // 显示输入框
  25.         this.isShowEdit = true  
  26.         // 获取焦点
  27.         this.$refs.inp.focus()
  28.     }  },
  29. }
  30. </script>
复制代码
3.问题

“体现之后”,立刻获取焦点是不能成功的!
缘故原由:Vue 是异步更新DOM (提升性能)
4.办理方案

$nextTick:等 DOM更新后,才会触发执行此方法里的函数体
语法: this.$nextTick(函数体)
  1. this.$nextTick(() => {
  2.   this.$refs.inp.focus()
  3. })
复制代码
注意:$nextTick 内的函数体 肯定是箭头函数,这样才气让函数内部的this指向Vue实例

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4