vue3面试复习

323 阅读24分钟

1.vue3整体情况的大体介绍

Vue2

  • What,vue2的一个框架定义:渐进式的Javascript功能性框架
  • When,vue2的时间点:2014
  • Where,应用面:PC网站、Webapp移动项目、微信相关、移动App、桌面端
  • Who,谁在用:中小型企业特别喜欢,大型企业也有很多的尝试
  • Why,市场需求
  • How,六何分析,总分法,比较法

Vue3

  • What,vue3的一个框架定义:渐进式的Javascript功能性框架
  • When,vue3的时间点:2020.9.18,2022年2.7,将vue3已经作为了默认的安装版本
  • Where,应用面:PC网站、Webapp移动项目、微信相关、移动App、桌面端
  • Who,谁在用:中小型企业特别喜欢,大型企业也有很多的尝试,谁开发的:尤雨溪
  • Why,市场需求
  • How,六何分析,总分法,比较法

生态环境

vue技术体系:vue+vue-router+vuex+vue-lazyload+elementUI+vee-validate+lodash+swiper...

扩展的周边:@vue/CLI,vue-devtools,vsCode的插件vetur

vue2的MVVM原理:Object.defineProperty(递归)、Observer(观察)、Dep(subs,addSub,notify)、Watcher(DOM,递归)

按需加载(Tree-Shaking,摇树功能):

import vue from 'vue'import VueRouter from 'vue-router'import Vuex from 'vuex'

import _ from 'lodash'-->import {cloneDeep} from 'lodash'

开发时候错误的类型:语法错误、编译错误、逻辑错误

vue2是属于optional API(选项API)、vue3是属于Composition API(组合API)

Vue2组件中主要包含的操作内容有哪些:

  • name名称
  • data数据定义
  • directives本地指令
  • filters本地过滤器
  • mixins本地混入
  • components本地组件注册
  • methods方法
  • computed属性计算
  • watch实时监控
  • 生命周期钩子函数(11)

vue2中代码的封装方式:function(函数)、class(类)、module(模块)、component(组件)、directive(指令)、filter(过滤器)、mixin(混入)、hook(钩子)、plugin(插件)、library(类库)、framework(框架)

Hook(useState、useEffect、useCallback......,React当中)

Vue3里产生的问题:

  • 在setup函数中如何接收属性?如何定义响应式数据?因为setup里连this都没有!

  • name名称、data定义、components本地组件注册、methods方法、computed属性计算、watch实时监控、生命周期钩子函数

    setup(){
        ref
        reactive
        toRefs
        isRef
        computed
        watch
        ...
    }
    
  • hook钩子的应用

  • vue2中的引入方式基本都是对象化的引入,产生的问题会使整个项目的体积增大

  • vue3中的引入方式基本都是函数化的引入,产生的结果是会使整个项目的体积急剧减小,利用的是tree-shaking摇树方式

vue3项目的创建方式

  • 仍旧可以利用Vue CLI命令行接口的方式进行vue create、vue ui的模式进行项目的创建(是基于webpack这个前端自动化构建工具)

  • 可以利用vite这个前端自动化构建工具进行vue3项目的创建,是因为vite有基于esbuild,而esbuild是基于go语言的,它的速度快是基于底层的结构机制。

    npm create vite@latest my-vue-app -- --template vue
    
  • vue CLI-->webpack-->nodejs-->v8引擎-->C、C++-->机器语言(层次结构很多,性能一般)

  • vite(esbuild)-->go语言-->机器语言(层次结构很少,性能很好)

vue3的代码与vue2代码模式的比较

区别版本(一般情况,如果开发了vue3,那么一律都使用ts版本):

可以利用Vue CLI进行项目创建:

vue2-js

vue2-ts(不考虑了)

vue3-js

vue3-ts(主要)

vue3-ts(默认脚手架中,并没有使用setup语法结构)

<script lang="ts">
// 是基于Vue CLI的vue3+ts
import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /srcexport default defineComponent({
  name: 'HomeView',
  components: {
    HelloWorld,
  },
});
</script>
<script lang="ts">
// 是基于Vue CLI的vue3+ts
import { defineComponent } from "vue";
​
export default defineComponent({
  name: "HelloWorld",
  props: {
    msg: String,
  },
});
</script>

可以利用Vite进行项目创建:

vue3-js

vue3-ts(重要)

<script setup lang="ts">
// script setup是语法糖
import HelloWorld from './components/HelloWorld.vue'; // 引入HelloWorld.vue组件
</script>

项目介绍,是基于Vue CLI进行创建的Vue3+TypeScript的内容:

  • 项目的启动仍旧npm run serve,速度非常的慢,因为它是基于webpack的
  • .prettierrc.json文件的拷贝,将单引号转成双引号
  • <script lang="ts">
    // 是基于Vue CLI的vue3+ts
    import { defineComponent } from "vue";
    import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src// 在vue3中使用vue2的语法结构还是可以的
    // 定义组件,代码结构和vue2的代码结构是一样的
    export default defineComponent({
      name: "HomeView",
      components: {
        HelloWorld,
      },
    });
    </script>
    

GUI管理工具(Graphic User Interface)图形化的用户界面,sourceTreeTortoiseGit

Git管理的几种方式:命令行、vscode的插件、图形化管理界面的软件

2.vue3技术体系内容的拆分讲解

路由

1)别名与后缀省略的设置

1]配置文件的设置

vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
const { resolve } = require('path'); // 引入nodejs当中的路径模块中的resolve方法,以便实现相对路径转换为绝对路径// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    // alias是设置别名,为什么是数组,说明可以设置多个别名
    // 将@符号替换成src目录所在的绝对路径
    alias: [{ find: '@', replacement: resolve(__dirname, 'src') }],
    // 设置可以省略的后缀名称
    extensions: ['.js', '.ts', '.vue', '.json'],
  },
});
​

2]package.json中将type为module属性进行删除

3]页面中就可以进行@以及.vue省略的操作

2)简单路由配置

1.模块引入(相同)

2.静态路由表配置(相同)

