Vue2基础知识(三) 组件化

news2024/12/30 2:20:30

在这里插入图片描述

目录

  • 一 组件
    • 1.1 组件的定义
    • 1.2 特点
    • 1.3 Vue-extend
    • 1.4 VueCompent
  • 二 脚手架
    • 2.1 安装
    • 2.2 结构目录
    • 2.3 Render函数
    • 2.4 修改默认配置
    • 2.5 Ref 属性
    • 2.6 Prop 属性
    • 2.7 Mixin 属性
    • 2.8 插件
    • 2.9 Scoped
  • 三 组件
    • 3.1 组件的注册
      • 3.1.1 局部注册
      • 3.1.2 全局注册
    • 3.2 组件的通信
      • 3.2.1 父子关系通信
      • 3.2.2 Prop 详解
      • 3.2.3 非父子组件通信
    • 3.3 组件的其他知识
      • 3.3.1 .sync 修饰符
      • 3.3.2 Vue异步更新
      • 3.3.3 $nextTick()

  • 💌 所属专栏:【Vue2】
  • 😀 作 者:长安不及十里
  • 💻工作:目前从事电力行业开发
  • 🌈目标:全栈开发
  • 🚀 个人简介:一个正在努力学技术的Java工程师,专注基础和实战分享 ,欢迎咨询!
  • 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘
  • 📌 格言:把戏把戏要过手

  • 📏 官网:https://v2.cn.vuejs.org
  • ⛳ 参考教程:https://www.bilibili.com/video/BV1HV4y1a7n4
  • 🔧 Vue脚手架:https://cli.vuejs.org/zh
  • 🔧 VueRouter:https://router.vuejs.org/zh
  • 🔧 VueX:https://vuex.vuejs.org/zh

一 组件

参考官网:Vue.js
image.png

1.1 组件的定义

  • 官方定义:组件(Component)是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。
  • 组件机制的设计,可以让开发者把一个复杂的应用分割成一个个功能独立组件,降低开发的难度的同时,也提供了极好的复用性和可维护性,组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。
  • 组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

1.2 特点

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一。
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用。

1.3 Vue-extend

参考官网:API — Vue.js
使用基础 Vue 构造器,创建一个子类,数是一个包含组件选项的对象。
简单来说,就是创建一个新的组件,也就是我们说的局部注册一个组件

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件的定义</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>

      <body>
      <div id="app">
      <my-component></my-component>
      <my-components></my-components>

      </div>
      <script>
      // 注册一个组件,全局注册
      Vue.component('my-component', {
        template: '<div>这是一个组件</div>',
        // 注意:zai组件中,data必须是一个函数,而不能直接是一个对象
        data() {
          return {
            name: 'shu'
          }
        },
        // methods
        methods: {
          sayHi() {
            console.log('hi');
          }
        },
        // computed
        computed: {
          sayHello() {
            return 'hello'
          }
        },
        // 过滤器
        filters: {
          sayGoodbye() {
            return 'goodbye'
          }
        },
      })
      // 注册一个局部组件
      const MyComponent = Vue.extend({
        data() {
          return {
            message: 'Hello, World!'
          }
        },
        template: '<div>{{ message }}</div>'
      })


      // 创建一个根实例
      var app = new Vue({
        el: '#app',
        components: {
          'my-components': MyComponent
        },

      })
      // 原型链
      Vue.prototype.$myMixin = {
        created() {
          console.log('Hello from $myMixin!')
        }
      }


      // 打印原型链
      console.log('@', MyComponent.prototype.__proto__);
      // 打印Vue原型链
      console.log('@', Vue.prototype);
      // 总结:组件的定义,有两种方式,一种是全局注册,一种是局部注册,
      // 全局注册:Vue.component('my-component', {template: '<div>这是一个组件</div>'})
      // 局部注册:const MyComponent = Vue.extend({template: '<div>这是一个组件</div>'})
      // 组件是一个独立的可复用的Vue实例,它有自己的data、methods、computed、watch、生命周期钩子等
    </script>

🌈总结

  1. 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
  2. 全局注册:Vue.component(‘my-component’, {template: ‘
    这是一个组件
    ’})
  3. 局部注册:const MyComponent = Vue.extend({template: ‘
    这是一个组件
    ’})

image.png

1.4 VueCompent

  • 组件其实是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend自动生成的
  • 每写一个组件Vue会通过Vue.extend生成一个全新的VueComponent,写一个school组件,新生成一个VueComponent,当我们再写一个student组件时,又会重新生成一个全新的VueComponent,注意:这里只是针对于非单文件组件。

🌈Vue与VueComponent的关系
显示原型(prototype)与隐式原型(proto):

  • 函数的prototype属性:在定义函数时自动添加的,默认值时一个空Object对象
  • 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性

访问一个对象属性时:

  • 先在自身属性中查找,找到返回
  • 如果没有,再沿着 __proto__这条链向上查找,找到返回
  • 如果最终没有找到,返回undefined

Object原型对象是原型链的尽头(proto=null)
image.png
核心重点: Vue强制更改了VueComponent的原型对象指向Object的原型对象的隐式链,将其改到指向Vue的原型对象上。

二 脚手架

参考官网:Vue CLI

2.1 安装

Node 版本要求
Vue CLI 4.x 需要 Node.js v8.9 或更高版本 (推荐 v10 以上)。你可以使用 n,nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。

  • 安装
