Maison > interface Web > Voir.js > le corps du texte

Explication détaillée des deux cœurs de VUE : développement réactif et basé sur des composants

WBOY
Libérer: 2022-08-09 10:23:23
avant
1422 Les gens l'ont consulté

Cet article vous apporte des connaissances pertinentes sur vue. Il présente principalement les deux problèmes fondamentaux concernant la vue, la réactivité et la composantisation. J'espère qu'il sera utile à tout le monde.

Explication détaillée des deux cœurs de VUE : développement réactif et basé sur des composants

【Recommandations associées : Tutoriel vidéo javascript, Tutoriel vue.js

vue2.0 responsive

1.1. Le

Object.defineProperty(obj, prop, descriptor) définira directement une nouvelle propriété sur un objet, ou modifiera une propriété existante d'un objet, et renverra cet objet.

obj - l'objet pour définir l'attribut prop - le nom ou le symbole de l'attribut à définir ou à modifier

descripteur - l'objet, le descripteur d'attribut à définir ou à modifier

// descriptor{
  value: undefined, // 属性的值
  get: undefined,   // 获取属性值时触发的方法
  set: undefined,   // 设置属性值时触发的方法
  writable: false,  // 属性值是否可修改,false不可改
  enumerable: false, // 属性是否可以用for...in 和 Object.keys()枚举
  configurable: false  // 该属性是否可以用delete删除,false不可删除,为false时也不能再修改该参数}
Copier après la connexion
Object.defineProperty(obj, prop, descriptor)方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

obj——要定义属性的对象
prop——要定义或修改的属性的名称或Symbol
descriptor——对象,要定义或修改的属性描述符

const a = {b : 1}
console.log(Object.getOwnPropertyDescriptor(a, 'b'))
// {value: 1, writable: true, enumerable: true, configurable: true}

Object.defineProperty(a, 'c', {value: '2'})
console.log(Object.getOwnPropertyDescriptor(a, 'c'))
// {value: '2', writable: false, enumerable: false, configurable: false}

a.c = 3
console.log(a.c)
// 2

Object.defineProperty(a, 'c', {value: '4'})
console.log(a.c)
// error: Uncaught TypeError: Cannot redefine property: c
Copier après la connexion

通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for…in 或 Object.keys 方法),可以改变这些属性的值,也可以删除这些属性。这个方法允许修改默认的额外选项(或配置)。
而默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。
示例:

// 模拟vue响应式过程const app = document.getElementById('app')const data = {
	a: {
	  b: {
	    c: 1
	  }
	}}function render () {
  const virtualDom = `这是我的内容${data.a.b.c}`		
  app.innerHTML = virtualDom}function observer (obj) {
  let value  for (const key in obj) {  // 递归设置set和get
    value = obj[key]
    if (typeof value === 'object'){
      arguments.callee(value)
    } else {
      Object.defineProperty(obj, key, {
        get: function(){
          return value        },
        set: function(newValue){
          value = newValue          render()
        }
      })
    }
  }}render()observer(data)setTimeout(() => {
  data.a.b.c = 22}, 2000)setTimeout(() => {
  data.a.b.c = 88}, 5000)
Copier après la connexion

1.2 set和get

Object.defineProperty(data, key, {
  get: function(){
    dep.depend() // 这里进行依赖收集
    return value  },
  set: function(newValue){
    value = newValue    // render()
    dep.notify()  // 这里进行virtualDom更新,通知需要更新的组件render
  }});
Copier après la connexion

上述方法实现了数据的响应,但存在很大的问题,我们触发一次set,就需要整个页面重新渲染,然而这个值可能只在某一个组件中使用了。
Explication détaillée des deux cœurs de VUE : développement réactif et basé sur des composants

所以将get和set优化:

const vm = new Vue({
	data: {
		a: 1
	}})// vm.a是响应式的vm.b = 2// vm.b是非响应式的
Copier après la connexion

dep是Vue负责管理依赖的一个类

补充: Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的

const a = {}
// 相当于
const a = Object.create(Object.prototype)

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
Copier après la connexion

