目录
02、toRef() — 为reactive对象的属性创建一个ref
04、isRef、isProxy、isReactive、isReadonly
九、onRenderTracked() 和onRenderTriggered() 钩子函数的使用
十六、onErrorCaptured — 处理理异步请求错误
一、官网
Vue3 官网: Vue.js - 渐进式 JavaScript 框架 | Vue.js
Vue-cli 官网:Vue CLI
二、Vue3的新特性
01、Vue3 采用渐进式开发,向下兼容
02、性能提升
- 打包大小减少41%
- 初次渲染快55%
- 更新快133%
- 内存使用减少54%
03、Composition API集合,解决Vue2组件开发问题
04、新的API加入
- Teleport 瞬移组件
- Suspense 解决异步加载组件问题
05、更好TypeScript 支持
【更好的性能、更小的bundle体积、更好的TypeScript支持】
三、Vue-cli 搭建 Vue3 开发环境
01、版本的要求
必须是最新版本(V4.5.4 以上版本)才有创建 Vue3 的选项
检查当前vue-cli的版本号:
vue --version
02、 创建项目
npm install -g @vue/cli
或者 yarn global add @vue/cli
vue create hello-vue3
自定义配置:
或者:
vue-cli 图形搭建环境:
vue ui
然后根据提示一步步创建即可
四、项目结构
|-node_modules -- 所有的项目依赖包都放在这个目录下
|-public -- 公共文件夹
---|favicon.ico -- 网站的显示图标
---|index.html -- 入口的html文件
|-src -- 源文件目录,编写的代码基本都在这个目录下
---|assets -- 放置静态文件的目录,比如logo.png就放在这里
---|components -- Vue的组件文件,自定义的组件都会放到这
---|App.vue -- 根组件,这个在Vue2中也有
---|main.ts -- 入口文件,因为采用了TypeScript所以是ts结尾
---|shims-vue.d.ts -- 类文件(也叫定义文件),因为.vue结尾的文件在ts中不认可,所以要有定义文件
|-.browserslistrc -- 在不同前端工具之间公用目标浏览器和node版本的配置文件,作用是设置兼容性
|-.eslintrc.js -- Eslint的配置文件,不用作过多介绍
|-.gitignore -- 用来配置那些文件不归git管理
|-package.json -- 命令配置和包管理文件
|-README.md -- 项目的说明文件,使用markdown语法进行编写
|-tsconfig.json -- 关于TypeScript的配置文件
|-yarn.lock -- 使用yarn后自动生成的文件,由Yarn管理,安装yarn包时的重要信息存储到yarn.lock文件中
这就是基本目录结构和用处了
五、setup() — 组合式API 的入口点
一个组件选项,在创建组件实例之前执行,一旦
props
被解析,并作为 组合式 API 的入口点。
由于在执行 setup
时未创建组件实例,因此在 setup
选项中没有 this
这意味着,除了 props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
使用
setup
函数时,它将接受两个参数:
props
context
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
setup(props) {
console.log(props) // { user: '' }
return {} // 这里返回的任何内容都可以用于组件的其余部分
}
// 组件的“其余部分”
}
setup
函数中的第一个参数是props,
正如在一个标准组件中所期望的那样,setup
函数中的props
是响应式的,当传入新的 prop 时,它将被更新。但是,因为
props
是响应式的,你不能使用 ES6 解构,因为它会消除 prop 的响应性。
如果需要解构 prop,可以通过使用 setup
函数中的 toRefs 来完成此操作:
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
使用setup函数 代替Vue2中的data 和 methods属性的好处:
不用暴露在界面中的变量和方法就可以不进行 return 了,这样子可以精确地控制暴露出去的变量和方法。
总结:
<template>
<div>
<h1>setup - 组件内使用 Composition API 的入口点</h1>
<p>{{ username }}</p>
</div>
</template>
<script>
export default {
name: "Setup",
props: {
username: { type: String, default: "草莓草莓" },
},
setup(props, ctx) {
console.log("setup");
console.log(props, ctx);
},
beforeCreate() {
console.log("beforeCreate");
},
};
</script>
01、创建组件实例,然后初始化props
02、紧接着,调用 setup 函数(作为组合式API的入口)
03、从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用
六、数据响应式
01、ref() — 单值数据响应式
接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property.
value(生成响应式对象)
ref
函数使任何响应式变量在任何地方起作用
ref
接受参数并返回它包装在具有value
property 的对象中,然后可以使用该 property 访问或更改响应式变量的值:
import { ref } from 'vue'
const counter = ref(0)
console.log(counter) // { value: 0 }
console.log(counter.value) // 0
counter.value++
console.log(counter.value) // 1
换句话说,
ref
对我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念。
当 ref 作为渲染上下文 (从 setup() 中返回的对象) 上的 property 返回并可以在模板中被访问时,
它将自动展开为内部值。不需要在模板中追加
.value
:
<template>
<div>
<span>{{ count }}</span>
<button @click="count ++">Increment count</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count
}
}
}
</script>
02、reactive() — 对象数据响应式
接收一个普通对象,然后返回该普通对象的响应式副本(生成响应式对象)
给 data 添加类型注解:
在无添加类型注解的情况下,默认是采用了TypeScript
的类型推断,这样子是不合理的。
我们先定义一个接口,用接口(interface
)来作类型注解。
编写完成后,为 data
变量作一个类型注解,这时候我们的代码才是严谨的。
03、readonly() — "深层”的只读代理
有时我们想跟踪响应式对象 (
ref
或reactive
) 的变化,但我们也希望防止在应用程序的某个位置更改它。例如,当我们有一个被 provide 的响应式对象时,我们不想让它在注入的时候被改变。为此,我们可以基于原始对象创建一个只读的 proxy 对象:
传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的。
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
// 通过 original 修改 count,将会触发依赖 copy 的侦听器
original.count++
// 通过 copy 修改 count,将导致失败并出现警告
copy.count++ // 警告: "Set operation on key 'count' failed: target is readonly."
七、响应式系统工具集 - 辅助函数
01、unref() — 拆出原始值的语法糖
如果参数是一个ref , 则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val 的语法糖。
function useFoo(x: number | Ref<number>) {
const unwrapped = unref(x) // unwrapped 一定是 number 类型
}
02、toRef() — 为reactive对象的属性创建一个ref
toRef 可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。
setup() {
const user = reactive({ age: 1 });
const age = toRef(user, "age");
age.value++;
console.log(user.age); // 2
user.age++;
console.log(age.value); // 3
}
当您要将一个 prop 中的属性作为 ref 传给组合逻辑函数时,toRef 就派上了用场:
export default {
setup(props) {
useSomeFeature(toRef(props, 'foo'))
},
}
03、toRefs() — 解构响应式对象数据
当我们想使用大型响应式对象的一些 property 时,可能很想使用 ES6 解构来获取我们想要的 property。遗憾的是,使用解构的 property 的响应性都会丢失。
对于这种情况,我们需要将我们的响应式对象转换为一组 ref。这些 ref 将保留与源对象的响应式关联:
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
Type of stateAsRefs:
{
foo: Ref<number>,
bar: Ref<number>
}
*/
// ref 和 原始property “链接”
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
当想要从一个组合函数中返回响应式对象时,用
toRefs
非常有用。该API 让消费组件在不丢失响应性的情况下对返回的对象进行解构/ 拓展。
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// 逻辑运行状态
// 返回时转换为ref
return toRefs(state)
}
export default {
setup() {
// 可以在不失去响应性的情况下破坏结构
const { foo, bar } = useFeatureX()
return {
foo,
bar
}
}
}
使用前需要先进行引入,引入后就可以对
data
进行包装。把 data 变成refData,
这样就可以使用扩展运算符的方式了,而且也具有响应式的能力。
这样写之后,你的template
就可以去掉 data. ,而是直接使用变量名和方法。
04、isRef、isProxy、isReactive、isReadonly
isRef:检查一个值是否为一个 ref 对象。
isProxy:检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。
isReactive:检查一个对象是否是由 reactive 创建的响应式代理。如果这个代理是由 readonly 创建的,但是又被 reactive 创建的另一个代理包裹了一层,那么同样也会返回 true。
isReadonly:检查一个对象是否是由 readonly 创建的只读代理。
<script lang="ts">
import { defineComponent, isRef, ref } from 'vue';
export default defineComponent({
setup(props, context) {
const name: string = 'vue'
const age = ref<number>(18)
console.log(isRef(age)); // true
console.log(isRef(name)); // false
return {
age,
name
}
}
});
</script>
八、Vue3.x的生命周期和钩子函数
Vue 是组件化编程,从一个组件诞生到消亡,会经历很多过程,这些过程就叫做生命周期。
钩子函数: 伴随着生命周期,给用户使用的函数,操控生命周期,主要是操控钩子函数。
Vue2.x 和 Vue3.x 生命周期对比:
Vue3.x生命周期详解:
- setup() :开始创建组件之前,在
beforeCreate
和created
之前执行。创建的是data
和method
- onBeforeMount() : 组件挂载到节点上之前执行的函数。
- onMounted() : 组件挂载完成后执行的函数。
- onBeforeUpdate(): 组件更新之前执行的函数。
- onUpdated(): 组件更新完成之后执行的函数。
- onBeforeUnmount(): 组件卸载之前执行的函数。
- onUnmounted(): 组件卸载完成后执行的函数
- onActivated(): 被包含在
<keep-alive>
中的组件,会多出两个生命周期钩子函数。被激活时执行。- onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行。
- onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数(以后用到再讲,不好展现)。
因为
setup
是围绕beforeCreate
和created
生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在setup
函数中编写。
这些函数接受一个回调函数,当钩子被组件调用时将会被执行:
export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}
实际例子如下:
Vue3.x 生命周期在调用前需要先进行引入
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
onMounted(getUserRepositories) // on `mounted` call `getUserRepositories`
return {
repositories,
getUserRepositories
}
},
可以在setup()
函数之后编写 Vue2
的生命周期函数。
Vue2.x的生命周期和Vue3.x的生命周期,都可用,但是不要混用。
beforeCreate() {
console.log("1-组件创建之前-----beforeCreate()");
},
beforeMount() {
console.log("2-组件挂载到页面之前执行-----BeforeMount()");
},
mounted() {
console.log("3-组件挂载到页面之后执行-----Mounted()");
},
beforeUpdate() {
console.log("4-组件更新之前-----BeforeUpdate()");
},
updated() {
console.log("5-组件更新之后-----Updated()");
},
Vue2.x 和 Vue3.x 生命周期对比:
Vue2--------------vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
通过这样对比,可以很容易的看出 Vue3 的钩子函数基本是在 Vue2 的基础上加了一个on,
但也有两个钩子函数发生了变化。
BeforeDestroy
变成了onBeforeUnmount
destroyed
变成了onUnmounted
除了这些钩子函数外,Vue3.x
还增加了onRenderTracked
和onRenderTriggered
函数
九、onRenderTracked() 和onRenderTriggered() 钩子函数的使用
Vue3.x新增的生命周期钩子函数,官方说是用来调试使用的
01、onRenderTracked 状态跟踪
跟踪页面上所有响应式变量和方法的状态,也就是我们用
return
返回去的值,它都会跟踪。只要页面有
update
的情况,它就会跟踪,然后生成一个event
对象,我们通过event
对象来查找程序的问题所在。
import { .... ,onRenderTracked,} from "vue";
onRenderTracked((event) => {
console.log("状态跟踪组件----------->");
console.log(event);
});
02、onRenderTriggered 状态触发
它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。
如果把
onRenderTracked
比喻成散弹枪,每个值都进行跟踪,那onRenderTriggered
就是狙击枪,只精确跟踪发生变化的值,进行针对性调试。
import { .... ,onRenderTriggered,} from "vue";
onRenderTriggered((event) => {
console.log("状态触发组件--------------->");
console.log(event);
});
十、watch 和 watchEffect — 侦听器
监听器(侦听器)。作用是用来侦测响应式数据的变化
使用 watch 同样需要先进行引入。它接受3个参数:
- 一个响应式引用 ref 或一个返回值的 getter 函数
- 一个回调
- 可选的配置选项
01、watch 侦听单个数据源
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
应用到实际例子中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
// 在我们组件中
setup (props) {
// 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
// 更新 `prop.user` 到 `user.value` 访问引用值
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// 在用户 prop 的响应式引用上设置一个侦听器
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
},
在我们的 setup
的顶部使用了 toRefs
。这是为了确保我们的侦听器能够对 user
prop 所做的更改做出反应。
02、watch 侦听多个数据源
第一个参数以数组形式传入,第二个参数回调返回的结果也是数组
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
Vue3 中监听 reactive 中的值,必须以 getter 函数 的形式,不然会报错。和 Vue2 的区别是不用写 deep 属性,默认就是深度监听了。
watch([result2, () => data.title], (newV, oldV) => {
console.log(newV, oldV) // [20, "111"] [20, "222"]
})
监听 reactive 中的多个值时:
watch([result2, () => [data.title, data.value1]], (newV, oldV) => {
console.log(newV, oldV)
})
与 watchEffect 比较,
watch
允许我们:
- 懒执行副作用;
- 更具体地说明什么状态应该触发侦听器重新运行;
- 访问侦听状态变化前后的值。
03、watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
<template>
<div>
<h1>watchEffect - 侦听器</h1>
<p>{{data.count}}</p>
<button @click="stop">手动关闭侦听器</button>
</div>
</template>
<script>
import { reactive, watchEffect } from "vue";
export default {
name: "WatchEffect",
setup() {
const data = reactive({ count: 1 });
const stop = watchEffect(() => console.log(`侦听器:${data.count}`));
setInterval(() => {
data.count++;
}, 1000);
return { data, stop };
},
};
</script>
十一、独立的 computed
计算属性
与 ref
和 watch
类似,也可以使用从 Vue 导入的 computed
函数在 Vue 组件外部创建计算属性。
computed
函数传递了第一个参数,它是一个类似 getter 的回调函数,输出的是一个只读 的响应式引用。为了访问新创建的计算变量的 value,我们需要像使用
ref
一样使用.value
property。
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
01、只传 getter
接受 getter 函数,并返回一个默认不可手动修改的响应式 ref 对象
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 报警告,computed value is readonly
报警告:
如果是这种使用方式,则会报错 ❌,而不是警告 ⚠️
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1
})
console.log(plusOne.value) // 2
console.log(count.value) // 1
plusOne.value = 1 // 报错
02、同时传 getter、setter
使用一个带有
get
和set
函数的对象来创建一个可手动修改的 ref 对象
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
console.log('触发set', val)
count.value = val - 1
}
})
console.log(plusOne.value) // 2
console.log(count.value) // 1
plusOne.value = 1 // 触发set 1
console.log(plusOne.value) // 1
console.log(count.value) // 0
实际项目中:
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
// 在我们组件中
setup (props) {
// 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
// 更新 `prop.user` 到 `user.value` 访问引用值
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// 在用户 prop 的响应式引用上设置一个侦听器
watch(user, getUserRepositories)
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(
repository => repository.name.includes(searchQuery.value)
)
})
return {
repositories,
getUserRepositories,
searchQuery,
repositoriesMatchingSearchQuery
}
}
},
十二、Vue3中模块化
Vue3.x版本最大的一个提升,就是有更好的重用机制,你可以把任何你想独立的模块独立出去。
把相同的逻辑关注点提取到一起,封装成一个独立的组合式函数
例如,创建 useUserRepositories
:
// src/composables/useUserRepositories.js
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'
export default function useUserRepositories(user) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
然后是搜索功能:
// src/composables/useRepositoryNameSearch.js
import { ref, computed } from 'vue'
export default function useRepositoryNameSearch(repositories) {
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(repository => {
return repository.name.includes(searchQuery.value)
})
})
return {
searchQuery,
repositoriesMatchingSearchQuery
}
}
现在在单独的文件中有了这两个功能,我们就可以开始在组件中使用它们了。以下是如何做到这一点:
// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
setup (props) {
const { user } = toRefs(props)
// 获取并返回传入指定的用户的储蓄库列表
const { repositories, getUserRepositories } = useUserRepositories(user)
// 搜索并返回包含某名字的储蓄库列表
const {
searchQuery,
repositoriesMatchingSearchQuery
} = useRepositoryNameSearch(repositories)
// 过滤
const {
filters,
updateFilters,
filteredRepositories
} = useRepositoryFilters(repositoriesMatchingSearchQuery)
return {
// 因为我们并不关心未经过滤的仓库
// 我们可以在 `repositories` 名称下暴露过滤后的结果
repositories: repositoriesMatchingSearchQuery,
getUserRepositories,
searchQuery,
filters,
updateFilters
}
}
}
或者:
<template>
<div>
<h1>组合式api架构</h1>
<h2>usemouse</h2>
<p>position x: {{x}}</p>
<p>position y: {{y}}</p>
<h2>useCount</h2>
<p>{{count}}</p>
<button @click="add">Add</button>
<button @click="minus">Minus</button>
</div>
</template>
<script>
import { onMounted, onUnmounted, ref } from "vue";
export default {
name: "CompositionApiArchitecture",
setup() {
const { x, y } = useMouse();
const { count, add, minus } = useCount();
return { x, y, count, add, minus };
},
};
function useMouse() {
const x = ref(0);
const y = ref(0);
const updateXY = (e) => {
x.value = e.x;
y.value = e.y;
};
onMounted(() => {
document.addEventListener("mousemove", updateXY);
});
onUnmounted(() => {
document.removeEventListener("mousemove", updateXY);
});
return { x, y };
}
function useCount() {
const count = ref(0);
const add = () => count.value++;
const minus = () => count.value--;
return { count, add, minus };
}
</script>
十三、axios
安装: npm install axios --save
十四、teleport — 瞬间移动组件
Vue2.x中组件的痛点:
01、组件的Dom都是在app节点下的;想改变节点,在Vue2时代是非常困难。
02、组件被包裹在其他组件中,容易被干扰,样式也变得比较混乱
使用 teleport 函数(方法):
这个函数也可以叫独立组件,它可以把你写的组件挂载到任何你想挂载的DOM上,不必嵌套在#app里,这样就不会互相干扰了,所以说是很自由很独立的。
在使用
Vue2
的时候是做不到的。
把你编写的组件用<teleport>闭合
标签进行包裹,在组件上有一个to
属性,这个就是要写你需要渲染的DOM
ID了。
<template>
<teleport to="#modal">
<div id="center">
<h2>组件</h2>
</div>
</teleport>
</template>
然后我们在打开/public/index.html,
增加一个modal
节点
<div id="app"></div>
<div id="modal"></div>
十五、 Suspense — 异步请求组件
背景:
在前端开发中,异步请求组件必不可少。比如读取远程图片,比如调用后台接口,这些都需要异步请求。在Vue2.x时代,判断异步请求的状态是一件必须的事情,但是这些状态都要自己处理,根据请求是否完毕展示不同的界面。尤大神深知民间饥苦,在Vue3.x中给我们提供了
Suspense
组件。
Suspense组件用于在等待某个异步组件解析时显示后备内容。
用法:
<Suspense></Suspense>提供了两个模板插槽,分别是请求回来前和请求回来后所要显示的内容。
注意点:如果你要使用Suspense
的话,要返回一个Promise对象,而不是原来的那种JSON
对象。
新建一个AsyncShow.vue
组件:
<template>
<h1>{{ result }}</h1>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve({ result: "JSPang" });
}, 2000);
});
},
});
</script>
放入父组件中:用Suspense闭合标签包住,包含了两个template插槽。
第一个
default
代表异步请求完成后,显示的模板内容。第二个
fallback
代表在加载中时,显示的模板内容。
<template>
<div>
<Suspense>
<template #default>
<AsyncShow />
</template>
<template #fallback>
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
用async...await
的写法,它是promise
的语法糖
<template>
<img :src="result && result.imgUrl" />
</template>
<script lang="ts">
import axios from 'axios'
import { defineComponent } from 'vue'
export default defineComponent({
async setup() { //promise 语法糖 返回之后也是promise对象
const rawData = await axios.get('https://apiblog.jspang.com/default/getGirl')
return {result:rawData.data}
}
})
</script>
十六、onErrorCaptured
— 处理理异步请求错误
在异步请求中必须要作的一件事情,就是要捕获错误,因为我们没办法后端给我们返回的结果,也有可能服务不通,所以一定要进行捕获异常和进行处理。
在vue3.x
的版本中,可以使用 onErrorCaptured
这个钩子函数来捕获异常。在使用这个钩子函数前,需要先进行引入
import { ref , onErrorCaptured } from "vue";
有了onErrorCaptured
就可以直接在setup()
函数中直接使用了。钩子函数要求我们返回一个布尔值,代表错误是否向上传递,我们这里返回了true
。
const app = {
name: "App",
components: { AsyncShow, GirlShow },
setup() {
onErrorCaptured((error) => {
console.log(`error====>`,error)
return true
})
return {};
},
};
写好后,我们故意把请求地址写错,然后打开浏览器的终端,看一下控制台已经捕获到错误了。在实际工作中,你可以根据你的真实需求,处理这些错误。
案例:
<template>
<div v-if="errMsg"> {{ errMsg }} div>
<Suspense v-else>
<template #default>
<article-info/>
template>
<template #fallback>
<div>正在拼了命的加载…div>
template>
Suspense>
template>
<script>
import { onErrorCaptured } from 'vue'
setup () {
const errMsg = ref(null)
onErrorCaptured(e => {
errMsg.value = \'呃,出了点问题!\'
return true
})}
return { error }
<script>
defineComponent
是用来解决TypeScript情况下,传统的Vue.extends
无法对组件给出正确的参数类型推断的。也就是说在TypeScript环境中如果参数类型推断不正常时,用
defineComponent()
组件来进行包装函数。
十七、全局配置
通过vue 实例上config来配置,包含Vue应用程序全局配置的对象。您可以在挂载应用程序之前修改下面列出的属性:
const app = Vue.createApp({})
app.config = {...}
为组件渲染功能和观察程序期间的未捕获错误分配处理程序。错误和应用程序实例将调用处理程序
app.config.errorHandler = (err, vm, info) => {}
可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。这可以代替Vue 2.x Vue.prototype扩展:
const app = Vue.createApp({})
app.config.globalProperties.$http = 'xxxxxxxxs'
可以在组件用通过 getCurrentInstance() 来获取全局globalProperties 中配置的信息,
getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,这样我们就能在setup中使用router和vuex, 通过这个属性我们就可以操作变量、全局属性、组件属性等等
十八、配置404页面
新增404页面:404.vue
配置路由时: