Vue组件(知识)

form最后一节。


组件基础

  • 组件的复用:  data必须是函数
  • 组织
  • 通过Prop向子组件传递data
  • 单个根元素
  • 通过event向父组件发送消息: 使用事件抛出一个value, 在组件上用v-model
  • 动态组件
  • 解析DOM模版时的⚠️.

深入组件

  • 组件注册
  • Prop
  • 自定义事件: this.$emit('my-event')用kebab-case做事件名称
  • 插槽
  • 异步组件
  • 处理边界情况


例子:

组件是可复用的 Vue 实例,且带有一个名字,如Vue.component("名字", {data..., template...})

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

组件可以复用


无限次反复用比如上例。多次使用<button-counter>, 每用一个,就创建了一个实例。

data是函数

和新建Vue中的data:{...}不一样,

data必须是函数 data: function(){...},  这是为了每个实例可以维护一份被返回对象的独立的拷贝。

即data不是共用的数据集合,它是函数,是方法,通过它每个实例都会得到各自的结果。

组件的组织


为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册

  • Vue.component 全局注册:
  • 全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

(深入看: 组件注册

通过 Prop 向子组件传递数据


Prop 是你可以在组件上注册的一些自定义特性

当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性

可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

单个根元素


每个组件只能有一个根元素,一般使用<div>包裹其他元素。

否则会报错 every component must have a single root element

⚠️:

JavaScript 的模板字符串来让多行的模板更易读。

它们在 IE 下并没有被支持,所以如果你需要在不编译的情况下支持 IE,请使用折行转义字符取而代之。

什么是模版字符串:

  • 模板字符串使用反引号 (` `) 来代替普通字符串中的用双引号和单引号。
  • 模板字符串可以包含特定语法(${expression})的占位符。

折行转义字符

在多行string每行结尾加上反斜杠    

var htmlSTring = "<div>

 This is a string.

</div>";

通过事件向父级组件发送消息


案例: https://codepen.io/chentianwei411/pen/EeXxrM/?editors=1010 

分析:

在我们开发 <blog-post> 组件时,它的一些功能可能要求和父级组件进行沟通。

例如我们可能会引入一个可访问性的功能来放大博文的字号,同时让页面的其它部分保持默认的字号。

注意:⚠️:

1.自定义组件可以使用v-for,但必须配合使用v-bind:key 

2. 任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props :

<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
  • 明确组件数据的来源能够使组件在其他场合重复使用。

3. vm.$emit( eventName, […args] )  (点击看api)关于事件的实例方法。

  • 触发当前实例上的事件。附加参数都会传给监听器回调。

  • 监听器指v-on,用于监听当前实例上的自定义事件,事件可以由vm.$emit触发,

参数的上传:

第一种:

在监听v-on:enlarge-text用$event获得上传的参数:v-on:enlarge-text="postFontSize += $event" 

第二种:

如果这个事件处理函数是一个方法,那么这个值将会作为第一个参数传入这个方法:

1. v-on:enlarge-text='onEnLargeText'

2.在nev Vue中添加这个方法:

methods: {
 onEnlargeText: function(enlargeAmount) {
  this.postFontSize += enlargeAmount
 }
}

 

在组件上用v-model 

点击:查看(详细分析了数据的流入和流出。及相关。)

还需要⚠️:

自己写的案例分析:https://codepen.io/chentianwei411/pen/bxozoj?editors=1010

在DOM上增加了一个组件实例<custom-input>,

  • 这个组件把外部数据传给 prop特性value, 
  • 有一个监听v-on:input事件。

关于$event.target.value

$event是触发的事件,这里是在输入框输入的动作。

$event.target是这个动作作用在哪个元素上。target特性返回被事件激活的元素。这里是输入框input元素。

value指的是input中的value特性,即输入的内容。

el的作用

定位html中的元素,这个元素将作为Vue实例的挂载目标。在实例挂载后,元素可以用vm.$el访问。

template: Vue实例的模版

模版会替换被挂载的html元素,被挂载的元素的内容会被忽略,除非使用inline-template。

 <custom-input v-bind:value="searchText"

这是把初始化中的data属性中的数据对象和自定义的prop特性value绑定在一切。

 <custom-input v-on:input='searchText = $event'

用于监听input事件,并对参数进行回调处理。

template中的 <input v-bind:value='value'> 

把Props: ['value']特性的值赋予了<input>的value属性。

template中的<input v-on:input=''"$emit('input', $event.target.value)">

  1. 当监听到input这个原生HTML事件时, 执行$emit这个实例方法。
  2. $emit会触发当前Vue实例上的自定义事件input(自定义,自定义,自定义),并传递参数。
  3. Vue实例上的监听器v-on会监听到这个触发事件input,并执行预期的行为。这里是执行一条inline statement。

动态组件

根据‘组件名称’来动态的调用组件。类似动态调用方法。把动态和异步组件读完。

<component v-bind:is='组件名称'></component>

用法:

渲染‘元组件’为动态组件。 根据v-bind:is的值,来决定渲染哪个组件。

这里是根据vm的计算属性中currentTabComponent函数来得到组件的名称

内置的组件:

component

Props: 

  • is - string | ComponentDefinition | ComponentConstructor
  • inline-template -boolean

在动态组件上使用keep-alive缓存组件

keep-alive

目的:提高效能:保留组件状态,或避免重新渲染。

Props:

  • include -字符串或正则表达式。 匹配的组件会被cache
  • exclude -字符串或正则表达式。任何匹配的组件都不会被cache

用法:

  • <keep-alive>包裹动态组件</keep-alive>, cache不活动的组件,不会销魂它们。
  • <keep-alive>是一个抽象组件,不会渲染DOM元素,不会出现在父组件链中。

钩子函数:(这两个钩子,放到声明组件的代码中)

  • activated: 在keep-alive组件激活时用
  • deactivated: 在keep-alive组件停用时调用。

Vue.component('tab-home', {
 template: '<div>Home component</div>',
 activated() {
  console.log('activatsse')
 },
 deactivated() {
  console.log('deactivated')
 }
})


异步组件

以工厂函数的方式定义组件。

Vue.component('async', function(resolve, reject) {  ...  })

这个工厂函数会异步解析你的组件定义。 

在这个组件被渲染的时候,会触发该工厂函数,并把结构缓存起来供未来重新渲染。

工厂函数?

当一个函数返回一个对象时,并且函数没有使用new关键字,这个函数称为factory function。

不是类,也不是构造函数。

个人理解:工厂函数好像就是类,传递不同的参数,返回不同的对象,但对象们的结构都一样,只是值不一样。

const noop = () => { foo: 'bar' };
console.log(noop()); //返回 undefined, 使用大括号,JavaScript默认你想要创建一个函数体。
 
const createFoo = () => ({ foo: 'bar' });
console.log(createFoo()); //返回 { foo: "bar" },大括号外面加上小括号,大括号里面的内容被解释为表达式

将异步组件和 webpack 的 code-splitting 功能一起配合使用,未学习这个功能!!
 

 

Promise是什么?(点击看api)

new Promise( function(resolve, reject) {...} );

一个代表了异步操作最终完成或者失败的对象。大多数人都在使用由其他函数创建并返回的promise.

promise本质上是一个绑定了回调的对象.

  • 通过.then形式添加回调函数。
  • 通过多次调用.then,可以添加多个回调函数。
  • 链式调用,凉鞋执行多个异步操作。  

诞生的原因猜测: 传统的多重回调的写法是各地狱,因此改用promise的方式。

   回调地狱:崩溃的写发和读发:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

现代的方法:
doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('Got the final result: ' + finalResult); }) .catch(failureCallback);