3.利用函数进行路由实例的创建(差异)

4.明确路由的历史模式采用什么样的方式,是history还是hash(差异)

5.需要将路由与当前的应用进行关联,利用use插件的模式进行链式写法,将路由与当前的Vue应用进行关联(差异)

6.利用<router-view>进行占位渲染(相同)

7.利用router-link进行路由的跳转(相同)

3)高亮显示

1.router-link将自动产生router-link-active的class样式,只需要自行进行样式设置即可

2.router-link可以手动单独设置active-class,工作量大,代码维护成本高

3.入口文件的router实例化配置的时候可以设置linkActiveClass进行自定义高亮样式类的统一设置,如此router-link中就不需要逐一设置

4.router-link的tag标签不再起作用,需要设置的是custom和v-slot进行自定义标签的设置和插槽的应用,可以利用v-slot进行对象属性的解构,将navigate和isActive等对象属性内容进行解构待用,然后在router-link的插槽部分进行自定义标签的设置,需要注意的是@click事件的绑定以及:class动态样式的设置

4)嵌套路由

1.嵌套路由操作的模式和流程和vue2的路由是一样的

2.在路由配置当中仍旧使用children进行嵌套路由的配置

3.嵌套路由默认页面是不需要设置path路由的,可以直接是''

4.嵌套路由如果设置了path路径,则会和父路由产生路由拼接

5.在嵌套路由当中,仍旧使用<router-view>进行子嵌套路由的渲染操作

5)路由的传参

路由传参,参数的类型:params、query

params:设参在路由、传参在链接、接参用参在组件

query:设参传参在链接、接参用参在组件

params参数操作:

1]路由中利用:xxx进行参数的占位设置

2]在路由中进行/xxx的参数传递操作

3]在组件中进行$route.params.xxx的参数获取处理

4]可以利用useRoute进行hook钩子处理,通过useRoute()实例化得到route当前路由对象,并且通过它进行params.xxx参数的获取

5]可以利用computed、watch、生命周期等操作进行参数的获取处理,目前我们利用watch进行指定参数内容的监控,而不是像vue2中对整个$route对象进行监控,同样也可以获取到路由参数,并且可以考虑给watch进行immediate:true的立即监控设置

query参数操作:

1]在路由中进行?a=1&b=2等查询参数的传递

2]在组件中可以利用$route.query.xxx进行参数的获取

3]也可以利用useRoute进行hook的操作,同样可以获取到query的参数

4]同样也可以考虑使用computed、watch、生命周期钩子函数等进行query参数的进一步操作处理

6)路由参数的映射

是在路由配置当中可以利用props属性进行路由参数的映射操作,其类型主要包括:布尔型、对象型、函数型

  • props为布尔型,表示是否映射参数,但是这时候只能映射params参数,query参数不能映射
  • props为对象类型,表示可以映射对象类型的属性参数,而params和query参数都将无法映射
  • props为函数类型,表示可以映射所有数据类型内容,包括params、query和自定义属性

结论:vue3的路由映射和vue2的路由映射是一样的,没有任何差异

7)命名路由

因为路由对象中有path、params、query、hash等属性内容,但是path与params是不能结合使用的,所以建议使用命名路由的操作模式,利用路由对象中的name进行指定路由目标的跳转。

  • 首先在路由中进行路由对象的name属性设置
  • 在组件的router-link跳转的时候就可以设置to的属性为对象模式,将name、params、query、hash等进行对象化设置,以替换字符串拼接模式。代码可读性可维护性也会更高。

8)命名视图(slot)

  • 可以在路由当中对静态路由表进行改造,将原来的component单数形式改成components复数形式,这样的话就可以设置default默认渲染路由对象以及xxx自定义渲染路由对象,而且这个xxx可以是多个的,只需要设置对应渲染的组件内容就可以。
  • <router-view>占位渲染的时候,其实和slot非常的相似,默认<router-view>中有name属性,并且属性值为default,如果是自定义,则可以修改成<router-view name="xxx">,那么,xxx如果有则显示,没有则不显示。
  • 命名视图渲染配置与应用可以对所有的路由层级(包括嵌套路由)都是支持的。
  • 命名视图渲染与vue2也没有任何的差异。

9)编程式路由跳转

  • vue2中编程式路由导航直接利用整体路由对象this.$router进行操作,而在vue3当中需要利用hook进行useRouter的引入,以及进行useRouter()的实例化,再利用实例化以后的router进行路由的跳转操作。
  • router与route之间的差异:router是整个路由对象(路由的跳转),而route是当前路由对象(路由的参数)
  • 编程式路由导航的时候仍旧可以使用字符串拼接模式以及对象模式的导航操作

10)地址过滤

  • vue2中可以实现地址重定向,可以利用的是redirect进行重定向,也可以使用*通配符进行所有的不匹配路由的重定向。
  • vue3中的路由重定向操作和vue2路由的简单重定向操作还是保持了一致,不光可以使用字符串形式,还可以使用对象形式
  • 404重定向操作,也就是任意页面的过滤匹配与vue2利用*通配符模式发生了变化,在vue3中需要利用正则表达式的模式进行重定向操作,而redirect可以是字符串形式,也可以是函数形式
  • 对于地址过滤还可以进行嵌套路由的过滤操作,需要注意的是path正则的内容,可以不带上/,因为父路由地址已经包含了/内容,但是也需要利用正则匹配模式进行地址过滤操作。

11)过渡动画

  • vue的动画效果:css动画(过渡动画、帧动画)、第三方类库、js动画,<transition name type mode appear ...></transition>,这些内容vue2和vue3都是一样的
  • 利用transition将<router-view>进行动画的包裹操作,可以进行直接的enter-active-class、leave-active-class等属性的字符串模式设置
  • 也可以在routes静态路由表中利用meta元信息配置方式进行动态的enter-active-class、leave-active-class动画效果的设置,这样的目的是在进行不同页面切换的时候可以有不同的动态效果
  • 可以直接利用$route.meta.xxx进行meta元信息的属性获取,并且将其值设置在transition的动画效果当中,这种方式利用的是<transition><router-view>的默认渲染与动画模式
  • 除了使用<router-view>的默认模式,还可以使用<router-view v-slot>的插槽模式,里面主要包括Component渲染目标组件以及route当前路由对象等内容,再结合<transition>就可以进行手动模式的路由渲染与动画效果

