创建一个Vue应用
确保已安装最新版本的 Node.js
npm create vue@latest
cd <your-project-name>
npm install
npm run dev
// 打包
npm run build
应用实例
每一个Vue应用都是通过createApp 函数创建一个新的 应用实例:
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
* 传入 createApp 的对象实际是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
挂载应用
应用实例必须在调用 .mount() 方法后才会渲染出来。该方法接收一个“容器参数”,可以是一个实际的DOM元素或者一个 CSS 选择器字符串
<div id="app"></div>
app.mount('#app)
* 应用根组件的内容将会被渲染在容器元素里面,容器元素自己将不会被视为应用的一部分。
应用配置
注册组件
app.component('TodoDeleteButton',TodoDeleteButton)
* 这使得TodoDeleteButton 在应用的任何地方都可用。
多个应用实例
createApp API 允许你在同一个页面中创建多个共存的Vue应用,每个应用都有自己的用于配置和全局资源的作用域
const app1 = createApp({
})
app1.mount('#container-1')
const app2 = createApp({
})
app2.mount('#container-2')
模板语法
1.文本插值 使用 双大括号 {{}} ,html 插入 使用 v-html
<span>Message: {{msg}}</span>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
2.Attribute 响应式绑定数据
<div :id="id"></div>
// 简写 请注意,这是一个只在 Vue 3.4 及以上版本中可用
<div :id></div>
// 布尔类型
<button :disabled="isButtonDisabled">Button</button>
3.动态绑定多个值
const objectOfAttrs = {
id:'container',
class:'wrapper'
}
//通过不带参数的 v-bind ,可将它们绑定到单个元素上
<div v-bind="objectOfAttrs"></div>
4.使用JavaScript 表达式
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
5.动态参数名
<a v-bind:[attributeName]="url"> ... </a>
//简写
<a :[attributeName]="url"> ... </a>
//绑定事件
<a @[eventName]="doSomething"></a>
6.修饰符 Modifiers
修饰符是以 点 开头的特殊后缀,表明指令需要以一些特殊方式绑定。如 .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault();
<form @submit.prevent="onSubmit"></form>
声明响应式状态
ref( ) 接受参数,设为默认值,并将其包裹在一个带有 .value 的属性的ref 对象中返回
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
</button>
</template>
* 当你在模板中使用一个ref,然后改变这个ref的值时,Vue会自动检测到这个变化,并且相应地更新DOM。这是通过一个基于依赖追踪的响应式系统实现的。当一个组件首次渲染时,Vue会追踪在渲染过程中使用的每一个ref 。然后当一个 ref 被修改时,它会触发追踪它的组件一次重新渲染。
reactive() 与将内部值包装在特殊对象中的ref 不同, reactive() 将使对象本身具有响应性
1.有限的值类型:只能用于对象类型。不能持有如 string 、number 、 boolean 这样的原始类型
2.不能替换整个对象
3.对解构操作不友好:当将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,将丢失响应性链接
const state = reactive({count:0})
// 当解构时,count 已经与 state.count 断开连接
let {count} = state;
// 不会影响原始的 state
count++
DOM 更新时机
要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:
import {nextTick} from 'vue'
asyncfunction increment() {
count.value++
await nextTick()
// 现在DOM 已经更新了
}
组合式 API (Composition API)与 <script setup>
<script setup>
import {ref, onMounted } from 'vue
const count = ref(0)
function increment(){
count.value++
}
onMount(()=>{
console.log(`打印 ${count.vaue}`)
})
</script>
计算属性
计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books
不改变,无论多少次访问 booksLength
都会立即返回先前的计算结果,而不用重复执行 getter 函数。
<script>
import {reactive, computed} from 'vue'
const author = reactive({
name:'Jocon',
books:['aa','bb','cc']
})
// 一个计算属性ref
const booksLength = computed(()=>{
return author.books.length >0?'Yes':'No'
})
// booksLength.value 访问计算结果
</script>
<template>
<p>是否借书?</p>
<p>{{booksLength}}</p>
</template>
可写计算属性
计算属性默认是只读的。可以通过同时提供 getter 和 setter 来创建:
<script setup>
import {ref,reactive} from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
//getter
get() {
return firstName.value + ' ' + lastName.value
},
//setter
set(newValue){
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
现在当你再运行 fullName.value = 'John Doe'
时,setter 会被调用而 firstName
和 lastName
会随之更新。
Class 与 Style 绑定
<div :class="{active:isActive,'text-danger': hasError}"></div>
const isActive = ref(true)
const hasError = ref(false)
//渲染结果
<div class="active"></div>
// 绑定对象
const classObject = reactive({
active:true,
'text-danger':false
})
<div :class="classObject"></div>
<div class="active"></div>
// 绑定计算属性
const isActive = ref(true)
const error = ref(null)
const classObject = computed(() => ({
active: isActive.value && !error.value,
'text-danger': error.value && error.value.type === 'fatal'
}))
<div :class="classObject"></div>
//绑定数组
const activeClass = ref('active')
const errorClass = ref('text-danger')
<div :class="[activeClass, errorClass]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>
<div :class="[{ active: isActive }, errorClass]"></div>
组件上使用,只有一个根元素的组件这些 class 会被添加到根元素上并与该元素上已有的 class 合并,如果你的组件有多个根元素,你将需要指定哪个根元素来接收这个 class。你可以通过组件的 $attrs
属性来实现指定:
<p :class="$attrs.calss">Hi !</p>
<span>This is a child component</span>
<MyComponent class="baz" />
//渲染如下
<p class="baz">Hi!</p>
<span>This is a child component</span>
绑定内联样式
const activeColor = ref('red')
const fontSize = ref(30)
<div :style="{color:activeColor ,fontSize:fontSize+'px}"></div>
const styleObject = reactive({
color: 'red',
fontSize: '13px'
})
<div :style="styleObject"></div>
条件渲染
v-if 元素是否被渲染
v-show 会在DOM渲染中保留该元素;v-show 仅切换了该元素上display css属性
v-show 不支持在 template 元素上使用
列表渲染 v-for
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
// 变量名使用 解构
<li v-for="({message},index) in items">
{{ index }} - {{ message }}
</li>
* 多层v-for 嵌套,每个v-for 作用域都可以访问到父级作用域
* 写法上 of 可替代 in
v-for 与对象
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
v-for 数字
<span v-for="n in 10">{{n}}</span>
// 此处 n 的值从 1 开始
不推荐v-if 和v-for 一起使用,v-if 优先级更高,这意味着 v-if 的添加将无法访问到v-for 作用域定义的变量别名,解决方法,在最外层包一个 <template> 在其上使用v-for
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
通过key管理状态
Vue 默认按照“就地更新”的策略来更新通过v-for渲染的列表。当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
为了给Vue一个提示,以便他可以跟踪每个节点的标识,从而重用和重新排列现有的元素,需要为每一个元素对应的块提供一个唯一的 key
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
事件处理
1.内联事件处理器:事件被触发时执行的内联 JavaScript 语句
const count = ref(0)
<button @click="count++">Add +</button>
<p>Count is : {{count}}</p>
// 传参
function say(message) {
alert(message)
}
<button @click="say('hello')">Say hello</button>
// 访问原生DOM事件
<button @click="warn('aaa',$event)"></button>
<button @click="(event) => warn('aa',event)"></button>
2.方法事件处理器:一个指向组件上定义的方法的属性名或是路径
const name = ref('Vue.js')
function greet(event) {
alert(`Hello ${name.value}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>
* 方法与内联事件判断:foo
、foo.bar
和 foo['bar']
会被视为方法事件处理器,而 foo()
和 count++
会被视为内联事件处理器。
事件修饰符
<!-- 单击事件将停止传递 -->
<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>
表单输入绑定
// input
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="请输入"/>
// checkbox 多个复选框绑定到同一个数组
const checkedNames = ref([])
<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>
//radio 单按钮
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
// select 选择器
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
修饰符
// .lazy 在 "change" 事件后同步更新而不是 "input"
<input v-model.lazy="msg" />
// .number 输入数字
<input v-model.number="age" />
// .trim 去除用户输入内容中两端的空格
<input v-model.trim="msg" />
生命周期
onMounted 钩子可以用来在组件完成初始化渲染并创建DOM节点后运行代码:
onUpdated:组件因为响应式状态变更而更新其DOM树之后调用。如果需要在某个特定的状态更改后访问更新后的DOM,请使用nextTick()
onUnmounted:在组件实例被卸载之后调用,可在这个钩子中手动清理一些副作用,例如计时器、DOM事件监听或者与服务器的链接。这个钩子在服务器端渲染期间不会被调用。
<script setup>
import {onMounted,onNumounted} from 'vue'
let intervalId
onMounted(()=>{
intervalId = setInterval(()=>{
})
})
onNuMounted(() => clearInterval(intervalId))
</script>
onBeforeMount:在组件被挂载之前调用,组件已经完成了其响应状态的设置,但还没创建DOM节点,这个钩子在服务器端渲染期间不会被调用。
onBeforeUpdate:组件即将因为响应式状态变更而更新其DOM树之前调用,这个钩子在服务器端渲染期间不会被调用。
onBeforeUumount:组件实例被卸载之前调用
侦听器
watch() :第一个参数可以是一个ref(包括计算属性)、一个响应式对象、一个getter函数、或多个数据源组成的数组
const x = ref(0)
const y = ref(0)
// 单个ref
watch(x,(newX) => {
console.log(`x最新值${newX}`)
})
//getter函数
watch(
()=>x.value+y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)
// 监听对象的属性值,不能直接监听要使用getter函数
watch(
()=>object.key,
(newValue) => {
}
)
//多个来源组成的数组
watch([x,() => y.value],([newX,newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})
即时回调的侦听器
传入 immediate:true 创建侦听器时,立即执行一遍回调,然后在相关状态更改时重新请求数据
watch(
source,
(newValue,oldValue) => {
// 立即执行,且当 `source`改变时再次执行
},
{immediate:true}
)
一次性侦听器
once:true,回调只在源变化时触发一次
watch(
source,
(newValue, oldValue) => {
// 当 `source` 变化时,仅触发一次
},
{ once: true }
)
watchEffect()
// 回调会立即执行,每当 todoId.value 变化时,回调会再次执行
watchEffect( async ()=> {
const response = await fetch(
`serverurl${todoId.value}`
)
data.value = await response.json()
})
模板引用
<script setup>
import {ref,onMounted} from 'vue';
// 声明一个ref 来存放该元素的引用
// 必须和模板里的ref 同名
const input = ref(null)
onMounted(()=>{
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
传递props 、组件之间事件传递
父组件:
<template>
<p>组件之间参数及事件传递</p>
<Child @enlarge-text="function" :title="title"/>
</template>
子组件:Child
<script setup>
const props = defineProps(['title'])
const emit = defineEmits(['enlarge-text'])
console.log(props.title)
emit('enlarge-text')
</script>
<template>
<h4>{{title}}</h4>
</template>
动态组件
<!-- currentTab 改变时组件也改变 -->
<component :is="tabs[currentTab]"></component>