初始化vue项目
确保node版本是v15以上,推荐nvm管理node版本。
npm install vite@latest
基础语法
属性绑定
v-bind:或者简写:
<template>
<button v-bind:disabled="state">按钮</button>
<button :disabled="state">按钮</button>
</template>
<script>
export default {
components: {VIfDemoComponent},
data() {
return {
msg: "模板",
state: false
}
}
}
</script>
条件渲染
v-if, v-else-if, v-else, v-show 用于标签内部
<template>
<h3 v-if="type === 'a'">a</h3>
<h3 v-else-if="type === 'b'">b</h3>
<h3 v-else-if="type === 'c'">c</h3>
<h3 v-else>not abc</h3>
<h3 v-show="show === true">show true</h3>
</template>
<script>
export default {
name: "VIfDemoComponent",
data() {
return {
type: 'a',
show: true
}
}
}
</script>
列表渲染
- v-for指令基于一个数组来渲染列表。 v-for有第二个参数index,数组的下标
<template>
<h3>列表渲染</h3>
<ul>
<li v-for="(film, index) in films">{{ film }}</li>
</ul>
</template>
<script>
export default {
name: "VForDemoComponent",
data() {
return {
films: ['钢铁侠', '变形金刚', '绿巨人']
}
}
}
</script>
- v-for也可以遍历对象的所有属性
参数顺序是固定的
第一个参数value是属性值
第二个参数key是属性键
第三个参数index是索引
<p v-for="(value, key, index) of book">{{value}}-{{key}}-{{index}}</p>
通过key管理状态
Vue默认采用“就地更新”的策略来更新通过v-for渲染的元素,当数据顺序变化时,Vue不会移动元素位置,而是重新渲染。重新渲染影响性能。
为v-for渲染的元素添加key属性,会给Vue一个提示,从而在发生顺序变化时重用和重新排序现有的元素。
<p v-for="(per, index) in result" :key="index">{{per.name}}-的能力是-{{per.ability}}</p>
项目中key的值一般采用数据的id
事件处理
v-on指令或者简写成@ 来监听DOM事件,并在触发时执行对应的js。
内联式和方法式
<template>
<h3>事件处理</h3>
<button v-on:click="addCount">加一</button>
<button @click="count--">减一</button>
count<p>{{count}}</p>
</template>
<script>
export default {
name: "VOnDemoComponent",
data() {
return {
count: 0
}
},
methods: {
addCount() {
this.count++
}
}
}
</script>
事件参数
只有event参数时可以直接获取。如果有自定义参数,用**$event**传递event参数
<template>
<h3>事件传参</h3>
<button @click="eventHandler">传递event</button>
<button @click="paramAndEventHandler('自定义参数', $event)">传递自定义参数和event</button>
</template>
<script>
export default {
name: "EventDemoComponent",
methods: {
eventHandler(e) {
console.log(e)
},
paramAndEventHandler(param, e) {
console.log(param)
console.log(e)
}
}
}
</script>
常见事件修饰符
阻止默认事件@click.prevent
阻止事件冒泡@click.stop
数组变化侦测
- 变更方法
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse() - 替换一个数组
变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter(),concat() 和 slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的
this.items = this.items.filter((item) => item.message.match(/Foo/))
计算属性
将复杂的计算逻辑从模板中提取到计算属性中。
与方法相比,计算属性会基于其响应式依赖被缓存,只要依赖不变,计算属性只会执行一次。
<template>
<h3>计算属性</h3>
<p>是否有出版的书籍:{{ownedBooks}}</p>
</template>
<script>
export default {
name: "ComputedDemo",
data() {
return {
author: {
name: 'Doe',
books: ['Vue2','Vue3','Vue4']
}
}
},
computed: {
ownedBooks() {
return this.author.books.length > 0 ? 'yes' : 'no'
}
}
}
</script>
Class绑定
- 绑定对象
- 绑定数组
- 数组嵌套对象
因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。
<template>
<h3>class绑定</h3>
<h3>0. 内联样式</h3>
<p :class="{'active': isActive, 'text-danger': hasError}">测试</p>
<h3>1. 绑定对象</h3>
<p :class="classObj">测试</p>
<h3>2. 绑定数组</h3>
<p :class="classArr">测试</p>
<h3>3. 数组嵌套对象</h3>
<p :class="['active', {'text-danger': hasError}]">测试</p>
</template>
<script>
export default {
name: "ClassDemo",
data() {
return {
isActive: true,
hasError: true,
classObj: {
'active': true,
'text-danger': true,
},
classArr: ['active', 'text-danger']
}
}
}
</script>
<style scoped>
.active {
font-size: 30px;
}
.text-danger {
background-color: red;
}
</style>
总结:绑定对象更适合于动态样式,绑定数组写起来更容易,只需要把需要的样式放进数组。数组嵌套对象和对象嵌套数组都可以,这里只展示了数组嵌套对象。
style绑定
和class绑定类似,也有内联样式,绑定对象,绑定数组。绑定包含多个样式对象的数组时,这些对象会合并渲染到同一元素上。
<template>
<h3>style绑定</h3>
<p :style="styleObj">style绑定对象</p>
<p :style="[styleObj, styleObj2]">style绑定数组</p>
</template>
<script>
export default {
name: "StyleDemo",
data() {
return {
styleObj: {
color: 'red',
fontWeight: 'bold'
},
styleObj2: {
fontSize: '30px'
}
}
}
}
</script>
侦听器
watch
注意点:侦听器函数的函数名必须和侦听的属性名一致,有两个参数,第一个是新值,第二个原始值。
<template>
<h3>侦听器</h3>
<p>{{msg}}</p>
<button @click="this.msg = 'World'">修改</button>
</template>
<script>
export default {
name: "WatchDemo",
data() {
return {
msg: 'Hello'
}
},
watch: {
msg(newVal, oldVal) {
console.log('初始值是'+oldVal+',新值是'+newVal)
}
}
}
</script>
表单输入绑定
v-model
修饰符:.lazy,.number, .trim
<template>
<h3>表单输入绑定</h3>
<p>message is: {{ msg }}</p>
<input v-model="msg" />
</template>
<script>
export default {
name: "VModelDemo",
data() {
return {
msg: ''
}
}
}
</script>
模板引用
其作用就是操作DOM元素。
挂载结束后引用都会被暴露在 this.$refs 之上。
<template>
<p>模板引用</p>
<p ref="xxx">初始p标签</p>
<button @click="getPMsg">获取p标签内容</button>
</template>
<script>
export default {
name: "RefDemo",
methods: {
getPMsg() {
console.log(this.$refs.xxx.innerText)
}
}
}
</script>
组件
全局组件注册
在main.js中注册主键可以用于全局。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import BaseUserComponent from './components/BaseUserComponent.vue'
const app = createApp(App);
app.component("BaseUserComponent", BaseUserComponent)
app.mount('#app');
组件传值props(父传子)
子组件用props中定义的属性接收父组件传递的参数。
父组件中可以属性绑定,传递动态的参数。
props可以接收任何类型。
props是只读的,不可修改。
子组件:
<template>
<div>子组件内容开始</div>
<div>
{{msg}}-{{msg2}}
</div>
<div>子组件内容结束</div>
</template>
<script>
export default {
name: "Child",
props: ['msg', 'msg2']
}
</script>
-------------------------------------
父组件:
<template>
<h3>父组件</h3>
<Child msg="消息1" :msg2="msg2"></Child>
</template>
<script>
import Child from "./Child.vue";
export default {
name: "Parent",
components: {Child},
data() {
return {
msg2: '消息2'
}
}
}
</script>
- props的校验,默认值,required
如果类型是数组和对象,默认值需要以函数形式返回
<script>
export default {
name: "Child",
props: {
msg: {
type: String,
default: '初始msg',
required: true
},
msg1: {
type: String
},
names: {
type: Array,
default() {
return ['Ann','Bob']
}
}
}
}
</script>
组件传值(子传父)
**1. this.
e
m
i
t
(
′
自定义事件
名
′
,
′
需要传递的参
数
′
)
2.
p
r
o
p
s
也能实现子传父
∗
∗
子组件中通过
t
h
i
s
.
emit('自定义事件名', '需要传递的参数') 2. props也能实现子传父** 子组件中通过this.
emit(′自定义事件名′,′需要传递的参数′)2.props也能实现子传父∗∗子组件中通过this.emit自定义事件名,父组件中使用该事件触点相应业务方法。
props实现子传父,更确切的说应该是父传子再传父。父元素传给子元素一个方法,子元素中调用该方法时携带参数,实现子传父。
子组件
<template>
<button @click="msg2Parent">传递数据</button>
</template>
<script>
export default {
name: "BComponent",
data() {
return {
childMsg: '子组件数据'
}
},
methods: {
msg2Parent() {
this.$emit("transferEvent", this.childMsg);
}
}
}
</script>
----------------------------------------
<template>
<BComponent @transferEvent="getData"></BComponent>
{{msg}}
</template>
<script>
import BComponent from "./BComponent.vue";
export default {
name: "AComponent",
components: {BComponent},
data() {
return {
msg: ''
}
},
methods: {
getData(data) {
this.msg = data
}
}
}
</script>
- 通过props接收函数的形式实现子传父
子元素:
<template>
<h3>BBcomponent</h3>
<p>{{ myEvent('子组件数据') }}</p>
</template>
<script>
export default {
name: "BBComponent",
props: {
myEvent: Function
}
}
</script>
------------------------------------------
<template>
<h3>AAcomponent</h3>
<BBComponent :myEvent="getData"></BBComponent>
<p>来自子组件的数据:{{msg}}</p>
</template>
<script>
import BBComponent from "./BBComponent.vue";
export default {
name: "AAComponent",
components: {BBComponent},
data() {
return {
msg: ''
}
},
methods: {
getData(data) {
this.msg = data
}
}
}
</script>
插槽slot
插槽的作用是将父组件的标签片段传入子组件中显示,子组件可以通过slot元素决定显示位置。
slot元素是一个插槽出口,标示了父元素提供的插槽内容在哪里被渲染。
子组件:
<template>
<p>这是子组件</p>
<slot></slot>
</template>
-----------------------------------------------
父组件:
<template>
<h3>这是父组件</h3>
<BaseSlot>
<div>这是父组件传递给子组件的内容</div>
</BaseSlot>
</template>
- 渲染作用域
父组件中定义插槽内容
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。 - 插槽默认值
子组件中定义插槽默认值
<slot>插槽默认值</slot>
- 具名插槽
v-slot命令可以简写为#
父组件中用templete标签包裹元素,templete标签中用v-slot指定插槽名。
子组件中在slot标签用name属性指定插槽名。
子组件:
<template>
<slot name="slot1"></slot>
<p>这是子组件</p>
<slot name="slot2"></slot>
</template>
-------------------------------------
父组件:
<template>
<h3>这是父组件</h3>
<BaseSlot>
<template v-slot:slot1>
<div>这是slot1</div>
</template>
<template #slot2>
<div>这是slot2</div>
</template>
</BaseSlot>
</template>
- 插槽数据传递
子组件数据传递到父组件插槽内容中需要区分两种情况。
- 普通插槽
子组件定义参数,在slot标签中定义参数属性。
父组件中,在子组件内用v-slot=‘xxx’的方式接受所有子组件的数据。 - 具名插槽
子组件中通普通插槽。
父组件中,在每个templete件标签内用#aaa='xxx’的方式接受所有子组件的数据。
子组件:
<template>
<slot name="slot1" :msg1="msg1"></slot>
<p>这是子组件</p>
<slot name="slot2" :msg2="msg2"></slot>
</template>
<script>
export default {
name: "BaseSlot",
data() {
return {
msg1: '子组件数据1',
msg2: '子组件数据2',
}
}
}
</script>
-------------------------------------
父组件:
<template>
<h3>这是父组件</h3>
<BaseSlot>
<template v-slot:slot1="obj">
<div>这是slot1 - {{obj.msg1}}</div>
</template>
<template #slot2="obj">
<div>这是slot2 - {{obj.msg2}}</div>
</template>
</BaseSlot>
</template>
组件的生命周期
可以用ref测试生命周期。只有渲染完成后this.$ref才能获取到DOM元素。
组件切换
Vue提供了component标签用于承载组件,通过is属性确定展示哪一个组件。
注意点:由于用的是字符串指定的组件,编译期不会自动导入组件,
需要手动把组件导入进来。
<template>
<component :is="switchComponent"></component>
<button @click="changeC">切换</button>
</template>
<script>
import ComponentA from "./ComponentA.vue";
import ComponentB from "./ComponentB.vue";
export default {
name: "isComponent",
components: {ComponentA,ComponentB},
data() {
return {
switchComponent: 'ComponentA'
}
},
methods:{
changeC(){
this.switchComponent=this.switchComponent==='ComponentA'?'ComponentB':'ComponentA'
}
}
}
</script>
组件保持存活
用<keep-alive></keep-alive>
包裹需要保持存活的片段,这样组件就不会进入卸载阶段。
异步组件
defineAsyncComponent
import {defineAsyncComponent} from "vue";
const ComponentB = defineAsyncComponent(() => {
import("./ComponentB.vue")
})
Provide & Inject
多级组件实现数据传递可以使用Prop 逐级透传,但是比较麻烦。
Vue提供了Provide & Inject的方式可以方便的实现。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
先祖组件:
<template>
<Inject1Component></Inject1Component>
</template>
<script>
import Inject1Component from "./Inject1Component.vue";
export default {
name: "ProviderComponent",
components: {Inject1Component},
data() {
return {
msg: '祖先数据'
}
},
provide() {
return {
msg: this.msg
}
}
}
</script>
-------------------------------------------
父组件:
<template>
<Inject2Component></Inject2Component>
<p>儿子组件获取祖先的数据:{{ inject1Msg }}</p>
</template>
<script>
import Inject2Component from "./Inject2Component.vue";
export default {
name: "Inject1Component",
components: {Inject2Component},
inject: ['msg'],
data() {
return {
inject1Msg: this.msg
}
}
}
</script>
-------------------------------------------
子组件:
<template>
<p>孙子组件获取祖先数据:{{inject2Msg}}</p>
</template>
<script>
export default {
name: "Inject2Component",
inject: ['msg'],
data() {
return {
inject2Msg: this.msg
}
}
}
</script>
- 全局provide
在main.js中定义全局可获取的数据,其他组件还是一样通过inject获取。
app.provide('globalData', '这是全局数据')