12)滚动行为

  • 直接进行hash值的设置与跳转在vue-router是是无法实现正常的滚动定位的
  • 可以利用scrollBehavior进行滚动行为的处理,这是一个函数,函数中带有to、from、savedPosition三个参数,可以利用savedPosition进行之前位置的定位操作,利用to.hash进行锚点定位
  • 需要注意的是与vue2不同,return 原来返回的是 selector,现在在vue3中 return 需要返回的是el元素,而且可以考虑返回 Promise 并且在 Promise中进行延时操作处理
  • 默认一般返回的是 {left:0,top:0}的对象,可以滚动到页面的顶部

13)路由懒加载

  • 将直接组件引入方式改成Promise的动态import组件引入方式,在vue2中可以在network中直接查看到xxx.js的动态组件加载效果,但是在vue3中dev开发模式下,仍旧显示的是xxx.vue组件文件
  • 利用npm run build进行项目的打包,并且进行dist目录的服务器运行模式,才能确认network中xxx.js的引入与使用
  • 可以利用/* webpackChunkName:'xxx' */进行魔法注释操作,但是需要利用vite-plugin-webpackchunkname插件的支持,需要在vite.config.js进行该插件的引入以及在plugins里进行插件的调用操作,而且webpackChunkName还可以实现单个以及分组的模块划分
  • 不管怎么样,在vue3中想要切实看到js的懒加载都需要在生产模式下才有效果,包括魔法注释的应用

14)缓存性能提升

  • keep-alive将实现路由的缓存性能提升操作,它将衍生出来的生命周期钩子函数是onActivated、onDeactivated
  • 想要实现keep-alive路由缓存的前提是需要给组件设置name名称,如果组件没有name名称,即便使用了include也没有实现路由的缓存操作
  • 需要利用<script>以及export default{ name:'xxx' }模式给当前的组件添加name属性
  • keep-alive的include、exclude主要包含字符串模式、正则模式和数组模式的设置,建议使用的是数组模式

15)路由守卫

路由守卫:全局守卫、路由独享守卫、组件守卫

全局守卫:beforeEach、beforeResolve、afterEach,需要注意的是afterEach是没有next放行函数的,因为路由跳转都已经完成了

路由独享守卫:beforeEnter,通常拿来实现的目标是确认前一个来源页的操作步骤,不能进行URL地址的直接输入处理

组件守卫:vue2当中是beforRouteEnter、beforeRouteUpdate、beforeRouteLeave(是需要在next当中获取到vm实例),但是在vue3当中onBeforeRouteUpdate、onBeforeRouteLeave

问题:nprogress进度条在请求的二次封装中进行应用是否合适?不合适的!在路由守卫中进行应用应该是更适合。

16)动态路由

  • 动态路由可以设置在静态路由表之外,根据需求动态的产生路由对象以及实现路由的跳转

  • 动态路由的实现过程:

    • 先进行对应模块的创建,和任何其它模块的模式是一样的
    • 不进行静态路由表的静态设置
    • 如果进行指定动态路由的链接地址跳转,暂时是不成功的
    • 需要先引入指定的动态路由模块,并且利用router.addRoute进行路由模块的动态添加,然后再可以实现router.push动态路由的跳转操作,addRoute动态路由添加的对象内容和静态路由表中的对象内容是一致的
    • 也可以通过removeRoute将路由对象从路由表中进行删除处理,如果删除,将无法实现指定路由模块的渲染

请求

问题:URL的组成部分有哪些?

问题:URL其实本质就是与路由相关,因为需要通过URL的指向去确认渲染指定的路由。

问题:URL的参数类型有哪些?路由的传参类型有哪些?params参数,query参数。

问题:URL在什么样的情况下会产生跨域问题?同源策略,跨域产生以后的影响是什么?(无法请求到数据,无法实现DOM操作,无法进行本地数据的处理)如何解决跨域?前端(JSONP、浏览器插件、proxy代理),后台(cors),运维(apache、nginx代理)

问题:restful API的理解?(GET/POST/PUT/DELETE)

问题:模拟数据需求(mockjs)和实现(vue项目中如何进行mockjs模拟数据的支撑操作)。

问题:模拟数据的结构由谁确认?后台开发。

问题:之前在vue电商项目中应用了模拟数据操作,但是它是否有一定的局限?

  • 对于数据,我们都是一次性获取,有没有进行分页、查询、排序等功能?
  • 对于vue中的模拟数据,如果脱离了之前的vue开发环境,其它的项目中能还是不能使用?
  • 对于vue中的模拟数据,我们有没有进行POST/PUT/DELETE等其它的操作?

问题:有没有更好的方式进行更为自由的模拟数据的构建?

  • 可以实现对数据的get/post/put/delete增、删、改、查等操作
  • 还需要支持restful API接口
  • 除了增、删、改、查,还需要更多的功能,比如分页、排序...
  • 模拟数据是可以独立存在的,可以应用在vue、react、jquery、小程序、app...所有需要的项目中
  • 可以自定数据,不只是英文的数据内容,尽可能使用中文的一些模拟数据

问题:content-type数据类型约束的问题,前端与后台content-type不保持一致,会造成后台将无法正常接收到你前端传递过去的数据,常见的content-type主要有:form-data、x-www-form-urlencoded、application/json

问题:请求的二次封装主要实现哪些功能?

问题:接口的统一管理如何实现?

仓库

vuex

内容作用映射调用其它
state设置状态mapState
getters获取内容mapGetters相当于属性计算computed
属性计算能否传递参数?可以传递(HOF高阶函数)
mutations修改数据mapMutationscommit可以异步,但不建议,不利调试
actions异步操作mapActionsdispatch可以同步,也可异步
modules模块拆分namespaced命名空间
plugins插件辅助扩展应用,比如数据持久化

store:有且只有一个状态管理器

mutations+actions 修改数据和异步操作,这两个层次就让人觉得很繁琐,如果这两个层次进行结合,思考的内容就会少很多,更容易让人理解。

modules模块拆分:模块拆分以后,模块的节点层次会发生变化,然后一个模块中还会调用另外模块的内容,比如说一个getters里调用另外的getters,一个actions中调用另外的actions,我们现在还都是在同一个模块中进行操作,但是有没有一个模块的actions调用另一个模块的actions?比如users模块去调用products模块的actions?

modules模块拆分中提出了namespaced命名空间的概念,让模块的state、getters、mutations、actions所有的层次结点的应用产生了翻天覆地的变化,和原来没有namespaced的操作是基本两种完全不同的写法。

...mapActions(['actions1,'actions2');
---->
...mapActions('moduleName',['actions1','ations2'])

Pinia

vue3当中推荐的状态管理器是Pinia,但不代表vuex不能使用,vuex仍旧是官方的状态管理器,而Pinia是原vue团队中的一个开发人员独立出去开发的一个状态管理器。

内容作用其它
state设置状态
getters获取内容
actions同异步修改数据一个模块的action调用另外模块的action
plugins插件辅助扩展应用,比如数据持久化
  • 每个一模块就是一个状态仓库

  • 数据的同步异步操作都在一个层次中处理完毕

  • 没有了模块拆分,也就没有所谓的命名空间等繁琐内容

Pinia的基本使用流程

环境关联,主要是在入口文件当中:

  • 先要引入创建pinia的方法
  • 实例化 pinia
  • 将pinia挂载到app上,与vue进行绑定

pinia仓库模块的创建,主要是在store目录当中:

  • 单个仓库的内容主要包括:state设置状态、getters获取数据、actions修改内容
  • 利用defineStore进行仓库模块的定义
  • 给仓库模块进行id唯一标识的设定
  • 设置state状态内容,可以声明成函数形式,需要注意的是state:()=>({ }),小括号和大括号内容的代码书写
  • 获取getters内容的时候,可以利用的是state数据的获取,可以进行直接state状态值的返回,也可以定义成一个高阶函数,利用返回的数据类型为函数型进行getters方法的参数化传递调用操作
  • 修改内容actions,actions可以实现同步以及异步的操作,并且支持直接进行state状态值的修改操作,但是state状态值的修改处理利用的是this关键字,通过 this.stateValue = value的形式进行状态值的修改。而且actions操作和vuex当中的actions操作类似,可以进行直接数据的修改,也可以进行指定数据类型的返回,包括返回promise对象。

pinia仓库内容的调用,主要分成了响应式数据内容以及actions方法的两种不同的调用,主要集中在视图组件当中:

  • 从指定的仓库模块中进行创建该模块仓库的创建方法进行引入
  • 在引入的仓库方法进行实例化构建以后获取到的state和getters函数返回的值是非响应式的
  • 可以从pinia当中引入storeToRefs的函数,利用它可以将实例化构建以后获取到的state和getters函数返回的结果值进行响应式数据的转换操作
  • actions 是不用 storeToRefs 转换的,仅仅需要从仓库实例中单独引入即可使用
  • 在setup语法糖环境中可以进行actions引入方法的直接调用

在一个仓库中调用另一个仓库的动作,主要是在store目录当中:

  • 如果需要调用另一个模块的内容,那么则可以将另一个模块的创建方法进行引入
  • 对另一个模块的内容进行实例化并且进行方法的解构引入
  • 进行另一个模块中的actions动作的执行
  • 如果在同一个模块中想对另一个actions动作进行执行,可以利用this.xxx()进行另一个actions动作的调用

实现plugins插件辅助

  • 在入口文件中将pinia-plugin-persistedstate插件进行引入
  • 在pinia实例化以后利用use将pinia与piniaPluginPersistedstate插件进行结合
  • 在是需要的仓库当中设置persist以确认进行持久化插件的应用,可以设置key、paths等不同的属性以确认实现自己需求的功能

如何在组件中实现v-model双向数据绑定,绑定的是仓库中的响应式数据

  • 在pinia中如何想要v-model双向数据绑定仓库的状态值,可以直接v-model绑定即可

UI框架

UI框架的类型:

1)前台样式类UI框架(bootstrap,主要实现的样式好看的布局以及多端适配)

2)中后台类的UI框架(antd,elementUI,主要实现的是后台管理系统的快速应用)

3)移动端的UI框架(vant3,主要实现的是移动端应用的界面布局与开发)

vue3+UI框架(element-plus、antdv)

  • element-plus的安装以及在入口文件中进行模块的引入、样式的引入、UI框架的use使用(完整安装使用)

  • 按需加载操作模式

    • unplugin-vue-components unplugin-auto-import两个插件的安装
    • vite.config.js的插引入与插件应用操作
    • 在main.js入口文件中只需要引入element-plus的样式内容
    • 在需要使用UI组件的页面当中进行需求组件的引入与直接使用即可
  • UI框架的学习:设置属性、触发事件、调用方法

Vue3+TS

1.创建支持ts的vue项目

  • 利用ts可以实现defineProps属性的类型约束,包括数据类型,包括是否必填等操作;

2.非响应式数据在vue3中ts的类型约束

  • 枚举类型的应用,使用它的代码提示,提高的是代码的可读性,在脚本部分的代码提示以及在模板部分的代码也可以实现提示处理

  • 如果在开发的时候设置的内容并没有在枚举类型中定义,那么编辑器将会报的是语法错误

  • 函数返回值的数据类型约束

  • 非响应式数据中的number数据类型的定义以及初始值的设置

  • 声明响应式数据,但是没有明确的确认其数据类型,而初始值可以和非响应式数据进行结合

  • 从哪里可以获取到event事件的类型?找到绑定事件的来源,将鼠标放置于事件绑定的对象上,可以查看到相应的提示信息

  • touchstart、touchmove、touchend几个基本的触摸事件(是在移动端发生的,PC端是没有的)

  • transform可以实现css的过渡动画,包括scale(缩放)、rotate(旋转)、translate(位移)、skew(拉伸)(css的几个过渡动画效果)

  • 明确感受到的体会:

    • 代码有很完善的提示,极大减少代码出错率
    • 有很好的类型的约束,如果代码在书写时发生手写错误,也会产生语法错误,以避免后续的编译、逻辑错误
    • 可以实现很好的团队化开发,提高开发的效率,可以实现牵一发而动全身的开发效果

3.响应式数据的类型约束ref类型

Ref类型声明方式

  • 利用Ref进行数据类型的约束,需要从vue中引入Ref,并且需要去区别ref的引入内容,Ref是interface接口类型,而ref是function函数类型
  • 声明的方式const 变量: Ref<类型> = ref(初始值);,声明完以后不进行初始值的设置时出现的错误内容是如下类似提示:不能将类型“Ref<number | undefined>”分配给类型“Ref”产生的结果类型是一个联合数据类型

ref泛型声明方式

  • 利用ref函数的泛型进行设置,回顾之前的概念可以在函数的右侧进行泛型的设置利用的是<数据类型>进行类型的强制设置,这时候是不需要Ref这个接口类型的声明的
  • 如果利用了ref进行泛型数据类型的设置,可以设置初始值也可以不设置初始值,并没有报任何的错误,但是进行数据类型查看的时候,如果设置了初始值则是单一数据类型,如果不设置数据的初始值则是联合数据类型,联合了undefined这个未定义的数据类型,程序不会报任何的错误

4.响应式数据的类型约束reactive类型

  • 类型声明与值的初始值是两个完全不同的概念,一定要仔细划分
  • 在初始值的时候可以利用断言进行类型的判断,这个判断其实就是从另一个方面进行了数据类型的约束
  • 通常情况下reactive是对象类型,所以可以利用interface进行对象数据类型的约束
  • readonly是只读属性设置(只读属性不能进行再赋值操作),?是可选属性设置(可以设置也可以不设置,其实它的本质是联合数据类型,是联合了undefined)
  • 初始值的时候可以设置interface中并不存在的属性值,也就是超出类型约束范围中的属性,并不报错,但是在修改该属性的时候,编辑器是会报错误内容的。然而如果项目环境配置比较松散,那么这个错误只是编辑器语法错误,并不会影响项目的正常运行。这里的原因是:typescript的作用是什么?它仅仅实现的是类型的静态检测,能不能实现类型的动态检测?而且ts是源于js归于js,最终ts将会被编译转换为js代码
  • type也是ts中的一个类型,并且type类型可以设置联合类型等其它类型,联合数据类型将限制用户在进行数据修改的时候值的范围
  • 在type数据类型中可以利用&符号进行多个数据类型的合并操作
  • reactive的ts声明方式也可以是const 变量:数据类型 = reactive({初始值}),也可以利用泛型的声明方式const 变量 = reactive<数据类型>({初始值})

5.Refs对象类型

可以给HTML网页元素设置ref

  • 在vue2当中如果给HTML元素设置了ref,可以利用this.$refs.xxx获取到元素对象
  • 在vue3中也可以给HTML元素设置ref,但是需要利用响应式数据ref进行响应式数据的声明,把这个响应式数据内容设置到ref属性上
  • 可以利用“响应式数据.value”进行ref元素的获取操作
  • 想要给ref进行数据类型的设置,需要明确HTML元素的数据类型,可以找到HTML元素的根源,查看提示信息以及相关的链接文档,查看到它的最终DOM类型,才能够真正确认HTML元素的数据类型
  • 在确认了HTML数据类型以后可以给ref响应式数据进行类型的约束,并且需要强调的是可能出现的联合数据类型,因为在HTML的DOM元素加载过程中,可能是没有及时加载完成,需要在onMounted生命周期过程中才可以真正找到。
  • 只在给ref进行HTML元素类型的设置以后,才能够将"响应式数据.value"进行更多属性与方法的代码提示操作

可以给自定义组件设置ref

  • 在vue2当中如果给自定义组件设置了ref,可以利用this.$refs.xxx获取到VueComponent组件实例
  • 子组件中方法在父组件中不能再随意调用,如果子组件中的方法想被父组件通过ref等方式查找并调用需要利用defineExpose进行方法的暴露
  • Teleport瞬间移动,可以将Teleport包裹的内容移动到to这个目标元素当中,不一定被包含在#app元素里面
  • 在vue3中也可以给自定义组件设置ref,但是需要利用响应式数据ref进行响应式数据的声明,把这个响应式数据内容设置到ref属性上
  • 可以利用“响应式数据.value”进行ref元素的获取操作,并且在获取到ref自定义组件以后,在defineExpose方法暴露的前提下,调用到子组件中所定义的方法
  • 我们没有办法直接判断子组件的数据类型,就算是将鼠标放置于组件上,也无法通过根源进行数据类型的获取,只能查看到ComponentPublicInstanceConstructor子组件是属于构造器函数
  • 在ref的泛型中可以利用ref<InstanceType<typeof 组件名称> | null>来进行组件实例类型的判断
  • 在利用了组件实例类型判断以后,ts就可以根据组件类型来确认组件中所包含的方法,有对应的代码提示
  • vue3中也包含nextTick()函数的应用,在进行响应式数据修改以后,如果想进行进一步的DOM处理,则需要进行时间的等待,因为响应式数据的渲染是需要时间的,但是这个等待过程不应该使用setTimeout等延时操作,这是因为我们并不清楚等待时间需要多久,那么则可以利用async/await,结合nextTick以确认DOM最终渲染完成,然后再执行对DOM相关的操作内容。

6)vue3中的method、computed、watch的ts类型支撑

method

  • 绑定的事件数据类型可以在@xxx绑定操作事件源上进行查看,确认其数据类型
  • 可以通过 e.target 确认其操作目标(或者直接查看元素的数据类型,通过之前所说的MDN文档)的数据类型,利用as断言模式将e.target进行数据类型强制声明,得到对应结果以后可以获取到指定类型内容的属性值

