面试题VUE篇

文章目录

  • Vue 的核心是什么/请简述你对 vue 的理解
  • 请简述 vue 的单向数据流
  • 槽口请简述
  • Vue 常用的修饰符有哪些
      • 1. 普通修饰符
      • 2. 事件修饰符
      • 3. 键盘修饰符
      • 4. 系统修饰符
  • v-text 与{{}}与 v-html 区别
  • v-on 可以绑定多个方法吗
  • Vue 循环的 key 作用
  • 什么是计算属性
  • Vue 单页面的优缺点
  • Vuex 是什么?怎么使用?在那种场景下使用
    • 使用
    • 使用场景
  • Vue 中路由跳转方式(声明式/编程式)
  • Vue 中如何进行动态路由设置?有哪些方式?怎么获取传递过来的数据?
  • Vue 路由传参的两种方式,params 和 query方式与区别
  • vue 跨域的解决方式
  • Vue 的生命周期
  • Vue 路由的实现
  • 路由守卫
  • Route 与 router 区别
  • Vue 路由模式 hash 和 history,简单讲一下
  • Vue 数据绑定的几种方式
  • Vue 注册一个全局组件
  • 常用库的常用组件以及属性
  • Vue-cli 中如何自定义指令
  • Vue 中指令有哪些
  • v-if和v-show的区别
  • 对 vue 中 keep-alive 的理解
  • 如何让组件中的 css 在当前组件生效
  • Mvvm 与 mvc 的区别
  • Vue 组件中的 data 为什么是函数
  • Vue 双向绑定的原理
  • 什么是虚拟dom?
  • 虚拟DOM在vue框架中是怎么工作的
  • 介绍一下diff算法
  • vue中涉及了哪些设计模式
  • 如果一个组件在多个项目中使用怎么办
  • Vue 首屏加载慢的原因,怎么解决的?
  • 白屏时间怎么检测,怎么解决白屏问题?
  • v-for 与 v-if 优先级
  • vue3是如何变得更快的?
      • a. Diff 方法优化
      • b. 新增的静态标记(PatchFlag)
      • c. 事件侦听器缓存
  • Vue 如何定义一个过滤器

Vue 的核心是什么/请简述你对 vue 的理解

  • Vue.js的核心是一个用于构建用户界面的渐进式MVVM框架。
  • 它主要专注于视图层,提供了一套简洁的API和高效的数据绑定机制,使开发者能够更容易地构建交互式的单页面应用(SPA)和动态用户界面
  • “渐进式” 指的是你可以逐步采用Vue.js,根据你项目的需要引入其功能,而不必强制性地将整个框架应用到整个项目。
  • MVVM:Model(模型, 代表应用程序的数据和业务逻辑)、View(视图, 代表用户界面)和ViewModel(视图模型,负责处理视图和模型之间的通信。)
  • MVVM模式的一个关键特点是数据绑定,这使得视图和模型之间的同步变得自动化。

请简述 vue 的单向数据流

  • Vue.js的单向数据流是指数据在应用中的流动方向是单一的,从父组件到子组件。父组件通过将数据作为props传递给子组件,子组件可以通过props来接收这些数据。
  • 父向子(单向数据流):
//父组件
const message = ref("hi!son")
<childComponent :messagefromDad="message">
//子组件
声明式:
let {placeholder}=defineProps(['placeholder'])
 <input     :placeholder="placeholder">
选项式:
export default{
	props:{
		messagefromDad:String
	}
}
  • 子向父(通过事件)
//son
声明式(setup语法糖写法)
const instance = getCurrentInstance();

onMounted(()=>{
  instance.emit('msg',"hello!dad!")
})


//dad
<son @msg="handleMsg"></son> //监听msg自定义事件
<div>search儿子传来的东西:{{ msgfromson }}</div>
let msgfromson=ref('...')
const handleMsg = (e)=>{
    console.log("传过来的语句是:",e);
    msgfromson=e
}

this.$emit 是 Vue.js 中用于触发自定义事件的方法
this.$emit('eventName', payload);payload(载荷) 是可选的参数,用于传递额外的数据给父组件。

槽口请简述

子组件的插槽是为了留给父组件,允许父组件为不同插槽位置分配不同的内容。
子:<slot name=xxx>
父:<template v-slot:xxx>
在这里插入图片描述

<!-- 父组件:App.vue -->
<template>
    <MyComponent>
      <!-- 使用具名插槽 -->
      <template v-slot:header>
        <h2>Header Content</h2>
      </template>

      <template v-slot:footer>
        <p>Footer Content</p>
      </template>
    </MyComponent>
</template>

<!-- 子组件:MyComponent.vue -->
<template>
  <div>
    <!-- 具名插槽位置 -->
    <slot name="header"></slot>
    <!-- 具名插槽位置 -->
    <slot name="footer"></slot>
  </div>
</template>

Vue 常用的修饰符有哪些

修饰符是一些附加在指令后面的特殊关键字,用于修改指令的行为。

1. 普通修饰符

  • .stop 阻止事件冒泡。
  • .prevent 阻止默认行为。
  • .capture 使用事件捕获模式。
  • .self 只在事件是从触发事件的元素自身触发时触发。不会触发子孙冒泡的事件。
  • .lazy<input v-model.lazy="message">将会在 “change” 事件而不是 “input” 事件时更新数据,从而减少更新的频率,提高性能。

2. 事件修饰符

  • .once 只触发一次。事件处理函数会在第一次触发后被自动解绑
    - .passive 指示浏览器不要等待 preventDefault 的调用,用于改善移动设备上的滚动性能【没看懂】

3. 键盘修饰符