2. 数组的响应式

vue 中处理数组的变化,直接通过下标触发视图的更改,只能使用push、shift等方法,而数组不能使用Object.defineProperty()
其实 Vue用装饰者模式来重写了数组这些方法

Object.create(proto,[propertiesObject]) 方法是创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

proto——新创建对象的原型对象;
propertiesObjectLes propriétés ordinaires ajoutées via les opérations d'affectation sont énumérables et seront énumérées (méthode for...in ou Object.keys) lors de l'énumération des propriétés d'objet. Les valeurs de ces propriétés peuvent être modifiées ou supprimées. Cette méthode permet de modifier les options supplémentaires (ou configuration) par défaut.

Par défaut, les valeurs de propriété ajoutées à l'aide de Object.defineProperty() sont immuables.

Exemple :

const o = Object.create(Object.prototype, {
  foo: { // foo会成为所创建对象的数据属性
    writable:true,
    configurable:true,
    value: "hello"
  },
  bar: { // bar会成为所创建对象的访问器属性
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }});console.log(o) // {foo: 'hello'}
Copier après la connexion

1.2 set et get

const arraypro = Array.prototype    // 获取Array的原型
const arrob = Object.create(arraypro) // 用Array的原型创建一个新对象,arrob.__proto__ === arraypro,免得污染原生Array;
const arr=['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']   // 需要重写的方法

arr.forEach(function(method) {
  arrob[method] = function () {
    arraypro[method].apply(this, arguments) // 重写时先调用原生方法
    dep.notify() // 并且同时更新
  }
})

// 对于用户定义的数组,手动将数组的__proto__指向我们修改过的原型
const a = [1, 2, 3]
a.__proto__ = arrob
Copier après la connexion

La méthode ci-dessus obtient une réponse aux données, mais il y a un gros problème si nous déclenchons set une fois, la page entière doit être restituée. La valeur ne peut être utilisée que dans un composant.

Insérer la description de l'image ici

Alors utilisez get et set Optimisation :

function def (obj, key, value) {
  Object.defineProperty(obj, key, {
    // 这里我们没有指定writeable,默认为false,即不可修改
    enumerable: true,
    configurable: true,
    value: value,
  });}// 数组方法重写改为arr.forEach(function(method){
  def(arrob, method, function () {
    arraypro[method].apply(this, arguments)  // 重写时先调用原生方法
    dep.notify()// 并且同时更新
  })})
Copier après la connexion
dep est une classe dont Vue est chargée de gérer les dépendances.

Ajout : Vue ne peut pas détecter l'ajout ou la suppression de propriétés. Étant donné que Vue effectuera une conversion getter/setter sur la propriété lors de l'initialisation de l'instance, la propriété doit exister sur l'objet de données pour que Vue puisse le convertir en un
function observer () {
  var self = this;
  data = new Proxy(data, {
    get: function(target, key){
      dep.depend() // 这里进行依赖收集
      return target[key]
    },
    set: function(target, key, newValue){
      target[key] = newValue;
      // render()
	  dep.notify()  // 这里进行virtualDom更新,通知需要更新的组件render
    }
  });}
Copier après la connexion
Copier après la connexion
2 réactif. La réactivité du tableau protode l'objet nouvellement créé. >—— L'objet prototype de l'objet nouvellement créé ; propertiesObject - facultatif, le type est un objet. Si ce paramètre est spécifié et n'est pas indéfini, les propres propriétés énumérables de l'objet transmis (c'est-à-dire lui-même les propriétés définies, plutôt que les propriétés d'énumération sur sa chaîne de prototypes) ajoutera la valeur de propriété spécifiée et le descripteur de propriété correspondant à l'objet nouvellement créé
<template>
  <div>
    {{ msg }}
  </div>
</template>

<script>
export default {
  name: "Test",
  data() {
    return {
      //编写响应式数据
      msg: "你好,世界",
    };
  },
};
</script>

<style>
.box {
  font-size: 20px;
  color: red;
}
</style>
Copier après la connexion
Copier après la connexion
<template>
  <div>
      //3.使用组件:可以使用单闭合或双闭合
      
    <test></test>
      <test>
  </test>