computed

  • computed属性计算其本质是一个函数,所以这里进行ts结合的时候其实是需要确认的是函数的返回值,可以利用泛型在computed<数据类型>(()=>{})进行ts类型的约束,最终实现的是computed函数返回值的类型限定
  • computed是可以实现参数传递的,这就意味着computed的参数也需要进行数据类型的约束。首先实现computed属性计算的传参需要利用HOF高阶函数模式进行实现,在computed的函数中需要返回一个function函数,在function函数中可以设置传递的参数,那么可以对该参数进行数据类型的约束。
  • 不要对computed进行泛型的直接返回值设置,因为现在是HOF,所以这时候的泛型只针对于第一层函数返回值,而不会针对于函数返回的函数类型。如果一定要设置computed的泛型类型,那么可以根据鼠标提示类型直接进行泛型类型的复制粘贴。

watch

  • 需要强化的是ts是静态类型的检测,不是动态类型的检测,而watch只有在运行以后才会确认,所以需要利用类型转换对操作目标进行先行处理,才能最终确认运行结果正常

7)props和emits(父子,子父)

props

  • props的类型约束使用的是defineProps,既可以使用vue所提供的类型检测操作也可以使用TypeScript的类型约束处理
  • props和vue结合的类型检测与vue2是基本一致的,可以进行数组、简单对象、复杂对象的属性接收,而复杂对象中包括type、default、required、validator4个内容
  • 当进行validator自定义校验操作的时候,如果属性传递不符合规范,并不会在代码阶段就进行信息提示,只有在运行阶段会进行警告提示
  • 所以vue3对defineProps进行类型约束其实和TypeScript进行结合会更简单,功能也会更为的强大,可以利用defineProps<属据类型>()进行Ts的类型属性约束,还可以利用withDefaults(defineProps<数据类型>(),{默认值对象})进行默认值的设置

emits

  • 对比vue2当中的子与父之间的通讯,vue3中的子与父通讯方式其本质与vue2是没有任何的差别的,但是因为vue2中的子组件是利用this.$emit进行事件的发射操作,而vue3中在setup中是没有this对象的,所以说只能利用defineEmits先行进行emit对象的创建与实例化
  • 如果通过 defineEmits进行了emit的实例化以后就可以通过 emit进行事件的传递操作,并且进行参数的携带,那么父组件中则需要利用@事件名称进行事件的绑定操作,并设置相应的callback回调函数
  • 在defineEmits中可以利用泛型对整个emit函数内容进行数据类型的约束设置,其方式主要是:defineEmits<{(e: '事件名称', 事件参数: { id: number; value: string }): void;}>()
  • 在进行事件名称以及事件参数设置类型完毕以后,并且强调emit的函数返回为void之后就可以在父组件中的事件绑定位置进行类型的查看,然后直接将提示的类型设置到callback回调函数的参数当中即可,这样的话在回调函数主体中就可以进行代码提示与数据类型约束操作。

8)provide和inject(实现祖孙之间的通讯)

  • 在父给件中可以利用provide进行数据或方法等内容的提供操作

  • 在子孙组件中可以利用inject进行数据或方法等内容的接收操作

  • 如果直接进行provide以及inject的提供或者接收,事实上程序是无法明确到底应该接收的是哪里传递过来的数据或方法

  • 对于这种情况一般会出现unknown的错误情况,如何思考这种问题的解决是一个重点

  • 思路:想要明确一个提供和一个接收产生一一对应的关系,最好的方式是给需要传递的内容进行唯一标识的处理(可追溯,条码,二维码...扫码识别)

    • 必须给需要传递的内容进行唯一标识的设置

      • 如何才能在vue3中产生一个唯一标识,可以利用InjectionKey,需要注意它的数据类型,如果是Ref数据,则可以利用泛型进行数据类型的指定声明。如果是函数,则可以直接利用InjectionKey确认其函数类型的唯一值
      • 可以利用Symbol数据类型进行唯一值的设置操作
    • 在传递数据的时候明确唯一的标识

    • 在接收数据的时候则通过唯一标识进行数据内容的明确接收

9)vue-router中的ts

  • 在静态路由表的路由配置项中默认情况下没有任何的ts的代码提示操作
  • 在createRouter函数调用的时候,函数的对象属性是包含有代码的提示操作的
  • 在组件当中,引入useRoute并进行实例化以后,利用route对象可以直接查看到当前route对象中所包含的所有属性
  • 想要确认routes静态路由表的数据类型,还是需要确认其数据的根源,可以在createRotuer的routes中进行查看信息操作,确认类型为RouteRecordRaw[],那么就可以给routes进行数据类型的定义,当然前提是将RouteRecordRaw内容从vue-router中进行引入。设置完数据类型以后,routes就有代码提示。
  • meta下的所有属性内容都是项目中自定义的属性,都不是vue3或者是vue-router的属性内容
  • 因为meta中的title、isAdmin、requiresAuth都是项目自定义属性,不归属vue3或者是vue-router的属性,所以根本没办法实现代码提示操作,因为我们没有进行任何的自定义配置操作
  • 如果想要给meta进行自定义属性的添加并实现代码提示,需要编写src/vue-router.d.ts描述文件,并且扩展RouteMeta这个interface接口类型。这样的话在静态路由表的meta中就可以出现自定义属性的提示。在useRoute实例化以后也可以进行route.meta.xxx的属性提示。
  • 声明文件的理解,系统级声明文件和自定义级声明文件进行项目的结合操作,它们最终产生的是并集。
  • 在routes路由配置中如果设置hidden等项目自定义的根级属性,则会报以错误信息。这是因为hidden这个项目属性并不归属于vue-router的基础的路由类型当中,像name、path、meta等路由属性内容才是归属于_RouteRecordBase这一interface接口类型
  • 如果想要添加hidden这样的根级的属性内容,则需要在vue-router.d.ts声明文件中进行_RouteRecordBase这一interface接口类型的自定义扩展操作,将自定义内容与系统内容进行并集处理。声明完以后routes静态路由表中的hidden则不再出错。
  • 在useRoute实例化以后,并没有办法直接获取到hidden自定义的根级路由属性。可以利用声明文件进行_RouteLocationBase的类型扩展操作,将hidden同样也进行自定义属性的设置操作。
  • _RouteLocationBase数据类型可以通过 route.fullPath的提示查看或者是点击查看声明文件进行数据类型的确认与获取。
  • 如果在进行引入useRouter以后并实例化,通过 router.getRoutes()获取到的是整个路由的数组内容,可以进行循环遍历,但是route对象中是不存在hidden这个自定义根级属性的。所以,需要在声明文件中进行RouteRecordNormalized的接口扩展处理,将hidden属性进行添加,而RouteRecordNormalized数据类型的获取仍旧是通过将鼠标放置于router.getRoutes().forEach(route=>{})的route对象上进行信息查看的。