使用:<input @keyup.enter="handleEnterKey">

  • .enter 监听 Enter 键。
  • .tab 监听 Tab 键。
  • .delete.backspace 监听删除/退格键。
  • .esc 监听 Esc 键。
  • .space 监听空格键。
  • .up 监听上箭头键。

4. 系统修饰符

  • .ctrl 用于监听 Ctrl 键。
  • .alt 用于监听 Alt 键。
  • .shift 用于监听 Shift 键。
  • .meta.cmd 用于监听 Meta 键 (Command 键或 Windows 键)。

v-text 与{{}}与 v-html 区别

<div v-text="message"></div>
<div>{{ message }}</div>
<div v-html="htmlContent"></div>
  • v-text 与{{}}效果一样,不会解析 HTML,会将表达式的结果作为纯文本插入到元素中。
  • 用于将表达式的结果作为 HTML 解析并插入到元素中,它可以导致XSS攻击(跨站脚本攻击)

v-on 可以绑定多个方法吗

可以。

不同事件键值对<button v-on="{ click: method1, mouseover: method2 }">Click me</button>
相同事件逗号分割<button v-on:click="[method1, method2]">Click me</button>

Vue 循环的 key 作用

性能和正确渲染:使用key有助于Vue识别每个节点的唯一性,节点内容更新时,比较同一key节点的内容,Vue可以更快速和更准确地判断哪些节点需要被更新、删除或添加,从而降低渲染的成本。

什么是计算属性

  • 计算属性是用来声明式的描述一个值依赖了其他的值,并且只有在依赖的属性发生变化时才会重新计算。
  • 每个计算属性都包括一个 getter 和 setter 方法,它们分别负责在读取属性值和修改属性值时执行相应的逻辑。
computed: {
  fullName: {
    get() {
      return this.firstName + ' ' + this.lastName;
    },
    set(value) {
      const names = value.split(' ');
      this.firstName = names[0];
      this.lastName = names[1];
    }
  }
}

Vue 单页面的优缺点

优点

  1. 快速响应和更流畅的用户体验:SPA 利用前端路由,只更新页面中的部分内容,不需要整页刷新,因此可以实现快速的用户体验。
  2. 减轻服务器负担: 由于不需要为每个页面请求都返回完整的 HTML 页面,服务器负担较轻。
  3. 代码复用: 可以通过组件化的方式将代码分割为可复用的组件,提高代码的可维护性。

缺点:
4. 首次加载较慢: SPA 首次加载需要下载这个应用必要的的 JavaScript 脚本(比如SPA的核心应用逻辑、路由配置和必要的资源),可能导致较长的首次加载时间。
5. SEO 难度较大: 搜索引擎优化对于 SPA 较为复杂,因为页面内容大部分是通过 JavaScript 动态生成的,而搜索引擎爬虫可能无法获取到这些内容。

搜索引擎优化(Search Engine Optimization)

Vuex 是什么?怎么使用?在那种场景下使用

vue中的状态管理库,主要用于管理应用程序中的共享状态(state)和对状态的操作

状态可以理解为应用中的共享的响应式数据

核心包括

  1. State(状态): 即应用程序的数据源,存储着应用程序中需要共享和管理的状态。
  2. Getter(获取器): 允许组件从Store中获取状态,类似于组件的计算属性。
  3. Mutation(突变): 通过提交 Mutation来改变状态,是唯一能够改变状态的地方。它们是同步的事务。
  4. Action(动作):类似于Mutation,但是可以包含异步操作。通过提交 Action 来触发 Mutation。
    Action 负责异步逻辑,然后通过 commit 触发对应的 Mutation 来改变状态。这种分工的设计可以使代码更模块化、可维护

使用

很重要,务必能默写下来
// store.js----------------------------------------------------
import { createStore } from 'vuex';

const store = createStore({
 state() {
   return {
     count: 0
   };
 },
 mutations: {
   increment(state) {
     state.count++;
   }
 },
 actions: {
   incrementAsync(context) {
   //Action 函数接收一个与 store 实例具有相同方法和属性的 context 对象
     setTimeout(() => {
       context.commit('increment');
     }, 1000);
   }
 },
 getters: {
   doubleCount: state => state.count * 2
 }
});

export default store;

//main.js引入store--------------------------------------------------
new Vue({
 // 将 store 实例传递给 Vue 实例
 store,
 render: h => h(App)
}).$mount('#app');

//.vue中使用--------------------------------------------------------------
   <button @click="increment">Increment</button>
   <button @click="incrementAsync">Increment Async</button>

<script>
export default {
 methods: {
   increment() {
     this.$store.commit('increment'); //commit(提交)用于触发同步的状态变更(Mutation)
   },
   incrementAsync() {
     this.$store.dispatch('incrementAsync'); //dispatch(分发),分发一个 Action 来处理异步逻辑
   }
 }
}
</script>

$store$表示store是 Vue 实例上的一个默认属性.
以 $ 开头的属性和方法都是 Vue 提供的一些特殊接口,用于访问和操作 Vue 实例
比如:

	this.$el:当前 Vue 实例的根 DOM 元素。
	this.$data:Vue 实例的数据对象
	this.$props:包含了父组件传递给当前组件的属性
	this.$emit('custom-event', 'Hello from child!');用于触发当前实例上的事件。
	this.$router.push('/about');  Vue Router 的实例,用于在组件中进行路由导航。
	this.$route  包含当前路由信息的对象,可以访问当前路由的参数、查询参数等。

使用场景

  1. 大型单页应用: 规模大,组件嵌套深,且有多个组件需要共享状态时,Vuex 的集中式状态管理能够更好地组织和管理应用的状态。
  2. 团队协作开发:使用 Vuex 能够使团队成员更容易理解和协作,因为状态是集中管理的,避免了状态散落在各个组件中导致的维护困难

