Vue @ Littlechai | 2021-09-08T18:38:10+08:00 | 10 分钟阅读 | 更新于 2021-09-08T18:38:10+08:00

Vue3

模板语法

插值

文本

1
2
3
<div id="app">
    {{ message }}
</div>

v-html

v-bind :用于html内的属性绑定

v-model

Text & Textarea

使用value property 和 input事件 v-model绑定的值对应 内容

checkbox 和 radio

使用radio property 和change事件 v-model绑定的值对应 value

checkboxv-model对应bool

Tips

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项

多选绑定数组

修饰符

.lazy

默认情况下v-model在每次input事件触发后将输入框的值与数据进行同步。.lazy修饰符将其转换为change事件之后进行同步,即失去焦点或者enter后进行同步

.number

自动将用户输入值转化为数值类型

.trim

自动过滤用户输入的首尾空白字符

v-on @用于添加事件

v-for 用于循环

1
2
3
<p v-for="item in items"></p>
<p v-for="(value, name) in items"></p>
<p v-for="(value, name, index) in items"></p>

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute:

1
2
3
<div v-for="item in items" :key="item.id">
  <!-- content -->
</div>

v-if v-else v-else-if

动态参数 避免大写

1
<a v-on:[eventname]="doSomething"> ... </a>

修饰符

1
<form v-on:submit.prevent="onSubmit">...</form>

Data Property

组件的 data 选项是一个函数。Vue 在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 $data 的形式存储在组件实例中

防抖与节流

防抖与节流都是为了优化性能,避免同一函数被频繁访问

打个比方:电梯15s上一次楼,防抖就规定15s运行一次,节流是没上来一个人重新计时15s,15s电梯运行

防抖

Vue 没有内置支持防抖和节流,但可以使用 Lodash 等库来实现。

如果某个组件仅使用一次,可以在 methods 中直接应用防抖:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
<script>
  Vue.createApp({
    methods: {
      // 用 Lodash 的防抖函数
      click: _.debounce(function() {
        // ... 响应点击 ...
      }, 500)
    }
  }).mount('#app')
</script>

但是,这种方法对于可复用组件有潜在的问题,因为它们都共享相同的防抖函数。为了使组件实例彼此独立,可以在生命周期钩子的 created 里添加该防抖函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
app.component('save-button', {
  created() {
    // 用 Lodash 的防抖函数
    this.debouncedClick = _.debounce(this.click, 500)
  },
  unmounted() {
    // 移除组件时,取消定时器
    this.debouncedClick.cancel()
  },
  methods: {
    click() {
      // ... 响应点击 ...
    }
  },
  template: `
    <button @click="debouncedClick">
      Save
    </button>
  `
})

组件 component

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const app = Vue.createApp({});

app.component('组件名', {
    template: '<h1>自定义组件</h1>'
})

app.mount('#app')

使用方法
<组件名></组件名>

计算属性与监听器

计算属性

computed

computed 也可以写在 methods 中,但computed是基于它们的响应依赖关系缓存的,也就是说,只要computed中的值不发生改变,就不会重新求值。

Setter

监听器 watch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
watch: {
    //对象写法
	name: {//监听对象
		handel() {//调用函数

		},
		immediate: true/false, //是否立刻触发一次
        deep: true/false //对象中任何属性变化都可以监听,否则无法监听对象内容
	}
    //函数写法
    name(newValue, oldeValue){
	
    }
}

事件处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<div @click="greet"></div>