</div>
</template>


<script>
//1、导入组件
import Test from "./Test.vue";
export default {
  name: "App",
  components:{
    //2、注册使用的组件
    Test,
  }
};
</script>
Copier après la connexion
Copier après la connexion
Le modèle de décorateur dans vueCe qui précède pour le nouvel objet méthode arrob, nous attribuons la valeur directement, il y aura donc un problème, c'est-à-dire que l'utilisateur peut accidentellement modifier notre objet, afin que nous puissions utiliser l'Object.defineProperty que nous avons mentionné plus tôt pour éviter ce problème, nous créons une méthode publique def Spécifiquement utilisée pour définir les attributs dont les valeurs ne peuvent pas être modifiées3. Supplément : Attributs de données et attributs d'accesseur des objets Il contient l'emplacement d'une valeur de données, où la valeur de données peut être lue et écriteQuatre descripteurs de attributs de données : signifiant configurable
Vue gère les changements dans. le tableau, déclenchant des changements de vue directement via les indices, seules les méthodes push, shift et autres peuvent être utilisées, et les tableaux ne peuvent pas utiliser Object.defineProperty()
En fait, Vue utilise le mode décorateur pour réécrire ces méthodes de tableaux
Object.create La méthode (proto,[propertiesObject]) consiste à créer un nouvel objet, en utilisant l'objet existant pour fournir le __proto__
<template>
  <div>{{ msg }}</div>
</template>

<script>
export default {
  name: "Vote",
  data() {
    return {
      msg: "今夜阳光明媚",
    };
  },
};
</script>
Copier après la connexion
Copier après la connexion
import Vote from './Vote.vue';
Vue.component('Vote', Vote)
Copier après la connexion
Copier après la connexion
Attributs de données :
🎜 indique si l'attribut peut être redéfini en supprimant l'attribut via delete, si les caractéristiques de l'attribut peuvent être modifiées, ou si l'attribut peut être redéfini. L'attribut est modifié en attribut d'accesseur. La valeur par défaut est true🎜🎜🎜🎜writable🎜🎜. , qui indique si la valeur de l'attribut peut être modifiée. La valeur par défaut est true🎜 🎜🎜🎜value🎜🎜 contient la valeur des données de cet attribut, la valeur par défaut est indéfinie🎜🎜🎜🎜.

Attribut d'accesseur :  Cet attribut ne contient pas de valeurs de données. Il contient une paire de méthodes get et set Lors de la lecture et de l'écriture d'attributs d'accesseur, ces deux méthodes sont utilisées pour effectuer des opérations.

Les quatre descripteurs de l'attribut accesseur :


signifiant
configurable indique si l'attribut peut être redéfini en supprimant l'attribut via delete, si les caractéristiques de l'attribut peuvent être modifié, ou s'il faut modifier l'attribut en attribut accesseur, la valeur par défaut est false
enumerable Indique si l'attribut peut être renvoyé via une boucle for-in, la valeur par défaut est false
get La fonction appelée lors de la lecture de l'attribut, la valeur par défaut La valeur est indéfinie
set Fonction appelée lors de l'écriture des propriétés, la valeur par défaut est indéfinie

vue3.0数据响应

vue3.0的响应式和vue2.0响应式原理类似,都是在get中收集依赖,在set中通知依赖更新视图,但vue3.0使用了es6新增的proxy来代替Object.defineProperty()

proxy相对于Object.defineProperty()的好处:

  1. Object.defineProperty需要指定对象和属性,对于多层嵌套的对象需要递归监听,Proxy可以直接监听整个对象,不需要递归;
  2. Object.defineProperty的get方法没有传入参数,如果我们需要返回原值,需要在外部缓存一遍之前的值,Proxy的get方法会传入对象和属性,可以直接在函数内部操作,不需要外部变量;
  3. set方法也有类似的问题,Object.defineProperty的set方法传入参数只有newValue,也需要手动将newValue赋给外部变量,Proxy的set也会传入对象和属性,可以直接在函数内部操作;
  4. new Proxy()会返回一个新对象,不会污染源原对象
  5. Proxy可以监听数组,不用单独处理数组