具体使用场景:登录状态,加入购物车,音乐播放

对比小程序开发中的原生状态管理
getApp().globalData.userInfo = userInfo;

Vue 中路由跳转方式(声明式/编程式)

  1. 声明式导航(Declarative Navigation):

    <!-- 在模板中使用 router-link 进行声明式导航 -->
    <router-link to="/home">Home</router-link>
    
  2. 编程式导航(Programmatic Navigation):

    // 在组件的方法中使用编程式导航
    methods: {
      goToHome() {
        this.$router.push('/home');
      }
    }
    
  3. 带参数的路由跳转:

    <!-- 在声明式导航中传递参数 -->
    <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    
    // 在编程式导航中传递参数
    this.$router.push({ name: 'user', params: { userId: 123 }});
    
  4. 带查询参数的路由跳转:

    <!-- 在声明式导航中传递查询参数 -->
    <router-link :to="{ path: 'search', query: { keyword: 'vue' }}">Search</router-link>
    
    // 在编程式导航中传递查询参数
    this.$router.push({ path: 'search', query: { keyword: 'vue' }});
    
    使用this.$route.query.keyword;来接收
    

Vue 中如何进行动态路由设置?有哪些方式?怎么获取传递过来的数据?

动态路由也可以叫路由传参
动态路由有 query 和 prrams 两种方式传参

Vue 路由传参的两种方式,params 和 query方式与区别

形式的区别

  • /user/:id 冒号
  • ?key=value 问号

用途的区别

  • Params适合传递对应资源的标识符,如用户ID。
  • Query适合传递一些非关键性的、可选的参数,如页面过滤条件等。

如果是传递密码,使用POST请求将敏感信息包含在HTTP的请求体中,使用HTTPS协议加密

vue 跨域的解决方式

配置代理
配置代理的目的是让前端的请求先经过开发环境的服务器,再由该服务器向后端服务器发起请求。这样,对于浏览器而言,所有请求都是发往同一个域名,就不会触发同源策略的限制,从而解决了跨域问题。

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};
  • '/api' 是匹配请求路径的规则,表示所有以 /api 开头的请求都会被代理。
  • target: 'http://api.example.com' 表示代理到的目标地址,即后端的真实地址。
  • changeOrigin: true 表示改变请求源,允许跨域。
  • pathRewrite: {'^/api': ''} 表示将请求路径中的 /api 替换为空字符串,确保最终的请求路径是正确的。
    这样配置后,比如前端发起的请求是 /api/data,实际上会被代理到 http://api.example.com/data,从而避免了浏览器的同源策略问题。
  • 这种方式仅在开发环境中使用,因为在生产环境中应该由后端服务正确配置 CORS 头信息以解决跨域问题。

Vue 的生命周期

生命周期(Lifecycle)是指一个对象从被创建到被销毁所经历的各个阶段。给了用户在不同阶段添加自己的代码的机会。
创建、挂载、更新、销毁

  • setup:
    • 替换了v2中的beforeCreate和created
    • 组件的入口,在组件实例创建之前执行,初始化组件的状态和行为
  • onBeforeMount 和 onMounted
    • 前者 未挂载到DOM上
    • 后者已经挂载了
  • onBeforeUpdate 和 onUpdated
    • 组件更新之前和之后执行逻辑
    • 监控数据的变化
  • onBeforeUnmount 和 onUnmounted
    • 替代了destroyed

数据的请求一般放在created 和 mounted 这两个生命周期中,他们的区别是:
1.如果数据请求不依赖于 DOM 的状态,而是在 组件实例创建时就需要获取,那么使用 created 是合适的。(例如,创建组件之前,就要请求服务器返回组件的颜色)
2.如果需要在组件被挂载到页面后再进行数据请求,以确保 DOM 的可用性,那么使用 mounted 是更合适的选择。(例如 请求服务器数据然后放在某个dom组件上,需要挂载后才能获得这个dom组件)

Vue 路由的实现

//创建一个路由实例,配置路由映射关系。
Vue.use(VueRouter);

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/user/:id', component: User }
  //在组件中通过 $route.params.id 获取参数的值
];

const router = new VueRouter({
  routes
});
//实现页面跳转
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>

路由守卫

  • 路由守卫允许你在路由发生变化前、后或者是变化过程中执行一些自定义的逻辑。
  • 它们是一些钩子函数,可以用于处理导航过程中的不同阶段。
  1. 全局前置守卫 (beforeEach):
    beforeEach 被用来全局性地导航守卫,可以在路由切换前执行一些逻辑。如果在全局前置守卫中调用 next(),则导航会继续进行;如果调用 next(false),则导航会被中断;如果调用 next('/path'),则导航会被重定向到新的路径。

    router.beforeEach((to, from, next) => {
      // 在路由切换前执行一些逻辑
      if (to.meta.requiresAuth && !auth.isAuthenticated) {
        // 如果需要验证身份但用户未认证,重定向到登录页
        //meta用于存储自定义的元信息,允许你为每个路由定义一些额外的属性
        //to表示即将要进入的目标路由对象
        next('/login');
      } else {
        // 继续路由导航
        next();
      }
    });
    
  2. 全局解析守卫 (beforeResolve):
    beforeResolve 类似于 beforeEach
    最大的区别在于在 beforeResolve 中,可以确保所有的组件都已经创建。适合等待异步组件解析。

    什么是“异步组件解析”
    有时候你希望当路由导航到 /async 路径时,才会实际加载和渲染某个组件。

component属性的值可以是一个组件对象或者一个返回组件对象的函数
在这里,component属性的值是一个函数,该函数返回一个 import() 调用,
实现了异步加载组件的效果。这是异步组件解析的一种常见用法。

const routes = [
 {
   path: '/async',
   component: () => import('./AsyncComponent.vue')
 },
 // ...
];
router.beforeResolve((to, from, next) => {
  // 在导航确认前执行逻辑
  // 适用于需要等待异步组件解析完成的情况
  next();
});
  1. 全局后置守卫 (afterEach):
    afterEach 在路由切换后执行,无论导航是成功的还是失败的,都会触发。

    router.afterEach((to, from) => {
      // 在路由切换后执行一些逻辑
    });
    
  2. 路由独享的守卫:
    你还可以在路由配置中直接定义守卫,这些守卫只会对特定的路由产生作用。

    const routes = [
      {
        path: '/profile',
        component: Profile,
        beforeEnter: (to, from, next) => {
          // 在进入 '/profile' 路由前执行逻辑
          // 这里的逻辑不同于全局前置守卫
          next();
        }
      }
    ];
    

Route 与 router 区别

  • “Route” 通常是指被路由器管理的一个单独的路径规则,包括路径的URL和对应的组件。
  • 而 “router” 则是指整个路由系统,用于处理路由切换的逻辑,包括提供一些API比如路由守卫的钩子函数在切换时执行一些操作

Vue 路由模式 hash 和 history,简单讲一下

Vue Router 支持两种模式:hash 模式和 history 模式。这两种模式决定了在浏览器中如何处理路由

