vue 相关

hello world

知识点:hello world 结构

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <script>
    Vue.createApp({
      template: '<div>hello world</div>'
    }).mount("#root");
  </script>
</body>
</html>

定时器例子

知识点: 数据绑定

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <script>
    Vue.createApp({
      data() {
        return {
          count: 1
        }
      },
      template: '<div>{{count}}</div>',
      mounted() {
        setInterval(() => {
          this.count++;
        }, 1000);
      }
    }).mount("#root");
  </script>
</body>
</html>

字符串反转例子

知识点: v-on 监听事件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <script>
    Vue.createApp({
      data() {
        return {
          content: 'hello world'
        }
      },
      methods: {
        handleReverse() {
          this.content = this.content.split('').reverse().join('');
        }
      },
      template: `
        <div>{{content}}</div>
        <button v-on:click="handleReverse">反转</button>
      `
    }).mount("#root");
  </script>
</body>
</html>

TodoLIst 例子

知识点:v-model 双向绑定,v-for 循环

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <script>
    Vue.createApp({
      data() {
        return {
          inputValue: '',
          list: []
        }
      },
      methods: {
        handleAddItem() {
          this.list.push(this.inputValue);
          this.inputValue = '';
        }
      },
      template: `
        <div>
          <input type="text" v-model="inputValue"><button v-on:click="handleAddItem">增加</button>
          <ul>
            <li v-for="(item, index) of list">{{item}} {{index}}</li>
          </ul>
        </div>
      `
    }).mount("#root");
  </script>
</body>
</html>

组件拆分 TodoList

  • 拆分组件
  • vue 中如果想在属性上绑定 vue 变量,需要在属性前增加 v-bind。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          inputValue: '',
          list: []
        }
      },
      methods: {
        handleAddItem() {
          this.list.push(this.inputValue);
          this.inputValue = '';
        }
      },
      template: `
        <div>
          <input type="text" v-model="inputValue"><button v-on:click="handleAddItem">增加</button>
          <ul>
            <todo-item v-for="(item, index) of list" v-bind:item="item" v-bind:index="index"/>
          </ul>
        </div>
      `
    });

    app.component('todo-item', {
      props: ['item', 'index'],
      template: '<li>{{index}}--{{item}}</li>'
    });

    app.mount("#root");
  </script>
</body>
</html>

Vue 中应用和组件的基础概念

// createApp 表示创建一个 Vue 应用,存储到 app 变量中
// 传入的参数表示,这个应用最外层的组件,应该如何展示
// mvvm 设计模式, m -> model 数据,v -> view 视图,vm -> viewModel 视图数据连接层
const app = Vue.createApp({
	data() {
		return {
			message: 'hello world'
		}
	},
	template: '<div>{{message}}</div>';
});
// vm 代表的就是 vue 应用的根组件
const vm = app.mount('#root');
// vm.$data.message

生命周期函数

// 生命周期函数:在某一时刻会自动执行的函数
const app = Vue.createApp({
	data() {
		return {
			message: 'hello world'
		}
	},
	// 在实例生成之前会自动执行的函数
	beforeCreate() {
		console.log('beforeCreate');
	},
	// 在实例生成之后会自动执行的函数
	created() {
		console.log('created');
	},
	// 在模板已经被编程函数之后立即自动执行的函数
	// 在组件内容被渲染到页面之前自动执行的函数
	beforeMount() {
		console.log('beforeMount');
	},
	// 在组件内容被渲染到页面之后自动执行的函数
	mounted() {
		console.log('mounted');
	},
	// 当 data 中的数据发生变化时会自动执行的函数
	beforeUpdate() {
		console.log('beforeUpdate');
	},
	// 当 data 中的数据发生变化,同时页面完成更新后,会自动执行的函数
	updated() {
		console.log('updated');
	},
	// 当 Vue 应用失效时,自动执行的函数
	beforeUnmount() {
		console.log('beforeUnmount');
	},
	// 当 vue 应用失效时,且 dom 完全销毁之后,自动执行的函数
	unmounted() {
		console.log('unmounted');
	}
});

想要显示的变量内容不作 html 转义