npm install -g @vue/cli
# OR
yarn global add @vue/cli
  • 校验
vue --version

image.png

  • 命令创建一个项目
用法:create [options] <app-name>

创建一个由 `vue-cli-service` 提供支持的新项目


选项:

  -p, --preset <presetName>       忽略提示符并使用已保存的或远程的预设选项
  -d, --default                   忽略提示符并使用默认预设选项
  -i, --inlinePreset <json>       忽略提示符并使用内联的 JSON 字符串预设选项
  -m, --packageManager <command>  在安装依赖时使用指定的 npm 客户端
  -r, --registry <url>            在安装依赖时使用指定的 npm registry
  -g, --git [message]             强制 / 跳过 git 初始化,并可选的指定初始化提交信息
  -n, --no-git                    跳过 git 初始化
  -f, --force                     覆写目标目录可能存在的配置
  -c, --clone                     使用 git clone 获取远程预设选项
  -x, --proxy                     使用指定的代理创建项目
  -b, --bare                      创建项目时省略默认组件中的新手指导信息
  -h, --help                      输出使用帮助信息
  • 图像化创建、
vue ui

上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。

  • 案例:

image.png
image.png

2.2 结构目录

├── build --------------------------------- 项目构建(webpack)相关配置文件,配置参数什么的,一般不用动 
│   ├── build.js --------------------------webpack打包配置文件
│   ├── check-versions.js ------------------------------ 检查npm,nodejs版本
│   ├── dev-client.js ---------------------------------- 设置环境
│   ├── dev-server.js ---------------------------------- 创建express服务器,配置中间件,启动可热重载的服务器,用于开发项目
│   ├── utils.js --------------------------------------- 配置资源路径,配置css加载器
│   ├── vue-loader.conf.js ----------------------------- 配置css加载器等
│   ├── webpack.base.conf.js --------------------------- webpack基本配置
│   ├── webpack.dev.conf.js ---------------------------- 用于开发的webpack设置
│   ├── webpack.prod.conf.js --------------------------- 用于打包的webpack设置
├── config ---------------------------------- 配置目录,包括端口号等。我们初学可以使用默认的。
│   ├── dev.env.js -------------------------- 开发环境变量
│   ├── index.js ---------------------------- 项目配置文件
│   ├── prod.env.js ------------------------- 生产环境变量
│   ├── test.env.js ------------------------- 测试环境变量
├── node_modules ---------------------------- npm 加载的项目依赖模块
├── src ------------------------------------- 我们要开发的目录,基本上要做的事情都在这个目录里。
│   ├── assets ------------------------------ 静态文件,放置一些图片,如logo等
│   ├── components -------------------------- 组件目录,存放组件文件,可以不用。
│   ├── main.js ----------------------------- 主js
│   ├── App.vue ----------------------------- 项目入口组件,我们也可以直接将组件写这里,而不使用 components 目录。
│   ├── router ------------------------------ 路由
├── static ---------------------------- 静态资源目录,如图片、字体等。
├── .babelrc--------------------------------- babel配置文件
├── .editorconfig---------------------------- 编辑器配置
├── .gitignore------------------------------- 配置git可忽略的文件
├── index.html ------------------------------ 	首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
├── package.json ---------------------------- node配置文件,记载着一些命令和依赖还有简要的项目描述信息 
├── .README.md------------------------------- 项目的说明文档,markdown 格式。想怎么写怎么写,不会写就参照github上star多的项目,看人家怎么写的

2.3 Render函数

参考官网:API — Vue.js

  • 简单的说,在vue中我们使用模板HTML语法组建页面的,使用render函数我们可以用js语言来构建DOM。 因为vue是虚拟DOM,所以在拿到template模板时也要转译成VNode的函数,而用render函数构建DOM,vue就免去了转译的过程。
  • render 函数即渲染函数,它接收一个createElement 方法作为第一个参数用来创建 VNode。(简单的说就是 render函数的参数也是一个函数)
/*
* render: 渲染函数
* 参数: createElement
* 参数类型: Function
*/
render: function (createElement) {}

createElement也是一个函数,它接受三个参数

  • 【必填】一个 HTML 标签名、组件选项对象,或者resolve 了上述任何一种的一个 async 函数。类型:{String | Object | Function}
  • 【可选】一个与模板中 attribute 对应的数据对象。 类型:{Object}
  • 【可选】子级虚拟节点 (VNodes) 类型:{String | Array}

示例:

//模板写法
 <div id="demo" style="color: #ff0000" @click="handleClick">
     Hello Vue!
 </div>

//渲染函数写法
render: function (createElement) {
      return createElement('div', {
        attrs: {
          id: 'demo'
        },
        //给div绑定样式
        style:{
          color: '#ff0000'
        },
        //给div绑定点击事件 
        on: {
          click: this.handleClick
        }
      }, 'Hello Vue!')
 },

将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,当然我们也可以看到一个页面在组成结构:结构+逻辑+样式
image.png

2.4 修改默认配置

参考官网:配置参考 | Vue CLI
就是Vue.config.js的配置
image.png

2.5 Ref 属性

image.png
Vue中的ref属性用于在模板或组件中给某个元素或组件注册一个唯一标识符。这个标识符可以被用来访问这个元素或组件的实例或属性。ref可以绑定到DOM元素、组件或是子组件上。

