<p>标签内容p>
<p>{{ msg }}p>
默认情况下 onClick 会被视为动态绑定, 所以每次都会去追踪它的变化,但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可
脚手架版本需在 4.5.0 版本以上
# 创建工程
vue create <project-name>
# 进入工程目录
cd <project-name>
# 运行
npm run serve
官网:https://vitejs.cn/
优势:
# 创建工程
npm init vite-app <project-name>
# 进入工程目录
cd <project-name>
# 安装依赖
npm install
# 运行
npm run dev
返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文。
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
参数
支持两个参数
组件选项对象作为第一个参数
可以将根 prop 传递给应用程序
const app = createApp(
{
props: ['username']
},
{ username: 'Evan' }
)
<div id="app">
{{ username }}
div>
返回一个“虚拟节点(VNode)
参数
接收三个参数:type
,props
和 children
type
String | Object | Function
props
Object
children
String | Array
h()
生成,或者使用字符串来获取“文本 VNode”,或带有插槽的对象。可选。render() {
return h('div', {}, [
'Some text comes first.',
h('h1', 'A headline')
])
}
<div>
Some text comes first.
<h1>A headlineh1>
div>
创建一个只有在需要时才会加载的异步组件。详细参考
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。
import { createApp, nextTick } from 'vue'
const app = createApp({
setup() {
const message = ref('Hello!')
const changeMessage = async newMessage => {
message.value = newMessage
await nextTick()
console.log('Now DOM is updated')
}
}
})
合并 prop,后面参数的 property 优先。
import { mergeProps } from 'vue'
setup(props, ctx) {
const mergeProperty = mergeProps(
{
// 该 class 将与 $attrs 中的其他 class 合并。
class: 'active'
},
ctx.attrs,
{ b: 3 }
)
console.log(mergeProperty) // {class: "active", a: "1", b: 3}
}
允许在 setup
的单文件组件函数中访问 CSS 模块。
WARNING
useCssModule
只能在 render
或 setup
函数中使用。
姓名:张三
getCurrentInstance
支持访问内部组件实例。
WARNING
getCurrentInstance
只能在 setup 或生命周期钩子中调用。
import { getCurrentInstance } from 'vue'
const MyComponent = {
setup() {
const internalInstance = getCurrentInstance()
internalInstance.appContext.config.globalProperties // 访问 globalProperties
}
}
props
被解析之后执行 。主要是实现数据和业务逻辑不分离data
和 methods
,所以为了避免错误使用,直接将内部的 this
改成了 undefined
返回对象。属性、方法均可以在模板中直接使用(重点)
setup() {
let name = '张三'
let age = 18
function alertAge() {
alert(age)
}
return {
name,
age,
alertAge
}
}
返回一个渲染函数。可以自定义渲染内容(了解)
// App.vue
import { h } from 'vue'
export default {
name: 'App',
setup() {
return () => h('h1', { a: 1 }, 'h函数自定义内容')
}
}
<h1 a="1">h函数自定义内容h1>
props
值为对象。包含:组件外部传递过来,且组件内部声明接收了的属性。
// 父组件
<Demo name="张三" :age="18" />
// Demo 组件
export default {
props: ['name', 'age'],
setup(props) {
console.log(props) // Proxy { name: "张三", age: 18 }
}
}
context
上下文对象
attrs :值为对象。包含:组件外部传递过来,但没有在 props 配置中声明的属性,相当于 this.$attrs
。
slots :收到的插槽内容,相当于 this.$slots
。
emit :分发自定义事件的函数,相当于this.$emit
。区别是需要在 emits
选项中配置。
expose :将 property 暴露给外部访问,比如通过父组件的 ref
,可以使用 expose
。此时组件内部不能使用
// app.vue
<com-a ref="compa" />
export default {
setup() {
const compa = ref(null)
onMounted(() => { // 使用子组件方法
compa.value.setObservedA(2)
})
return {
compa
}
}
}
// comp-a.vue
export default {
setup(props, { expose }) {
const observed = reactive({
a: 1
})
function setObservedA(value) {
observed.a = value
}
expose({ // 将 setObservedA 方法暴露给外部访问
setObservedA
})
return { // 组件内部现在不能使用 setObservedA 方法,因为没有 return 出去
observed
}
}
}
作用:实现祖孙间组件通信
实现:父组件有一个 provide
选项提供数据,子组件有一个 inject
选项接收数据
// 祖组件
import { provide, ref } from 'vue'
setup() {
const count = ref(0)
provide('count', count)
}
// 子组件
import { inject } from 'vue'
setup() {
const count = inject('count')
}
reactive
作用:定义一个对象类型响应式的数据(基本类型别用,用 ref
函数)
备注:
<div>工作:{{ job.type }}div>
<button @click="jobChange">修改工作button>
import { reactive } from 'vue'
setup() {
const job = reactive({
type: '前端',
salary: '60K'
})
function jobChange() {
job.type = '后端'
}
return {
job,
jobChange
}
}
重要:
当将 ref
分配给 reactive
property 时,ref 将被自动解包。
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
浅层次创建一个响应式对象,只有第一层属性是响应式
const state = shallowReactive({
foo: 1,
nested: {
bar: 2
}
})
// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
让一个响应式数据变为只读的(深层次)
让一个响应式数据变为只读的(浅层次),响应式对象第一层数据不可修改
返回 reactive
或 readonly
代理的原始对象。对 ref
的数据无效
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
标记一个对象,使其永远不会转换为 proxy。返回对象本身。
const person = reactive({
name: '张三',
age: 18
})
// person.car 不再是响应式
person.car = makeRaw({})
ref
作用:定义一个响应式的数据
备注:
Object.defineProperty
的get
与set
完成的reactive
函数
<div>姓名:{{ name }}div>
<div>工作:{{ job.type }}div>
<ul>
<li v-for="(item, index) in list" :key="index" @click="listChange(index)">{{ item }}li>
ul>
<button @click="nameChange">修改名字button>
<button @click="jobChange">修改工作button>
import { ref } from 'vue'
setup() {
// 创建 ref 对象
const name = ref('张三') // -- 基本类型
const job = ref({ // -- 对象类型
type: '前端',
salary: '60K'
})
const list = ref([1, 2, 3]) // -- 数组类型
// 修改 ref 对象
function nameChange() {
name.value = '李四'
}
function jobChange() {
job.value.type = '后端'
}
function listChange(index) {
list.value[index] = list.value[index] + 1
}
return {
name,
job,
list,
nameChange,
jobChange,
listChange
}
}
获取DOM
<com-a ref="coma" />
const coma = ref(null)
onMounted(() => {
console.log(coma.value.$el) // 组件 Dom
})
return {
coma
}
如果参数是一个 ref
,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val
的语法糖函数。
function useFoo(x: number | Ref<number>) {
const unwrapped = unref(x) // unwrapped 现在一定是数字类型
}
为源响应式对象上的某个 property 新创建一个 ref
。它会保持对其源 property 的响应式连接。
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
为源响应式对象上的所有 property 新创建一个 ref
。它会保持对其源 property 的响应式连接。
<div>姓名:{{ firstName + lastName }}div>
<div>薪资:{{ job.salary }}div>
const person = reactive({
firstName: '张',
lastName: '三',
job: {
salary: '20K'
}
})
return { ...toRefs(person) }
浅层次创建 ref 对象,创建基本数据类型时与 ref 无区别,创建对象类型时直接改变属性值不再是响应式,必须要替换源值
let x = shallowRef({
y: 0
})
x.value.y++ // 值改变,但页面不渲染
const flag = x.value.y === 0 // 直接替换,页面渲染
if (flag) {
x.value = { y: 1 }
} else {
x.value = { y: 0 }
}
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。参考链接
手动执行与 shallowRef
关联的任何作用 (effect)。
const shallow = shallowRef({
greet: 'Hello, world'
})
// 第一次运行时记录一次 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 这不会触发作用 (effect),因为 ref 是浅层的
shallow.value.greet = 'Hello, universe'
// 记录 "Hello, universe"
triggerRef(shallow)
里面不能用异步代码(不推荐),但是能执行
import { reactive, computed } from 'vue'
const person = reactive({
firstName: '张',
lastName: '三'
})
person.fullName = computed(() => person.firstName + '-' + person.lastName)
console.log(person.fullName) // 张-三
接受一个具有 get
和 set
函数的对象,用来创建可写的 ref 对象
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
侦听 ref 定义的数据
单一源
import { ref, watch } from 'vue'
let count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal) // 0 undefined
}, {
immediate: true
})
多个源
import { ref, watch } from 'vue'
let count = ref(0)
let name = ref('李四')
watch([count, name], (newVals, oldVals) => {
console.log(newVals, oldVals) // [0, "李四"] []
}, {
immediate: true
})
侦听 reactive 定义的数据
**注意:**直接侦听 reactive 定义的数据时,oldValue 会无法获取,deep 默认开启
单一源
const person = reactive({
firstName: '张'
})
watch(
() => person.firstName,
(newVal, oldVal) => {
console.log(newVal, oldVal) // 张 undefined
},
{
immediate: true
}
)
多个源
const person = reactive({
firstName: '张',
lastName: '三'
})
watch(
[() => person.firstName, () => person.lastName],
(newVal, oldVal) => {
console.log(newVal, oldVal) // ["张", "三"] []
},
{
immediate: true
}
)
深度侦听
const person = reactive({
job: {
salary: '20K'
}
})
// 这种方式依旧无法获取 oldVal
watch(
() => person.job,
(newVal, oldVal) => {
console.log(newVal, oldVal)
},
{
deep: true
}
)
// 这种方式就能正确获取
watch(
() => person.job.salary,
(newVal, oldVal) => {
console.log(newVal, oldVal)
}
)
停止侦听
let count = ref(0)
const stop = watch(
count,
(newVal, oldVal) => {
if (newVal === 1) { // 当 count 为 1 时,移除侦听器。但依旧会执行一次下面的代码
stop()
}
console.log(newVal, oldVal) // 1 0
setTimeout(() => {
console.log(111) // 111
}, 1000)
}
)
不用指定侦听哪个属性,回调中用到了哪个就侦听哪个,默认先执行一次
let count = ref(0)
// 当count发生改变时触发
watchEffect(() => {
console.log(count.value) // 0
})
停止侦听
let count = ref(0)
// 当count发生改变时触发
const stop = watchEffect(() => {
console.log(count.value) // 0
if (count.value === 1) {
stop()
}
})
onInvalidate 函数作入参,解决初次就执行的问题
setup()
或生命周期钩子函数中使用了 watchEffect
,则在组件卸载时)let count = ref(0)
watchEffect(onInvalidate => {
onInvalidate(() => {
console.log('-----', count.value)
})
})
effectScope
创建一个 effect 作用域对象,以捕获在其内部创建的响应式 effect (例如计算属性或侦听器),使得这些 effect 可以一起被处理。
import { reactive, effectScope, watch } from 'vue'
const job = reactive({
type: '前端',
salary: '60K'
})
const scope = effectScope()
scope.run(() => {
watch(() => job.type, newVal => {
console.log(newVal)
})
watch(() => job.salary, newVal => {
console.log(newVal)
})
})
function jobChange() {
job.type = '后端'
// 处理该作用域内的所有 effect
scope.stop()
}
getCurrentScope
返回当前活跃的 effect 作用域。
onScopeDispose
在当前活跃的 effect 作用域上注册一个处理回调。该回调会在相关的 effect 作用域结束之后被调用。
import { reactive, effectScope, watch, onScopeDispose } from 'vue'
const job = reactive({
type: '前端',
salary: '60K'
})
const scope = effectScope()
scope.run(() => {
watch(() => job.type, newVal => {
console.log(newVal)
})
onScopeDispose(() => {
console.log('调用 scope.stop() 执行')
})
})
检查值是否为一个 ref 对象。
const count = ref(0)
const name = '张三'
console.log(isRef(count), isRef(name)) // true false
检查对象是否是由 reactive
或 readonly
创建的 proxy。
检查对象是否是由 reactive
创建的响应式代理。
检查对象是否是由 readonly
创建的只读代理。
__v_ref
这个私有属性判断这个数据是否是 ref 类型,如果值为true
就是一个 ref 类型的数据.value
,模板中读取数据自动添加.value
.value
类型:Array | Object
详细:emits 可以是数组或对象,从组件触发自定义事件,emits 可以是简单的数组,也可以是对象,后者允许配置事件验证。
详细参考
类型: Array
详细:一个将暴露在公共组件实例上的 property 列表。
用法:
export default {
// increment 将被暴露,
// 但 count 只能被内部访问
expose: ['increment'],
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
将组件 HTML 结构传送到指定位置
-
string。移动
内容的目标元素<teleport to="body">
<div>我的结构在 body 标签下div>
teleport>
disabled
- boolean
。此可选属性可用于禁用
的功能,这意味着其插槽内容将不会移动到任何位置,而是在你在周围父组件中指定了
的位置渲染。<teleport to="#popup" :disabled="displayVideoInline">
<video src="./my-movie.mp4">
teleport>
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
<template>
<Suspense>
<template #default>
<Child />
template>
<template #fallback>
<h3>加载中h3>
template>
Suspense>
template>
<script>
import { defineAsyncComponent } from 'vue'
import Child from defineAsyncComponent(() => import('./child'))
script>
// child 组件
async setup() {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(0)
}, 3000)
})
return await p
}
npm install vue-router@4
// main.js
import { createApp } from 'vue'
import router from '@/router'
import App from './App.vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
Propsto
目标路由的链接。当被点击后,内部会立刻把 to
的值传到 router.push()
<router-link to="/home">Homerouter-link>
<a href="/home">Homea>
replace
设置 replace
属性的话,当点击时,会调用 router.replace()
active-class
exact-active-class
custom
的 v-slot
注意
记得把
custom
配置传递给,以防止它将内容包裹在
元素内。
提示
如果你在
a
元素上添加一个target="_blank"
,必须省略@click="navigate"
的处理。
<router-link
to="/foo"
custom
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']"
>
<a :href="href" @click="navigate">{{ route.fullPath }}a>
li>
router-link>
渲染成
import NotFound from '@/views/NotFound'
const routes = [
{
path: '/:path(.*)',
component: NotFound
}
]
路由所在的初始路由地址。可用于导航守卫中,以区分初始导航。
import { START_LOCATION } from 'vue-router'
router.beforeEach((to, from) => {
if (from === START_LOCATION) {
// 初始导航
}
})
返回当前路由地址。相当于在模板中使用 $route
。必须在 setup()
中调用。
返回 router 实例。相当于在模板中使用 $router
。必须在 setup()
中调用。
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query
}
})
}
}
}
使用
import { RouterLink } from 'vue-router'
Vue Router 不再是一个类,而是一组函数。不用再写 new Router()
,而是要调用 createRouter
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
添加一条新的路由记录到路由。如果路由有一个 name
,并且已经有一个与之名字相同的路由,它会先删除之前的路由。
router.addRoute({ path: '/about', component: About })
// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
router.replace(router.currentRoute.value.fullPath)
获取所有 路由记录的完整列表。
确认是否存在指定名称的路由。
函数签名:
hasRoute(name: string | symbol): boolean
通过名称删除现有路由。
通过名称删除现有路由。
函数签名:
removeRoute(name: string | symbol): void
路由模式
"history"
: createWebHistory()
"hash"
: createWebHashHistory()
"abstract"
: createMemoryHistory()
应该添加到路由的初始路由列表。
在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动。有关更多详细信息,请参见滚动行为。
npm install vuex@next --save
// main.js
import { createApp } from 'vue'
import store from '@/store'
import App from './App.vue'
const app = createApp(App)
app.use(store)
app.mount('#app')
调用 useStore
函数,与在组件中使用选项式 API 访问 this.$store
是等效的。
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
访问:
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 在 computed 函数中访问 state
count: computed(() => store.state.count),
// 在 computed 函数中访问 getter
double: computed(() => store.getters.double),
// 使用 mutation
increment: () => store.commit('increment'),
// 使用 action
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
创建一个 store 实例。
import { createStore } from 'vuex'
const store = createStore({ ...options })