const router = new VueRouter({
  mode: 'history'/'hash',
  routes: [...]
});
  1. Hash 模式:

    • URL格式: 在 hash 模式下,URL 中的路径会带有一个 # 符号,例如 http://example.com/#/about

    而在前端路由的上下文中,“Hash 模式” 实际上是指 URL 中的 hash 部分(即 # 号后面的部分)的使用。
    在传统的网页中,哈希主要用于页面内锚点(锚点链接),例如 https://example.com/page#section1,其中 section1 是页面内的某个锚点。

    • 特点: Hash 模式通过监听 window.location.hash 的变化来实现路由的切换。因为 hash 的改变不会导致浏览器向服务器发出请求,所以可以避免一些问题,特别是在使用单页应用(SPA)时。
    • 优点: 不需要特殊的服务器配置,可以在所有的 web 服务器上运行。
    • 缺点: URL 中带有 #,可能看起来不太美观;此外,由于 hash 值变化不会触发页面刷新,有些浏览器历史记录、页面跳转等功能可能受到一些限制。(浏览器的历史记录中记录的是 hash 值的变化,而不是整个 URL 的变化。这可能导致在浏览器的历史记录中看到多个相同的 URL)
  2. History 模式:

    • URL格式: 在 history 模式下,URL 中的路径更加自然,不带有 #,例如 http://example.com/about。要求服务器配置**,确保在任何路由下都返回同一个 HTML 页面,以防止在刷新页面时出现 404 错误。
    • 优点: URL 更加美观,不带有 #,更符合传统的 URL 格式。
    • 缺点: 需要特殊的服务器配置,以处理在刷新页面时的路由问题。

Vue 数据绑定的几种方式

<span>{{ message }}</span>//插值表达式(Interpolation)
<img :src="imageUrl"> //绑定属性(v-bind)
<button @click="handleClick">Click me</button>//事件绑定(v-on)
<input v-model="username">//双向绑定(v-model),确保视图和数据的同步

计算属性(computed)
监听属性(watch)

Vue 注册一个全局组件

//在入口文件中
// 注册全局组件
Vue.component('my-component', MyComponent);//my-component' 是你为组件指定的全局标签名

在其他模板中使用:    <my-component></my-component>
//注册局部组件
import MyLocalComponent from './MyLocalComponent.vue';

export default {
  components: {
    'my-local-component': MyLocalComponent
  }
  // ...其他配置
};

常用库的常用组件以及属性

tedesign

按钮
 <t-button theme="primary" icon="search" shape="square" size="large"></t-button>
分割线
 <t-divider dashed content="文字信息" align="left" />
input
<t-input
  label="手机号"
  placeholder="输入手机号码"
  value="{{phoneNumber}}"
  type="number"
  tips="{{phoneError ? '手机号输入不正确' : ''}}"
  bindchange="onPhoneInput"
>
进度条
<t-progress percentage="{{percentage}}" ariaLabel="{{ percentage + '%' }}" />

Vue-cli 中如何自定义指令

  1. 创建插件文件:
    在项目的 src 目录下创建一个 plugins 文件夹,然后在该文件夹内创建一个 JavaScript 文件,用于定义你的自定义指令。

    // src/plugins/customDirective.js
    
    export default {
      install(Vue) {
        // 在这里定义你的自定义指令
        Vue.directive('custom', {
          // 指令钩子函数
          bind(el, binding) {
            // 指令绑定时的逻辑
            el.style.color = binding.value;
          },
          // 更多钩子函数和配置项可以根据需要添加
        });
      }
    };
    
  2. 注册插件:
    src/main.js 文件中注册你的插件。

    import Vue from 'vue';
    import App from './App.vue';
    import customDirectivePlugin from './plugins/customDirective';
    
    Vue.config.productionTip = false;
    
    // 注册自定义指令插件
    Vue.use(customDirectivePlugin);
    
    new Vue({
      render: h => h(App),
    }).$mount('#app');
    
  3. 使用自定义指令:

    <template>
      <div v-custom="'red'">This text is affected by the custom directive.</div>
    </template>
    

Vue 中指令有哪些

v-for:循环数组,对象,字符串,数字
v-on:绑定事件监听
v-bind:动态绑定一个或者多个属性
v-model:表单控件或者组件上创建双向绑定
v-if v-else v-else-if 条件渲染

v-show 根据表达式真假,切换元素的 display
v-html 更新元素的 innerhtml
v-text 更新元素的 textcontent
v-pre 跳过这个元素和子元素的编译过程,这意味着不会对其中的 Mustache 插值或指令进行编译,而直接输出原始的代码。
v-clock 这个指令保持在元素上知道关联实例结束编译
v-once 只渲染一次

v-if和v-show的区别

  • 两种不同的条件渲染方式
  • 主要区别在于元素在 DOM 中的存在与否以及性能方面的考虑。
  • 对于vif,当表达式为假时,元素从 DOM 中移除
  • vshow, 无论表达式为真还是假,元素始终保留在 DOM 中
  • 频繁切换时,show不需要重复创建和销毁元素,性能更好

对 vue 中 keep-alive 的理解

keep-alive 是 vue 的内置组件,用于缓存动态组件,以避免在组件切换时销毁和重新创建组件实例,从而提升性能

<!-- ParentComponent.vue -->
//示例:两个动态切换的组件,分别是 HomeComponent 和 AboutComponent
<template>
  <div>
    <button @click="toggleComponent">Toggle Component</button>
    <keep-alive>
      <component :is="currentComponent" :key="currentComponent"></component>
    </keep-alive>
  </div>
</template>
<script>
import HomeComponent from './HomeComponent.vue';
import AboutComponent from './AboutComponent.vue';
export default {
  data() {
    return {
      currentComponent: 'HomeComponent',
    };
  },
  methods: {
    toggleComponent() {
      this.currentComponent = (this.currentComponent === 'HomeComponent') ? 'AboutComponent' : 'HomeComponent';
    },
  },
};
</script>

如何让组件中的 css 在当前组件生效

在 styled 中加上 scoped

Mvvm 与 mvc 的区别

  • MVVM(Model-View-ViewModel)和 MVC(Model-View-Controller)都是设计模式,用于组织和管理应用程序的结构。
  • 最主要的区别在于MVC数据流动是单向的,只能从模型流动到视图(MVC 中,一般来说,需要手动处理用户输入的数据,将其更新到模型中)。而MVVM的视图和视图模型之间的数据变化会同步更新。

Vue 组件中的 data 为什么是函数

// 不推荐写法
Vue.component('my-component', {
  data: {
    count: 0
  },
  template: '<div>{{ count }}</div>'
});

上述代码中,data 直接使用了一个对象。这样的写法存在潜在的问题,因为组件在 Vue 中是可以复用的。如果在多次使用了这个组件,那么它们会共享同一个数据对象,导致状态混乱。

为了解决这个问题,Vue 要求 data 必须是一个函数,而不是直接是一个对象。这样每个组件实例在创建时都会调用 data 函数,返回一个独立的数据对象,确保每个组件实例都有属于自己的数据对象,不会相互影响。

// 推荐写法
Vue.component('my-component', {
  data() {
    return {
      count: 0
    };
  },
  template: '<div>{{ count }}</div>'
});

Vue 双向绑定的原理

另一种问法:Vue 双数据绑定过程中,这边儿数据改变了怎么通知另一边改变

步骤:

  1. 数据劫持
    • 使用proxy为vm.$data属性设置getter和setter方法来劫持读取和设置该属性的操作,
  2. 依赖收集(Dependency Collection):
    在数据劫持的过程中,当读取属性值时,就说明要为这个属性绑定观察者,Vue会将这个属性依赖的观察者(Watcher)收集起来,以便在数据变化时通知这些观察者进行更新。
  3. 编译(Compilation):
    • Vue使用模板编译器解析Vue模板,识别其中的指令,特别是v-model等双向绑定的指令。
    • 为找到的指令和表达式创建对应的Watcher实例绑定点相关的Watcher实例,并在数据变化时触发更新。
  4. 更新视图(Updating the View):
    • 当数据发生变化时,通过数据劫持中的setter,触发对应属性的setter,进而触发依赖收集中的Watcher实例的update方法,最终更新视图。
    • 如果是双向绑定,当视图中的表单元素发生变化时,Watcher也会通知数据进行更新。

什么是虚拟dom?

  • 是一个对象来描述真实的 DOM 结构
  • 传统开发中更新页面直接获取dom元素然后直接在dom上修改,这种直接更新DOM的方式可能会导致性能问题
  • 当应用状态发生变化时,首先更新虚拟DOM,然后通过比较虚拟DOM与实际DOM的差异,最终只更新必要的部分,而不是整个页面,避免频繁操作实际DOM

虚拟DOM在vue框架中是怎么工作的

  • Vue的模板首先通过渲染函数得到一个虚拟DOM树(VNode)
  • 当数据发生变化时,Vue会生成一个新的虚拟DOM树。
  • 然后,新的虚拟DOM树与旧的虚拟DOM树进行比较,找到差异。(Diff算法)目标是尽量减少实际DOM操作的次数。
  • 找到差异后,Vue会生成一系列的DOM操作指令,然后批量执行这些指令,最终将实际DOM结构更新为新的状态。

介绍一下diff算法

  • 新旧虚拟DOM树的根节点开始递归进行深度优先遍历
  • 比较相同位置的新旧节点的属性、类型、子节点等等。
  • 如果节点不同,然后生成一组描述这些差异的操作指令,这些操作指令最终用于更新实际的DOM结构

vue中涉及了哪些设计模式

  1. 观察者模式(Observer)

    • Vue.js 使用观察者模式来实现数据绑定
    • 是一种一对多的依赖关系
    • 举例:天气预报系统,其中包括一个天气台作为主题,以及一群订阅者(观察者)。每当天气发生变化时,天气台就会向所有订阅者发送通知。订阅者们收到通知后,可以根据自己的兴趣决定是否更新自己的状态。
  2. 发布-订阅模式(Pub-Sub)

    • Vue 中的事件系统就是一个典型的发布-订阅模式的实现,通过 $on$emit 方法来监听和触发事件。
    • 其实类似于观察者模式
  3. 单例模式(Singleton)

    • Vue 实例是单例的,一个应用中通常只有一个根实例。避免多个实例导致资源浪费或不一致的状态。

    想象一下,你是一家咖啡店的老板。你决定在咖啡店里安装一个特殊的咖啡机,这个咖啡机有一些特殊的功能,比如能够调整咖啡的温度和浓度。你希望确保整个咖啡店只有一台这样的特殊咖啡机,并且所有的员工都能够通过一个统一的方式来使用它。
    这里就是单例模式的应用:
    1. 特殊咖啡机的制造:你安排工程师制造了这个特殊咖啡机。为了确保只有一台,你让这个咖啡机的构造方式非常特殊,不能通过常规的购买或其他方式获得。这就好比单例模式中,通过私有构造函数来限制实例的创建。 以防止通过 new 关键字直接实例化对象。
    2. 全局访问点:为了让所有员工能够使用这个特殊咖啡机,你在咖啡店的中央厨房放置了这台咖啡机,并贴上了一张大大的标签,上面写着“特殊咖啡机”。所有的员工都通过这个全局访问点来获取咖啡,而不是在各个角落都放一台相同的咖啡机。这就好比单例模式中,通过静态实例和一个全局访问点来获取单例对象。
    3. 只有一台咖啡机:由于特殊咖啡机是唯一的,无论员工们多么努力,他们都只能使用这一台咖啡机。这就好比单例模式确保在整个应用程序中只有一个实例存在。

  4. 策略模式(Strategy)

    • Vue 的指令(Directives)可以被看作是一种策略模式的应用,不同的指令实现了不同的策略,例如 v-ifv-show 可以互相替换。

    形象地说,当你在网上购物并且决定结账时,你会选择支付方式,比如信用卡或 PayPal。
    这时,你的购物车(上下文)就会调用选中支付方式(具体策略类)来完成支付。如果有新的支付方式出现,你只需添加一个新的具体策略类,而不需要修改购物车的代码。
    这种模式的优势在于你可以轻松地切换支付方式,而不必改变购物车的代码。这就是策略模式的核心思想:定义一系列算法,将它们封装起来,并且使它们可以互相替换,而不影响客户端(购物车)的使用。

  5. 装饰者模式(Decorator)

    • Mixins 在 Vue 中的使用可以被视为一种装饰者模式,通过混入(mixin)来扩展组件的功能。装饰者模式的优点在于它提供了一种灵活的方式来扩展对象的功能
    • 什么时mixin?理解成小插件,将一些功能模块化,然后在多个组件中复用。

      好比点奶茶时加的各种小料。

  6. 组合模式(Composite)

    • Vue 组件的嵌套和组合可以被看作是一种组合模式的实现,通过简单组件的组合可以构建复杂的用户界面。
  7. 代理模式(Proxy)

    • Vue 中使用了代理模式来实现数据的响应性。通过 Object.definePropertyProxy 来劫持对象的属性访问,从而实现数据的监听和更新。

如果一个组件在多个项目中使用怎么办

将该组件封装为一个npm包,然后在每个项目中通过npm进行安装和引用
具体流程

  1. 创建 npm 账号: 如果你没有 npm 账号,你需要在 npm 官方网站 上注册一个账号。

  2. 初始化项目: 在你的 Vue 组件项目中,运行以下命令初始化 package.json 文件:

    npm init
    

    根据提示填写相关信息,确保设置了正确的 nameversiondescription 等字段。

  3. 编写组件代码: 编写你的 Vue 组件,确保组件代码正常运行。你的组件文件通常会被放在 src 文件夹下。

  4. 创建入口文件: 在项目根目录创建一个入口文件,通常命名为 index.js。这个文件会作为你的 npm 包的入口。

    // index.js
    import YourComponent from './src/YourComponent.vue';
    
    // Export your component
    export default YourComponent;
    
  5. 创建 .npmignore 文件(可选): 如果你希望在发布 npm 包时忽略一些文件,可以创建一个 .npmignore 文件。这个文件的作用类似于 .gitignore,用于指定哪些文件或文件夹不会被包含在 npm 包中。

  6. 打包和构建: 你需要使用构建工具(如 webpack、Rollup 等)来将你的组件打包成一个可用于发布的文件。

  7. 发布到 npm: 运行以下命令发布你的 npm 包:

    npm login  # 登录到你的 npm 账号
    npm publish
    

    这将把你的组件发布到 npm 仓库中。

  8. 在其他项目中安装并引用: 在其他 Vue 项目中,你可以通过以下命令安装并引用你的组件:

    npm install your-component-package-name
    
    // 在项目中引用你的组件
    import YourComponent from 'your-component-package-name';
    
    // 在组件中使用
    export default {
      components: {
        YourComponent
      },
      // 其他组件配置...
    };
    

Vue 首屏加载慢的原因,怎么解决的?

原因:
太多/太大的资源和组件需要加载。

解决:

  1. 资源:压缩,懒加载
  2. 组件:动态导入模块(也叫路由懒加载,只有在访问相关路由时才会被加载)import('./MyComponent.vue');
  3. 按需加载UI框架

白屏时间怎么检测,怎么解决白屏问题?

白屏时间是指用户打开网页后到页面开始呈现内容之间的时间。
检测:
chrome开发者工具中的lighthouse
在这里插入图片描述
解决:

  1. 代码分割
  2. 按需加载组件
  3. 懒加载
  4. 压缩
  5. v-cloak(cloak:披风)
<style>
  [v-cloak] {
    display: none;
  }
</style>

<div v-cloak>
  <!-- 这里的内容会在 Vue 实例初始化完成后显示 -->
  {{ message }}
</div>

v-cloak解决的问题: 如果 Vue 实例初始化过程中,这些 Mustache 表达式尚未被解析,页面就有可能出现短暂的未编译内容显示。
通过上述样式,[v-cloak] 的元素在 Vue 实例初始化之前会被隐藏,而在 Vue 实例初始化完成后,v-cloak 指令会被移除,元素显示出来。

v-for 与 v-if 优先级

for比if高,下面这个例子,会先执行vfor渲染好多li,然后根绝if判断要不要渲染出来这个li。

<ul>
  <li v-for="item in items" v-if="item.isActive">
    {{ item.name }}
  </li>
</ul>

vue3是如何变得更快的?

Vue 3.0 在性能方面进行了多方面的优化,其中包括:

a. Diff 方法优化

在 Vue 2.x 中,虚拟 DOM 的对比是全量的,即每次更新都会对整个虚拟 DOM 树进行对比,导致性能开销较大。Vue 3.0 引入了一些优化措施,使得 diff 过程更加高效。

b. 新增的静态标记(PatchFlag)

在 Vue 3.0 中,引入了静态标记(PatchFlag)的概念。在与上次虚拟节点进行对比时,只会比较带有 patch flag 的节点,通过 flag 的信息可以得知当前节点需要具体对比的内容。这一优化避免了对不必要的节点进行比较,提高了 diff 过程的效率。

同时,Vue 3.0 还引入了静态提升(hoistStatic)的机制。在 Vue 2.x 中,无论元素是否参与更新,每次都会重新创建。而在 Vue 3.0 中,对于不参与更新的元素,只会被创建一次,并在每次渲染时被不停地复用。这减少了创建和销毁的开销,提高了性能。

c. 事件侦听器缓存

Vue 3.0 引入了事件侦听器缓存的机制,其中 cacheHandlers 是一个相关的配置项。默认情况下,如果一个事件处理函数像 onClick 被视为动态绑定,Vue 会追踪它的变化。但是,由于通常这是同一个函数,实际上没有必要每次都重新追踪变化。

因此,启用 cacheHandlers 配置后,Vue 3.0 会直接缓存起来并复用,避免了不必要的追踪和处理,提高了事件处理的效率。

Vue 如何定义一个过滤器

过滤器本质就是一个函数,对输入的数据进行某些处理后输出。

【!】在Vue 3中,过滤器的概念已经被废弃。直接写个函数处理一下即可。

1.定义

// 全局定义一个名为 'capitalize' 的过滤器
Vue.filter('capitalize', function(value) {
  if (!value) return '';
  // 将输入的字符串首字母转为大写
  return value.toString().charAt(0).toUpperCase() + value.slice(1);
});

//局部定义
export default {
  data() {
    return {
      message: 'hello world',
    };
  },
  filters: {
    uppercase: function(value) {
      return value.toUpperCase();
    },
  },
};

2.使用

<!-- 在模板中使用 'capitalize' 过滤器 -->
<p>{{ message | capitalize }}</p>

message 的值将被传递给 capitalize 过滤器进行处理,然后显示在页面上。注意,在过滤器使用管道符 | 连接到表达式或插值之后。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/425990.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新版阿里云Linux CentOS7 ecs-user用户安装Mysql8详细教程(超简单)

经过两天的踩坑后&#xff0c;终于成功安装&#xff0c;并找到了最快捷的安装方式。接下来就由我来给大家介绍不踩坑安装大法&#xff01; 一、下载Mysql 首先前往Mysql官网下载 MySQL官方下载地址 第一步&#xff0c;选择安装包&#xff0c;这是最关键的一步&#xff0c;选错安…

进程与线程:通过实际生活来解析计算机的基本运作单位

进程与线程 进程与线程&#xff1a;详细解析计算机的基本运作单位1. 进程&#xff1a;独立的执行环境1.1 进程的特点&#xff1a; 2. 线程&#xff1a;轻量级的执行单元2.1 线程的特点&#xff1a; 3. 区别和联系4. 表格 进程与线程&#xff1a;详细解析计算机的基本运作单位 在…

AsConvSR | NTIRE2023-RTSR-Track1冠军方案

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/p9u6RYkd37MmN12rUCMCuQ 前段时间&#xff0c;NTIRE2023各个竞赛落下帷幕&#xff0c;近期各个冠亚军方案提出者也在逐步公开方案细节。今天给大家概要介绍一下"RTSR-Track1"赛道冠军方案&#xff…

【Leetcode每日一题】前缀和(难度⭐)(25)

1. 题目解析 题目链接&#xff1a;DP34 【模板】前缀和 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于计算题目所给区间数组元素和返回即可。 2. 算法原理 为了提高计算效率&#xff0c;我们可以预先计算出一个「前缀…

【NDK系列】Android tombstone文件分析

文件位置 data/tombstone/tombstone_xx.txt 获取tombstone文件命令&#xff1a; adb shell cp /data/tombstones ./tombstones 触发时机 NDK程序在发生崩溃时&#xff0c;它会在路径/data/tombstones/下产生导致程序crash的文件tombstone_xx&#xff0c;记录了死亡了进程的…

Appium移动端自动化测试-(Java)

目录 环境搭建ADB调试工具adb构成adb工作原理adb常用命令电脑连接多个设备跟模拟器使用adb包名与界面名的概念如何获取包名和界面名文件传输获取app启动时间获取手机日志其他命令 Appium全自动化测试框架&#xff08;python&#xff09;冲错了序言 环境搭建Appium客户端安装App…

【洛谷学习自留】p5707 上学迟到

解题思路&#xff1a; 1.先用给出的时间和速度&#xff08;如果无法整除&#xff0c;则时间加一&#xff09;&#xff0c;计算出时间&#xff08;分&#xff09;&#xff0c;然后将时间加上10分钟。 2.创建一个计时器&#xff0c;设置一个日期&#xff0c;保证时分秒部分&#…

【简说八股】Redisson的守护线程是怎么实现的

Redisson Redisson 是一个 Java 语言实现的 Redis SDK 客户端&#xff0c;在使用分布式锁时&#xff0c;它就采用了「自动续期」的方案来避免锁过期&#xff0c;这个守护线程我们一般也把它叫做「看门狗」线程。 Redission是一个在Java环境中使用的开源的分布式缓存和分布式锁实…

经典的算法面试题(1)

题目&#xff1a; 给定一个整数数组 nums&#xff0c;编写一个算法将所有的0移到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 注意&#xff1a;必须在原数组上操作&#xff0c;不能拷贝额外的数组。尽量减少操作次数。 这…

最长上升子序列(LIS)简介及其例题分析

一.最长上升子序列&#xff08;LIS&#xff09;的相关知识 1.最长上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;简称LIS&#xff0c;也有些情况求的是最长非降序子序列&#xff0c;二者区别就是序列中是否可以有相等的数。假设我们有一个序…

10.轮廓系数-机器学习模型性能的常用的评估指标

轮廓系数&#xff08;Silhouette Coefficient&#xff09;是评估聚类算法效果的常用指标之一。它结合了聚类的凝聚度&#xff08;Cohesion&#xff09;和分离度&#xff08;Separation&#xff09;&#xff0c;能够量化聚类结果的紧密度和分离度。 背景 1.聚类分析的背景 在…

操作系统(1)——学习导论(Ⅱ)

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/6grrU) 学习导论&#xff08;Ⅱ&#xff09;操作系统-赏前人佳作大型操作系统大型操作系统的一些特点和功能举例 服务器操作系统服务器操作系统特点和功能举例 多处理器操作系统举例 个人计算机操作系统举例 掌上计算机操作…

