Vue组件基础内容包括哪些

北京中科白瘕风 http://disease.39.net/yldt/bjzkbdfyy/

主要内容

Vue组件自定义事件插槽动态组件和异步组件边界处理情况组件生命周期过渡动画组件复用性自定义指令过滤器渲染函数JSXVue渐进式ElementUI组件库学习目标

第一节Vue组件基础

Vue组件自定义事件

不同于组件和prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个camelCase名字的事件

不同于组件和prop,事件名不会被用作一个JavaScript变量名或属性名,所以就没有理由使用camelCase或PascalCase了。并且v-on事件监听器在DOM模板中会被自动转换为全小写(因为HTML是大小写不敏感的),所以v-on:myEvent将会变成v-on:myevent——导致myEvent不可能被监听到。

因此,我们推荐你始终使用kebab-case的事件名

.sync修饰符

在有些情况下,我们可能需要对一个prop进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。

这也是为什么我们推荐以update:myPropName的模式触发事件取而代之。举个例子,在一个包含titleprop的假设的组件中,我们可以用以下方法表达对其赋新值的意图:

然后父组件可以监听那个事件并根据需要更新一个本地的数据属性。例如:

为了方便起见,我们为这种模式提供一个缩写,即.sync修饰符:

插槽

插槽内容

Vue实现了一套内容分发的API,这套API的设计灵感源自WebComponents规范草案,将slot元素作为承载分发内容的出口。

它允许你像这样合成组件:

然后你在navigation-link的模板中可能会写为:

当组件渲染的时候,slot/slot将会被替换为“YourProfile”。插槽内可以包含任何模板代码,包括HTML:

甚至其它的组件:

如果navigation-link没有包含一个slot元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

编译作用域

当你想在一个插槽中使用数据时,例如:

该插槽跟模板的其它地方一样可以访问相同的实例属性(也就是相同的“作用域”),而不能访问navigation-link的作用域。例如url是访问不到的:

作为一条规则,请记住:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

后备内容

有时为一个插槽设置具体的后备(也就是默认的)内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个submit-button组件中:

我们可能希望这个button内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在slot标签内:

现在当我在一个父级组件中使用submit-button并且不提供任何插槽内容时:

后备内容“Submit”将会被渲染:

但是如果我们提供内容:

则这个提供的内容将会被渲染从而取代后备内容:

具名插槽

有时我们需要多个插槽。例如对于一个带有如下模板的base-layout组件:

对于这样的情况,slot元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:

一个不带name的slot出口会带有隐含的名字“default”。

在向具名插槽提供内容的时候,我们可以在一个template元素上使用v-slot指令,并以v-slot的参数的形式提供其名称:

现在template元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有v-slot的template中的内容都会被视为默认插槽的内容。

然而,如果你希望更明确一些,仍然可以在一个template中包裹默认插槽的内容:

任何一种写法都会渲染出:

注意v-slot只能添加在一个template上(只有一种例外情况),这一点和已经废弃的slot特性不同。

作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的current-user组件:

我们想让它的后备内容显示用户的名,以取代正常情况下用户的姓,如下:

然而上述代码不会正常工作,因为只有current-user组件可以访问到user而我们提供的内容是在父级渲染的。

为了让user在父级的插槽内容中可用,我们可以将user作为slot元素的一个特性绑定上去:

绑定在slot元素上的特性被称为插槽prop。现在在父级作用域中,我们可以给v-slot带一个值来定义我们提供的插槽prop的名字:

在这个例子中,我们选择将包含所有插槽prop的对象命名为slotProps,但你也可以使用任意你喜欢的名字。

具名插槽的缩写

跟v-on和v-bind一样,v-slot也有缩写,即把参数之前的所有内容(v-slot:)替换为字符#。例如v-slot:header可以被重写为#header:

然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:

如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:

动态组件异步组件

在动态组件上使用keep-alive

我们之前曾经在一个多标签的界面中使用is特性来切换不同的组件:

当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。

重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个keep-alive元素将其动态组件包裹起来。

修改后的结果,组件的状态可以被缓存

异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:

如你所见,这个工厂函数会收到一个resolve回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用reject(reason)来表示加载失败。这里的setTimeout是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和webpack的code-splitting功能一起配合使用:

你也可以在工厂函数中返回一个Promise,所以把webpack2和ES语法加在一起,我们可以写成这样:

当使用局部注册的时候,你也可以直接提供一个返回Promise的函数:

处理加载状态

这里的异步组件工厂函数也可以返回一个如下格式的对象:

第二节组件基础与生命周期

处理边界情况

访问元素组件

在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作DOM元素。不过也确实在一些情况下做这些事情是合适的。

访问根实例

在每个newVue实例的子组件中,其根实例可以通过$root属性进行访问。例如,在这个根实例中:

所有的子组件都可以将这个实例作为一个全局store来访问或使用。