10)axios中的ts

单个对象的返回类型约束:

  • 直接利用axios进行接口请求,无法明确获取到指定接口地址的返回数据的参数提示
  • 得去确认axios.get返回的数据类型,只有该类型确认了以后才能够确定result的数据类
  • 在点击axios.get以后能够进入到axios的index.d.ts声明文件,并且确认请求响应的返回类型是AxiosResponse
  • AxiosResponse<T = any, D = any>是一个泛型,它的T指的是data的数据类型,并且T是泛型
  • 将result的数据类型限制为AxiosResponse<ITodo, any>,那么ITodo就是AxiosResponse的data的数据类型
  • 如果不想对result进行数据类型的设定,可以考虑给axios.get进行泛型的设置,将鼠标移动到axios.get上则可以查看到axios.get返回类型的提示,可以将提示类型进行直接的复制粘贴,但是所看的都是any类型,所以没办法实现代码的提示应用,那么可以将AxiosResponse中的第一个any再次修改成ITodo,这时可以获取到result.data的属性提示
  • 当我们进行axios.get点击查看的时候,可以获取到的是 get<T = any, R = AxiosResponse, D = any>(url: string, config?: AxiosRequestConfig): Promise;,那么查看源码可以获取到get的T和AxiosResponse中的T是同一个数据类型,那么就可以给axios.get进行ITodo的泛型设置,后面的数据类型的约束可以省略。可以最终解释为axios.get请求返回的data数据类型就是ITodo这个类型,所以result.data以后是能够看到ITodo的属性提示的。

多个值数组的返回类型约束:

  • 既然可以进行单个对象的获取,也可以实现多个数据数组的结果返回,但是请求不会自动识别数组的结果返回值,需要由开发人员对数据类型进行中括号[]数组类型的设置操作
  • 在进行请求的时候还可以进行更多参数内容的配置操作,而config的类型就是AxiosRequestConfig
  • 如果直接将result.data的结果值想要赋值给ref响应式数据,这时候需要给ref响应式数据明确其数据类型
  • 不能将类型“ITodo[]”分配给类型“null”,result.data的数据类型已经明确,是ITodo[],而ref声明的是null,所以类型推断就是null类型。result.data和res.value的数据类型不对应,所以需要给res的ref进行泛型的设置
  • res已经进行过数据类型的限定,可以是ITodo[] 或者是 null,那么就可以进行条件的判断,在判断以后对数组内容进行操作时可以实现代码的提示

请求的二次封装的ts结合

  • reqeuest/baseAxios的后缀是ts文件,但是其中的代码和之前的js代码没有任何的差异

  • 我们以为在进行请求的二次封装以后直接返回的是response.data的数据,所以想当然认为请求接口操作以后可以直接用interface的数据类型进行约束,但却报了错误信息:类型“AxiosResponse<any, any>”缺少类型“ITodo”中的以下属性: userId, id, title, completed

  • 解决上述错误的方式有多种:

    • 在接口当中进行interface数据类型的定义,并且在每个请求接口中进行函数返回值的数据类型设定,给每个接口的返回值进行Promise数据类型的定义,而这个T就是interface所定义的数据类型,比如Promise<ITodo>,这种操作方式的模式比较繁杂也不是很容易操作,过程繁琐
    • 可以再利用axios.d.ts的声明文件,将AxiosResponse的数据类型内容进行继承处理,继承于我们刚刚理解的Promise返回的数据类型。这样的话可以直接在组件中进行数据类型的定义,并且直接将接口请求的返回值设置成指定的数据类型即可。

10)pinia中的ts

pinia当中的ts类型约束操作比较简单,可以利用pinia的类型自动推测和用户强制类型的限制两种方式进行实现

  • pinia的类型自动推测操作比较简单,仅仅需要实现的是对state函数的返回值类型进行类型约束,那么对应到的actions、getters将会实现自动类型的推测操作,在脚本层和模板层都将可以实现数据类型的限制和代码的提示功能
  • 如果开发的时候需要强制类型约束,那么主要对pinia的三个层次进行约束操作,包括state、getters、acdtions的内容,那么类型的定义方式可以利用代码提示功能,将state的函数返回值进行定义,然后将鼠标移动到getters、actions的函数上查看其函数的数据类型,以便确认它们的数据类型的定义。在定义好以后可以给defineStore进行泛型数据类型的强制约束,其实现的结果与自动推测没有任何的差异。

3.vue3+typescript技术体系为支持的后台管理系统

day01