Jmeter接口测试---随机数、加密、cookie鉴权、断言、CSV参数化

随机数 第一步&#xff1a;选择工具-函数助手对话框 第二步&#xff1a;选择random&#xff0c;设置最大值最小值&#xff0c;复制函数字符串到指定位置 加密接口 类型&#xff1a;AES、DES、Base64、RSA&#xff08;可以解密&#xff09; | MD5、SHA、HmacSHA&#xff08;不…

【Linux系统化学习】线程概念

目录 线程的概念 线程的引出 什么是线程 理解线程比进程更加的轻量化 线程的优点 现成的缺点 线程异常 线程用途 Linux进程VS线程 线程的简单现象 线程的概念 有关操作系统的书籍或者课本都会这样描述线程&#xff1a; 线程是比进程轻量化的一种执行流线程是进程内部…

CogCaliperTool

关于visionpro工具的博客偏少&#xff0c;以下是本人自己查阅visionpro官方文档完成的&#xff08;注意标红的计分函数模式&#xff09;: 本主题介绍Caliper工具&#xff0c;这是一种视觉工具&#xff0c;可在图像的定义明确的区域内提供快速准确的图案检测和定位。 卡尺工具…

GPU 硬件与 CUDA 程序开发工具

GPU 硬件简介 从十多年前起&#xff0c;GPU 的浮点数运算峰值就比同时期的 CPU 高一个量级&#xff1b;GPU 的内存带宽峰值也比同时期的 CPU 高一个量级。 CPU 和 GPU 的显著区别是&#xff1a;一个典型的 CPU 拥有少数几个快速的计算核心&#xff0c;而一个典型的 GPU 拥有几…