proxy劣势: vue3.0将放弃对低版本浏览器的兼容(兼容版本ie11以上)

这样上边的observe方法就可以优化成:

function observer () {
  var self = this;
  data = new Proxy(data, {
    get: function(target, key){
      dep.depend() // 这里进行依赖收集
      return target[key]
    },
    set: function(target, key, newValue){
      target[key] = newValue;
      // render()
	  dep.notify()  // 这里进行virtualDom更新,通知需要更新的组件render
    }
  });}
Copier après la connexion
Copier après la connexion

模块化(组件化)开发

按照功能(或按照复用性)把一个页面拆成各个板块(模块),每一个模块都是一个单独的文件(单独的组件),最后把各个模块(组件)拼在一起即可!!

目的 :方便团队协作开发 实现复用

组件分类

功能型组件「UI组件库中提供的一般都是功能型组件:element/iview/antdv/vant/cube..」

  •     + 一般UI组件库提供的功能组件就够用了
  •     + 偶尔UI组件库中不存在的,才需要自己封装「难点」
  •     + 我们经常会把功能型组件进行二次封装(结合自己项目的业务逻辑)「特殊亮点」

业务型组件

  •   + 通用业务型组件「好多页面都需要用到的,我们把其封装成为公共的组件」
  •   + 普通组件

以后开发项目,拿到设计稿的第一件事情:划分组件「按照功能版块划分、本着复用性原则,拆的越细越好(这样才能更好的实现复用)」

组件的创建及使用

创建一个 Xxx.vue 就是创建一个vue组件{局部组件、私有组件},组件中包含:结构、样式、功能

 结构:基于template构建        

       + 只能有一个根元素节点(vue2)
     + vue的视图就是基于template语法构建的(各种指令&小胡子...),最后vue会把其编译为真实的DOM插入到页面指定的容器中
      首先基于 vue-template-compiler 插件把template语法编译为虚拟DOM「vnode」
      其次把本次编译出来的vnode和上一次的进行对比,计算出差异化的部分「DOM-DIFF」
      最后把差异化的部分变为真实的DOM放在页面中渲染
 

样式:基于style来处理

  • + lang="less" 指定使用的CSS预编译语言「需要提前安装对应的loader」
  • + scoped 指定当前编写的样式是私有的,只对当前组件中的结构生效,后期组件合并在一起,保证样式之间不冲突

 功能:通过script处理

    + 导出的这个对象是VueComponent类的实例(也是Vue的实例):对象 -> VueComponent.prototype -> Vue.prototype
    + 在对象中基于各种 options api 「例如:data、methods、computed、watch、filters、生命周期函数...」实现当前组件的功能
       + 在组件中的data不再是一个对象,而是一个“闭包”
       + 各个组件最后会合并在一起渲染,为了保证组件中指定的响应式数据是“私有的”,组件之间数据即使名字相同,也不会相互污染...所以需要基于闭包来管理

注意;App.vue页面入口相当于首页,写好的组件都导入到这个里面

<template>
  <div>
    {{ msg }}
  </div>
</template>

<script>
export default {
  name: "Test",
  data() {
    return {
      //编写响应式数据
      msg: "你好,世界",
    };
  },
};
</script>

<style>
.box {
  font-size: 20px;
  color: red;
}
</style>
Copier après la connexion
Copier après la connexion

私有组件(使用的时候首先进行导入,然后注册,这样视图中就可以调用组件进行渲染了)

  • 需要使用私有组件的时候,需要先导入import Test from "./Test.vue";

  • 然后注册:这样就可以调用组件进行渲染了

<template>
  <div>
      //3.使用组件:可以使用单闭合或双闭合
      
    <test></test>
      <test>
  </test>
</div>
</template>


<script>
//1、导入组件
import Test from "./Test.vue";
export default {
  name: "App",
  components:{
    //2、注册使用的组件
    Test,
  }
};
</script>
Copier après la connexion
Copier après la connexion