项目常见问题清单

  • 问题:前端开发工程师岗位职责主要包括哪些?(你上家公司主要负责的工作内容或者是范围主要包括哪些?)

  • 问题:作为互联网技术部门的一员,你对于公司的组织结构是否了解?你们之前的公司主要划分成了哪些部分?每个部分的职责是什么?人员数有多少?

  • 问题:你之前做过什么项目?B2C(尚品汇)、B2B、C2C、O2O、P2P、F2C......

  • 问题:你做这些项目的时候负责了什么模块?你们之前的这个项目,开发周期是如何的?

    • 我能不能说我负责了注册模块、登陆模块、首页模块?(注册、登陆都是页面)
  • 概念:项目、模块、页面、功能(10star)

  • 问题:你们这个项目是几个人员参与开发?(产品经理,项目经理,设计、前端、后台、测试...)

  • 你如何去理解前台项目和后台管理系统?(后台数据支撑平台,主要是给前台项目做数据的支持与管理操作)

  • 概念:SPA(单页面应用,SEO不足)、MPA(多页面应用)

  • 问题:react、vue(SPA),如果要实现一个需要大规模推广的平台,用react、vue进行SPA项目的构建是不合适的。通常情况react、vue做的SPA项目主要就是后台管理系统。

  • 问题:如果一定要用vue、react进行项目的开发,又不能进行SPA项目的开发,这时候可以使用什么解决方案?SSR(server side render)Next.js(基于react)、Nuxt.js(基于vue)(SPA的不足)

  • 问题:你对于框架的理解是如何的?什么是功能性框架?什么是UI框架?

  • 问题:你对于jquery、react、vue这些框架的理解是如何的?它们与之配合的UI框架你常用的是什么?antd、antdv、elementui、iview、element-plus、bootstrap、vant3....

  • 问题:你有了解哪些后台管理系统的脚手架?你为什么去选择之前所应用的脚手架?

  • 问题:如果想要进行一个内容的查找,你到哪里进行搜索?github

  • 问题:如果你到github上进行项目的查找,会使用哪些纬度进行项目的好坏判断?

  • 问题:在进行一个模块操作的时候,有没有对时间进行规划?能还是不能预计出模块的开发进度?

  • 问题:在你接手到一个模块的任务,会进行怎么样的流程处理?会进行哪些内容的抽离操作?哪些难点的筛选?陌生的专业术语是否清晰?

  • 问题:如果现在立即给你一个脚手架项目(或者是公司里的半成本项目),你如何才能在最短的时间里进行掌握与开发?

  • 问题:如何才能更快更好的入手脚手架项目(公司现有需要维护的项目)?

    • 有文档一定要先看文档(问题:如果原来的项目没有文档,这时候你应该怎么处理?)

      • 如果没有文档,一定要想办法,考虑补全文档(很容易给领导加深好感)
    • 目录结构分析

      • 按照开发行业命名习惯快速理解目录的功能
    • 文件结构分析

      • 从根级到嵌套目录文件的分析
    • 代码结构分析

      • 一定要先找到入口文件
      • 然后从入口文件逐层分析
  • 问题:图标的演化历史是怎么样的?小图标逐一引入->雪碧图->IconFont图标(svg下载,需要将svg图标加到项目库,然后再下载使用项目库内容)->SVG图标(直接一个图标一个图标的直接下载使用)

  • 问题:项目的开发环境主要划分成哪几种?开发、生产、测试

  • 问题:什么是eslint?它的作用是什么?在团队化开发的时候有什么作用?为什么一定要用eslint?

  • 问题:为什么eslint需要和prettier插件进行配合,prettier实现的功能是什么?

  • 问题:你们之前的项目预编译程序语言使用的是?less、scss、stylus?

  • 问题:项目的操作步骤:

    • 先确认路由
    • 再实现布局
    • 请求的二次封装,请求二次封装的主要内容实现哪些?
    • token的存储一般设置在哪里?
    • 如果没有接口的时候模拟数据如何构建?

用户登陆操作

  • 既然是登陆,先找到登陆的界面,也就是src/views/login/index.vue

  • 忽略中间的所有过程,直接查看登陆页登陆按钮的事件

  • 通过登陆按钮回调代码的确认,可以明确登陆操作是在pinia仓库模块中的,应该是设置在pinia用户模块的action动作里面,所以这里就要求大家能够回顾并且掌握的是pinia这个状态管理器操作模式与流程

  • 通过查看pinia仓库中的login方法能够确认的是当前的登陆只是一个虚假的登陆,没有实现真正意义上的接口登陆操作,所以我们需要进行登陆功能的改进

  • 问题:登陆的操作流程应该是如何?

    • 登陆操作是前端与后台的交互,需要有接口的确认

    • 登陆失败,给以提示,登陆成功,设置token(后台做的)

    • 后台操作的内容需要返回给前端,所以登陆成功以后的token最终是交给前端的,那么这就产了一个问题,token应该存储于前端的哪里?(cookie、localStorage(jwt)、sessionStorage、websql、indexedDB)

    • 可以从本地拿到token,有失效时间的限制问题

      • 如果有效,那么继续使用
      • 如果失效,那么需要重新登陆
    • 如果说退出系统,则需要进行token的清除操作

  • 先通过后台提供的接口进行接口的调试操作,确认接口正常的运行

    • swagger直接调试
    • postman接口调试
    • thunder client
  • 实现本地代理地址服务器的设置(端口会自动改变)

  • 登陆的时候需要进行的是接口的真正请求操作,所以我们一定要查看请求的二次封装,二次封装的主要工作是实现token的获取和头信息的设置

  • 通常请求需要给接口进行统一的封装,所以可以创建一个api目录用以存放所有的请求接口函数,但是从接口调试以后可以先明确指定的接口其数据类型的约束,可以先进行接口数据类型的ts模型定义

  • 设置用户登陆的接口,利用请求的二次封装、数据类型的定义、枚举类型的设置进行各个不同请求方式接口的定义

  • 去确认当前的token存储的方式,现在使用提localStorage(按正常情况如果只是token其实用cookie会更好)

  • 修改pinia的仓库,将actions部分登陆、信息获取、退出做修改,登陆的时候不光是登陆,登陆完以后还需要获取用户的信息。

问题:左侧侧边栏菜单与路由之间的关系是什么?

  • 左侧的侧边栏菜单应该是根据路由动态的生成,并且允许这个菜单在点击以后实现路由地跳转

搭建后续模块的流程

  • 先建立目标的目录与最为简单的模板文件
  • 修改路由配置,将首页部分的内容进行复制与修改,注意的事项是嵌套路由的设置