考研复试类比社团招新,无所谓“公平”,导师选谁都是他的权力

这篇文章是抖音和b站上上传的同名视频的原文稿件&#xff0c;感兴趣的csdn用户可以关注我的抖音和b站账号&#xff08;GeekPower极客力量&#xff09;。同时这篇文章也为视频观众提供方便&#xff0c;可以更加冷静地分析和思考。文章同时在知乎发表。 我考研一战的时候计算机考…

Linux网络编程—— IO多路复用

Linux网络编程—— IO多路复用 1. I/O 多路复用&#xff08;I/O多路转接&#xff09;1.1 常见的几种I/O模型 2. select3. poll4. epoll :star: 1. I/O 多路复用&#xff08;I/O多路转接&#xff09; I/O 多路复用 使得程序能 同时监听 多个文件描述符&#xff0c;能够提高程序的…

kafka消费者重平衡是什么?怎么避免?

消费者重平衡是指主题下的分区怎么分配给消费者的过程。下面这个图可以看出该过程&#xff1a;原来有2个消费者&#xff0c;3个分区&#xff0c;其中一个消费者肯定就的处理2个分区了。那么当新加入消费者时&#xff0c;则每个消费者就只处理一个分区了。处理这个分区过程的叫协…

【HTML5】浏览器不能显示字体报错Failed to decode downloaded font问题解决

把网上的项目中字体通过链接保存下来在本地上使用&#xff0c;在本地服务器上运行站点发现&#xff0c;用Chrome浏览器访问的时候&#xff0c;出现错误提示不能正常显示字体&#xff0c;怎么解决呢&#xff0c;看看怎么搞。 文章目录 发现问题提示警告提示错误 字体检查打开文件…