<template>
  <div>
    <input ref="myInput" type="text">
    <button @click="focusInput">Focus Input</button>
  </div>
</template>

<script>
export default {
  methods: {
    focusInput() {
      this.$refs.myInput.focus()
    }
  }
}
</script>
  • 我们在input元素上使用ref属性来注册一个名为myInput的标识符,然后在focusInput方法中使用this.$refs.myInput来访问该元素的实例,并调用focus()方法聚焦该元素。
  • 需要注意的是,$refs是一个特殊属性,它包含了所有通过ref注册的元素和组件的实例。这个属性只在组件渲染完成后才会被填充。
  • 在组件中,ref可以绑定到子组件上,如下面的例子所示:
<template>
  <div>
    <my-component ref="myComponent"></my-component>
    <button @click="callChildMethod">Call Child Method</button>
  </div>
</template>

<script>
import MyComponent from './MyComponent.vue'

export default {
  components: {
    MyComponent
  },
  methods: {
    callChildMethod() {
      this.$refs.myComponent.childMethod()
    }
  }
}
</script>

需要注意的是,当ref用于绑定到组件上时,$refs属性将引用组件实例而不是DOM元素。

2.6 Prop 属性

参考官网:组件基础 — Vue.js

  • 我的理解,在我们的实际开发过程中,我们的组件存在父子组件的关系,但是父子组件需要通信,这时就需要prop属性
  • Prop 是你可以在组件上注册的一些自定义 attribute,当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property

下面我们来看个案例

<template>
    <div>
        <h1 >son</h1>
        <h2>来自父亲的消息{{msg}}</h2>
    </div>
</template>

<script>

export default {
    name: "SonComponent",
    // 通过props接收父组件传递过来的数据
    props: {
        msg: String
    },
    methods: {
        click() {
            this.$emit('click')
        }
    }
}
</script>
<template>
    <div>
        <h1>father</h1>
        <button @click="click">给儿子发送消息</button>
        <SonComponent :msg="msg"></SonComponent>
    </div>
</template>

<script>
import SonComponent from './Son.vue'
export default {
    name: "FatherComponent",
    data: function () {
        return {
            msg: '我是你爸爸'
        }
    },
    components: {
        SonComponent
    },
    methods: {
        click() {
            this.msg = '我是你爸爸,我给你发了一条消息'
        }
    }
}
</script>

当我们点击按钮时,子组件可以接受到父组件传递的值,具体参考官网,其中还包括类型检查,动态传递Prop,单向数据流等等

2.7 Mixin 属性

  • 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
  • 将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗余度,也可以让后期维护起来更加容易。
  • 这里需要注意的是:提取的是逻辑或配置,而不是HTML代码和CSS代码。其实大家也可以换一种想法,mixin就是组件中的组件,Vue组件化让我们的代码复用性更高,那么组件与组件之间还有重复部分,我们使用Mixin在抽离一遍。
// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

2.8 插件

参考官网:API — Vue.js
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入,该方法需要在调用 new Vue() 之前被调用,当 install 方法被同一个插件多次调用,插件将只会被安装一次。
来个案例:

// myPlugin.js

const MyPlugin = {}

MyPlugin.install = function (Vue, options) {
  // 添加全局方法或属性
  Vue.myGlobalMethod = function () {
    console.log('myGlobalMethod is called')
  }

  // 添加全局资源(指令、过滤器、组件)
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 绑定时的逻辑
    },
    // ...其他生命周期钩子
  })

  Vue.filter('my-filter', function (value) {
    // 过滤器的实现逻辑
  })

  Vue.component('my-component', {
    // 组件选项
  })
}

export default MyPlugin

我们首先定义了一个名为 MyPlugin 的对象,并向其添加了一个名为 install 的方法。然后,在 install 方法中,我们可以添加全局方法或属性、全局资源(指令、过滤器、组件)等,这些添加的全局内容可以在 Vue 实例中直接使用。
使用:

// main.js

import Vue from 'vue'
import MyPlugin from './myPlugin.js'

Vue.use(MyPlugin)

// 现在可以在应用程序中使用 Vue.myGlobalMethod、<my-component> 等全局内容了

需要注意的是,我们在自定义插件时,应该尽可能保持插件的功能单一性,将不同的功能分散在不同的插件中。这样可以提高插件的可复用性,并方便我们管理和维护应用程序的功能。

2.9 Scoped

image.png
Vue中的style标签上有一个特殊的属性scoped,当style标签拥有scoped属性时候,它的css样式只能作用于当前的Vue组件,防止组件之间污染。

<!-- Add "scoped" attribute to limit CSS to this component only -->
  <style scoped>h3 {
    margin: 40px 0 0;
  }

  ul {
    list-style-type: none;
    padding: 0;
  }

  li {
    display: inline-block;
    margin: 0 10px;
  }

  a {
    color: #42b983;
}</style>

三 组件

3.1 组件的注册

我们的组件必须先注册才能使用,分为局部注册于全局注册
image.png

3.1.1 局部注册

image.png

  • 首先我们定义一个组件:PartialRegistration
<template>
  <div class="part">我是局部注册组件</div>
</template>
<script>
export default {
  // 组件名称
  name: 'PartialRegistration',
  // 组件数据
  data() {
    return {
      // ...
    }
  },
}
</script>