访问父级组件实例

和$root类似,$parent属性可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以prop的方式传入子组件的方式。

另外在一些可能适当的时候,你需要特别地共享一些组件库。举个例子,在和JavaScriptAPI进行交互而不渲染HTML的抽象组件内,诸如这些假设性的Google地图组件一样:

这个google-map组件可以定义一个map属性,所有的子组件都需要访问它。在这种情况下google-map-markers可能想要通过类似this.$parent.getMap的方式访问那个地图,以便为其添加一组标记。你可以在这里查阅这种模式。

请留意,尽管如此,通过这种模式构建出来的那个组件的内部仍然是容易出现问题的。比如,设想一下我们添加一个新的google-map-region组件,当google-map-markers在其内部出现的时候,只会渲染那个区域内的标记:

那么在google-map-markers内部你可能发现自己需要一些类似这样的hack:

很快它就会失控。这也是我们针对需要向任意更深层级的组件提供上下文信息时推荐依赖注入的原因。

访问子组件实例或子元素

尽管存在prop和事件,有的时候你仍可能需要在JavaScript里直接访问一个子组件。为了达到这个目的,你可以通过ref特性为这个子组件赋予一个ID引用。例如:

现在在你已经定义了这个ref的组件里,你可以使用:

来访问这个base-input实例,以便不时之需。比如程序化地从一个父级组件聚焦这个输入框。在刚才那个例子中,该base-input组件也可以使用一个类似的ref提供对内部这个指定元素的访问,例如:

甚至可以通过其父级组件定义方法:

这样就允许父级组件通过下面的代码聚焦base-input里的输入框:

当ref和v-for一起使用的时候,你得到的引用将会是一个包含了对应数据源的这些子组件的数组。

组件生命周期

每个Vue实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

比如created钩子可以用来在一个实例被创建之后执行代码:

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如mounted、updated和destroyed。生命周期钩子的this上下文指向调用它的Vue实例。

生命周期图示

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高

组件复用性

这里我们又回到data函数状态下,data必须是一个纯函数。

当我们定义这个button-counter组件时,你可能会发现它的data并不是像这样直接提供一个对象:

取而代之的是,一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

如果Vue没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例:

这里可以增加列表性实例的引用。

第三节VueAPI

过渡动画

Vue在插入、更新或者移除DOM时,提供多种不同方式的应用过渡效果。

包括以下工具:

在CSS过渡和动画中自动应用class可以配合使用第三方CSS动画库,如Animate.css在过渡钩子函数中使用JavaScript直接操作DOM可以配合使用第三方JavaScript动画库,如Velocity.js单元素/组件的过渡

Vue提供了transition的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

条件渲染(使用v-if)条件展示(使用v-show)动态组件组件根节点这里是一个典型的例子:

当插入或删除包含在transition组件中的元素时,Vue将会做以下处理:

1.自动嗅探目标元素是否应用了CSS过渡或动画,如果是,在恰当的时机添加/删除CSS类名。

2.如果过渡组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用。

3.如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM操作(插入/删除)在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,和Vue的nextTick概念不同)

过渡类名

在进入/离开的过渡中,会有6个class切换。

1.v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

2.v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

3.v-enter-to:2.1.8版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效(与此同时v-enter被移除),在过渡/动画完成之后移除。

4.v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

5.v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

6.v-leave-to:2.1.8版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效(与此同时v-leave被删除),在过渡/动画完成之后移除。

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的transition,则v-是这些类名的默认前缀。如果你使用了transitionname=my-transition,那么v-enter会替换为my-transition-enter。

v-enter-active和v-leave-active可以控制进入/离开过渡的不同的缓和曲线,在下面章节会有个示例说明。

CSS过渡

常用的过渡都是使用CSS过渡。

下面是一个简单例子:

CSS动画

CSS动画用法同CSS过渡,区别是在动画中v-enter类名在节点插入DOM后不会立即删除,而是在animationend事件触发时删除。

自定义过渡的类名

我们可以通过以下特性来自定义过渡类名:

enter-class

enter-active-class

enter-to-class(2.1.8+)

leave-class

leave-active-class

leave-to-class(2.1.8+)

他们的优先级高于普通的类名,这对于Vue的过渡系统和其他第三方CSS动画库,如Animate.css结合使用十分有用。

自定义指令

除了核心功能默认内置的指令(v-model和v-show),Vue也允许注册自定义指令。注意,在Vue2.0中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子

当页面加载时,该元素将获得焦点(注意:autofocus在移动版Safari上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:

如果想注册局部指令,组件中也接受一个directives的选项:

然后你可以在模板中任何元素上使用新的v-focus属性,如下:

钩子函数

一个指令定义对象可以提供如下几个钩子函数(均为可选):

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。update:所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新(详细的钩子函数参数见下)。


转载请注明:http://www.aierlanlan.com/tzrz/5484.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了