Vue3 + Vue-cli (1) 基础篇

目录

一、官网

二、Vue3的新特性

三、Vue-cli 搭建 Vue3 开发环境

四、项目结构

五、setup()  — 组合式API 的入口点

六、数据响应式

01、ref()  — 单值数据响应式

02、reactive()  — 对象数据响应式

03、readonly()  — "深层”的只读代理

七、响应式系统工具集 - 辅助函数

01、unref()  — 拆出原始值的语法糖

02、toRef()  —  为reactive对象的属性创建一个ref

03、toRefs()  — 解构响应式对象数据

04、isRef、isProxy、isReactive、isReadonly

八、Vue3.x的生命周期和钩子函数

九、onRenderTracked() 和onRenderTriggered() 钩子函数的使用

01、onRenderTracked 状态跟踪

02、onRenderTriggered 状态触发

十、watch 和 watchEffect — 侦听器

01、watch 侦听单个数据源 

02、watch 侦听多个数据源

03、watchEffect

十一、独立的 computed 计算属性

01、只传 getter

02、同时传 getter、setter

十二、Vue3中模块化

十三、axios

 十四、teleport — 瞬间移动组件

十五、 Suspense — 异步请求组件

十六、onErrorCaptured — 处理理异步请求错误

十七、全局配置

十八、配置404页面


一、官网

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 的入口点

官网地址:组合式 API 常见问答 | Vue.js

一个组件选项,在创建组件实例之前执行,一旦 props 被解析,并作为 组合式 API 的入口点

 由于在执行 setup 时未创建组件实例,因此在 setup 选项中没有 this

这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态计算属性方法

使用 setup 函数时,它将接受两个参数:

  1. props
  2. 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

组合式 API 常见问答 | Vue.js

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() :开始创建组件之前beforeCreatecreated之前执行。创建的是 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还增加了onRenderTrackedonRenderTriggered函数

九、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 一样使用 .valueproperty

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属性,这个就是要写你需要渲染的DOMID了。

<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

配置路由时:

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值