vue基础(2):深入理解 Vue 组件、Vue中的动画特效

文章目录

  • 一、深入理解 Vue 组件
    • 1.1 使用组件容易遇到的坑
      • 1.1.1 使用`is='组件名'`解决html表签嵌套规范造成的bug
        • 解决方案
      • 1.1.2 非根组件(子组件)中`data`必须是函数,返回一个对象。
      • 1.1.3 使用引用`ref="引用名"`获取dom元素和组件引用
      • 1.1.4 子组件的单个根元素
    • 1.2 父子组件间的数据传递
      • 1.2.1 父组件传值给子组件
      • 1.2.2 子组件传值给父组件
    • 1.3 组件参数校验与非 props 特性
      • 1.3.1 组件参数校验
      • 1.3.2 非props特性
    • 1.4 自定义组件绑定原生事件
      • 1.4.1 在路由``上绑定原生事件
    • 1.5 非父子组件间的传值
      • 1.5.1 利用`bus`(总线机制/发布订阅模式/观察者模式)解决非父子组件间传值问题
    • 1.6 Vue中使用插槽``
      • 1.6.1 插槽``的作用
      • 1.6.2 插槽``基本用法
        • 多个``重复渲染的问题
      • 1.6.3 具名插槽
      • 1.6.4 插槽定义默认值
      • 1.6.5 作用域插槽
    • 1.7 动态组件``
      • 在子组件的根元素上使用`v-once`指令,可以提升效率
  • 二、Vue中的动画特效
    • 2.1 Vue动画或过渡原理
    • 2.2 在vue中使用动画 `关键帧@keyframes`
    • 2.3 利用自定义过渡类名使用第三方动画库`animate.css`
      • 2.3.1 自定义过渡类名
      • 2.3.2 使用第三方动画库`animate.css`
    • 2.4 解决第一次刷新页面没有动画的问题
    • 2.5 在vue中同时使用过渡和动画
      • 2.5.1 使用`type`使过渡和动画同时结束
      • 2.5.2 自定义时长`:duration`
    • 2.6 Vue中的 Js 动画和使用第三方js动画库`Velocity.js`
      • 2.6.1 Vue中的JS动画
      • 2.6.2 使用第三方js动画库`Velocity.js`
    • 2.7 Vue中的多个元素或组件过渡
      • 2.7.1 Vue中的多个元素过渡
      • 2.7.2 Vue中多个组件过渡
    • 2.8 Vue中的列表过渡``
    • 2.9 使用`component`和`slot`封装动画

一、深入理解 Vue 组件

1.1 使用组件容易遇到的坑

总结一些组件再使用时容易遇到的坑:

1.1.1 使用is='组件名'解决html表签嵌套规范造成的bug

html嵌套规范,会造成bug的标签有table、ul(有的浏览器会)、select等

	<div id="app">
		<table>
			<tbody>
				<row>row>
				<row>row>
				<row>row>
				<row>row>
			tbody>
		table>		
	div>
	<script type="text/javascript">
		Vue.component('row',{
			template: '这是表格',
		});
		var vm = new Vue({
			el: '#app',
		});
	script>

标签被渲染到

外面了。
vue基础(2):深入理解 Vue 组件、Vue中的动画特效_第1张图片