<style scoped>
.part {
  color: red;
  width: 100px;
  height: 100px;
  background-color: antiquewhite;
  text-align: center;
}
</style>


  • 在需要使用的组件中注册该组件
<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 16:49:48
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 17:30:38
 * @FilePath: \vue-demo01\src\App.vue
-->
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <PartialRegistration/>
  </div>
</template>

<script>
// 引入组件
import PartialRegistration from './components/PartialRegistration.vue'
export default {
  name: 'App',
  // 局部注册组件
  components: {
    // 简写形式
    PartialRegistration
    // 完整形式
    // PartialRegistration: PartialRegistration
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

我们可以通过浏览器的Vue工具进行查看
image.png

3.1.2 全局注册

image.png

  • 全局注册组件一般是我们需要常用的组件进行封装,供组件的其他地方进行使用
  • 首先我们定义一个全局组件:GlobalRegistration
<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 17:38:59
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 17:41:19
 * @FilePath: \vue-demo01\src\components\GlobalRegistration.vue
-->
<template>
  <div class="global">我是全局注册组件</div>
</template>
<script>
export default {
  // 组件名称
  name: 'GlobalRegistration',
  // 组件数据
  data() {
    return {
      // ...
    }
  },
}

</script>
<style scoped>
.global {
  color: rgb(0, 38, 255);
  width: 100px;
  height: 100px;
  margin-left: 200px;
  background-color: antiquewhite;
  text-align: center;
}

</style>
  • 由于是全局使用组件,所以我们需要在main.js中来注册他
/*
 * @Author: EasonShu
 * @Date: 2023-10-21 16:49:48
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 17:42:25
 * @FilePath: \vue-demo01\src\main.js
 */
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 注册全局组件
import GlobalRegistration from './components/GlobalRegistration.vue'
Vue.component('GlobalRegistration', GlobalRegistration)

new Vue({
  render: h => h(App),
}).$mount('#app')


  • 使用
<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 16:49:48
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 17:30:38
 * @FilePath: \vue-demo01\src\App.vue
-->
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <PartialRegistration/>
    <GlobalRegistration />
  </div>
</template>

<script>
// 局部注册组件
import PartialRegistration from './components/PartialRegistration.vue'
export default {
  name: 'App',
  // 局部注册组件
  components: {
    // 简写形式
    PartialRegistration
    // 完整形式
    // PartialRegistration: PartialRegistration
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

image.png
image.png

3.2 组件的通信

image.png

3.2.1 父子关系通信

image.png
父->子
父组件通过 props 将数据传递给子组件

  • 我们首先定义一个父组件:FatherComponent,首先介绍将父组件消息传递给子组件
<template>
<div> 
  <h1 class="father">我是父组件</h1>
  <hr>
  <SonComponent :msg="msg"></SonComponent>
</div>
</template>
<script>
import SonComponent from './SonComponent.vue'
export default {
  // father组件中注册了son组件
  name: 'FatherComponent',
  // 局部注册组件
  components: {
    SonComponent
  },
  data() {
    return {
      msg: '我是父组件的数据,我会传给子组件'
    }
  },
}

</script>

<style>
.father {
  color: blue;
}
</style>
  • 子组件需要用prop属性来接受父组件的值
<template>
  <div> 
    <h1 class="son">我是子组件</h1>
    <hr>
    <p class="son">我是父组件传递过来的数据: {{msg}}</p>
  </div>
  
  </template>
  
  <script>
  export default {
    // 组件名称
    name: 'SonComponent',
    // 接收父组件传递过来的数据
    props: ['msg'],
    data() {
      return {
       
      }
    },
    // 父组件传递过来的数据
    mounted() {
      console.log("xxxxx"+this.msg)
    }
  }
  
  </script>
  
  <style>
  .son {
    color: red;
  }
  </style>

image.png
子->父
子组件利用 $emit 通知父组件,进行修改更新
父组件

<template>
<div> 
  <h1 class="father">我是父组件</h1>
  <hr>
  <SonComponent :msg="msg" @son-change="handleChanges"></SonComponent>
  <hr>
  <p class="father">我是子组件传递过来的数据: {{msg}}</p>
</div>
</template>
<script>
import SonComponent from './SonComponent.vue'
export default {
  // father组件中注册了son组件
  name: 'FatherComponent',
  // 局部注册组件
  components: {
    SonComponent
  },
  data() {
    return {
      msg: '我是父组件的数据,我会传给子组件'
    }
  },
  methods: {
    // 监听子组件传递过来的数据
    handleChanges(val) {
      console.log("xxxxxx"+val)
      this.msg = val
    }
  }
}

</script>

<style>
.father {
  color: blue;
}
</style>

子组件

<template>
  <div> 
    <h1 class="son">我是子组件</h1>
    <hr>
    <p class="son">我是父组件传递过来的数据: {{msg}}</p>
    <hr>
    <button @click="handleClick">点击我向父组件传递数据</button>
  </div>
  
  </template>
  
  <script>
  export default {
    // 组件名称
    name: 'SonComponent',
    // 接收父组件传递过来的数据
    props: ['msg'],
    data() {
      return {
       
      }
    },
    // 父组件传递过来的数据
    mounted() {
      console.log("xxxxx"+this.msg)
    },
    methods: {
      handleClick() {
        // 向父组件传递数据
        this.$emit('son-change', '哈哈哈,我是子组件传递过来的数据')
      }
    }
  }
  
  </script>
  
  <style>
  .son {
    color: red;
  }
  </style>

image.png
image.png

3.2.2 Prop 详解

props主要用于组件的传值,他的工作就是为了接收外面传过来的数据,与data、el、ref是一个级别的配置项,基本的使用上面都讲了下面我们来看看具体的配置信息,props 校验
image.png

  • 父组件
<template>
<div> 
  <h1 class="father">我是父组件</h1>
  <hr>
  <SonComponent :msg="msg" @son-change="handleChanges" :person="person"></SonComponent>
  <hr>
  <p class="father">我是子组件传递过来的数据: {{msg}}</p>
</div>
</template>
<script>
import SonComponent from './SonComponent.vue'
export default {
  // father组件中注册了son组件
  name: 'FatherComponent',
  // 局部注册组件
  components: {
    SonComponent
  },
  data() {
    return {
      msg: '我是父组件的数据,我会传给子组件',
      person: {
        name: '张三',
        age: 18,
        school: '清华大学',
        city: '北京',
        isMarry: false
      }

    }
  },
  methods: {
    // 监听子组件传递过来的数据
    handleChanges(val) {
      console.log("xxxxxx"+val)
      this.msg = val
    }
  }

}

</script>

<style>
.father {
  color: blue;
}
</style>

子组件:

<template>
  <div> 
    <h1 class="son">我是子组件</h1>
    <hr>
    <p class="son">我是父组件传递过来的数据: {{msg}}</p>
    <hr>
    <button @click="handleClick">点击我向父组件传递数据</button>
    <hr>
    <h1 class="son">我是父组件传递过来的对象数据</h1>
    <p class="son">姓名: {{person.name}}</p>
    <p class="son">年龄: {{person.age}}</p>
    <p class="son">学校: {{person.school}}</p>
    <p class="son">城市: {{person.city}}</p>
    <p class="son">是否结婚: {{person.isMarry}}</p>
  </div>
  
  </template>
  
  <script>
  export default {
    // 组件名称
    name: 'SonComponent',
    // 接收父组件传递过来的数据
    props: {
      msg: {
        type: String, // 数据类型
        default: '我是子组件的默认数据' ,// 默认值
        required: true ,// 是否必须传递
        validator: (value) => {
          // value是父组件传递过来的数据
          // 如果返回true,表示验证通过,如果返回false,表示验证不通过
          return value.length > 5
        }
      },
      person: {
        type: Object,
        default: () => {
          return {
            name: '张三',
            age: 18,
            school: '清华大学',
            city: '北京',
            isMarry: false
          }
        }
      }
    },
    data() {
      return {
       
      }
    },
    // 父组件传递过来的数据
    mounted() {
      console.log("xxxxx"+this.msg)
    },
    methods: {
      handleClick() {
        // 向父组件传递数据
        this.$emit('son-change', '哈哈哈,我是子组件传递过来的数据')
      }
    }
  }
  
  </script>
  
  <style>
  .son {
    color: red;
  }
  </style>

image.png
注意:

  • 所有 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来不行。这样会防止子组件意外变更父组件的状态,从而导致你的应用的数据流向难以理解。
  • 每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。如果你在一个子组件内部改变 prop,Vue 会在浏览器的控制台中发出警告
  • 点击按钮子组件会修改父组件传递过来的 prop,浏览器会报错

image.png

3.2.3 非父子组件通信

event bus 事件总线

image.png

  • 写一个工具类
/*
 * @Author: EasonShu
 * @Date: 2023-10-21 18:23:38
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 18:23:42
 * @FilePath: \vue-demo01\src\utils\EventBus.js
 */
import Vue from 'vue'
const EventBus = new Vue()
export default EventBus

image.png

  • 将这个方法全局注册
/*
 * @Author: EasonShu
 * @Date: 2023-10-21 16:49:48
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 18:26:18
 * @FilePath: \vue-demo01\src\main.js
 */
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 注册全局组件
import GlobalRegistration from './components/GlobalRegistration.vue'
Vue.component('GlobalRegistration', GlobalRegistration)
// 注册EventBus
import EventBus from './utils/EventBus.js'
Vue.prototype.$bus = EventBus

new Vue({
  render: h => h(App),
}).$mount('#app')


  • 父组件
<template>
<div> 
  <h1 class="father">我是父组件</h1>
  <hr>
  <SonComponent :msg="msg" @son-change="handleChanges" :person="person"></SonComponent>
  <hr>
  <p class="father">我是子组件传递过来的数据: {{msg}}</p>
  <hr>
  <!-- 利用EventBus 传递消息 -->
  <button @click="handleClick">利用EventBus 传递消息</button>
</div>
</template>
<script>
import SonComponent from './SonComponent.vue'
export default {
  // father组件中注册了son组件
  name: 'FatherComponent',
  // 局部注册组件
  components: {
    SonComponent
  },
  data() {
    return {
      msg: '我是父组件的数据,我会传给子组件',
      person: {
        name: '张三',
        age: 18,
        school: '清华大学',
        city: '北京',
        isMarry: false
      }

    }
  },
  methods: {
    // 监听子组件传递过来的数据
    handleChanges(val) {
      console.log("xxxxxx"+val)
      this.msg = val
    },
    handleClick() {
      // 利用EventBus 传递消息
      this.$bus.$emit('father-change', '哈哈哈,我是父组件传递过来的数据')
    }
  }

}

</script>

<style>
.father {
  color: blue;
}
</style>
  • 子组件
<template>
  <div> 
    <h1 class="son">我是子组件</h1>
    <hr>
    <p class="son">我是父组件传递过来的数据: {{msg}}</p>
    <hr>
    <button @click="handleClick">点击我向父组件传递数据</button>
    <hr>
    <h1 class="son">我是父组件传递过来的对象数据</h1>
    <p class="son">姓名: {{person.name}}</p>
    <p class="son">年龄: {{person.age}}</p>
    <p class="son">学校: {{person.school}}</p>
    <p class="son">城市: {{person.city}}</p>
    <p class="son">是否结婚: {{person.isMarry}}</p>
    <hr>
    <!-- 接受来EventBus的消息 -->
    <p class="son">接受来EventBus的消息的数据: {{events}}</p>

  </div>
  
  </template>
  
  <script>
  export default {
    // 组件名称
    name: 'SonComponent',
    // 接收父组件传递过来的数据
    props: {
      msg: {
        type: String, // 数据类型
        default: '我是子组件的默认数据' ,// 默认值
        required: true ,// 是否必须传递
        validator: (value) => {
          // value是父组件传递过来的数据
          // 如果返回true,表示验证通过,如果返回false,表示验证不通过
          return value.length > 5
        }
      },
      person: {
        type: Object,
        default: () => {
          return {
            name: '张三',
            age: 18,
            school: '清华大学',
            city: '北京',
            isMarry: false
          }
        }
      }
    },
    data() {
      return {
       events: ''
      }
    },
    // 父组件传递过来的数据
    mounted() {
      console.log("xxxxx"+this.msg)
      // 接受来EventBus的消息
      this.$bus.$on('father-change', (val) => {
        this.events = val
      })
    },
    methods: {
      handleClick() {
        // 向父组件传递数据
        this.$emit('son-change', '哈哈哈,我是子组件传递过来的数据')
      }
    }
  }
  </script>
  <style>
  .son {
    color: red;
  }
  </style>

image.png
后面我们还会介绍Vuex

3.3 组件的其他知识

3.3.1 .sync 修饰符

  • .sync修饰符可以实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值。
  • 一般情况下,想要实现父子组件间值的传递,通常使用的是 props 和自定义事件 $emit 。
  • 其中,父组件通过 props 将值传给子组件,子组件再通过 $emit 将值传给父组件,父组件通过事件j监听获取子组件传过来的值。
  • 如果想要简化这里的代码,可以使用.sync修饰符,实际上就是一个语法糖。

父组件

<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 17:50:39
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 18:46:16
 * @FilePath: \vue-demo01\src\components\FatherComponent.vue
-->
<template>
<div> 
  <h1 class="father">我是父组件</h1>
  <hr>
  <SonComponent :msg="msg" @son-change="handleChanges" :isShow.sync="isShow" :person="person"></SonComponent>
  <hr>
  <p class="father">我是子组件传递过来的数据: {{msg}}</p>
  <hr>
  <!-- 利用EventBus 传递消息 -->
  <button @click="handleClick">利用EventBus 传递消息</button>
  <hr>
  <!-- 监听子组件按钮的显示状态 -->
  <p>子组件按钮状态:{{ isShow }} </p>
</div>
</template>
<script>
import SonComponent from './SonComponent.vue'
export default {
  // father组件中注册了son组件
  name: 'FatherComponent',
  // 局部注册组件
  components: {
    SonComponent
  },
  data() {
    return {
      msg: '我是父组件的数据,我会传给子组件',
      person: {
        name: '张三',
        age: 18,
        school: '清华大学',
        city: '北京',
        isMarry: false
      },
      isShow: true
    }
  },
  methods: {
    // 监听子组件传递过来的数据
    handleChanges(val) {
      console.log("xxxxxx"+val)
      this.msg = val
    },
    handleClick() {
      // 利用EventBus 传递消息
      this.$bus.$emit('father-change', '哈哈哈,我是父组件传递过来的数据')
    }
  }

}

</script>

<style>
.father {
  color: blue;
}
</style>
  • 子组件
<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 17:51:40
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 18:48:38
 * @FilePath: \vue-demo01\src\components\SonComponent.vue
-->
<template>
  <div> 
    <h1 class="son">我是子组件</h1>
    <hr>
    <p class="son">我是父组件传递过来的数据: {{msg}}</p>
    <hr>
    <button @click="handleClick">点击我向父组件传递数据</button>
    <hr>
    <h1 class="son">我是父组件传递过来的对象数据</h1>
    <p class="son">姓名: {{person.name}}</p>
    <p class="son">年龄: {{person.age}}</p>
    <p class="son">学校: {{person.school}}</p>
    <p class="son">城市: {{person.city}}</p>
    <p class="son">是否结婚: {{person.isMarry}}</p>
    <hr>
    <!-- 接受来EventBus的消息 -->
    <p class="son">接受来EventBus的消息的数据: {{events}}</p>
    <!-- 按钮的可用状态 -->
    <button :disabled="isShow">我是按钮</button>
    <!-- 改变按钮的状态 -->
    <button @click="changeStatus">改变按钮的状态</button>
  </div>
  
  </template>
  
  <script>
  export default {
    // 组件名称
    name: 'SonComponent',
    // 接收父组件传递过来的数据
    props: {
      msg: {
        type: String, // 数据类型
        default: '我是子组件的默认数据' ,// 默认值
        required: true ,// 是否必须传递
        validator: (value) => {
          // value是父组件传递过来的数据
          // 如果返回true,表示验证通过,如果返回false,表示验证不通过
          return value.length > 5
        }
      },
      person: {
        type: Object,
        default: () => {
          return {
            name: '张三',
            age: 18,
            school: '清华大学',
            city: '北京',
            isMarry: false
          }
        }
      },
      isShow: {
        type: Boolean,
        default: true
      }
    },
    data() {
      return {
       events: '',
       isShows: true
      }
    },
    // 父组件传递过来的数据
    mounted() {
      console.log("xxxxx"+this.msg)
      // 接受来EventBus的消息
      this.$bus.$on('father-change', (val) => {
        this.events = val
      })
    },
    methods: {
      handleClick() {
        // 向父组件传递数据
        this.$emit('son-change', '哈哈哈,我是子组件传递过来的数据')
      },
      changeStatus() {
        // 向父组件传递数据
        this.$emit('update:isShow', !this.isShow)
      }
    }
  }
  
  </script>
  
  <style>
  .son {
    color: red;
  }
  </style>

image.png

3.3.2 Vue异步更新

Vue的异步更新

  • Vue.js是一种用于构建用户界面的渐进式 JavaScript 框架。
  • 其中一个非常重要的特性是异步更新。
  • 异步更新是指当数据发生变化时,Vue不会立即更新DOM。
  • 相反,它会在下一个“tick”或渲染循环中异步执行DOM更新。这种机制可以提高性能,减少不必要的操作
  • 当我们直接修改 Vue 实例的数据时,Vue 会在内部将数据更新操作放入一个异步队列中,而不是立即进行更新。

3.3.3 $nextTick()

image.png

  • $nextTick() 是 Vue.js 框架中的一个方法,它主要用于 DOM 操作。当我们修改 Vue 组件中的数据时,Vue.js 会在下次事件循环前自动更新视图,并异步执行 $nextTick() 中的回调函数。这个过程可以确保 DOM 已经被更新,以及可以操作到最新的 DOM。
  • 具体来说,当修改了 Vue 组件中的数据时,Vue.js 并不会立即进行视图更新。Vue.js 会将修改的数据记录下来,并在下一次事件循环时才更新视图。而 $nextTick() 方法则是用于等待这个事件循环结束后再执行回调函数。这样可以确保我们操作 DOM 的时候,DOM 已经被 Vue 更新过了。

案例:

<!--
 * @Author: EasonShu
 * @Date: 2023-10-21 19:50:08
 * @LastEditors: Do not edit
 * @LastEditTime: 2023-10-21 19:51:18
 * @FilePath: \vue-demo01\src\components\NextTickComponent.vue
-->
<template>
  <div>
  <div>{{message}}</div>
  <!-- 更新消息 -->
  <button @click="updateMessage">Update Message</button>
  </div>
</template>
<script>
  export default {
    name: 'NextTickComponent',
    data () {
      return {
        message: 'Hello Vue'
      }
    },
    methods: {
      updateMessage () {
        this.message = 'Updated Message'
        // 在 DOM 更新后操作 DOM
        this.$nextTick(() => {
          // 通过 DOM API 更新文本
          this.$el.textContent = 'DOM Updated!'
        })
      }
    }
  }
</script>

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

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

相关文章

差分时钟与DDR3

Zynq上的存储器接口 所有 Zynq-7000 AP芯片上的存储器接口单元包括一个动态存储器控制器和几个 静态存储器接口模块。动态存储器控制器可以用于 DDR3、DDR3L、DDR2 和 LPDDR2。 静态存储器控制器支持一个 NAND 闪存接口、一个 Quad-SPI 闪存接口、一个并行数 据总线和并行 NOR …

python中的yolov5结合PyQt5,使用QT designer设计界面没正确启动的解决方法

python中的yolov5结合PyQt5&#xff0c;使用QT designer设计界面没正确启动的解决方法 一、窗体设计test: 默认你已经设计好了窗体后&#xff1a; 这时你需要的是保存生成的untitle.ui到某个文件夹下&#xff0c;然后在命令行中奖.ui转换为.py&#xff08;&#xff0c;通过​​…

JAVA面经整理(MYSQL篇)

索引: 索引是帮助MYSQL高效获取数据的排好序的数据结构 1)假设现在进行查询数据&#xff0c;select * from user where userID89 2)没有索引是一行一行从MYSQL进行查询的&#xff0c;还有就是数据的记录都是存储在MYSQL磁盘上面的&#xff0c;比如说插入数据的时候是向磁盘上面…

web 安全总结

1、web安全总结 1.1 web安全简介 1.1.1 http协议 http 协议是超文本传输协议-明文传输 https 协议是http协议的基础上进行升级&#xff0c;是数据在传输过程中进行加密 1.1.2 http请求 http请求分为&#xff1a;请求方法、请求头、请求体 GET、PUT、POST、OPTIONS、move、…

Qt中Json的操作

在 Json的两种格式中介绍了Json的格式以及应用场景。由于这种数据格式与语言无关,下面介绍一下Json在Qt中的使用。 从Qt 5.0开始提供了对Json的支持,我们可以直接使用Qt提供的Json类进行数据的组织和解析。相关的类常用的主要有四个,具体如下: Json类介绍 QJsonDocument |…

Mac M1下使用Colima替代docker desktop搭建云原生环境

文章目录 为什么不使用docker desktop1.docker desktop卸载2.docker、docker compose安装3.colima安装3.1获取镜像地址3.2将下载好的iso文件放到colima指定路径3.3重新执行colima start 4.minikukekubernetes安装5.关闭minikube Mac M1下使用Colima替代docker desktop搭建云原生…

【数据分享】我国专精特新“小巨人”企业数据(excel格式\shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平&#xff01;比如一个城市的金融企业较多&#xff0c;那这个城市的金融产业肯定比较发达&#xff1b;一个城市的制造业企业较多&#xff0c;那这个城市的制造业肯定比较发达。之前我们分享过2023年高…

使用WPF模仿Windows记事本界面

本次仅模仿Windows记事本的模样&#xff0c;并未实现其功能。 所有代码如下&#xff1a; <Window x:Class"控件的基础使用.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/…

高校教务系统登录页面JS分析——广东海洋大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…

01、字符传实现为什么是SDS而不是char*?

问题&#xff1a; 1. sds 是什么 &#xff1f; 2. sds 相对于char * 有什么好处 &#xff1f;解决了哪些疑难杂症&#xff1f; 3. sds 有什么不足&#xff1f;可以优化的点&#xff1f; 思考下&#xff1a; 平常工作开发中&#xff0c;我们记录一条用户信息、订单信息&…

SAP-QM-检验批和物料凭证

业务场景&#xff1a; 在做数字化项目中可能会导出一些数据&#xff0c;例如&#xff0c;通过检验批要找到物料凭证&#xff0c;因为启用了质检模块&#xff0c;收货操作是103105&#xff0c;当做103收货时产生检验批1000*************,然后通过QM系统的QA11决策之后收货&…

代码随想录Day25 回溯算法 LeetCode T51 N皇后问题

目录 前言 LeetCode T51 N皇后问题 题目思路: 回溯三部曲: 2.终止条件 3.一次搜索逻辑 4.isValid合法性判断 5.Array2List 题目代码: 总结: 前言 又来到了我们的周末,今天我们挑战一道困难题:N皇后问题,相信大家都玩过一个经典的小游戏:8皇后 游戏规则是:在一个n*n的…

Python学习第2天-安装pycharm

文章目录 前言一、下载二、安装1.选择安装目录2.安装配置 总结 前言 好用的工具可以极大地提高生产力&#xff0c;开发Python推荐使用jetbrains全家桶的pycharm。 一、下载 通过官网下载安装包。 二、安装 1.选择安装目录 2.安装配置 一路Next&#xff0c;安装完成 总结 …

ANR系列之八:疑难ANR问题处理记录

前言&#xff1a; 本文仅是记录作者自身处理过的ANR问题&#xff0c;以及帮助他人解决过的ANR问题。本文中所介绍的ANR处理记录仅供参考&#xff0c;并不适用所有场景。并且最终结论和分析并不一定就是绝对正确的。 案例1.页面切换时前台应用焦点未获得 案例编号&#xff1a;…

从零开始学CAPL

从零开始学CAPL CAPL和C语言的关系和介绍C语言的基础语法常量变量标识符关键字数据类型整数数据字符数据实型数据(浮点数据)运算符以及优先级类型转换printf函数 选择程序结构设计if语句switch语句 循环结构程序设计while语句do……while语句for语句break和continue 数组函数 C…

VueComponent的原型对象

一、prototype 每一个构造函数身上又有一个prototype指向其原型对象。 如果我们在控制台输入如下代码&#xff0c;就能看到Vue构造函数的信息&#xff0c;在他身上可以找到prototype属性&#xff0c;指向的是Vue原型对象&#xff1a; 二、__proto__ 通过构造函数创建的实例对…

Mybatis拦截器

MyBatis插件介绍 MyBatis提供了一种插件(plugin)的功能&#xff0c;虽然叫做插件&#xff0c;但其实这是拦截器功能。 MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用&#xff0c;通过织入拦截器&#xff0c;在不同节点修改一些执行过程中的关键属性&…

apache搭建静态网站,moongoose搭建网站后台,出现的跨域问题解决

文章目录 1&#xff0c;问题描述1.1&#xff0c;当网页和后台是不同服务时会产生跨域问题1.2&#xff0c;跨域问题 2&#xff0c;nginx端口转发解决跨域问题2.1&#xff0c;下载并安装nginx2.1.1&#xff0c;解压后如下所示2.1.2&#xff0c;进入解压目录后&#xff0c;执行配置…

SAP-QM-动态检验规则

Dynamic Modification Rule &#xff08;动态修改规则&#xff09; 1、决定样本大小的方式有3种&#xff1a; 手动输入比例大小采样过程 物料主数据质量视图 2、采样过程的创建方式有2种 跟批量大小有关系&#xff1a;百分比/AQL跟批量大小没有关系&#xff1a;固定值 而当…

Jetpack:014-Jetpack中的小红点

文章目录 1. 概念介绍2. 使用方法2.1 Badge2.2 BadgedBox 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack库中底部栏相关的内容&#xff0c;本章回中主要介绍 小红点。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01; 1. 概念介绍 我们在本章回中…