const app = Vue.createApp({
	data() {
		return {
			message: '<strong>hello world</strong>'
		}
	},
	tempate: '<div v-html="message"></div>';
});
const vm = app.mount('#root');

{{}} 插值表达式中可以插入 js 表达式,但是不能写 js 语句

内容不随变量的变化而变化,而是只使用第一次的值

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world'
		}
	},
	template: '<div v-once>{{message}}</div>'
});
const vm = app.mount('#root');

元素是否显示

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world',
			show: true
		}
	},
	template: '<div v-if="show">{{message}}</div>'
});
const vm = app.mount('#root');

事件绑定

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world'
		}
	},
	methods: {
		handleClick() {
			alert('click');
		}
	},
	template: '<div v-on:click="handleClick">{{message}}</div>'
});
const vm = app.mount('#root');

v-on,v-bind 的简写

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world'
		}
	},
	methods: {
		handleClick() {
			alert('click');
		}
	},
	template: '<div @click="handleClick" :title="message">{{message}}</div>'
});
const vm = app.mount('#root');

动态属性

动态属性可以在属性变量上加 [] 中括号来实现

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world',
			name: 'title',
			event: 'click'
		}
	},
	methods: {
		handleClick() {
			alert('click');
		}
	},
	template: '<div @[event]="handleClick" :[name]="message">{{message}}</div>'
});
const vm = app.mount('#root');

修饰符阻止默认行为

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world',
			name: 'title',
			event: 'click'
		}
	},
	methods: {
		handleClick() {
			alert('click');
		}
	},
	template: `
		<form action="https://www.baidu.com">
			<button type="submit" @click.prevent="handleClick"></button>
		</form>
	`
});
const vm = app.mount('#root');

获取事件对象和其他参数

<div @click="handleClick($event, str1)">test</div>
<script>
const app = Vue.createApp({
	methods: {
		handleClick(e, str) {
			console.log(e)
		}
	}
})
</script>

计算属性

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world',
			count: 2,
			price: 5
		}
	},
	// 当计算属性依赖的内容发生变更时,才会重新执行
	computed: {
		total() {
			return this.count * this.price;
		}
	},
	methods: {
		// 只要页面重新渲染,就会重新计算
		getTotal() {
			return this.count * this.price;
		}
	},
	template: `<div>{{total}}</div>`
});
const vm = app.mount('#root');

侦听器

const app = Vue.createApp({
	data() {
		return {
			message: 'hello world',
			count: 2,
			price: 5,
			newTotal: 0,
		}
	},
	watch: {
		// price 发生变化时,函数会执行
		price() {
			this.newTotal = this.price * this.count;
		}
	},
	computed: {
		// 当计算属性依赖的内容发生变更时,才会重新执行计算
		total() {
			return this.count * this.price;
		}
	},
	template: `<div>{{newTotal}}</div>`
});
const vm = app.mount('#root');

总结

computed 和 method 都能实现的一个功能,建议使用 computed,因为有缓存
computed 和 watcher 都能实现的功能,建议使用 computed 因为更加简洁

样式

const app = createApp({
	data: {
		classString: 'red',
		classObject: {red: false, green: true},
		classArr: ['red', 'green', {brown: true}]
	},
	template: `
		<div :class="classArr"></div>
	`
});
const vm = app.mount('#root');

父组件与子组件的 class

  • 如果子组件的最外层只有一个标签那么父组件中调用时传入的 class 就直接会生效,如果子组件的最外城有多个标签那么外部父组件调用时传入的 class 就不会直接生效,需要子组件自己使用。
const app = createApp({
	template: `
		<div class="red">
			hello world
			<demo />
		</div>
	`
});
app.component('demo', {
	template: `
		<div :class="$attrs.class">one</div>
		<div>two</div>
	`;
});
const vm = app.mount('#root');

行内样式的定义

const app = Vue.createApp({
	data: {
		styleString: 'color: yellow',
		styleObject: {
			color: 'yellow'
		},
	},
	template: '<div :style="styleObject">hello world</div>';
});
const vm = app.mount('root');

v-if,v-show

如果频繁显示隐藏,用 v-show,避免频繁的销毁创建元素