解决方案

	<div id="app">
		<table>
			<tbody>
                <tr is="row">tr>
                <tr is="row">tr>
                <tr is="row">tr>
                <tr is="row">tr>
			tbody>
		table>		
	div>
	<script type="text/javascript">
		Vue.component('row',{
			template: '
', }); var vm = new Vue({ el: '#app', }); script>


1.1.2 非根组件(子组件)中data必须是函数,返回一个对象。

子组件不像是根组件只会有一个,既然注册了子组件就是想要复用,为了保证每一个子组件数据的唯一性,避免多个子组件数据共享,通过函数将数据存在到独立的函数作用域中。

    <div id="app">
        <table>
            <tbody>
                <tr is="row">tr>
                <tr is="row">tr>
                <tr is="row">tr>
                <tr is="row">tr>
            tbody>
        table>
    div>
    <script type="text/javascript">
    Vue.component('row', {
        template: "
", data: function() { return { num: 0, }; }, methods: { addNum: function() { this.num++; }, } }); var vm = new Vue({ el: '#app', }); script>


1.1.3 使用引用ref="引用名"获取dom元素和组件引用

虽然使用vue我们只需要关心数据,不用操作dom。但是复杂的动画就不要指望vue,还是要使用dom。

1.使用ref获取dom元素

  • 通过在html标签上加ref="xxx"属性,可通过this.$refs.xxx取得该dom节点
    <div id="app">
		<div ref="hello" @click="getDom">{{message}}div>
    div>
    <script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {
        	message: ' hello poorpenguin',
        },
        methods: {
        	getDom: function(){
        		console.log(this.$refs.hello);
        	}
        }
    });
    script>

在这里插入图片描述

2.使用ref获取组件引用

  • 组件上添加了ref属性,可以取得的是该组件的引用(也可以理解为该组件对象),因此可以访问该组件上的属性
这只是举个例子,并不推荐这样写
	<div id="app">
		<row ref="numOne" @numchange="changeTotal">row>
		  +  
		<row ref="numTwo" @numchange="changeTotal">row>
		  =  {{total}}
    div>
    <script type="text/javascript">
   		Vue.component('row',{
   			template: '{{number}}',
   			data: function(){
   				return {
   					number: 0,
   				};
   			},
   			methods: {
   				addNum: function(){
   					this.number++;
   					this.$emit('numchange');
   				}
   			}
   		});

	    var vm = new Vue({
	        el: '#app',
	        data: {
	        	total: 0,
	        },
	        methods: {
	        	changeTotal: function(){
	        		this.total = this.$refs.numOne.number + this.$refs.numTwo.number;
	        	}
	        }

	    });
    script>

两个组件对象。
在这里插入图片描述
在这里插入图片描述


1.1.4 子组件的单个根元素

当子组件的模板template中有多个dom元素的时候,要使用一个元素将他们包裹起来。每个组件必须只有一个根元素,所有要将模板的内容包裹在一个父元素内。

		模板中这样写是会报错的。
		var counter = {
			props: ['count'],
			template: '123123

poorpenguin

{{count}}
'
, }; 这样式正确的写法 var counter = { props: ['count'], template: '
123123

poorpenguin

{{count}}
'
, };



1.2 父子组件间的数据传递

1.2.1 父组件传值给子组件

  1. 父组件通过 属性形式 传值给子组件(不一定要使用v-bind将属性和根组件中的变量绑定)

不使用指令v-bind,直接使用属性进行传值。但是这样" "中的就不是js表达式,子组件接收到的就是字符串

	<div id="app">
		<counter count="3">counter>
	div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			template: '
{{count}}
'
, }; var vm = new Vue({ el: '#app', components: { 'counter': counter, } });
script>

使用指令v-bind,就可以将属性和根组件中的变量进行绑定,这时" "中就是js表达式,也可以直接传递数字。

	<div id="app">
		<counter :count="num1">counter>	这里使用v-bind命令
		或者<counter :count="3">counter>	这样传递的也是数字,而不是字符串
	div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			template: '
{{count}}
'
, }; var vm = new Vue({ el: '#app', data: { num1: 3, }, components: { 'counter': counter, } });
script>
  1. 子组件通过props[]接收传递过来的属性,但是不能直接操作接收的属性

子组件通过props[]接收传递过来的属性,并将传递过来的属性赋值给在data中定义新属性。
因为单项数据流,修改Object的值会导致别的引用了该对象的子组件内数据的变化,用在子组件内复制一份该对象,修改子组件内自己的data来代替。

	<div id="app">
		<counter :count="num1">counter>
	div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '
{{number}}
'
, methods: { addNum: function(){ this.number++ }, } }; var vm = new Vue({ el: '#app', data: { num1: 3, }, components: { 'counter': counter, } });
script>

错误示范:

	子组件
		var counter = {
			props: ['count'],
			template: '
{{count}}
'
, methods: { addNum: function(){ this.count++ }, } };



1.2.2 子组件传值给父组件

  1. 子组件事件触发的形式this.$emit("自定义事件名","传给父组件的数据"),向父组件传值
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '
{{number}}
'
, methods: { addNum: function(){ this.number++ this.$emit('numchange',this.number); }, } };
  1. 父组件通过@自定义事件名="父组件的方法“监听子组件传值
	<div id="app">
		<counter :count="num1" @numchange="show">counter>	监听子组件的事件
	div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '
{{number}}
'
, methods: { addNum: function(){ this.number++ this.$emit('numchange',this.number,'阿西吧'); }, } }; var vm = new Vue({ el: '#app', data: { num1: 3, }, components: { 'counter': counter, }, methods: { show: function(value,str){ 接收子组件的传值 console.log(value,str); }, } });
script>



1.3 组件参数校验与非 props 特性

1.3.1 组件参数校验

1.3.2 非props特性

props 特性:父组件向子组件传递一个属性,子组件通过 props 声明了相应属性进行接收,在 DOM 中不会显示该属性。

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin">my-component>
	div>
	<script type="text/javascript">
		Vue.component('my-component',{
			props: ['content','title'],
			template: `

{{title}}

{{content}}
`
, }); var vm = new Vue({ el: '#app', });
script>

在这里插入图片描述