或者使用箭头函数:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);




解析 DOM 模板时的注意事项 

部分HTML标签限制子元素的标签类型,

如<ul>内部只能是<li>,如果在里面用自定义组件则会渲染报错。

可以使用is特性,进行变通:

<table>
<tr is="blog-post-row"></tr>
</table>

但是⚠️,如果从以下来源使用模板的话,这条限制是不存在的:

案例: 列表渲染一章,最下面


Class用在组件上

  • 可以直接在组件上写:
    <my-component class="baz boo"></my-component>
  • 也可以用在v-bind:class='{active: isActive}'上面, 当isActive是true时,类加上active.
<p class="foo bar active">Hi</p>

深入Prop  

camelCase vs kebab-case

当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

 当定义组件的时候,即在JavaScript中用:props: ['postTitle']

Prop类型:

  • 使用字符串数组的形式:
    props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
  • 使用对象形式
  • 使用对象形式:可以进行Prop的验证。
    • type,  可以是任意类型null, 多个类型的任意一种(用数组)
    • required: true/false  是否必须填
    • default,默认可以是值,也可以是function(){ return ...}
    • 自定义验证函数validator
  • 用到这个组件的时候,传入的数据必须符合prop指定的类型,并通过验证,否则browser的控制台会提示❌)
props: {
title: null,         #type可以任意类型,用null
likes: [Number, String, Date] #可以设置多个类型。
isPublished: Boolean,
commentIds: Array,
author: [Object, Function, Symbol]
}