...
template:  `
	<div v-if="show"></div>
	<div v-show="show"></div>
`;
...
template: `
	<div v-if="conditionOne">if</div>
	<div v-else-if="conditionTwo">else if</div>
	<div v-else>else</div>
`;

v-for

const app = Vue.createApp({
	data: {
		listArray: ['first', 'second', 'thrid'],
		listObject: {
			first: 1,
			second: 2,
			thrid: 3
		}
	},
	template: `
		<div v-for="item in listArray">
			{{item}}
		</div>
		<div v-for="(item, index) in listArray">
			{{item}} -- {{index}}
		</div>
		<div v-for="(value, key) in listObject">
			{{value}} -- {{key}}
		</div>
		<div v-for="(value, key, index) in listObject">
			{{value}} == {{key}} -- {{index}}
		</div>
	`
});

为了提升 vue 的效率,在进行 v-for 循环的时候要给循环加 key值

template: `
	<div v-for="(item, index) of listArray" :key="item">
		{{item}} -- {{index}}
	</div>
`

页面展示的变化

数组

  1. 使用数组的变更函数 push, pop, shift, unshift, splice, sort, reverse
  2. 直接替换数组
this.listArray = ['bye', 'world'];
this.listArray = ['bye'].concat(['world']);
this.listArray = ['bye', 'world'].filter(item => item === 'bye');
  1. 直接更新数组的内容
this.listArray[1] = 'hello';

对象
直接添加对象的内容,也可以自动的展示出来

this.listObject.age = 18;

直接循环数字

template: `
	<div v-for="item in 10"> {{item}} </div>		
` 	// 会直接循环从 1 到 10 数字

如果再循环的过程中想要加判断

template: `
	<div v-for="(value, key, index) in listObject" :key="index">
		<div v-if="key != 'lastName'">
			{{value}} -- {key}
		</div>
	</div>
`

如果不想要外层的 div,可以用占位符

template: `
	<template v-for="(value, key, index) in listObject" :key="index">
		<div v-if="key !== 'lastName'">
			{{value}} -- {{key}}
		</div>
	</template>
`;

事件

额外获取参数同时传递事件对象
data() {
	return {
		counter: 0
	}
},
methods: {
	handleBtnClick(num, $event) {
		this.counter += num;
	}
},
template: '<button @click="handleBtnClick(2, $event)">button</button>',

事件执行多个函数

template: `
	<button @click="handleBtnClick(), handleBtnClick1()" type="button">button</button>
`

事件修饰符

stop 阻止事件冒泡
self 判断点击的为自己
prevent 阻止默认行为
capture 会把事件运营模式变成的捕获模式
once 事件绑定只执行一次
passive 在事件为 scroll 时,会提升性能

template: `
	<div @click="handleDivClick">
		<button @click.stop="handleBtnClick" type="button">button</button>
	</div>
`

按键修饰符

enter,tab,delete,esc,up,down,left,right

template: `
	<div>
		<input @keydown.delete="handleKeyDown" />
	</div>
`

鼠标修饰符

left,right,middle

精确修饰符

exact

// 在按住 ctrl 并且没有按住其他键时,点击触发
template: `
	<div>
		<div @click.ctrl.exact>123</div>
	</div>
`

双向绑定

  1. input
template: `
	<div>
		<input v-model="message">
	</div>
`
  1. textarea
template: `
	<div>
		<textarea v-model="message" />
	</div>
`
  1. checkbox
const app = Vue.createApp({
	data() {
		return {
			message: true
		}
	},
	template: `
		<div>
			<input type="checkbox" v-model="message" >
		</div>
	`
});
const app = Vue.createApp({
	data() {
		return {
			message: []
		}
	},
	template: `
		<div>
			<input type="checkbox" v-model="message" value="first" />
			<input type="checkbox" v-model="message" value="second" />
			<input type="checkbox" v-model="message" value="thrid" />
		</div>
	`
});

checkbox 修改 true,false 选中的值

const app = Vue.createApp({
	data() {
		return {
			message: 'world'
		}
	},
	template: `
		<div>
			<input type="checkbox" v-model="message" true-value="hello" false-value="world">
		</div>
	`
});
  1. radio