非props特性:父组件向子组件传递一个属性,子组件没有通过 props 声明相应属性进行接收:
1、子组件中是无法使用父组件传递过来的值
2、该属性会显示在子组件模板中的根元素上

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin">my-component>
	div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `

123

123
`
, }); var vm = new Vue({ el: '#app', });
script>

vue基础(2):深入理解 Vue 组件、Vue中的动画特效_第2张图片




1.4 自定义组件绑定原生事件

什么是原生事件

  • 在dom元素上绑定的事件就是原生事件
  • 在子组件(自定义组件)的模板template中dom元素绑定的也是原生事件。
	<div id="app">
		<my-component title="标题1" content="hello poorpenguin">my-component>
		<div @click="clickDiv">div>
	div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `

123

123
`
, methods: { clickDiv: function(){ alert('这是子组件中原生事件'); } } }); var vm = new Vue({ el: '#app', methods: { clickDiv: function(){ alert('这是原生事件'); } } });
script>

什么是自定义事件

  • 在自定义的组件上使用@click是无法直接触发click事件的,vue会将其当作自定义的事件,需要在组件内去触发这个自定义的事件才会执行。

  • 在自定义组件里通过 this.$emit(‘事件名’) 触发绑定在该组件上对应的自定义事件

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin" @click="show">my-component>
		<div @click="clickDiv">div>
	div>
	....
  • 如果需要在自定义的组件上直接实现@click事件需要使用@click.native监听原生点击事件。
		<my-component @click.native="handleClick">my-component>
	div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `

123

123
`
, }); var vm = new Vue({ el: '#app', methods: { handleClick: function(){ alert('自定义事件'); } } });
script>



1.4.1 在路由上绑定原生事件

在router-link上直接绑定点击事件是不起作用的,需要使用.native修饰符才能绑定原生事件

<router-link to="/" tag="div" class="header-abs" v-show="isShow" @click.native="removeScrollListener">
	<i class="iconfont">i>
router-link>




1.5 非父子组件间的传值

一个网页可以拆分成多个组件,根组件下有多个子组件,每一个子组件下也有许多子组件。

  • 父子组件,就是具备父子关系的组件(如下图1和2就是父子组件)
  • 非父子组件,就是两个组件传值,但是两个组件不具备父子关系。(如下图的1和33和3就是分父子组件)
    vue基础(2):深入理解 Vue 组件、Vue中的动画特效_第3张图片

解决非父子组件间的传值,有两个解决方案

  1. 使用Vuex
  2. 利用vue的总线机制。

1.5.1 利用bus(总线机制/发布订阅模式/观察者模式)解决非父子组件间传值问题

前面强调过,每一个组件都是一个Vue实例,如果在Vue的原型prototype上定义一个属性,那后续实例化出来的根组件还是子组件都会有这个属性。

利用bus解决的三步骤:

  1. 在Vue的prototype上定义bus属性 Vue.prototype.bus = new Vue();
  2. 在组件的mounted生命周期钩子里使用this.bus.$on(‘事件名’, function(value){});来监听所定义的bus属性上对应的事件被触发,然后在回调函数里进行操作。
  3. 在组件的methods定义的方法里使用 this.bus.$emit(‘事件名’, value); 触发事件并传值。

兄弟组件间的值传递:

	<div id="app">
		<child content="poor">child>
		<child content="penguin">child>
	div>
	<script type="text/javascript">
		//在Vue的原型上定义一个bus属性,并传值一个Vue实例
		Vue.prototype.bus = new Vue();

		Vue.component('child',{
			props: {
				content: String,
			},
			template: '
{{selfContent}}
'
, data: function(){ return { selfContent: this.content, }; }, mounted: function(){ var this_ = this; //这里保存一下this, this.bus.$on('childchange',function(value){ //这里的this指向bus,所以要在外面保存this this_.selfContent = value; }); }, methods: { handleClick: function(){ this.bus.$emit('childchange',this.selfContent); }, } }); var vm = new Vue({ el: '#app', });
script>



1.6 Vue中使用插槽

1.6.1 插槽的作用

插槽在Vue中很重要,如果想要父组件向子组件传递dom元素,子组件在模板template展现传递过来的dom元素,就需要用来插槽。

父组件使用属性传递dom元素

有人会说可以使用属性传递dom元素啊

	<div id="app">
		<child content="

poorpenguin

"
>
child> div> <script type="text/javascript"> var child = { props: ['content'], template: `

属性传递

{{content}}
`
, }; var vm = new Vue({ el: '#app', components: { 'child': child, } });
script>

vue基础(2):深入理解 Vue 组件、Vue中的动画特效_第4张图片
可以发现HTML标签并没有被解析。
那使用v-html命令

	template: `

属性传递

{{content}}
`
,

虽然HTML标签被解析了,但是外面又包裹了一层

这是表格
{{num}}