一、简介
1.定义:是一款用于构建用户界面的js框架,基于标准html、css和js构建,提供了一套声明式的、组件化的编程模型,可以高效地开发用户界面。
2.核心功能:
声明式渲染,基于标准html拓展了一套模版语法,使得可以声明式地描述最终输出的html和js状态之间的关系。
响应性:vue会自动跟踪js状态,并在其发生变化时响应式地更新DOM。
3.渐进式框架:vue的设计注重灵活性和“可以被逐步集成”这个特点。
4.单文件组件(标志性功能):用类似于html格式的文件来书写vue组件,被称为单文件组件(也被称为*.vue文件),即vue的单文件组件会将一个组件的逻辑(js),模版(html)和样式(css)封装在同一个文件里。
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
5.API风格:选项式API和组合式API
使用选项式API,可以用包含多个选项的对象来描述组件的逻辑,例如data、methods和mounted。选项所定义的属性都会暴露在函数内部的this上,它会指向当前的组件实例。
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},
// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
通过组合式API,可以使用导入的API函数来描述组件逻辑。在单文件组件中,组合式API通常会与
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
二、创建一个应用
1.每个vue应用都是通过createApp函数创建一个新的应用实例。传入的对象是一个组件,每一个应用都需要一个“根组件”,其他组件作为其子组件。使用单文件组件,可以直接从另一个文件中导入根组件。
大多数应用基本是一颗嵌套的、可重用的组件树组成的。
//普通
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
//单文件组件
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
2.app实例必须调用.mount()方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的DOM元素或者是一个css选择器字符串
//html
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
//js
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
app根组件的内容将会被渲染在容器元素里面。容器元素自己将不会被视为应用的一部分。
.mount() 方法应该始终在整个app配置和资源注册完成后被调用。同时请注意,不同于其他资源注册方法,它的返回值是根组件实例而非app实例。
根组件的模版template通常是组件本身的一部分,但也可以通过在挂载容器内编写template来单独提供。
如果根组件没有设置template选项时,会自动使用容器的innerHTML作为模版。DOM内模版通常用于无构建步骤的vue应用程序。
3.应用配置
app实例会暴露一个.config对象,允许来配置一些高级应用级的选项,如定义app级的错误处理器,用来捕获所有子组件上的错误、注册app内可用的资源(组件等)
4.多个app实例
允许创建多个vue app,拥有自己的用于配制和全局资源的作用域。
三、template语法
使用基于html的template语法,能够声明式地将组件实例的数据绑定到呈现的DOM上。底层机制中,vue会将template编译成高度优化到js代码,当app状态变化时,能够推导出需要重新渲染的组件的最少的数量,使用最少的dom操作。模版语法主要分为插值语法和指令语法。
1.插值语法
使用双大括号{{xxx}},括号中的内容会被替换成相应组件实例中对应属性的值,一旦该属性发生变化,模版中用到该数据的地方也会自动更新。
也支持绑定js表达式,调用函数
<span>Message: {{ msg }}</span>
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
2.指令语法
“v-”作为前缀,表明是有vue提供的特殊attribute,指令的作用是在其表达式的值变化时响应式地更新dom
v-html:绑定html
v-bind:
绑定bool值的时候,根据true / false 值来决定 attribute 是否应该存在于该元素上,当attribute为真值或一个空字符串,则会包含这个attribute,否则被忽略
可以通过对象,将包含多个attribute的js对象绑定到单个元素上
v-if:基于表达式的值的真假来移除/插入元素v-on:监听DOM事件,缩写@
指令参数可以使用js表达式,包含在方括号内
//v-html绑定html
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
//绑定一个attribute
<div v-bind:id="dynamicId"></div>//元素的id attribute与组件的dynamicId属性保持一致
<div :id="dynamicId"></div>//v-bind简写
四、响应式状态state和方法methods
1.用 data 选项来声明组件的响应式状态。
此选项的值应为返回一个对象的函数。Vue 将在创建新组件实例的时候调用此函数,并将函数返回的对象用响应式系统进行包装。此对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上。不在data上定义,直接向组件添加新属性,无法出发响应式更新。
export default {
data() {
return {
count: 1
}
},
// `mounted` 是生命周期钩子
mounted() {
// `this` 指向当前组件实例
console.log(this.count) // => 1
// 数据属性也可以被更改
this.count = 2
}
}
2.通过methods向组件添加方法,methods是一个包含所有对象的方法。
vue自动为methods中的方法绑定指向组件实例的this,不能在定义method是时使用箭头函数。
当改变嵌套对象或者数组时,变化也是可以被检测到的。
修改了响应式state时,dom会自动更新,但不是同步的,会在nexttick更新周期中缓冲所有状态的修改,确保不管进行了多少次状态的修改,每个组件都只被更新一次。
对于有状态的方法,多个复用的组件会共享state,可以在created生命周期hook中创建该方法,每个实例就可以有自己的state。
五、计算属性
基于响应式state,进行一些操作和计算得到的属性。例如 full name=first name+last name,first name和last name是响应式属性,full name是计算属性。
也可以通过函数来获得这个计算结果,但计算属性值仅会在其依赖的响应式属性更新的时候才重新计算。而函数方法调用总是会在重新渲染发生时再次执行。
计算属性默认是只读的,如果要修改的话,需要同时提供getter和setter。
计算属性的getter不应该具有副作用,不要进行异步请求或者更改dom,仅是描述如何根据其他值派生一个值。
应该避免更新计算属性值。
六、class和style绑定
class
class放在html标签中,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名之间用空格分隔。
:class是为class提供的功能增强的用法,使得class的值除了是字符串以为,也可以是对象或数组。
可以通过给:class传递对象来动态切换class
//state
data(){
return {
isActive:true,
hasError:false
}
}
//template
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"//可以直接写成绑定一个对象
></div>
//结果
<div class="static active"></div>
对于只有一个根元素的组件,当使用了 class attribute 时,这些 class 会被添加到根元素上并与该元素上已有的 class 合并。
若有多个根元素组件,可以通过组件的$attrs来指定根元素。
:style绑定js对象,对应html元素的style属性。支持驼峰命名,也支持kebab-cased命名。
支持绑定包含多个样式对象的数组,会被合并渲染。
七、条件渲染
1.v-if
条件性渲染一块内容,当指令表达式返回真值时才渲染
2.v-else
为v-if添加一个else块,必须跟在v-if或者v-else-if后面
3.v-else-if
提供v-if的else-if块,可以连续多次重复使用,必须紧跟在一个 v-if 或一个 v-else-if 元素后面
当要切换多个元素,可以在 元素上使用 v-if,v-else和v-else-if
5.v-show
和v-if用法基本一样,但会在dom渲染中保留该元素,仅切换了该元素上名为display的css属性,不支持在 上使用,不能和v-else搭配使用
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。如果需要频繁切换,使用 v-show ;如果在运行时绑定条件很少改变,使用 v-if 。
6.v-if和v-for
当 v-if 和 v-for 同时存在于一个元素上的时候,v-if 会首先被执行。
可以在v-if上包装一层template,再在其上使用v-for
八、列表渲染
1.v-for
使用"item in items"的语法,items是源数据的数组,item是迭代项的别名
支持访问父作用域内的属性和变量,支持使用可选的第二个参数表示当前项的位置索引
可以使用of替代in
//js
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
//template
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
2.v-for和对象
可以遍历对象的所有属性,顺序基于对象调用Object.keys()的返回值来决定
3.使用范围值
n in 10,从1开始
4.template
可以在template中使用。
5.通过key管理state
默认情况下,数据项的顺序改变时,vue不会随之移动dom元素的顺序,就地更新,只适用于渲染结果不依赖子组件state或者临时dom状态。
推荐为每个元素对应的块提供唯一的key属性,最好是字符串或者number。
6.数组变化
会修改数组的方法:
push,pop,shift,unshift,splic,sort,reverse
不修改数组,返回新数组的方法:
filter,concat,slice
7.希望显示数组经过过滤或者排序后的内容,而不实际变更或重置原始数据,可以通过计算属性来返回,当使用可变的方法时,需要先创建原数组的副本
九、事件处理
1.监听事件
使用v-on指令,简写@来监听dom事件,并在事件触发时执行对应的js,用法:v-on:click=“handler” @click=“handler”
事件处理器(handler)的值可以是:
2.内联事件处理器:事件被触发时执行的内联js语句(类似于onclick),@click=“count++”," "中是js语句,支持调用方法,“func(x)”,支持传入$event变量,来访问原生DOM事件
3.方法事件处理器:一个指向组件上定义的方法的属性名或者是路径," "中是函数名称
//js
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// 方法中的 `this` 指向当前活跃的组件实例
alert(`Hello ${this.name}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
}
//template
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>
4.事件修饰符
event.preventDefault()方法是用于取消事件的默认行为
event.stopPropagation()阻止捕获和冒泡阶段中当前事件的进一步传播
使用事件修饰符可以更专注于数据逻辑而不是dom细节
修饰符:.表示的指令后缀
.stop :停止事件传递
.prevent:阻止事件的默认行为
.self:只当在 event.target 是当前元素自身时触发处理函数
.capture:使事件触发从包含这个元素的顶层开始往下触发
.once:绑定了事件以后只能触发一次,第二次就不会触发
.passive:不想阻止事件的默认行为
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
5.按键修饰符
系统按键修饰符与keyop事件一起使用时,该按键必须在事件发出时处于按下状态
.exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。
6.鼠标按键修饰符
将处理程序限定为由特定鼠标按键触发的事
十、表单输入绑定
1.基本用法
v-model会根据所使用的元素自动适用对应的DOM属性和事件结合:
文本类型的 和 元素会绑定 value property 并侦听 input 事件;
和 会绑定 checked property 并侦听 change 事件;
会绑定 value property 并侦听 change 事件
v-model会忽略任何表单元素上初始的 value、checked 或 selected attribute,所以应该在 JavaScript 中data部分声明该初始值。
文本:对于需要使用ime的语言,在拼字输入的阶段不会触发更新
选择器:如果v-model表达式的初始值不匹配任何一个选择项,select元素会渲染成一个未选择的状态,在ios上会导致无法选择第一项
//文本
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
//多行文本
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
//复选框 单选
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
//多选绑定同一个数组或者集合
export default {
data() {
return {
checkedNames: []
}
}
}
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
//单选按钮
<div>Picked: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
//多选
<div>Selected: {{ selected }}</div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
2.值绑定
通过在选项中使用v-bind,可以将v-modal的值绑定到组件实例上的动态数据上
<!-- `picked` 在被选择时是字符串 "a" -->
<input type="radio" v-model="picked" value="a" />
<!-- `toggle` 只会为 true 或 false -->
<input type="checkbox" v-model="toggle" />
<!-- `selected` 在第一项被选中时为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
3.修饰符
.lazy
.number
.trim
十一、生命周期
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此期间,会运行生命周期hooks的函数。
十二、侦听器
1.基本示例
使用watch选项在每次响应式属性发生变化的时候触发一个函数
2.深层侦听器
watch默认是浅层的,被侦听的属性只在被赋新值的时候触发回调函数,嵌套的属性发生变化不会触发,需要使用深层侦听器
export default {
watch: {
someObject: {
handler(newValue, oldValue) {
// 注意:在嵌套的变更中,
// 只要没有替换对象本身,
// 那么这里的 `newValue` 和 `oldValue` 相同
},
deep: true
}
}
}
3.即时回调的侦听器
watch是懒执行,通过handler和immediate:true,可以在组件实例创建时立即调用侦听器
export default {
// ...
watch: {
question: {
handler(newQuestion) {
// 在组件实例创建时会立即调用
},
// 强制立即执行回调
immediate: true
}
}
// ...
}
4.回调的触发时机
回调会在vue组件更新之前被调用,在侦听器回调中访问的dom是更新前的状态
通过flush:'post’可以在回调中访问被更新后的dom
export default {
// ...
watch: {
key: {
handler() {},
flush: 'post'
}
}
}
5.可以通过组件实例的 $watch() 来命令式地创建一个侦听器
6.用 watch 选项或者 $watch() 实例方法声明的侦听器,会在宿主组件卸载时自动停止
十三、模版引用
1.ref
可以通过ref属性在一个特定dom元素或子组件实例被挂载后,直接获得它的引用。挂载结束后引用暴露在this.$refs上。只能在组件挂载之后才能访问模版引用。
<script>
export default {
mounted() {
this.$refs.input.focus()
}
}
</script>
<template>
<input ref="input" />
</template>
2.在v-for中使用模版引用时,相应的引用中包含的值是一个数组,但顺序不保证与源数组相同。
3.ref可以绑定一个函数,会在每次组件更新的时候都被调用,需要使用动态的:ref绑定。当绑定的元素被卸载时,函数也会被调用一次。
十四、组件基础
1.定义一个组件
使用构建步骤时,一般会将vue组件定义在一个单独的.vue文件中,被叫做单文件组件(SFC)。
不使用构建步骤时,使用一个包含vue特定选项的js对象来定义。
//vue
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
//js
export default {
data() {
return {
count: 0
}
},
template: `
`
}
2.使用一个组件
要使用一个子组件,需要在父组件中导入它,同时要将导入的组件暴露给模版,需要在components上注册这个组件,这个组件会以注册时的名字作为模版中的标签名。
组件可以被重用多次,每个组件都维护自己的状态,每当使用一个组件,就是创建了一个新的实例。
单文件组件中推荐子组件使用驼峰命名,区分大小写,可以使用/>关闭一个标签。
3.传递props
在组件的props列表中声明要传递的值,当这个值被传递给props时,将成为该组件实例的一个属性,可以像其他组件属性一样,在模版和组件的this上下文中访问。
可以拥有任意多的props,接受任意类型的值,被注册后,可以以自定义属性的形势传递数据给它。
4.监听事件
父组件通过v-on:事件、@事件来选择性地监听子组件上抛的事件。
子组件通过调用内置的$emit方法,通过传入事件名称来抛出一个事件。
5.插槽
通过插槽可以像html元素一样向组件中传递内容,可以通过作为一个占位符,父组件传递进来的内容就会渲染在这里。
//template
<AlertBox>
Something bad happened.
</AlertBox>
//js
<template>
<div class="alert-box">
<strong>This is an Error for Demo Purposes</strong>
<slot />
</div>
</template>
<style scoped>
.alert-box {
/* ... */
}
</style>
6.动态组件
通过元素和is来实现。被传递给:is的值可以是被注册的组件名,导入的组件对象。
当使用 来在多个组件间作切换时,被切换掉的组件会被卸载。