Vue.createApp({
  data() {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    greet(event) {
      // `methods` 内部的 `this` 指向当前活动实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}).mount('#event-with-method')

内联处理器

使用$event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<div @click="say('hi', $event)"></div>

Vue.createApp({
  data() {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    say(message, event) {
      // `methods` 内部的 `this` 指向当前活动实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}).mount('#event-with-method')

多事件处理器

1
2
3
<button @click="one($event), two($event)">
  Submit
</button>

事件修饰符

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form @submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>

按键修饰符

1
<input @keyup.enter="submit"/>
1
<input @keyup.page-down="onPageDown"/>

按键别名

Vue 为最常用的键提供了别名:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统修饰键

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

  • .ctrl
  • .alt
  • .shift
  • .meta

Tips: Mac中meta对应command键(⌘),Windows中meta对应徽标键 (⊞),

Tips:必须修饰键的情况下释放其它按键,才能出发keyup,而单单释放修饰键也不会触发事件

.exact

.exact保证了只有确定的修饰符组合才能触发按键, 例如

当且仅当按下ctrl时才触发,同时按其他键不会触发

1
<button @click.ctrl.exact="onCtrlClick">A</button>

鼠标按钮修饰符

  • .left
  • .right
  • .middle

组件基础

基本实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const app = Vue.createApp({})

app.component('button-counter',{
	data() {
		return {
			count: 0
		}
	},
	template:`
		<button @click="count++">
			You clicked me {{ count }} times.
		</button>
	`
})

传递数据

props & emits

监听子组件事件

使用事件抛出一个值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<button @click="$emit('enlargeText', 0.1)">
  Enlarge text
</button>
抛出值可用$event接受

<blog-post ... @enlarge-text="onEnlargeText"></blog-post>
methods: {
  onEnlargeText(enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}
抛出值用参数接受

在组件上使用v-model

1
2
3
<input v-model="searchText">
等价于
<input :value="searchText" @input="searchText = $event.target.value">

在组件上使用时,v-model等价于

1
2
3
4
<custom-input
    :model-value="searchText"
    @update:model-value="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 modelValue 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 update:modelValue 事件抛出
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="app">
    <custom-input
        v-model="searchText"
    ></custom-input>
    <span> {{ searchText }} </span>
</div>

const app = Vue.createApp({
        data() {
            return {
                searchText: ""
            }
        }
    })
    
app.component('custom-input', {
    props: ['modelValue'],
    emits: ['update:modelValue'],
    template: `
        <input
            :value="modelValue"
            @input="$emit('update:modelValue', $event.target.value)"
        >
    `

})
app.mount('#app')

也可以使用computed来实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div id="app">
        <custom-input></custom-input>
    </div>

const app = Vue.createApp({
})

app.component('custom-input', {
    data() {
        return {
            value: ""
        }
    },
    props: ['modelValue'],
    emits: ['update:modelValue'],

    template: `
        <input v-model="value">
        <span> {{ value }} </span>
    `,

    computed: {
        value: {
            get() {
                return this.modelValue
            },
            set(value) {
                this.$emit('update:modelValue', value)
            }
        }
    }
})
app.mount('#app')

插槽

我们时常在html中添加内容,在模板中我们应该使用插槽来指出内容的位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<alert-box>
	There is something wrong
</alert-box>

app.component('alert-box',{
    template: `
        <strong>Error!</strong>
        <slot></slot>

    `
})

动态组件

实现不同组件的动态切换,使用isattribute

1
<component :is="getCurrentTag" class="tag"></component>

image-20210914162525721

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<body>
    
    <div id="app">
        <button
            v-for="tag in tags"
            :key="tag"
            :class="['tag-button', {active: tag == currentTag}]" 
            @click="currentTag = tag"
        >
        {{ tag }}
        </button>
        <component :is="getCurrentTag" class="tag"></component>
    </div>

</body>


<script>
    const app = Vue.createApp({
        data() {
            return {
                currentTag: "Home",
                tags: ["Home", "Posts", "Archive"]
            }
        },
        computed: {
            getCurrentTag(){
                return this.currentTag.toLowerCase()
            }
        }
    })
    app.component('home', {
        template: `
            <div>Home<div>
        `
    })
    app.component('posts', {
        template: `
            <div>Posts<div>
        `
    })
    app.component('archive', {
        template: `
            <div>Archive<div>
        `
    })
    app.mount("#app")


</script>

解析DOM模板时的注意事项

Element Placement Restrictions

有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。

1
2
3
4
5
6
7
8
9
不合法
<table>
  <blog-post-row></blog-post-row>
</table>

合法
<table>
  <tr is="vue:blog-post-row"></tr>
</table>

Case Insensitivity

js中的大写字母,要在html中使用 kebab-cased (横线字符分隔)

1
postTitle <=> post-title

深入组件

组件注册

全局注册

局部注册

注意局部注册的组件在其子组件中不可用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const ComponentA = {

}
const ComponentB = {

}

const app = Vue.creat({
    components: {
        'component-a': ComponentA,
        'component-b': ComponentB
    }
})
1
2
3
4
5
6
7
8
const ComponentA = {

}
const ComponentB = {
	components: {
		'component-a': ComponentA
	}
}

Props

prop类型

字符串数组

1
props: ['value', 'message', 'title']

对象

值+数据类型给出

1
2
3
4
5
props: {
	value: Number,
	message: String,
	title: String
}

传递静态或动态的Prop

使用v-bind来传递动态的Prop,同时v-bind可以告诉这是一个JavaScript表达式,而不是字符串

1
2
<blog :ids="[123,456]"></blog>
使用v-bind vue才能识别这是一个数组

传入一个对象的所有property

可以使用不带参数的v-bind

1
2
3
4
5
6
7
8
peo: {
	name: 'Frank',
	age: 17
}

<blog v-bind="peo"></blog>
等价于
<blog :name="peo.name" :age="peo.age"></blog>

Prop验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app.component('', {
    //数据类型检测,null和undefined可以通过任意数据类型检测
	propA: 数据类型,
    //多个可能的类型
    propB: [数据类型,数据类型],
    
    propC: {
		required: true, //必填
        default: property //默认值
    },
    
    propD: {
        //对象或数组默认值必须从一个工厂函数获取
		type: Object,
        default() {
			return {
                message: 1
            }
        }
    }
    
    //自定义验证函数
    propE: {
		validator(value){
    		return ['success','failure'].includes(value)
		}
	}
    
})

非prop的Attribute

Atrribute继承

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<my-component status="actived"></my-component>

app.component('my-component',{
	template: `
		<div>
        	<input>    
		</div>
	`
})

渲染后,status会继承在div上,也就是root山

禁用Atrribute继承

如果你希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false

禁用继承的常见情况是需要atrribute应用在除根节点外的所有其它元素

通过将 inheritAttrs 选项设置为 false,你可以访问组件的 $attrs property,该 property 包括组件 propsemits property 中未包含的所有属性 (例如,classstylev-on 监听器等)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<my-component status="actived"></my-component>

app.component('my-component',{
	inheritAttrs: false,
	template: `
		<div>
        	<input v-bind="$attrs">    
		</div>
	`
})

渲染后,status会在input上

多个根节点上的 Attribute 继承

多根节点的组件不具有自动fallthrough行为。若为显式绑定$attrs,运行时会给予警告

1
2
3
4
5
6
app.component('custom-layout', {
  template: `
    <header>...</header>
    <main>...</main>
    <footer>...</footer>`
})

自定义事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div v-color="color"></div>


export default {
    directives: {
		color: {
            //第一次被绑定时立即触发
			bind(el, binding) {
				el.style.color = 'red'
            },
            //每次dom更新时触发
            update(el, binding){
                el.style.color = 'red'
			}
        }
        
        //若bind和update相同,可简写为
        color(el, binding){
    		el.style.color = 'red'
		}
    }
}

定义自定义事件

可以通过emits选项在组件上定义发出的事件

1
2
3
app.component('custom-form',{
	emits: ['click', 'submit']
})

验证抛出的事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
app.component('custom-form',{
	emits: {
        //无验证
        click: null,
        
        submit: ({mail, password}) => {
			if(mail && password){
                return true
            } else {
                return false
            }
        }
    }
})

多个v-model绑定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<div id="app">
        <p>First name {{ firstName }}</p>
        <p>Last name {{ lastName }}</p>
        <user-name 
            v-model:first-name="firstName"
            v-model:last-name="lastName"
        ></user-name>
    </div>

    const app = Vue.createApp({
        data(){
            return {
                firstName: 'Frank',
                lastName: 'John'
            }
        }
    })
    app.component('user-name', {
        props: {
            firstName: String,
            lastName: String
        },
      /*   emits: ['update:firstName','update:lastName'], */
        template: `
            <input
                :value="firstName"
                @input="$emit('update:firstName',$event.target.value)"
            >
            <input
                :value="lastName"
                @input="$emit('update:lastName',$event.target.value)"
            >
        `
    })
    app.mount('#app')

处理v-model修饰符

v-model修饰符在modelModifiersprop中,若为带参数的v-model绑定,则在args+Modifiers中,实际上v-model修饰符的作用在于if判定,对于有修饰符进行特殊处理

以下实现了首字母大写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!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>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <p>First name {{ firstName }}</p>
        <p>Last name {{ lastName }}</p>
        <user-name 
            v-model:first-name.trans="firstName"
            v-model:last-name.trans="lastName"
        ></user-name>
    </div>
</body>

<script>
    const app = Vue.createApp({
        data(){
            return {
                firstName: 'Frank',
                lastName: 'John'
            }
        }
    })
    app.component('user-name', {
        props: {
            firstName: String,
            lastName: String,
            firstNameModifiers:{
                default: () => ({})
            },
            lastNameModifiers: {
                default: () => ({})
            }
        },
        methods: {
            changeFirstName(e){
                let value = e.target.value
                console.log(value)
                if(this.firstNameModifiers.trans){
                    value = value.charAt(0).toUpperCase() + value.slice(1)
                }
                this.$emit('update:firstName', value)
            },
            changeLastName(e){
                let value = e.target.value
                console.log(value)
                if(this.lastNameModifiers.trans){
                    value = value.charAt(0).toUpperCase() + value.slice(1)
                }
                this.$emit('update:lastName', value)
            }
        },
      /*   emits: ['update:firstName','update:lastName'], */
        template: `
            <input
                :value="firstName"
                @input="changeFirstName"
            >
            <input
                :value="lastName"
                @input="changeLastName"
            >
             
        `
    })
    app.mount('#app')
</script>
</html>

插槽

插槽内容

插槽可以包含任何模板代码,包括 HTML、组件

渲染作用域

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

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

备用内容

1
2
3
4
5
6
<button type="submit">
  <slot>Submit</slot>
</button>

<submit-button></submit-button>
当组件中无内容时显示Submit,有内容时显示内容

具名插槽

具名插槽用于需要多插槽的场景

插槽具有name属性,用<template>v-slot来对应

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//html
<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

//组件
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

Provide/Inject

适用于跨级传参/接受参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
app.component('todo-list', {
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide() {
    return {
      todoLength: this.todos.length
    }
  },
  template: `
    ...
  `
})

provide/inject 绑定并不是响应式的

1
2
3
4
5
6
7
8
app.component('todo-list', {
  // ...
  provide() {
    return {
      todoLength: Vue.computed(() => this.todos.length)
    }
  }
})

动态组件&异步组件

keep-alive

使用keep-alive可以让组件失活,即在第一次创建后缓存下来

1
2
3
<keep-alive>
    <component :is="getCurrentTag" class="tag"></component>
</keep-alive> 

异步组件(待补)

待补

模板引用

ref为子组件的直接引用提供了id,其位于$refs

Tips:$refs 只会在组件渲染完成之后生效,应避免在模板或计算属性中使用

过渡&动画(待补)

单元素/组件的过渡

过渡class

1
<transiton name='name'></transiton> 默认name为v

Transition Diagram

可复用&组合

axios

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
axios({
    method: '请求类型',
    url: 'URL'
    
    params: {

	}
}).then((result) => {
    //result为请求结果
})

方法返回值为Promise可以用await,await将其转换为一个值

addEventListener('click',async function(){
    const { data } = ...
    const result = await axios({
        method: 'GET',
        url: 'URL',
        params: {
			name: 'qwq'
        }
    })
})
请求数据储存在result.data中

解构赋值(重命名)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
addEventListener('click',async function(){
    const { data: res } = await axios({
        method: 'GET',
        url: 'URL',
        params: {
			name: 'qwq'
        }
    })
    consolo.log(res.data)
})

直接get or post

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
document.querySelector('#btn').addEventListener('click',async function(){
    const {data: res} = await axios.get('http://www.liulongbin.top:3006/api/getbooks',{
        params: {bookname: '西游记'}
    })
    console.log(res.data)
})

document.querySelector('#btn').addEventListener('click',async function(){
    const {data: res} = await axios.get('http://www.liulongbin.top:3006/api/getbooks',{
        data: {bookname: '西游记'}
    })
    console.log(res.data)
})

生命周期

© 2021 - 2022 小柴Yeah

Powered by Hugo with theme Dream.

avatar

小柴YeahThe time is no time, when it is past