创建全局组件

1. 创建一个局部组件 

<template>
  <div>{{ msg }}</div>
</template>

<script>
export default {
  name: "Vote",
  data() {
    return {
      msg: "今夜阳光明媚",
    };
  },
};
</script>
Copier après la connexion
Copier après la connexion

@2 在main.js入口中,导入局部组件Vote,把其注册为全局组件

import Vote from './Vote.vue';
Vue.component('Vote', Vote)
Copier après la connexion
Copier après la connexion

@3 这样在任何组件(视图中),无需基于components注册,直接可以在视图中调用

插槽

调用组件的方式

调用组件的时候,可以使用:

双闭合

双闭合的方式可以使用插槽slot

 @1 在封装的组件中,基于 标签预留位置
 @2 调用组件的时候,基于双闭合的方式,把要插入到插槽中的内容写在双闭合之间

  单闭合

组件的名字可以在“kebab-case”和“CamelCase”来切换:官方建议组件名字基于CamelCase命名,渲染的时候基于kebab-case模式使用!

 插槽的作用

  • 让组件具备更高的复用性(或扩展性)
  • 我们封装好一个组件,把核心部分都实现了,但是我们期望用户调用组件的时候,可以自定义一些内容,防止在已经封装好的组件内部:

插槽分为了默认插槽、具名插槽、作用域插槽

默认插槽:只需要在调用组件<test><test></test></test>内插入我们想要的插入的html代码,会默认放到组件源代码的<slot name="default"></slot>插槽中

组件内部
slot预留位置  默认name:default
<slot></slot>
调用组件的时候
//只有一个的时候可以不用template包裹
<test>
<div>头部导航</div>
</test>
Copier après la connexion

具名插槽:组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字

  • 在调用组件<test><test></test></test>内自己写的代码,我们用template包裹代码,并把v-slot:xxx写在template上,这时就会将xxx里面的代码,包裹到组件源代码的<slot name="”xxx“"></slot>的标签中
  • ==组件内部:== <slot name="xxx"></slot> 默认名字是default
  • ==调用组件:==需要把v-slot写在template上
  组件内部
      <slot>
      默认名字是default
    调用组件:需要把v-slot写在template上
      <template>
         ...
      </template>
      <template>
         ...
      </template>
    v-slot可以简写为#:#xxx</slot>
Copier après la connexion

   作用域插槽:把组件内部定义的数据,拿到调用组件时候的视图中使用

        组件中data内的数据只能在本模块中使用,如果想让调用组件的插槽也能获取数据,就需要对组件内对的slot做bind绑定数据,调用组件的template标签做#top="AAA",获取数

   ==组件内部==:
 <slot name="top" :list="list" :msg="msg"></slot>
Copier après la connexion
  • 把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg
==调用组件==:
 <template #top="AAA"></template>
Copier après la connexion
  • 定义一个叫做AAA的变量,来接收插槽中绑定的所有数据(对象格式)
  • 如果插槽名是default则使用v-slot="AAA":default="AAA"获取数据
 组件内部
      <slot name="top" :list="list" :msg="msg"></slot>
      把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg

    调用组件
      <template #top="AAA"></template>
      定义一个叫做AAA的变量,来接收插槽中绑定的数据
      AAA={
        list:[...],
        msg:...
      }
Copier après la connexion

组件传参

调用组件的时候

每创建一个组件其实相当于创建一个自定义类,而调用这个组件就是创建VueCommponent(或者Vue)类的实例

  • 实例(this)->VueComponent.prototype->Vue.prototype->Object.prototype
  • 当前实例可以访问Vue.prototype上的一些公共属性和方法

组件中的script中存在的状态值和属性值?

  • ==状态值==:data中的数据值称为状态值
  • ==属性值==:props中的数据值称为属性值
  • 状态值和属性值是直接挂载到_vode对象的私有属性中(所以状态值和属性值名字不能重复)
  • 我们在视图template标签中调用状态值和属性值,不需要加this,直接调用状态名或属性名
  • 我们在功能script标签中调用状态值和属性值,需要加this调用
  • ==computed(计算属性)==:也是挂载实例上的,所以他们三个都不能重名