传递静态或者动态Prop类型:

 需要用 v-bind, 即使数据是静态的,仍然需要 v-bind来告诉Vue:

 <blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>

 或者用一个变量进行动态赋值:

 <blog-post v-bind:comment-ids="post.commentIds"></blog-post>

 ⚠️(即使加了双括号v-bind="xxxxx"这还是Javascript表达式)

可以传递任何类型的值给一个prop。每个类型略有使用区别:

  • 传入一个数字:
  • 传入一个boolean:
  • 传入一个数组:
  • 传入一个对象:
  • 传入一个对象的所有属性。绑定对象名即可 v-bind:prop-name

父子组件中的prop的数据流动是单向的:

但⚠️: JS中对象和数组是通过引用传入的。所以对这两种类型的prop, 在子组件中改变这个对象或数组,改变的是对象和数组本身,因此会影响到父组件的状态。

非Prop的特性

一个非prop特性是:传向一个组件的特性,但该组件没有对应prop定义的特性。

这个非prop特性会添加到这个组件的根元素上。

因为组件的作者不能遇见组件会被什么场景使用,为了让组件更加的灵活,所以组件可以接受任意的特性。

禁用特性继承

Vue.component('my-component', {
inheritAttrs: false,
// ...
})

自定义事件


 推荐你始终使用 kebab-case 的事件名。

自定义组件v-model: 见上面用v-model案例,或者api。其实就是语法糖。

将原生事件绑定到组件

在v-on上使用修饰符.native,它用于监听根元素上的原生事件。

base-input v-on:focus.native="onFocus"></base-input>

案例:

JS对象知识点)知识点:

A Javascript object is a collection of named values

Object是mutable: 对象的地址是引用的,不是by value。

JS variable是by value的。

Object.assign(target, ..sources) 返回一个新的Object,他的值包括target和sources,但不重复同时value会被覆盖。

vm.$listeners

一个对象,包含父作用域(不含.native)的事件监听器。

可以通过v-on='$listeners'把它传入内部组件,即父组件上的事件监听,也可以用在内部组件中了。

vm.$attrs(没有案例,不是很懂。)

类型: { |key: string|: string}

详细: 包含了父作用域中不作为prop被识别获取的特性绑定。可以用v-bind='$attrs'传入内部组件。

inheritAttrs选项和$attrs是一对儿。同时出现在2.4版本

选项/其他 #inheritAttrs

类型 boolean

详细:当写包裹一个目标元素(或另一个组件)的组件时,目标元素(或子组件)不能得到父作用域的非prop特性绑定。这些特性会‘回退’并且作为普通的HTML特性应用在目标元素上(或子组件的根元素上)。

去掉这个默认行为,可以使用inheritAttrs: false。不继承属性。

想要继承特性绑定,可以使用v-bind='$attrs',把这些特性绑定到目标元素(非根元素)上。

inheritAttrs选项和$attrs是一对儿。同时出现在2.4版本


.sync修饰符 没有看


<solt>插槽

通过slot可以分发内容。

Vue自定义的标签。见:插槽

理解: 组件实例内的html会被保留。

<solt> 有一个特别的name特性,用于和其他solt区别

  • 可以在父组件的<template>元素上使用slot的name特性
  • 可以直接用在一个普通的元素上

插槽的默认内容

可以为solt提供默认内容。如在template中有一个插槽

  • 如果组件实例内没有html内容就会使用插槽默认内容。
  • 如果父组件为这个插槽提供了内容,默认的内容就会被替换掉。 

编译作用域 (不是很理解,没有案例)

<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>

可以在组件实例中的插槽位置加上{{xxx}},来插入数据。

但不能访问url, 这属于<navigation-link> 的作用域。

  • 父组件模板的所有东西都会在父级作用域内编译;
  • 子组件模板的所有东西都会在子级作用域内编译。 

作用域slot 


处理边界情况(特殊情况)

访问元素/组件

有些特定情况,需要触及另一个组件实例内部,或手动操作DOM元素。

访问子组件实例/子元素

除了prop,event, 有时需要在JS中直接访问一个子组件。

方法:

通过使用ref特性,为子组件/元素赋予一个id引用。

例如,这个子组件:

<base-input ref="username"></base-input>

然后,用this.$refs.username来访问组件实例。

注意⚠️: ref的使用是非响应式的,并且只会在组件渲染完成后生效。所以尽量避免使用它。

可以用于从父组件聚焦一个输入框:

<input ref="input">

然后添加一个方法:
methods: { // 用来从父级组件聚焦输入框 focus: function () { this.$refs.input.focus() } }
原文地址:https://www.cnblogs.com/chentianwei/p/9600104.html