vue3语法
我眼中的vue3
将网页组件化,提高代码可复用性
将数据渲染到网页非常方便
响应式数据,做到数据与展示的数据双向绑定或单向绑定
方法只有纯粹的数据逻辑,而不再去处理DOM
事件细节。
重点难点
父子组件通信的运用
生命周期钩子函数的理解和运用
配套的vue-cli
(脚手架)vue-router
(路由)vuex
(全局状态管理)axios
(替代Ajax异步请求)等的使用vue3
的组件式api的应用,新版本内嵌setup
理解
渲染模板与虚拟DOM
VNode
主要内容
1.v-bind
属性绑定(简写 ':')
最常用,主要用于把数据绑定到DOM
的attribute
<a :href="url"></a>
<div :[a?b:c]="data"></div> 绑定动态参数 即可以选择绑定的参数
2.v-on
事件绑定(简写 '@')
用于绑定事件
<botton @click="function"></botton>
<div @[a?b:c]="function"></div>绑定动态事件 即可以选择绑定的参数
3.{{}}
用于绑定文本
4.v-if
v-for
用于绑定DOM
结构
5.v-html
用于将传递的语言转化为html
(内容全部替换掉)
6.computed
计算属性
计算属性是一种属性而非函数,监听响应式数据的变化,并随着响应式数据变化而变化,
注意:计算属性的值的变化是因为内部响应式数据的变化而引起的!
computed: {
a_squrt: {
reuturn Math.sqrt(this.a)
}}
- 与函数比较:
结果完全一样,但是计算属性在原始响应式数据改变的情况下,不在此计算
而函数则调用一次计算一次,计算属性主要用于响应式数据,如Date.now
则不行
建议vue
中多使用计算属性,以减少计算次数
- 与属性比较:
默认计算属性只有getter
,没有setter
,即不能对计算属性赋值,可以自定义set
computed: {
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
7.watch
侦听器
相对于计算属性,数据变化需要做出更复杂的操作,如执行异步操作 (访问一个API
),并设置一个执行该操作的条件可以在watch
内部写
const nums = ref(9)
watch(nums, (newValue, oldValue) => {
console.log(`watch 已触发,nums由${oldValues}更新为${newValues}`)
})
8.class
的绑定
- 对象语法
class
的名称加不加引号都一样,(class
有短横线时需要加引号)最终isActive
或hasError
转换为boolean
值为真时,
此DOM元素的class
内如下,且:class
与class
可共存,取元素并集
且class的有无也会随着isActive
和hasError
的更新而更新
// 1
<div :class="{ active: bool1, 'text-danger': bool2 }">
<div :class="computedClass"></div>
data(){
return {
bool1:true,
bool2:true
}
}
computed: {
computedClass: {
return {
active: this.bool1,
'text-danger':this.bool2
}
}
}
最终结果都是
<div class="active text-danger"></div>
- 数组语法
数组语法与对象语法不同的是,会将这里的activeClass
和errorClass
作为属性(响应式属性和计算属性)获取其值,而不是类本身
<div :class="[activeClass, errorClass]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}}
- 混合语法
<div :class="[{ active: isActive }, errorClass]"></div>
语法同样适用于自定义组件
自定义组件含有多个根元素时:
<template>
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
</template>
9.style
绑定
- 对象语法
看着非常像 CSS,但其实是一个JavaScript
对象。CSS
property
名可以用驼峰式 (camelCase
) 或短横线分隔 (kebab-case
,记得用引号括起来) 来命名,即fontSize
=== 'font-size'
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
//或者绑定到一个对象上去
<div :style="styleObject"></div>
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}}
- 数组语法
将所有对象绑定到此DOM,去并集,一些样式会被覆盖,如padding
,height
,margin
,letf
等等
<div :style="[baseStyles, overridingStyles]"></div>
10.v-if
v-show
条件渲染
大坑!!!!
使用
v-if
进行切换时,条件块内的事件监听器和子组件会被适当地被销毁和重建。
这个时候往往会出一些问题,比如一开始运行的时候,如果v-if
为false
,JavaScript
会找不到组件(如d3.select('#test'),而此时组件不存在)
解决办法是在v-if
由false
变为true
时,且等待组件加载完毕,再进行JavaScript
操作,那么更好的办法是将此段要显示的html
与需要操作其的JavaScript
封装为一个组件,这样可以通过子组件内部的钩子函数来调控JavaScript
启动时间,或者直接使用v-show
进行操作,因为v-show
仅仅只是简单地将css
样式内的display
设置为none
简单来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好
11.v-for
列表渲染
这个也是
vue
最爽的地方之一,可以很轻松的把成千上万条数据渲染成列表或表格
xuan数组
<ul id="array-with-index">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
xuan对象
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2020-03-22'
}
}
}
注意渲染对象是前面的是值,后面的才是key
维护状态
当xaun的对象或者数组改变时,相比大家已经想到,绑定的DOM
数据也会动态更新,那么问题来了,怎么更新呢,假如把所有的数据全部换掉,vue
该怎么去更新DOM
呢,默认情况下,是进行就地更新,即位置顺序,假如后端给过来的数据突然不按照顺序来了,第一名排在的第二个位置,这显然是不合理的,所以v-for
可以进行key
的设置,以确保按此规则更新.
<div v-for="item in items" :key="item.id"></div>
更新原数组的函数:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
不会更新原数组的函数:filter()
、concat()
和slice()
也可以直接更新替换原来数组,
vue
依然能够动态更新,而且并不是重新渲染数组,据说是一种智能的启发式方法官方不推荐
v-for
和v-if
在同一元素上使用,可以考虑如下方案
<template v-for="item in items" :key="item.id">
<li v-if="!item.isComplete">
{{ todo.name }}
</li>
</template>
同样,自定义组件也可以使用
v-for
,但是数值的传递仍然要通过props
12.v-modol
双向绑定
主要用于
input
select
等表单元素,将页面变化的值同时传递给响应式属性input
使用value
,:value
,checked
等确定需要绑定的内容
//message与输入框文字绑定
<input v-model="message" placeholder="edit me" />
//selected值与option内的文字绑定
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
多个复选框或者,一个多选框绑定元素时,初始化为数组
组件的v-model
双向绑定见下方
13.props
组件传值
父组件传递值给子组件
父组件通过v-bind:
绑定函数传递给子组件
子组件通过props
接收
注意 组件传值不适用v-bind只能传递字符串,不会传递其他任何类型如boolean,Number,Array等
//父,value为父组件响应式属性
<blog-post :data="value"></blog-post>
<blog-post v-bind="post"></blog-post> //post为对象,这样做将其所有property 都作为 prop 传入
//子,
<template>
<div class="blog-post">
{{data}}
</div>
</template>
//...props接收方式1
props:['data']
//...
//...props接收方式2
props:{
data:{
type:Number,
required:true,
default:0
}
}
//...
注意 对象的default值需要用工厂函数进行定义
default() {
return { message: 'hello' }
}
type类型有
String
Number
Boolean
Array
Object
Date
Function
Symbol
如果子组件需要监听父组件传递的prop的变化,又要对该prop进行修改,可以使计算属性动态修改和监听
在父组件定义了应该点击事件函数,希望在子组件调用,且该函数调用了父组件的响应式数据
父组件通过v-on:
绑定函数传递给子组件
子组件通过emit
接收
//父,postFontSize为父组件属性
<blog-post @enlarge-text="postFontSize += 0.1"></blog-post>
//子,click绑定父组件传递的函数
<template>
<div class="blog-post">
<button @click="$emit('enlargeText')">
hello world
</button>
</div>
</template>
注意短横线命名法会转化为驼峰命名法,二者等价,上文已经提到过
也可以在javaScrip
内接收
emits: ['enlargeText']
子组件属性传递给父组件--通过v-model
的方式
以子组件输入为例,子组件input
的value
绑定的父组件
<input :value="inputValue" @input="$emit('update:inputValue', $event.target.value)"/>
或定义计算属性代替,(这里计算属性需要设置
setter
,否则无法更新)
template:'<input v-model="getValue">'
props: ['inputValue'],
emits: ['update:inputValue']
getValue: {
get() {
return this.inputValue
},
set(value) {
this.$emit('update:inputValue', value)
}
}
或者在js处抛出事件
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName">
</user-name>
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)">
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)">
`})
故想要在父子组件绑定双向响应数据,需要额外做的事情是将子组件更新的值返回到父组件,故需要自己定义
update
更新父组件事件
v-model
的修饰符
除了内置修饰符之外,v-model
还可以自定义修饰符
如何在子组件捕获修饰符
自定义的修饰符会产生一个新的prop
,若v-model
未绑定名称,新的prop
名称为modelModifiers
,否则生成的 prop 名称将为 arg + "Modifiers"
<my-component v-model.capitalize="myText"></my-component>
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
methods: {
emitValue(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
}
<slot
>插槽
很好理解,我们需要在子组件中间插入一段别的组件或者html
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
所以插槽内的代码无法直接访问子组件,子组件也无法调用插槽内的属性
//父组件
<todo-button>
<div>
//...
</div>
</todo-button>
//子组件
<button class="btn-primary">
<slot></slot>
</button>
当有多个插槽时,可以使用name和v-slot的方式确定父组件插入的内容放到哪个插槽。
v-slot
的值不需要加引号
//父组件
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer></div>
//子组件
<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>
通过插槽slot绑定可以把子组件的值传递给父组件调用
父组件定义一个template
通过v-slot:来接收子组件slot的一个插槽prop
通过这个插槽prop
调用值,同理,多个插槽也是一样
//子组件
<ul>
<li v-for="( item, index ) in items">
<slot :item="item" :index="index" :another-attribute="anotherAttribute"></slot>
</li>
</ul>
//父组件
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
</todo-list>
<transition
> 过渡
vue也配备了良好的交互过渡封装
在我们开始使用vue时可以不用过多的考虑过渡,毕竟按钮什么的动效可以用各种UI框架
如Element-UI
LAyui
等等,专注于样式
<transition> 会自动引入六个class (只要有)
默认:v-enter-from
v-enter-active
v-enter-to
v-leave-from
v-leave-active
v-leave-to
如果使用了 <transition name="my-transition">,那么 v-enter-from
会替换为 my-transition-enter-from
以此类推
可以通过 <transition :duration="1000">...</transition>设置时间
ref
模板引用
ref本来只是将数值变成响应式属性,方便赋值继续引用而不是拷贝值
但以如下方式定义ref变量且在DOM中引用
则该ref便成为模板实例,(因为用户难免还是要去操作DOM,比如我需要在vue中使用d3)
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// DOM 元素将在初始渲染后分配给 ref
console.log(root.value) // <div>This is a root element</div>
})
return {
root
}
}
}</script>
生命周期函数
这块是重点,也放到了后面