vue中的单向数据流

父子组件传递数据时,只能由父组件流向子组件,不能由子组件流向父组件。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

组件传参的分类7种:

  • 父组件向子组件传参:props
  • 子组件向父组件传参:发布订阅(@xxx给子组件标签自定义事件、$emit)
  • 组件相互传参(兄弟):发布订阅(on、emit)【2和3传参是一种】
  • 祖先向后代传参(provide[提供],inject[接收])
  • vue实例属性传参(parent、children[n]、root、refs)
  • vuex
  • localStorage sessionStorage

1.父组件向子组件传参

父组件向子组件传参:props

  • 我们传给组件的值,默认是==字符串类型==的,比如msg
  • 如果想传==数字类型==的,则需要调用v-bind或冒号的形式传值
  • 我们每调用一次coma,都会生成一个独立的VueComponent的实例

第一步:父组件在组件调用标签中自定义属性

//如果想把data中的状态值传递过去需要v-bind绑定
<coma msg="hello" :num="num"></coma>
Copier après la connexion

注意 如果想把data中的状态值传递过去需要v-bind绑定

第二步:子组件通过props接收(数组,对象)

  • props中的属性是只读的,子组件不能修改这些值,否则会报错
  • 解决只读问题:用自定义变量接收传递过来的值,页面使用自定义变量
  • props可以是对象或数组类型,对象可以对数据做校验,数组不能
    // props的值是只读的 能改,会报错
    <input type="text" v-model="num" />
    //数组格式
    props:["msg","num"]
    //对象格式
     props: {
    msg: {
      //传参类型必须是字符串
      type: String,
      //必须传参
      required: true,
    },
    num: {
      type: Number,
      //如果不传参默认是102
      default: 102,
    },
  },
    //----------------------------------------
    //用自定义变量numa接收num,然后页面使用numa(解决只读问题)
    <h1>我是子组件 coma------{{ msg }}----{{ numa }}</h1>
    <input type="text" v-model="numa" />
    props: ["msg", "num"],
      data() {
        return {
        numa: this.num,
       };
     },
Copier après la connexion

2.子组件向父组件传参

子组件向父组件传参,基于==发布订阅(@xxx给子组件标签自定义事件、$emit)==

第一步:父组件在调用子组件的标签上需要自定义一个事件,这个事件及绑定的方法就会添加到子组件的事件池中:底层实质上是调用了this.$on("myEvent",fn)

<Coma @myEvent="getData"></Coma>
 methods: {
    getData() {},
  },
Copier après la connexion

