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>
`
页面展示的变化
数组
- 使用数组的变更函数 push, pop, shift, unshift, splice, sort, reverse
- 直接替换数组
this.listArray = ['bye', 'world'];
this.listArray = ['bye'].concat(['world']);
this.listArray = ['bye', 'world'].filter(item => item === 'bye');
- 直接更新数组的内容
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>
`
双向绑定
- input
template: `
<div>
<input v-model="message">
</div>
`
- textarea
template: `
<div>
<textarea v-model="message" />
</div>
`
- 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>
`
});
- 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>
`
});
- 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 的修饰符
- lazy
input 框失去焦点时,model 绑定的数据才发生变化
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template: `
<div>
<input v-model.lazy="message" />
</div>
`
});
- 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 对向外触发的事件进行校验
- 通过数组校验
app.component('counter', {
props: ['count'],
emits: ['add'],
methods: {
handleClick() {
this.$emit('add', this.count + 3);
}
},
template: `
<div @click="handleClick">{{count}}</div>
`
});
- 通过对象校验
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 离开结束
- 不自定命名
<!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>
- 自定义名称
<!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>