const app = Vue.createApp({
	data() {
		return {
			message: ''
		}
	},
	template: `
		<div>
			<input type="radio" v-model="message" value="first" />
			<input type="radio" v-model="message" value="second" />
			<input type="radio" v-model="message" value="thrid" />
		</div>
	`
});
  1. select
const app = Vue.createApp({
	data() {
		return {
			message: ''
		}
	},
	template: `
		<div>
			<select v-model="message">
				<option value="">请选择</option>
				<option value="A">A</option>
				<option value="B">B</option>
				<option value="C">C</option>
			</select>
		</div>
	`
});
const app = Vue.createApp({
	data() {
		return {
			message: []
		}
	},
	template: `
		<div>
			<select v-model="message" multiple>
				<option value="A">A</option>
				<option value="B">B</option>
				<option value="C">C</option>
			</select>
		</div>
	`
});

v-model 的修饰符

  1. lazy
    input 框失去焦点时,model 绑定的数据才发生变化
const app = Vue.createApp({
	data() {
		return {
			message: 'hello'
		}
	},
	template: `
		<div>
			<input v-model.lazy="message" />
		</div>
	`
});
  1. number
    对数据类型做转换,将字符串类型转为数字类型
const app = Vue.createApp({
	data() {
		return {
			message: 123
		}
	},
	template: `
		<div>
			<input v-model.number="message" type="number" />
		</div>
	`
});

trim
去掉前后的空格

组件的定义以及复用性

  • 组件:页面的一部分
  • 组件具有复用性
  • 全局组件:只要定义了处处都能用,用起来简单,但是性能不高,名字建议,小写字母单词,中间用横线间隔
  • 局部组件,定义了,要注册之后才能使用,性能比较高,使用起来有些麻烦,建议大写字母开头驼峰命名
  • 局部组件使用时要做一个名字和组件间的映射对象,你不写映射,Vue 底层也会自动尝试帮你做映射

全局组件

app.component('hello-world', {
	template: '<div>hello world</div>'
});

局部组件

const HelloWorld = {
	template: '<div>hello world</div>'
}
const app = Vue.createApp({
	components: {
		// 'hello-world': HelloWorld
		HelloWorld
	},
	template: `
		<div>
			<hello-world />
		</div>
	`
});

父子组件传值

const app = Vue.createApp({
	template: `
		<div><test content="hello world" /></div>
	`
});
app.component('test', {
	props: ['content'],
	template: '<div>{{content}}</div>'
});

如果使用静态传参,那么只能传字符串类型,而动态传参可以传数值类型等

const app = Vue.createApp({
	data() {
		return {
			num: 123
		}
	},
	template: `
		<div><test :content="num" /></div>
	`
});
app.component('test', {
	props: ['content'],
	template: `
		<div>{{typeof content}}</div>
	`
});
const vm = app.mount('#root');

传值的校验

type: String,Boolean,Array,Object,Function,Symbol
required:必填
default:默认值

app.component('test', {
	props: {
		content: Number
	}
	template: `
		<div>{{typeof content}}</div>
	`
});
app.component('test', {
	props: {
		content: {
			type: Number,
			// required: true,
			default: 789
		}
	},
	template: `<div>{{content}}</div>`
});

对传过来的值做更为深层次的校验

app.component('test', {
	props: {
		type: Number,
		validator: function (value) {
			return value < 1000;
		},
		default: function() {
			return 456;
		}
	},
	template: `<div>{{content}}</div>`
});

绑定较多变量时的简写方式
v-bind=“params” 等价于 :a=“params.a” :b=“params.b” :c=“params.c”

const app = Vue.createApp({
	data() {
		return {
			params: {
				a: '123',
				b: '456',
				c: '789'
			}
		}
	},
	template: `<div><test v-bind="params" /></div>`
});
app.component('test', {
	props: ['a', 'b', 'c'],
	template: `
		<div>{{a}} -- {{b}} -- {{c}}</div>
	`
});
  • 属性传的时候,使用 content-abc 这种命名,接的时候使用 contentAbc 命名
  • 单向数据流的概念:子组件可以使用父组件传递过来的数据,但是绝对不能修改传递过来的数据

Non-prop 属性

  • Non-props 属性为子组件未用 props 接收的负组件传过来的属性
  • 如果子组件最外层只有一个元素,则子组件会自动继承父组件的 Non-props,如果不想继承,则需要添加 inhertAttrs: false,最外层如果多个元素,如果想继承父组件传过来的 Non-props,则需要在子元素上添加 v-bind="$attrs"
  • 在其他位置也可以用到 Non-props 属性 this.$attrs
const app = Vue.createApp({
	template: `
		<div>
			<counter msg="hello" msg1="hello1">
		</div>
	`
});
app.component('counter', {
	mounted() {
		console.log(this.$attrs);
	},
	// inhertAttrs : false,
	template: `
		<div v-bind:msg="$attrs.msg">Counter</div>
		<div v-bind="$attrs">Counter</div>
		<div>Counter</div>
	`
});

子组件触发父组件事件

 const app = Vue.createApp({
      data() {
        return {
          count: 1
        }
      },
      methods: {
        handleAdd(step) {
          this.count += step;
        }
      },
      template: `
        <div><counter :count="count" @add="handleAdd" /></div>
      `
    });
    app.component('counter', {
      props: ['count'],
      methods: {
        handleClick() {
          this.$emit('add', 2);
        }
      },
      template: `
        <div @click="handleClick">{{count}}</div>
      `,
    });
    app.mount("#root");
  • 通过 emit 对向外触发的事件进行校验
  1. 通过数组校验
app.component('counter', {
	props: ['count'],
	emits: ['add'],
	methods: {
		handleClick() {
			this.$emit('add', this.count + 3);
		}
	},
	template: `
		<div @click="handleClick">{{count}}</div>
	`
});
  1. 通过对象校验
app.component('counter', {
	props: ['count'],
	emits: [{
		add: (count) => {
			if (count > 0) {
				return true;
			}
			return false;
		}
	}],
	methods: {
		handleClick() {
			this.$emit('add', this.count + 3);
		}
	},
	template: `
		<div @click="handleClick">{{count}}</div>
	`
});

父子组件数据双向绑定,简写

const app = Vue.createApp({
	data() {
		return {
			count: 1
		}
	},
	template: '<counter v-model="count" />'
});
app.component('counter', {
	props: ['modelValue'],
	methods: {
		handleClick() {
			this.$emit('update:modelValue', this.modelValue + 3);
		}
	},
	template: '<div @click="handleClick">{{modelValue}}</div>'
});
app.mount('#root');
v-model 自定义修饰符
const app = Vue.createApp({
  data() {
    return {
      count: 'a'
    }
  },
  template: `
    <div><counter v-model.uppercase="count"/></div>
  `
});
app.component('counter', {
  props: {
    'modelValue': String,
    'modelModifiers': {
      default: () => ({})
    }
  },
  methods: {
    handleClick() {
      let newValue = this.modelValue + 'b';
      if (this.modelModifiers.uppercase) {
        newValue = newValue.toUpperCase();
      }
      this.$emit('update:modelValue', newValue);
    }
  },
  template: `
    <div @click="handleClick">{{modelValue}}</div>
  `,
});
app.mount("#root");

slot 插槽

  • slot 中使用的数据,作用域的问题
  • 父模版里调用的数据属性,使用的都是父模版里的数据
  • 子模版里调用的数据属性,使用的都是子模版里的数据
const app = Vue.createApp({
  data() {
    return {
      text: '提交'
    }
  },
  template: `
    <myform>
      <div>{{text}}</div>  
    </myform>
    <myform>
      <button type="button">{{text}}</button>  
    </myform>
    <myform>
    </myform>
  `
});
app.component('myform', {
  template: `
    <div>
      <input type="text">
      <slot>default value</slot>  
    </div>
  `,
});
app.mount("#root");
  • 具名插槽
const app = Vue.createApp({
  template: `
    <layout>
      <template v-slot:header>
        <div>header</div>
      </template>
      <template v-slot:footer>
        <div>footer</div>
      </template>
    </layout>
  `
});
app.component('layout', {
  template: `
    <slot name="header"></slot>
    <div>content</div>
    <slot name="footer"></slot>
  `,
});
app.mount("#root");

v-slot:header 可简化为 #header

const app = Vue.createApp({
  template: `
    <layout>
      <template #header>
        <div>header</div>
      </template>
      <template #footer>
        <div>footer</div>
      </template>
    </layout>
  `
});
  • 作用域插槽
const app = Vue.createApp({
  // {item} 为解构
  template: `
    <list v-slot="{item}">
      <div>{{item}}</div>
    </list>
  `
});
app.component('list', {
  data() {
    return {
      list: [1,2,3]
    }
  },
  template: `
    <slot v-for="item in list" :item="item"></slot>
  `,
});
app.mount("#root");

动态组件

  • keep-alive 会将组件中修改的内容保存到缓存中,再次使用时从缓存中取出来
const app = Vue.createApp({
  data() {
    return {
      currentItem: 'input-item'
    }
  },
  methods: {
    handleClick() {
      if (this.currentItem === 'input-item') {
        this.currentItem = 'common-item';
      } else {
        this.currentItem = 'input-item';
      }
    }
  },
  template: `
    <keep-alive>
      <component :is="currentItem" />
    </keep-alive>
    <button @click="handleClick" type="button">转换</button>
  `
});
app.component('input-item', {
  template: '<input type="text" />'
});
app.component('common-item', {
  template: '<div>hello world</div>'
});
app.mount("#root");
  • 异步组件
const app = Vue.createApp({
  template: `
    <common-item />
    <async-common-item />
  `
});
app.component('common-item', {
  template: `
    <div>hello world</div> 
  `,
});
app.component('async-common-item', Vue.defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        template: '<div>this is an async component</div>'
      });
    }, 4000);
  });
}));
app.mount("#root");

其他基础知识点

  • v-once 让某个元素标签只渲染一次
  • ref 实际上是获取 Dom 节点 / 组件引用 的一个语法
const app = Vue.createApp({
	data() {
		return {
			count: 1
		}
	},
	mounted() {
		this.$ref.count.innerHTML = 'hello';
	},
	template: `
		<div>
			<div ref="count">
				{{count}}
			</div>
		</div>
	`
});
const app = Vue.createApp({
	data() {
		return {
			count: 1
		}
	},
	mounted() {
		this.$ref.common.sayHello();
	},
	template: `
		<div>
			<common-item ref="common">
		</div>
	`
});
app.component('common-item', {
	methods: {
		sayHello() {
			alert('hello');
		}
	},
	template: `<div></div>`
});
  • provide / inject 跨层传递参数
const app = Vue.createApp({
	provide: {
		count: 1
	}
	...
});
app.component('child', {
	...
});
app.component('child-child', {
	inject: ['count']
});
const app = Vue.createApp({
	data() {
		return {
			count: 1
		}
	},
	provide() {
		return {
			count: this.count
		};
	}
	...
});
app.component('child', {
	...
});
app.component('child-child', {
	inject: ['count']
});

使用 transition 标签实现单元素的过度和动画效果

使用<transition> 标签 配合固定的 css 样式
v-enter-from 开始进入
v-enter-active 进入过程
v-enter-to 进入结束
v-leave-from 开始离开
v-leave-active 离开过程
v-leave-to 离开结束

  1. 不自定命名
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
  <style>
    .v-enter-from {
      opacity: 0;
    }
    .v-enter-active, .v-leave-active {
      transition: opacity 2s;
    }
    .v-enter-to {
      opacity: 1;
    }
    .v-leave-to {
      opacity: 0;
    }
  </style>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          show: false
        }
      },
      template: `
        <transition>
          <div v-if="show">hello world</div>
        </transition>
        <button @click="show = !show">button</button>
      `
    });
    app.mount('#root');
  </script>
</body>

</html>
  1. 自定义名称
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
  <style>
    .com-enter-from {
      opacity: 0;
    }
    .com-enter-active, .com-leave-active {
      transition: opacity 2s;
    }
    .com-enter-to {
      opacity: 1;
    }
    .com-leave-to {
      opacity: 0;
    }
  </style>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          show: false
        }
      },
      template: `
        <transition name="com">
          <div v-if="show">hello world</div>
        </transition>
        <button @click="show = !show">button</button>
      `
    });
    app.mount('#root');
  </script>
</body>

</html>

自定义动画名称

template: `
	<div enter-from-class=""
		enter-active-class=""
		enter-to-class=""
		leave-from-class=""
		leave-active-class=""
		leave-to-class=""
		v-show="show">
		hello world
	</div>
`

按照动画或过度的时间执行

template: `
	<transiton type="transition">
		<div>hello world</div>
	</transition>
`
template: `
	<transiton type="animation">
		<div>hello world</div>
	</transition>
`

直接定义动画时间

template: `
	<transition :duration="1000">
		<div>hello world</div>
	</transition>
`
template: `
	<transition :duration="{enter: 1000, leave: 3000}">
		<div>hello world</div>
	</transition>
`

js 动画

const app = Vue.createApp({
data() {
  return {
    show: false
  }
},
methods: {
  handleBeforeEnter(el) {
    el.style.color = "red";
  },
  handleEnter(el, done) {
    const timer = setInterval(() => {
      const color = el.style.color;
      if (color === 'red') {
        el.style.color = 'blue';
      } else {
        el.style.color = 'red';
      }
    }, 1000);
    setTimeout(() => {
      clearInterval(timer);
      done();
    }, 3000)
  },
  handleEnterEnd() {
    alert(111);
  }
},
template: `
  <transition
    :css="false"
    @before-enter="handleBeforeEnter"
    @enter="handleEnter"
    @after-enter="handleEnterEnd"
  >
    <div v-if="show">hello world!</div>
  </transition>
  <button @click="show = !show">button</button>
`
});
app.mount('#root');

多个单元素之间的切换

  • 先出后入 为 mode=“out-in” 先入后出 mode=“in-out”
template: `
	<transition mode="out-in">
		<div v-if="show">hello world</div>
		<div v-else>bye world</div>
	</transition>
`
  • 元素初始就有动画效果
template: `
	<transition mode="out-in" appear>
		<div v-if="show">hello world</div>
		<div v-else>bye world</div>
	</transition>
`
  • 组件间切换
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
  <style>
    .v-leave-to,
    .v-enter-from {
      opacity: 0;
    }
    .v-leave-active,
    .v-enter-active {
      transition: opacity 2s ease 0s;
    }
    .v-leave-from,
    .v-enter-to {
      opacity: 1;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script>
    const componentA = {
      template: '<div>hello world</div>'
    };
    const componentB = {
      template: '<div>bye world</div>'
    };
    const app = Vue.createApp({
      components: {
        'component-a': componentA,
        'component-b': componentB
      },
      data() {
        return {
          currentComponent: 'component-a'
        }
      },
      methods: {
        handleClick() {
          if (this.currentComponent === 'component-a') {
            this.currentComponent = 'component-b';
          } else {
            this.currentComponent = 'component-a';
          }
        }
      },
      template: `
        <transition mode="out-in" appear>
          <component :is="currentComponent" />
        </transition>
        <button @click="handleClick" type="button">button</button>
      `
    });
    app.mount('#root');
  </script>
</body>
</html>

列表动画

.v-move 列表中移动的项的移动渐变动画

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>
  <style>
    .list-item {
      display: inline-block;
      margin-left: 10px;
    }
    .v-enter-from {
      opacity: 0;
      transform: translateY(20px);
    }
    .v-enter-active {
      transition: all 2s ease 0s;
    }
    .v-enter-to {
      opacity: 1;
      transform: translateY(0);
    }
    .v-move {
      transition: all 2s ease 0s;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          list: [1, 2, 3]
        }
      },
      methods: {
        handleClick() {
          this.list.unshift(this.list.length + 1);
        }
      },
      template: `
        <transition-group>
          <span class="list-item" v-for="item in list" :key="item">{{item}}</span>
        </transition-group>
        <button @click="handleClick">增加</button>
      ` 
    });
    app.mount('#root');
  </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值