第二步:子组件用this.$emit()接受(this.$emit(myEvent,参数1,参数2)), 参数可以是子组件的,顺便传给父组件,实现子组件向父组件传值

  <button @click="goParentData">向父组件发送数据</button>
 data() {
    return {
      flag: "你很美",
      n: 101,
    };
  methods: {
    goParentData() {
      //执行父组件自定义的事件
      this.$emit("myEvent", this.flag, this.n);
    },
  },
Copier après la connexion

第三步:父组件使用传递过来的数据

 data() {
    return {
      n: 0,
      flag: "",
    };
  },
  methods: {
    getData(...parans) {
      console.log(parans);
      //传递过来的是数组
      this.n = parans[0];
      this.flag = parans[1];
    },
  },
Copier après la connexion

3.组件之间相互传参 原生事件法 (发布订阅)

b--->c发送数据

  • c向事件池中添加方法(自定义方法):$on
  • b执行方法把参数传过去:$emit

第一步:全局的main.js中创建一个全局的EventBus,挂载 vue的原型上 this.$bus

  • 作用:将EventBus看作定义在公有属性上的事件池(事件公交),之后基于这个$bus.$on()绑定的事件函数,在哪个vue实例上都可以基于$bus.$empty()执行,还可以传值
//创建一个全局的 Eventbus
let Eventbus=new Vue();
//挂载 vue的原型上  后期基于this.$bus
Vue.prototype.$bus=Eventbus;
Copier après la connexion

第二步:comc向事件池中绑定事件:this.$bus.$on("事件名",函数)

  created() {
    //向事件池中添加方法
    this.$bus.$on("myEvent", () => {});
  },
Copier après la connexion

第三步:comb从事件池中获取事件函数并执行:this.$bus.$emit("事件名",想传的参数)

    <button @click="send">发送数据给comc</button>
      data() {
        return {
          msg: "我是comb",
    };
    methods: {
    send() {
      //执行事件池中的方法,并且传参
      this.$bus.$emit("myEvent", this.msg);
    },
Copier après la connexion

第四步 comc使用传递过来的数据

   <h1>组件 comc----{{ msg }}</h1>
     data() {
        return {
          msg: "",
      };
  //创建之后的钩子函数向事件池中添加方法
 created() {
    //向事件池中添加方法
    this.$bus.$on("myEvent", (value) => {
      console.log(value);
      this.msg = value;
    });
  },
Copier après la connexion

4.祖先和后代相互传参

  • 第一步:祖先要使用provide方法传参,不是写在methods里面,与methods同级
 data() {
    return {
      title: "我是about祖先",
    };
  },
  provide() {
    return {
      title: this.title,
    };
  },
Copier après la connexion

第二步:后代使用inject属性接受祖先中的参数,inject是data中的数据,是数组类型

inject: ["title"],因为inject是数组类型,所以它符合如果数据项不是对象类型,则不做劫持,如果数据项是对象,则这个对象中的属性会做劫持。

  data() {
    return {
      title: "我是about祖先",
    };
  },
//祖先 传递的title是非响应式
  provide() {
    return {
      title: this.title,
    };
  },
//------------------------------
  data() {
    return {
      //obj非响应式
      obj: {
        //title是响应式
        title: "我是about祖先",
      },
    };
  },
  //祖先 传递的参数失去响应式,但里面的值会是响应式
  provide() {
    return {
      obj: this.obj,
    };
  },
Copier après la connexion

vue实例属性传参

vue的实例中存在一些属性能够获取不同关系的元素,获取之后就可以基于这个元素获取其中的数据或方法了:

  • $parent 获取父元素的数据/方法 获取父元素的整个vm实例
  • 子组件可以在任何生命周期函数中获取父元素【父子组件的生命周期
  created() {
    console.log(this.$parent.title);
  },
Copier après la connexion
  • $children 获取子元素的数据/方法(mounted钩子函数,要有下标)
  • this.$children[n]:获取第n个子元素的vm实例
  • 父组件只能在mounted生命周期函数里或之后获取子元素【父子组件的生命周期
  mounted() {
    console.log(this.$children[0].msg);
  },
Copier après la connexion
  • $root获取根组件的数据/方法
  • this.$root:获取根元素的vm实例(main.js中new 的Vue实例)
et mv = new Vue({
  router,
  data() {
    return {
      rootmsg: "我是草根"
    }
  },
  render: h => h(App)
}).$mount(&#39;#app&#39;)
---------------------
  mounted() {
    console.log(this.$root.rootmsg);
  },
Copier après la connexion
  • this.$refs:this的子元素中需要定义ref属性:比如ref="xxx"
  • ==如果ref定义在DOM标签中==:this.$refs.xxx获取的是DOM对象
  • ==如果ref定义在子组件标签中==:this.$refs.xxx获取的是子组件的vm实例
  //获取的是dom元素
  <div ref="one">11111</div>
   mounted() {
    console.log(this.$refs.one);
  },
-----------------------------------
获取的是组件
  <comb ref="b"></comb>
  mounted() {
    console.log(this.$refs.b);
  },
//如果不是组件获取的就是dom元素,如果是组件,获取的就是组件的实例
Copier après la connexion

父子组件的生命周期

重点:父组件更新默认不会触发子组件更新,但是**==如果子组件中绑定调用了父组件的数据aaa,父组件的aaa数据更新触发重新渲染时,使用aaa数据{{$parent.aaa}}的子组件也会触发更新==**

一、父子组件生命周期执行过程

  • 父->beforeCreated
  • 父->created
  • 父->beforeMount
  • 子->beforeCreate
  • 子->created
  • 子->beforeMount
  • 子->mounted
  • 父->mounted

二、子组件更新过程:

  • 父->berforeUpdate
  • 子->berforeUpdate
  • 子->updated
  • 父->updated

三、父组件更新过程:

  • 父->berforeUpdate
  • 父->updated

四、父组件销毁过程:

  • 父->beforeDestory
  • beforeDestory
  • destoryed
  • 父->destoryed

扩展------------------------

父组件绑定在子组件标签中的事件,是无法触发的,如何解决?

@xxx.native: 监听组件根元素的原生事件。

  • 例子<my-component @click.native="onClick"></my-component>

  • 原理:在父组件中给子组件绑定一个==原生(click/mouseover...)==的事件,就将子组件变成了普通的HTML标签,不加'. native'父组件绑定给子组件标签的事件是无法触发的

虚拟DOM

虚拟DOM对象:_vnode,作用:

第一步:vue内部自己定义的一套对象,基于自己规定的键值对,来描述视图中每一个节点的特征:

  • tag标签名
  • text文本节点,存储文本内容
  • children:子节点
  • data:属性
  • 第二步:基于vue-template-compiler去渲染解析 template 视图,最后构建出上述的虚拟DOM对象
  • 第三步:组件重新渲染,又重新生成一个 _vnode
  • 第四步:对比两次的 _vnode. 获取差异的部分
  • 第五步:把差异的部分渲染为真实的DOM

组件库

  • element-ui:饿了么
  • antdv :蚂蚁金服
  • iview :京东
  • Element - The world's most popular Vue UI framework
  • ==vue2.xx==:elemnetui
  • ==vue3.xx==:element plus

如何在项目中使用功能性组件?

==第一步==:安装element-ui:$npm i element-ui -s

==第二步==:导入:

完整导入:整个组件库都导入进来,想用什么直接用Vue.use(xxx)即可

缺点:如果我们只用几个组件,则无用的导入组件会造成项目打包体积变大[不好],所以项目中推荐使用按需导入

按需导入

1、需要安装依赖$ npm install babel-plugin-component

样式私有化

在Vue中我们基于scoped设置样式私有化之后:

  • 会给组件创建一个唯一的ID(例如:data-v-5f109989)

  • 在组件视图中,我们编写所有元素(包含元素调用的UI组件),都设置了这个ID属性;但是我们调用的组件内部的元素,并没有设置这个属性!!

   <div data-v-5f1969a9 class="task-box">
      <button data-v-5f1969a9 type="button" class="el-button el-button--primary">
        <span>新增任务</span>
      </button>
    </div>
Copier après la connexion

而我们编写的样式,最后会自动加上属性选择器:

 .task-box {
      box-sizing: border-box;
      ...
    }
---------编译后成为:---------
    .task-box[data-v-5f1969a9]{
      box-sizing: border-box;
    }
Copier après la connexion
  • ==组件样式私有化的原理==:设置唯一的属性(组件ID)、组件内部给所有样式后面都加上该属性选择器
  • ==问题==:组件内部的元素没有设置这个属性,但是我们编写的样式是基于这个属性选择器在css设置的选择器,
  • ==解决==:在组件内部的元素选择器前加/deep/:
    /deep/.el-textarea__inner,
    	/deep/.el-input__inner{
   		 border-radius: 0;
 		 }
Copier après la connexion

API

  • 在真实项目中,我们会把数据请求和axios的二次封装,都会放到src/api路径下进行管理

//main.js
	import api from &#39;@/api/index&#39;;
	// 把存储接口请求的api对象挂载搭配Vue的原型上:
    后续在各个组件基于this.$api.xxx()就可以发送请求了,无需在每个组件中再单独导入这个api对象。
	Vue.prototype.$api=api;
Copier après la connexion

【相关推荐:javascript视频教程web前端

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
vue
source:csdn.net
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal