<span>{{ message }}span>
<span>{{ number + 1 }}span>
<span>{{ ok ? 'YES' : 'NO' }}span>
<span>{{ message.split('').reverse().join('') }}span>
v-bind
指令可以用于响应式地更新 HTML attribute
2.6.0
开始,可以用方括号括起来的 JavaScript
表达式作为一个指令的参数
<a v-bind:href="url">...a>
<a :href="url">...a>
<a :[key]="url"> ... a>
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute
名里是无效的。
v-on
指令,用于监听 DOM
事件。
<a v-on:click="doSomething">...a>
<a @click="doSomething">...a>
<a @[event]="doSomething"> ... a>
modifier
) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault()
。
<form v-on:submit.prevent="onSubmit">...form>
将原生事件绑定到组件,可以使用 v-on
的 .native
修饰符。
<base-input v-on:focus.native="onFocus">base-input>
2.3.0
新增。在有些情况下,可能需要对一个 prop
进行双向绑定。
父组件把一个属性传递给子组件,子组件修改了这个属性,可以同步到父组件中。
v-bind.sync
用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”
,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。由于真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源。所以推荐以 update:myPropName
的模式触发事件。
举例:
// 子组件
// 修改父组件的属性
this.$emit('update:title', newTitle);
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
>text-document>
<text-document v-bind:title.sync="doc.title">text-document>
<text-document v-bind.sync="doc">text-document>
XSS
风险。v-bind:
简写:
style
对象属性使用驼峰式写法<template>
<p :class="{ black: isBlack, yellow: isYellow }">使用 class 对象p>
<p :class="[black, yellow]">使用 class 数组p>
<p :style="styleData">使用 stylep>
template>
export default {
data() {
return {
isBlack: true,
isYellow: true,
black: 'black',
yellow: 'yellow',
styleData: {
fontSize: '40px',
color: 'red',
backgroundColor: '#ccc'
}
};
}
}
v-if
、v-else
的用法:可以使用变量,也可使用 ===
表达式。
<template>
<div>
<p v-if="type === 'a'">Ap>
<p v-else-if="type === 'b'">Bp>
<p v-else>otherp>
<p v-show="type === 'a'">A by v-showp>
<p v-show="type === 'b'">B by v-showp>
div>
template>
export default {
data() {
return {
type: 'a'
};
}
}
v-if
和 v-show
的区别?
v-if
控制dom
节点的渲染和销毁,适用于切换不频繁的场景;v-show
是通过display: none;
来控制显示隐藏,适用于切换频繁的场景。v-for
遍历,需要写key
(尽量不要是random
和index
,需要是和业务相关的id
);v-for
和v-if
不能同时使用。<template>
<div>
<p>遍历数组p>
<ul>
<li v-for="(item, index) in listArr" :key="item.id">
{{index}} - {{item.id}} - {{item.title}}
li>
ul>
<p>遍历对象p>
<ul >
<li v-for="(val, key, index) in listObj" :key="key">
{{index}} - {{key}} - {{val.title}}
li>
ul>
div>
template>
export default {
data() {
return {
flag: false,
listArr: [
{ id: 'a', title: '标题1' }, // 数据结构中,最好有 id ,方便使用 key
{ id: 'b', title: '标题2' },
{ id: 'c', title: '标题3' }
],
listObj: {
a: { title: '标题1' },
b: { title: '标题2' },
c: { title: '标题3' },
}
}
}
}
computed
有缓存,data
不变则不会重新计算。watch
是浅度监听, watch
监听引用类型,拿不到 oldval
。watch
如何深度监听?
<template>
<div>
<input v-model="name"/>
<input v-model="info.city"/>
div>
template>
<script>
export default {
data() {
return {
name: '章三',
info: {
city: '北京'
}
}
},
watch: {
name(val, oldVal) {
console.log('watch name', val, oldVal) // 值类型,可正常拿到 oldVal 和 val
},
info: {
handler(val, oldVal) {
console.log('watch info', val, oldVal) // 引用类型,拿不到 oldVal 因为指针相同,此时已经指向了新的 val
},
deep: true // 深度监听
}
}
}
</script>
event
参数;event
参数传递($event
)。<template>
<div>
<p>{{num}}p>
<button @click="increment1">+1button>
<button @click="increment2(2, $event)">+2button>
div>
template>
export default {
data() {
return {
num: 0
}
},
methods: {
increment1(event) {
// eslint-disable-next-line
console.log('event', event, event.__proto__.constructor) // 是原生的 event 对象
// eslint-disable-next-line
console.log(event.target)
// eslint-disable-next-line
console.log(event.currentTarget) // 注意,事件是被注册到当前元素的,和 React 不一样
this.num++
// 1. event 是原生的
// 2. 事件被挂载到当前元素
// 和 DOM 事件一样
},
increment2(val, event) {
// eslint-disable-next-line
console.log(event.target)
this.num = this.num + val
},
loadHandler() {
// do some thing
}
},
mounted() {
window.addEventListener('load', this.loadHandler)
},
beforeDestroy() {
//【注意】用 vue 绑定的事件,组建销毁时会自动被解绑
// 自己绑定的事件,需要自己销毁!!!
window.removeEventListener('load', this.loadHandler)
}
}
vue
中事件被绑定到哪里?
event
是原生的;修饰符可以串联;可以只有修饰符。
v-on:click.stop
阻止单击事件继续传播v-on:submit.prevent
提交事件不再重载页面v-on:click.capture
内部元素触发的事件先在此处理,然后才交由内部元素进行处理v-on:click.self
只当在 event.target
是当前元素自身时触发处理函数
<a v-on:click.stop="doThis">a >
<form v-on:submit.prevent="onSubmit">form>
<a v-on:click.stop.prevent="doThat">a >
<form v-on:submit.prevent>form>
<div v-on:click.capture="doThis">...div>
<div v-on:click.self="doThat">...div>
<button @click.ctrl="onClick">Abutton>
<button @click.ctrl.exact="onCtrlClick">Abutton>
<button @click.exact="onClick">Abutton>
v-model
textarea
、checkbox
、radio
、select
lazy
(防抖,输完才会变化)、number
(转换成数字)、trim
(去掉两端空格)<template>
<div>
<p>输入框: {{name}}p>
<input type="text" v-model.trim="name"/>
<input type="text" v-model.lazy="name"/>
<input type="text" v-model.number="age"/>
<p>多行文本: {{desc}}p>
<textarea v-model="desc">textarea>
<p>复选框 {{checked}}p>
<input type="checkbox" v-model="checked"/>
<p>多个复选框 {{checkedNames}}p>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jacklabel>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">Johnlabel>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mikelabel>
<p>单选 {{gender}}p>
<input type="radio" id="male" value="male" v-model="gender"/>
<label for="male">男label>
<input type="radio" id="female" value="female" v-model="gender"/>
<label for="female">女label>
<p>下拉列表选择 {{selected}}p>
<select v-model="selected">
<option disabled value="">请选择option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<p>下拉列表选择(多选) {{selectedList}}p>
<select v-model="selectedList" multiple>
<option disabled value="">请选择option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
div>
template>
export default {
data() {
return {
name: '章三',
age: 18,
desc: '自我介绍',
checked: true,
checkedNames: [],
gender: 'male',
selected: '',
selectedList: []
};
}
}
// event.js
import Vue from 'vue';
export default new Vue();
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.title}}
<button @click="deleteItem(item.id)">删除button>
li>
ul>
div>
template>
// 子组件 List.vue
import event from './event';
export default {
// props: ['list']
props: {
// prop 类型和默认值
list: {
type: Array,
default() {
return [];
}
}
},
data() {
return {
};
},
methods: {
deleteItem(id) {
this.$emit('delete', id);
},
addTitleHandler(title) {
console.log('on add title', title);
}
},
created() {
console.log('list created');
},
mounted() {
console.log('list mounted');
// 绑定自定义事件
event.$on('onAddTitle', this.addTitleHandler);
},
beforeUpdate() {
console.log('list before update');
},
updated() {
console.log('list updated');
},
beforeDestroy() {
// 及时销毁,否则可能造成内存泄露
event.$off('onAddTitle', this.addTitleHandler);
}
}
<template>
<div>
<input type="text" v-model="title"/>
<button @click="addTitle">addbutton>
div>
template>
// 子组件 Input.vue
import event from './event';
export default {
data() {
return {
title: ''
};
},
methods: {
addTitle() {
// 调用父组件的事件
this.$emit('add', this.title);
// 调用自定义事件
event.$emit('onAddTitle', this.title);
this.title = '';
}
}
}
<template>
<div>
<Input @add="addHandler"/>
<List :list="list" @delete="deleteHandler"/>
div>
template>
// 父组件 Index.vue
import Input from './Input';
import List from './List';
export default {
components: {
Input,
List
},
data() {
return {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
}
]
};
},
methods: {
addHandler(title) {
this.list.push({
id: `id-${Date.now()}`,
title
});
},
deleteHandler(id) {
this.list = this.list.filter(item => item.id !== id);
}
},
created() {
console.log('index created');
},
mounted() {
console.log('index mounted');
},
beforeUpdate() {
console.log('index before update');
},
updated() {
console.log('index updated');
},
}
beforeCreate
、created
(vue
实例初始化完成)beforeMount
、mounted
(dom
渲染完成,一般进行ajax
请求)beforeUpdate
(data
改变,dom
渲染之前)、updated
(data
改变,vdom
重新渲染完成)beforeDestroy
(解除绑定,销毁子组件,解绑事件监听器)、destroyed
以上述“vue组件如何通讯”的代码作为例子讲解。
父组件初始化完成
子组件初始化完成
子组件挂载完成
父组件挂载完成
父组件开始更新
子组件开始更新
子组件更新完成
父组件更新完成
父组件开始销毁
子组件开始销毁
子组件销毁完成
父组件销毁完成
index created
list created
list mounted
index mounted
index before update
list before update
list updated
index updated
v-model
的原理就是监听 input
事件,然后实时修改 data
。
<template>
<input type="text"
:value="text1"
@input="$emit('change1', $event.target.value)"
>
template>
export default {
model: {
prop: 'text1', // 对应 props text1
event: 'change1'
},
props: {
text1: String,
default() {
return '';
}
}
}
<template>
<p>{{ name }}p>
<CustomVModel v-model="name"/>
template>
由于vue
是异步渲染,data
改变之后,dom
不会立刻渲染,$nextTick
会在dom
渲染之后被触发,以获取最新的dom
节点。
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{item}}
li>
ul>
<button @click="addItem">添加一项button>
div>
template>
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c'];
};
},
methods: {
addItem() {
this.list.push(`${Date.now()}`);
this.list.push(`${Date.now()}`);
this.list.push(`${Date.now()}`);
// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
// 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1;
console.log( ulElem.childNodes.length );
})
}
}
}
父组件想往子组件内部插一些东西。
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示
slot>
a>
template>
// SlotDemo.vue
export default {
props: ['url'],
data() {
return {};
}
}
<template>
<SlotDemo :url="website.url">
{{website.title}}
SlotDemo>
template>
// Index.vue
import SlotDemo from './SlotDemo';
export default {
components: {
SlotDemo
},
data() {
return {
name: '章三',
website: {
url: 'http://imooc.com/',
title: 'imooc',
subTitle: '程序员的梦工厂'
},
showFormDemo: false
}
}
}
把子组件的data
扔出来,让父组件获取到。
子组件的
指定:slotData
将数据暴露出去;父组件通过v-slot
定义一个名字xxx
,然后通过xxx.slotData
获取到子组件暴露出去的数据。
<template>
<a :href="url">
<slot :slotData="website">
{{website.subTitle}}
slot>
a>
template>
// ScopedSlotDemo.vue
export default {
props: ['url'],
data() {
return {
website: {
url: 'http://wangEditor.com/',
title: 'wangEditor',
subTitle: '轻量级富文本编辑器'
}
};
}
}
<template>
<ScopedSlotDemo :url="website.url">
<template v-slot="slotProps">
{{slotProps.slotData.title}}
template>
ScopedSlotDemo>
template>
// Index.vue
import ScopedSlotDemo from './ScopedSlotDemo';
export default {
components: {
ScopedSlotDemo
},
data() {
return {
name: '章三',
website: {
url: 'http://imooc.com/',
title: 'imooc',
subTitle: '程序员的梦工厂'
},
showFormDemo: false
}
}
}
<template>
<div class="container">
<header>
<slot name="header">slot>
header>
<main>
<slot>slot>
main>
<footer>
<slot name="footer">slot>
footer>
div>
template>
// NamedSlotDemo.vue
export default {
props: {},
data() {
return {};
}
}
<template>
<NamedSlotDemo>
<template v-slot:header>
<h1>将插入 header slot 中h1>
template>
<p>将插入到 main slot 中,即未命名的 slotp>
<template v-slot:footer>
<p>将插入 footer slot 中p>
template>
NamedSlotDemo>
template>
// Index.vue
import NamedSlotDemo from './NamedSlotDemo';
export default {
components: {
NamedSlotDemo
},
data() {
return {};
}
}
用法::is="组件名字"
。
需要根据数据,动态渲染组件。即组件类型不确定。
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{item}}
li>
ul>
<button @click="addItem">添加一项button>
div>
template>
// NextTick.vue
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c']
}
},
methods: {
addItem() {
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
this.list.push(`${Date.now()}`)
// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调
// 3. 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1
// eslint-disable-next-line
console.log( ulElem.childNodes.length )
})
}
}
}
<template>
<component :is="NextTickName"/>
template>
// Index.vue
import NextTick from './NextTick'
export default {
components: {
NextTick
},
data() {
return {
NextTickName: 'NextTick' // 对应组件的名字
};
}
}
用法:import()
函数。
按需加载,异步加载大组件。
<template>
<div>
<p>输入框: {{name}}p>
<input type="text" v-model.trim="name"/>
<input type="text" v-model.lazy="name"/>
<input type="text" v-model.number="age"/>
<p>多行文本: {{desc}}p>
<textarea v-model="desc">textarea>
<p>复选框 {{checked}}p>
<input type="checkbox" v-model="checked"/>
<p>多个复选框 {{checkedNames}}p>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jacklabel>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">Johnlabel>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mikelabel>
<p>单选 {{gender}}p>
<input type="radio" id="male" value="male" v-model="gender"/>
<label for="male">男label>
<input type="radio" id="female" value="female" v-model="gender"/>
<label for="female">女label>
<p>下拉列表选择 {{selected}}p>
<select v-model="selected">
<option disabled value="">请选择option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<p>下拉列表选择(多选) {{selectedList}}p>
<select v-model="selectedList" multiple>
<option disabled value="">请选择option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
div>
template>
// FormDemo.vue
export default {
data() {
return {
name: '章三',
age: 18,
desc: '自我介绍',
checked: true,
checkedNames: [],
gender: 'male',
selected: '',
selectedList: []
}
}
}
<template>
<FormDemo v-if="showFormDemo"/>
<button @click="showFormDemo = true">show form demobutton>
template>
// Index.vue
export default {
components: {
FormDemo: () => import('../BaseUse/FormDemo'),
},
data() {
return {
showFormDemo: false
};
}
}
vue
缓存组件。频繁切换,不需要重复渲染的时候使用,比如切换tab
页。
<template>
<div>
<button @click="changeState('A')">Abutton>
<button @click="changeState('B')">Bbutton>
<button @click="changeState('C')">Cbutton>
<keep-alive>
<KeepAliveStageA v-if="state === 'A'"/>
<KeepAliveStageB v-if="state === 'B'"/>
<KeepAliveStageC v-if="state === 'C'"/>
keep-alive>
div>
template>
// KeepAlive.vue
import KeepAliveStageA from './KeepAliveStateA'
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'
export default {
components: {
KeepAliveStageA,
KeepAliveStageB,
KeepAliveStageC
},
data() {
return {
state: 'A'
}
},
methods: {
changeState(state) {
this.state = state
}
}
}
<template>
<p>state Ap>
template>
// KeepAliveStateA.vue
export default {
mounted() {
// eslint-disable-next-line
console.log('A mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('A destroyed')
}
}
<template>
<p>state Bp>
template>
// KeepAliveStateB.vue
export default {
mounted() {
// eslint-disable-next-line
console.log('B mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('B destroyed')
}
}
<template>
<p>state Cp>
template>
// KeepAliveStateC.vue
export default {
mounted() {
// eslint-disable-next-line
console.log('C mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('C destroyed')
}
}
<template>
<<KeepAlive />
template>
// Index.vue
import KeepAlive from './KeepAlive';
export default {
components: {
KeepAlive
},
data() {
return {
};
}
}
多个组件有相同的逻辑,抽离出来,就使用mixin
。
但是它会有一些问题,vue3
提出来的composition API
可以解决这些问题。
// mixin.js
export default {
data() {
return {
city: '北京'
}
},
methods: {
showName() {
// eslint-disable-next-line
console.log(this.name)
}
},
mounted() {
// eslint-disable-next-line
console.log('mixin mounted', this.name)
}
}
<template>
<div>
<p>{{name}} {{major}} {{city}}p>
<button @click="showName">显示姓名button>
div>
template>
// MixinDemo.vue
import myMixin from './mixin'
export default {
mixins: [myMixin], // 可以添加多个,会自动合并起来
data() {
return {
name: '章三',
major: 'web 前端'
}
},
methods: {
},
mounted() {
// eslint-disable-next-line
console.log('component mounted', this.name)
}
}
mixin
的问题:
mixin
可能造成命名冲突mixin
组件可能会出现多对多的关系,复杂度较高// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
vuex
是vue
的状态管理库,方便组件之间的通讯。
出处:https://coding.imooc.com/lesson/419.html
state
:状态树getter
:派生状态(也叫做store
的计算属性)mutation
:更改 store
中状态的唯一方法是提交 mutation
action
:处理异步操作module
:由于单一状态比较臃肿,所以vuex
支持将将 store
分割成模块。dispatch
Action
通过 store.dispatch
方法触发。commit
mutation
更改store
中的状态。store.commit
。mapState
mapState
辅助函数帮助我们生成计算属性。mapGetters
store
中的 getter
映射到局部计算属性。mapActions
action
,可以使用 mapActions
辅助函数将组件的 methods
映射为 store.dispatch
调用(需要先在根节点注入 store
)。mapMutations
mutation
,可以使用 mapMutations
辅助函数将组件中的 methods
映射为 store.commit
调用(需要在根节点注入 store
)。hash
模式:默认,如http://www.abc.com#/user/10
h5 history
模式:需要server
端支持,如http://www.abc.com/user/10
const User = {
// 获取参数如 10 、20
template: 'User {{ $route.params.id }}'
};
const router = new VueRouter({
routes: [
// 动态路径参数:以冒号开头。
// 能命中 '/user/10'、'/user/20' 等格式的路由
{
path: '/user/:id',
componnet: User
}
]
});
export default new VueRouter({
routes: [
{
path: '/',
component: () => import('./../components/Navigator')
},
{
path: '/feedback',
component: () => import('./../components/FeedBack')
}
]
});