前端学习笔记

前端学习笔记:

1.vue-router有几种工作模式

Vue Router 有三种主要的工作模式,分别是 hash 模式、history 模式 和 abstract 模式。每种模式的实现方式和应用场景不同,以下是详细介绍:

1. hash 模式

  • 特点

    • 使用 URL 中的 # 符号,类似于 http://example.com/#/home
    • # 后面的部分不会被发送到服务器,它仅在客户端生效,用来表示前端路由。
  • hash 模式是基于浏览器的 hashchange 事件,当 URL 中的 # 部分发生变化时,会触发页面的切换。

  • 优点

    • 不依赖于服务器的支持,因此兼容性最好。
    • 不需要服务器配置,适用于静态页面。
  • 缺点

    • URL 不够美观,因为会带有 #
  • 适用场景:适合简单的前端应用或不想对服务器进行额外配置的项目。

  • 使用方式
    Vue Router 默认就是 hash 模式,无需特别配置:

    const router = new VueRouter({
      mode: 'hash',
      routes: [...]
    });
    

2. history 模式

  • 特点

    • 使用现代浏览器提供的 HTML5 History API 来实现 URL 路由控制,类似于 http://example.com/home
    • 在这种模式下,URL 中不会带有 #,显得更加美观和直观。
  • 优点

    • URL 美观且符合 RESTful 风格。
    • 直接导航到某个 URL 时,仍然可以保持单页应用的效果。
  • 缺点

    • 需要后端服务器的支持。在刷新页面或直接访问某个 URL 时,服务器端需要将所有路由重定向到 index.html,否则会出现 404 错误。
  • 适用场景:适合现代的前后端分离项目或希望有干净 URL 的项目。

  • 使用方式
    配置 Vue Router 为 history 模式:

    const router = new VueRouter({
      mode: 'history',
      routes: [...]
    });
    
    • 服务器配置:因为 history 模式依赖服务器的配合,服务器需要配置将所有路由都指向 index.html

    Apache 配置

    <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteBase /
      RewriteRule ^index\.html$ - [L]
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule . /index.html [L]
    </IfModule>
    

    Nginx 配置

    location / {
      try_files $uri $uri/ /index.html;
    }
    

3. abstract 模式

  • 特点

    • abstract 模式并不依赖浏览器的 API,而是将路径的变更模拟为普通的 JavaScript 对象操作。
    • 这种模式通常用于非浏览器环境,例如 Node.js 服务端渲染或进行单元测试时。
  • 优点

    • 可以在不依赖浏览器的情况下模拟路由操作,适合测试或服务端渲染。
  • 缺点

    • 不能在浏览器中使用,只能用于非浏览器环境。
  • 适用场景:适用于服务端渲染(SSR)或测试环境。

  • 使用方式
    配置 Vue Router 为 abstract 模式:

    const router = new VueRouter({
      mode: 'abstract',
      routes: [...]
    });
    

总结:

  1. hash 模式:URL 中带有 #,兼容性好,不需要服务器配置,适合简单项目。
  2. history 模式:URL 美观,符合现代应用开发需求,但需要服务器配置支持。
  3. abstract 模式:用于非浏览器环境,适合服务端渲染或测试。

通常情况下,在浏览器应用中,hash 模式适合无需服务器配置的项目,而 history 模式则适合希望有更加美观 URL 的现代应用。

2.说说SPA

SPA 是一种通过在客户端动态加载和更新页面内容的 Web 应用架构。它通常只加载一次初始页面,之后的所有交互都在当前页面内通过 JavaScript 实现局部更新,不会重新加载整个页面。SPA 通过 AJAX 或 Fetch 请求服务器上的数据,并通过 JavaScript 将新内容注入页面的某些部分。前端框架如 Vue.js 和 React 等非常适合构建这种类型的应用,能够为用户提供更加流畅的体验。不过,SPA 的缺点在于首屏加载时间相对较长,特别是在应用体积较大的时候。此外,SEO 方面的支持也较为复杂,需要结合服务器端渲染(SSR)或预渲染技术来改善搜索引擎的可见性。简单的来说,SPA就相当与一个杯水,杯子里面可以换成牛奶咖啡等饮料,但是杯子不会变。

3.WebSocket心跳机制

WebSocket 心跳机制用于确保客户端和服务器之间的连接保持活跃,尤其是在长时间没有数据传输时。通常,WebSocket 是一种长连接协议,允许客户端和服务器持续通信。然而,如果长时间没有数据传输,网络环境或者服务器会认为连接已断开,可能会主动关闭连接。这时心跳机制就显得很重要。

心跳机制的实现步骤

心跳机制主要分为两部分:客户端发送心跳服务器响应心跳。可以通过定时发送“ping”消息来实现。

1. 客户端发送心跳包

客户端定时向服务器发送一个“ping”或自定义的心跳消息,服务器收到后返回一个“pong”或相应的响应。这种方式可以检测连接是否正常,并防止长时间没有消息传递时连接断开。

2. 服务器响应心跳包

服务器接收到客户端的心跳消息后,会返回一个响应消息,通知客户端连接正常。如果服务器长时间没有收到客户端的心跳包,可以认为客户端掉线,关闭连接。

客户端代码示例

下面是一个简单的基于 JavaScript 实现的 WebSocket 心跳机制代码示例:

const ws = new WebSocket('ws://yourserver.com');

let heartCheck = {
    timeout: 5000, // 心跳间隔时间
    timeoutObj: null,
    serverTimeoutObj: null,
    
    // 重置心跳
    reset: function() {
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        this.start();
    },

    // 开始心跳
    start: function() {
        const self = this;
        this.timeoutObj = setTimeout(function() {
            // 向服务器发送心跳包
            ws.send('ping');
            // 如果超时没有收到响应,关闭 WebSocket 连接
            self.serverTimeoutObj = setTimeout(function() {
                ws.close(); // 关闭连接
            }, self.timeout);
        }, this.timeout);
    }
};

// 连接成功时启动心跳检测
ws.onopen = function() {
    console.log('WebSocket connected');
    heartCheck.start();  // 启动心跳
};

// 收到服务器消息
ws.onmessage = function(event) {
    console.log('Received from server: ', event.data);
    
    // 如果收到心跳响应,重置心跳机制
    if (event.data === 'pong') {
        heartCheck.reset(); 
    }
};

// 连接关闭时
ws.onclose = function() {
    console.log('WebSocket closed');
};

// 连接发生错误时
ws.onerror = function() {
    console.log('WebSocket error');
};

服务器端处理心跳

服务器端需要检测“ping”消息,并返回“pong”作为响应。以下是一个简单的 Node.js WebSocket 服务器示例:

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function(ws) {
    ws.on('message', function(message) {
        console.log('Received from client: %s', message);

        // 如果收到的是心跳包,则返回心跳响应
        if (message === 'ping') {
            ws.send('pong');
        }
    });

    ws.on('close', function() {
        console.log('Client disconnected');
    });
});

心跳机制的优势

  • 保持连接活跃:定期发送心跳包,避免 WebSocket 长时间没有数据传输导致连接超时断开。
  • 检测连接状态:客户端和服务器可以通过心跳检测连接是否还在维持,并在必要时重新建立连接。
  • 减少资源浪费:如果检测到连接异常,可以及时关闭连接,避免资源浪费。

注意事项

  • 心跳频率:心跳包发送的间隔时间不宜过短,以免增加服务器和网络的负载;也不宜过长,以确保及时检测连接状态。
  • 异常处理:在 WebSocket 断开时,应该有重连机制,确保客户端能自动重新建立连接。

4.虚拟DOM

基本概念

基本上所有框架引入了虚拟 DOM 来对真实 DOM 进行抽象,也就是现在大家所熟知的 VNode 和 VDOM。

  • Virtual DOM 就是用 js 对象来描述真实 DOM,是对真实 DOM 的抽象,由于直接操作 DOM 性能低但是 js 层的操作效率高,可以将 DOM 操作转化成对象操作,最终通过 diff 算法比对差异进行更新 DOM(减少了对真实 DOM 的操作)。

  • 虚拟 DOM 不依赖真实平台环境从而可以实现跨平台。

VDOM 是如何生成的?

  • 在 Vue 中我们常会为组件编写模板 - template
  • 这个模板会被编译器编译为渲染函数 - render
  • 在接下来的挂载过程中会调用 render 函数,返回的对象就是虚拟 dom
  • 会在后续的 patch 过程中进一步转化为真实 dom。

VDOM 如何做 diff 的?

  • 挂载过程结束后,会记录第一次生成的 VDOM - oldVnode
  • 当前响应式数据发生变化时,将会引起组件重新 render,此时就会生成新的 VDOM - newVnode
  • 使用 oldVnode 与 newVnode 做 diff 操作,将更改的部分应用到真实 DOM 上,从而转换为最小量的 dom 操作,高效更新视图。

5.Vue2/Vue3响应式原理

Vue 2 和 Vue 3 的响应式系统核心分别是基于 Object.definePropertyProxy 实现的。

Vue 2 响应式实现

实现原理:
  1. 对每个对象的属性进行数据劫持。
  2. 使用 Dep 来收集依赖,在数据发生变化时通知依赖更新。
// 模拟依赖收集类 Dep
class Dep {
  constructor() {
    this.subscribers = [];
  }
  // 添加订阅者
  addSub(sub) {
    this.subscribers.push(sub);
  }
  // 通知所有订阅者更新
  notify() {
    this.subscribers.forEach(sub => sub.update());
  }
}
// 模拟观察者 Watcher
class Watcher {
  constructor(obj, key, callback) {
    this.obj = obj;
    this.key = key;
    this.callback = callback;
    // 将当前 watcher 实例指向全局 Dep.target,用于依赖收集
    Dep.target = this;
    // 触发 getter 进行依赖收集
    this.value = obj[key];
    Dep.target = null;
  }
  update() {
    const newValue = this.obj[this.key];
    const oldValue = this.value;
    if (newValue !== oldValue) {
      this.value = newValue;
      this.callback(newValue);
    }
  }
}
// 模拟 Vue 2 响应式定义函数
function defineReactive(obj, key) {
  let value = obj[key];
  const dep = new Dep();

  Object.defineProperty(obj, key, {
    get() {
      // 收集依赖
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        value = newValue;
        // 通知依赖更新
        dep.notify();
      }
    }
  });
}

// 模拟 Vue 2 的响应式处理
function reactive(obj) {
  Object.keys(obj).forEach(key => defineReactive(obj, key));
}

// 测试 Vue 2 响应式实现
const data = { name: 'Vue2' };
reactive(data);

// 创建 Watcher,监听 name 属性
new Watcher(data, 'name', (newValue) => {
  console.log('name 变化为:', newValue);
});

// 修改响应式数据
data.name = 'Vue.js';
data.name = 'Vue 2.x';
说明:
  • defineReactive 是响应式的核心,它使用 Object.defineProperty 劫持对象的属性,创建 getter 和 setter。
  • Dep 是依赖管理器,用于存储依赖(如 Watcher)。
  • Watcher 观察某个属性的变化,一旦数据改变,调用 update 函数。

Vue 3 响应式实现

实现原理:
  1. 使用 Proxy 来代理对象,并通过 getset 捕获对象的读写操作。
  2. 使用 tracktrigger 来进行依赖收集和通知更新。
// 模拟 Vue 3 中的依赖收集
const targetMap = new WeakMap();

function track(target, key) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Set();
    depsMap.set(key, dep);
  }
  dep.add(activeEffect);
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  let dep = depsMap.get(key);
  if (dep) {
    dep.forEach(effect => effect());
  }
}

// 模拟 Vue 3 中的 reactive
function reactive(target) {
  return new Proxy(target, {
    get(obj, key) {
      const result = Reflect.get(obj, key);
      // 收集依赖
      track(obj, key);
      return result;
    },
    set(obj, key, value) {
      const result = Reflect.set(obj, key, value);
      // 触发依赖
      trigger(obj, key);
      return result;
    }
  });
}

// 模拟 Vue 3 中的 effect
let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

// 测试 Vue 3 响应式实现
const data = reactive({ name: 'Vue3' });

// 注册 effect,监听 name 变化
effect(() => {
  console.log('name 变化为:', data.name);
});

// 修改响应式数据
data.name = 'Vue.js';
data.name = 'Vue 3.x';
说明:
  • Proxy 代理对象,可以监听属性的所有操作(包括读、写、删除等)。
  • track 用于依赖收集,将依赖保存到 targetMap 中。
  • trigger 用于触发依赖,遍历依赖并执行。
  • effect 函数自动执行,并在其中访问响应式数据时进行依赖收集。

总结

  • Vue 2 使用 Object.defineProperty 实现响应式,只能劫持已有的属性,不能检测新增或删除的属性。
  • Vue 3 使用 Proxy,能够全面代理对象,监听所有操作(包括属性新增、删除、数组索引操作等)。
  • Vue 2 的实现有更多限制,需要手动处理数组和新增属性,而 Vue 3 的实现更加灵活和高效。

6.Vue2是怎么解决响应式的缺陷

在 Vue 2 中,由于 Object.defineProperty 无法直接监听对象属性的新增和删除,因此 Vue 2 采取了一些补救措施来处理这些场景。

1. 使用 $set 方法监听新增属性

Vue 2 提供了一个全局方法 Vue.set(或者在组件实例中使用 this.$set)来处理对象属性的新增。$set 方法会确保新增的属性也是响应式的。

示例:

const vm = new Vue({
  data() {
    return {
      person: {
        name: 'John',
      },
    };
  },
});

// 直接新增属性,Vue 2 无法检测
vm.person.age = 30; // 不是响应式的,无法触发视图更新

// 使用 $set 方法新增属性,Vue 2 可以检测到
vm.$set(vm.person, 'age', 30); // 响应式,视图会更新
原理:

$set 方法会为新增的属性重新定义 getter 和 setter,从而实现对新增属性的响应式监听。底层实际上是通过调用 defineReactive 方法对新增的属性进行响应式处理。

Vue.set = function (target, key, value) {
  if (Array.isArray(target) && typeof key === 'number') {
    // 如果是数组,直接使用 splice 方法来修改数组
    target.length = Math.max(target.length, key);
    target.splice(key, 1, value);
    return value;
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = value;
    return value;
  }
  // 处理对象,使用 defineReactive 让新增属性成为响应式的
  defineReactive(target, key, value);
  target.__ob__.dep.notify(); // 手动触发依赖更新
  return value;
};
2. 使用 $delete 方法删除属性

类似地,Vue 2 提供了 $delete 方法用于删除对象属性,并确保在删除时触发视图的更新。

示例:

const vm = new Vue({
  data() {
    return {
      person: {
        name: 'John',
        age: 30,
      },
    };
  },
});

// 直接删除属性,Vue 2 无法检测
delete vm.person.age; // 不是响应式的,无法触发视图更新

// 使用 $delete 方法删除属性,Vue 2 可以检测到
vm.$delete(vm.person, 'age'); // 响应式,视图会更新
原理:

$delete 方法不仅删除了对象的属性,还会触发依赖的通知,从而更新视图。

Vue.delete = function (target, key) {
  if (Array.isArray(target) && typeof key === 'number') {
    // 如果是数组,使用 splice 删除
    target.splice(key, 1);
    return;
  }
  if (!target.hasOwnProperty(key)) {
    return;
  }
  // 删除属性
  delete target[key];
  if (!target.__ob__) {
    return;
  }
  // 手动触发依赖更新
  target.__ob__.dep.notify();
};

总结

  • 新增属性:Vue 2 通过 Vue.setthis.$set 来实现响应式,可以手动添加新属性并使其响应式。
  • 删除属性:Vue 2 通过 Vue.deletethis.$delete 来实现删除属性并通知视图更新。
  • Vue 2 的依赖收集: 通过 Object.defineProperty 劫持对象属性的 gettersetter,当属性值被读取时进行依赖收集,当属性值发生变化时通知依赖进行更新。
  • Vue 3 的依赖收集: 使用 Proxy 代理对象的读取和修改操作,通过 track 函数收集依赖,通过 trigger 函数触发依赖更新。

虽然这种方式是 Vue 2 的补救措施,但相比 Vue 3 的 Proxy 来说,它相对较为复杂。Vue 3 通过 Proxy 天然支持对象属性的新增和删除,因此不需要额外的 $set$delete 方法。

7.vue2中如何检测数组变化的

在 Vue 2 中,虽然 Object.defineProperty 可以劫持对象属性来实现响应式,但对于数组,由于 Object.defineProperty 只能劫持对象的属性,而不能直接监听数组下标或其原型链的变化,因此 Vue 2 使用了对数组的原型方法进行重写的方式来监听数组的变化。

Vue 2 中检测数组变化的方式

Vue 2 对数组的常用变更方法进行了重写,以确保数组在发生变更时可以触发依赖更新。

1. 重写数组的变更方法

Vue 2 对数组的原型方法进行拦截,重写了 7 个能够修改数组内容的方法:

当使用这些方法修改数组时,Vue 2 能够检测到数组发生了变化,并通知相关依赖进行更新。

const arrayProto = Array.prototype; // 保存数组的原型
const arrayMethods = Object.create(arrayProto); // 创建数组方法的拦截对象

['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function(method) {
  const original = arrayProto[method]; // 保存原始的数组方法
  arrayMethods[method] = function mutator(...args) {
    const result = original.apply(this, args); // 调用原始数组方法
    const ob = this.__ob__; // 获取 Observer 实例

    let inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break;
      case 'splice':
        inserted = args.slice(2);
        break;
    }

    if (inserted) ob.observeArray(inserted); // 对新插入的元素进行响应式处理
    ob.dep.notify(); // 手动触发依赖更新,通知视图更新
    return result;
  };
});
2. 将重写的方法应用到数组

当 Vue 2 将一个数组变成响应式时,数组会被绑定到重写后的方法集合上,这样 Vue 2 就能拦截数组的变更操作。

function observe(value) {
  if (Array.isArray(value)) {
    protoAugment(value, arrayMethods); // 将重写后的数组方法绑定到数组实例上
    observeArray(value); // 遍历数组元素,将每个元素变成响应式
  } else {
    // 对象的响应式处理
    walk(value);
  }
}

protoAugment 是用来改变数组的原型指向,使其使用 Vue 重写的数组方法:

function protoAugment(target, src) {
  target.__proto__ = src; // 将 target 数组的原型指向重写后的 arrayMethods
}

通过这种方式,Vue 2 可以拦截数组的修改方法,从而检测到数组的变化并触发视图更新。

3. 数组的索引和长度的变化
  • 索引变化:Vue 2 无法通过直接修改数组的索引来触发响应式更新。例如,直接使用 vm.items[1] = newItem 这样的方式无法监听到数组的变化。要触发更新,需要使用 Vue.set() 方法:

    Vue.set(vm.items, 1, newItem); // 使数组索引的变化变为响应式
    
  • 长度变化:同样地,Vue 2 无法直接监听数组长度的变化。如果通过设置 arr.length 的方式截断数组,Vue 2 无法自动检测变化并更新视图。

总结

  • Vue 2 重写了数组的 7 个方法(如 pushsplice 等),以确保 Vue 能够检测到数组的变更并更新视图。
  • 直接修改数组的索引和长度不会触发视图更新,必须使用 Vue.set 来设置新的数组元素或修改数组长度。
  • 这种方式虽然有效,但相对比较有限和繁琐,Vue 3 使用 Proxy 直接代理数组对象,从而更为灵活和强大。

8.如何封装组件

封装组件是 Vue 开发中的重要部分,能够使代码更加模块化、可重用。

1. 基本组件封装

首先创建一个 Vue 组件的基本结构。以下是一个简单的封装步骤:

(1) 创建组件文件

src/components 目录下创建一个新的组件文件,比如 MyButton.vue

<template>
  <button @click="handleClick" class="my-button">
    <slot></slot>
  </button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    // 接受父组件传递的参数,控制按钮是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    handleClick() {
      // 点击事件
      if (!this.disabled) {
        this.$emit('click'); // 向父组件触发自定义事件
      }
    },
  },
};
</script>

<style scoped>
.my-button {
  padding: 10px;
  background-color: #42b983;
  border: none;
  color: white;
  cursor: pointer;
}
.my-button:disabled {
  background-color: grey;
  cursor: not-allowed;
}
</style>
(2) 使用组件

在父组件中使用这个 MyButton 组件。

  • 首先,确保你在父组件中导入并注册该组件:
<template>
  <div>
    <MyButton @click="handleButtonClick" :disabled="isDisabled">
      Click Me
    </MyButton>
  </div>
</template>

<script>
import MyButton from './components/MyButton.vue';

export default {
  components: {
    MyButton,
  },
  data() {
    return {
      isDisabled: false,
    };
  },
  methods: {
    handleButtonClick() {
      alert('Button Clicked!');
    },
  },
};
</script>

2. 封装更复杂的组件

封装更复杂的组件时,你可以通过以下方式实现组件的可重用性和灵活性:

(1) 组件的动态内容

使用 <slot> 可以让组件变得更灵活。slot 允许父组件在使用组件时插入自定义的内容。上面的例子中,我们在 <button> 标签中使用了 <slot></slot>,这样父组件可以在 MyButton 中传递按钮文本。

(2) Props 和事件
  • Props: 通过 props,组件可以接收父组件传递的数据。
  • 自定义事件: 组件可以使用 this.$emit 来向父组件发送事件,如点击事件、输入事件等。
(3) 复合组件

如果组件较为复杂,可以将它分解为多个小的组件,然后通过父组件组合使用。例如,一个表单组件可以由多个输入组件和一个按钮组件组成。

<template>
  <div>
    <MyInput v-model="formData.username" placeholder="Username" />
    <MyInput v-model="formData.password" type="password" placeholder="Password" />
    <MyButton @click="submitForm">Submit</MyButton>
  </div>
</template>

<script>
import MyInput from './MyInput.vue';
import MyButton from './MyButton.vue';

export default {
  components: {
    MyInput,
    MyButton,
  },
  data() {
    return {
      formData: {
        username: '',
        password: '',
      },
    };
  },
  methods: {
    submitForm() {
      // 表单提交逻辑
      console.log(this.formData);
    },
  },
};
</script>

3. 使用 emitv-model 双向绑定

实现 v-model 的封装

假如你想封装一个自定义的输入框组件,并且支持 v-model

<template>
  <input :value="value" @input="handleInput" />
</template>

<script>
export default {
  name: 'MyInput',
  props: {
    value: String,
  },
  methods: {
    handleInput(event) {
      this.$emit('input', event.target.value); // 实现双向绑定
    },
  },
};
</script>

在父组件中,你可以这样使用:

<template>
  <div>
    <MyInput v-model="username" />
    <p>Username: {{ username }}</p>
  </div>
</template>

<script>
import MyInput from './components/MyInput.vue';

export default {
  components: {
    MyInput,
  },
  data() {
    return {
      username: '',
    };
  },
};
</script>

4. 组件封装的最佳实践

  • 单一职责: 尽量保持每个组件的功能单一,专注于完成一项任务。
  • 复用性: 使用 propsemitslot 等手段,保证组件能够灵活复用。
  • 组合组件: 如果组件较为复杂,可以将组件拆分成多个子组件。

9.Vue2 和 Vue3 的主要区别

双向绑定机制

  • Vue2:使用 Object.defineProperty() 实现响应式,后添加的属性无法被自动劫持,需要手动调用 $set
  • Vue3:使用 Proxy 来代理对象,支持深层次监听,可以自动响应新增的属性,不需要手动调用 $set

$set 的变动

  • Vue2 中,新增属性需要使用 $set 手动添加以确保响应式生效。
  • Vue3 使用 Proxy,不再需要 $set,可以自动响应属性变化。

API 写法

  • Vue2 采用的是 选项式 API(Options API),组件内代码按功能划分(如 datamethodscomputed 等)。
  • Vue3 引入了 组合式 API(Composition API),通过 setup 函数来组织代码,使逻辑和数据能够按功能模块划分,更加清晰、可维护。

指令优先级

  • Vue2 中,v-for 的优先级高于 v-if
  • Vue3 中,v-if 的优先级高于 v-for,这提高了性能。

ref 和 $children 的变化

  • Vue3 中,$ref 的使用需要通过 ref() 声明,而不是自动挂载到 this.$refs
  • $children 在 Vue3 中变化较大,鼓励使用 provide/inject 或者 emit 来实现组件通信。

更好的 TypeScript 支持

  • Vue3 通过重写的架构提供了对 TypeScript 的原生支持,使得类型推断和类型检查更加简单和高效。

10.Vue3 中如何使用 setup 组织代码?

在 Vue3 中,setup 函数是组合式 API 的核心。为了让项目维护更容易,通常会使用 Hooks 来细化功能模块。

// example of organizing code in Vue 3 setup
<script setup>
import { ref, onMounted } from 'vue';

// Using hooks to organize logic
const useFetchData = () => {
  const data = ref(null);
  onMounted(async () => {
    data.value = await fetchData();
  });
  return data;
};

const data = useFetchData();
</script>

使用 Hooks(函数式编写),使代码更加模块化,提高可读性和复用性。

11.Vue3 中如何获取类似 Vue2 中的 this

在 Vue3 的 setup 函数中没有直接的 this,可以通过 getCurrentInstance 获取当前组件的实例来访问类似 this 的属性。

import { getCurrentInstance } from 'vue';

const instance = getCurrentInstance();
console.log(instance.proxy); // proxy相当于this

12.Vue3 常用 API 介绍

  1. createApp:创建一个应用实例,等同于 Vue2 中的 new Vue()
  2. provide/inject:用于跨层级的组件间通信,父组件提供数据,后代组件注入。
  3. directive:自定义指令,如权限控制等。
  4. app.config.globalProperties:在全局范围内添加方法或属性,类似 Vue2 中的 Vue.prototype
  5. nextTick:等待 DOM 更新完成的异步方法。
  6. computed:计算属性,具有缓存功能。
  7. reactiveref:定义响应式数据,reactive 用于对象,ref 用于基本类型。
  8. watch:用于监听数据的变化。
  9. markRaw:标记某个对象为静态,Vue 不对它进行响应式代理。
  10. definePropsdefineEmits:在 setup 函数中使用,用来接收父组件传递的值和自定义事件。

13.Vue3 常用的响应式数据类型

  1. ref:用于定义基本类型的响应式数据。
    const count = ref(0);
    
  2. reactive:用于定义对象或数组等复杂类型的响应式数据。
    const state = reactive({ name: 'Vue' });
    
  3. toRef:将一个对象的某个属性单独解构为一个 ref
    const nameRef = toRef(state, 'name');
    
  4. toRefs:将整个对象的属性全部解构为 ref
    const { name, age } = toRefs(state);
    

14.Teleport 组件及其使用场景

Teleport 是 Vue3 中新增的一个功能,用于将组件渲染到 DOM 树中的指定位置,而不是默认的父组件内部。这在一些特定场景下非常有用,比如模态框、弹窗等需要在页面顶部或其他全局区域显示的元素。

使用场景

  • 渲染全局的模态框、提示框等。
  • 将组件渲染到特定的容器中,而非父组件中。

使用示例

<template>
  <teleport to="body">
    <div class="modal">
      <h2>Modal Content</h2>
    </div>
  </teleport>
</template>

在这个示例中,虽然 teleport 被定义在某个组件内,但它会将其内容渲染到 body 标签下。

15.vue-router,history模式下需要后台配置,后台如何配置,如何监听url变化

在使用 vue-routerhistory 模式时,路由会使用 HTML5 的 history.pushStatehistory.replaceState 方法来管理路由,这样 URL 就不会再带上 # 符号。然而,这种模式下需要确保后端能够正确处理所有的路由请求,以便在用户直接访问某个 URL 时,能返回正确的页面。

后台配置

假设你使用的是 Node.jsExpress 框架,下面是一个简单的配置示例:

const express = require('express');
const path = require('path');

const app = express();

// Serve static files from the Vue app
app.use(express.static(path.join(__dirname, 'dist')));

// Handle all routes
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

解释

  1. 静态文件服务:使用 express.static 中间件来提供静态文件,这里假设你的 Vue 应用已经构建并放在 dist 目录中。
  2. 路由处理:使用 app.get('*', ...) 捕获所有路由请求,并返回 index.html。这确保了所有的路由请求都会加载 Vue 应用的入口文件。
  3. 启动服务器:设置服务器监听特定的端口。

URL 变化监听

在 Vue 应用中,可以使用 vue-router 提供的 watch 来监听路由的变化。以下是一个示例:

<template>
  <div>
    <h1>当前路由:{{ $route.path }}</h1>
  </div>
</template>

<script>
export default {
  watch: {
    '$route'(to, from) {
      // 当路由变化时执行的逻辑
      console.log('路由变化', from.path, '到', to.path);
    },
  },
};
</script>

解释

  • 在 Vue 组件中,使用 watch 监听 $route 对象。当路由变化时,$route 会更新,从而触发对应的回调函数。
  • tofrom 参数可以用来获取新旧路由的信息。

总结

vue-routerhistory 模式下,后端需要配置以正确处理所有的路由请求,通常是在后端返回应用的入口文件。同时,在 Vue 应用中可以通过监听 $route 的变化来响应路由的变化。这样就能实现无缝的 SPA 路由管理。

16.this的指向和绑定规则

在 JavaScript 中,this 关键字是一个重要的概念,它的值会根据不同的上下文而变化。理解 this 的指向和绑定规则对于编写正确和高效的 JavaScript 代码至关重要。下面是一些主要的规则和概念:

1. 全局上下文

在全局上下文中,this 指向全局对象。在浏览器中,这个全局对象是 window

console.log(this); // 在浏览器中输出 window 对象

2. 函数调用

在普通函数中,this 的指向取决于函数如何被调用:

  • 在非严格模式下,如果直接调用函数,this 将指向全局对象。
function show() {
    console.log(this);
}
show(); // 输出 window 对象(在浏览器中)
  • 在严格模式下,this 的值为 undefined
"use strict";
function show() {
    console.log(this);
}
show(); // 输出 undefined

3. 对象的方法

当函数作为对象的方法调用时,this 指向该对象。

const obj = {
    name: 'Alice',
    greet: function() {
        console.log(this.name);
    }
};

obj.greet(); // 输出 "Alice"

4. 构造函数

在构造函数中,this 指向新创建的实例。

function Person(name) {
    this.name = name;
}

const person1 = new Person('Bob');
console.log(person1.name); // 输出 "Bob"

5. 箭头函数

箭头函数不绑定 this,它会从外部上下文中继承 this 的值。这意味着,this 的指向是固定的,不会随调用方式而改变。

const obj = {
    name: 'Charlie',
    greet: () => {
        console.log(this.name); // 这里的 this 指向全局对象,而不是 obj
    }
};

obj.greet(); // 输出 undefined (在浏览器中,this.name 是 undefined)

6. call, apply, bind 方法

  • callapply 方法用于改变函数内部 this 的指向。
    • call 接受参数列表。
    • apply 接受一个数组作为参数。
function show() {
    console.log(this.name);
}

const obj = { name: 'David' };

show.call(obj); // 输出 "David"
show.apply(obj); // 输出 "David"
  • bind 方法返回一个新的函数,并永久绑定 this 的值。
const boundShow = show.bind(obj);
boundShow(); // 输出 "David"

7. DOM 事件

在事件处理函数中,this 通常指向触发事件的元素。

const button = document.getElementById('myButton');
button.addEventListener('click', function() {
    console.log(this); // 输出触发事件的按钮元素
});

总结

  • this 的指向取决于调用上下文。
  • 全局上下文中的 this 指向全局对象。
  • 函数作为对象的方法调用时,this 指向该对象。
  • 构造函数中的 this 指向新创建的实例。
  • 箭头函数不绑定 this,而是继承外部上下文的 this
  • callapplybind 方法可以用于明确绑定 this 的值。

17.v-model的原理

v-model 是 Vue 中的双向数据绑定的语法糖,用于简化表单输入与数据的同步。它背后其实是通过事件监听和数据更新机制来实现的。

基本思路

  • 数据到视图:初始时,数据会被设置到视图(比如输入框)。
  • 视图到数据:当用户输入变化时,监听输入事件并更新数据。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Manual v-model</title>
</head>
<body>
    <div id="app">
        <input type="text" id="myInput">
        <p>你输入的内容是: <span id="output"></span></p>
    </div>

    <script>
        // 模拟 Vue 的 data
        const appData = {
            text: '初始内容' // 双向绑定的内容
        };

        // 获取输入框和显示区域
        const input = document.getElementById('myInput');
        const output = document.getElementById('output');

        // 1. 将数据渲染到视图 (数据 => 视图)
        input.value = appData.text;
        output.innerText = appData.text;

        // 2. 监听输入框变化,更新数据 (视图 => 数据)
        input.addEventListener('input', function(event) {
            appData.text = event.target.value; // 更新数据
            output.innerText = appData.text;   // 更新显示
        });

    </script>
</body>
</html>

解释

  1. 初始数据渲染:我们定义了一个简单的 appData 对象,模拟 Vue 的 data,其中 text 属性是我们希望双向绑定的数据。

  2. 数据到视图:我们在页面加载时,将 appData.text 的值设置到输入框 (input.value = appData.text) 和显示区域 (output.innerText = appData.text) 中。

  3. 视图到数据:当用户在输入框中输入内容时,我们通过 input.addEventListener('input', ...) 监听输入事件。每次用户输入时,我们将输入框的值更新到 appData.text,并且同时更新显示的内容。

    这就实现了视图和数据的双向绑定。

Vue 中的 v-model

在 Vue 中,v-model 实现的原理类似,底层做了以下两件事:

  1. 绑定数据到 value 属性:相当于 <input :value="text">
  2. 监听输入事件,更新数据:相当于 <input @input="text = $event.target.value">

Vue 的 v-model 让这个过程更加简洁,简化了我们手动监听和更新数据的代码。

18. vue 对数组的方法做了重写的操作,如何实现对 vue2 中对数组操作的 push()方法

在 Vue 2 中,Vue 对数组的变更检测采用了数据劫持的方式来监听数组的变化。在 Vue 2 中,数组的部分变异方法(例如 push()pop()shift()unshift()splice() 等)被重写,以确保 Vue 可以检测到数组的变更并触发视图更新。

Vue 2 的响应式系统基于 Object.defineProperty,而对数组的变异方法(如 push)进行拦截和重写,主要是为了能够监听数组的增删改操作,从而保证在操作数组时能正确地更新视图。

接下来,我们将通过示例来实现类似 Vue 2 对 push() 方法的重写操作。

实现对数组 push() 方法的重写

1. 思路
  • 我们需要劫持数组的 push 方法。
  • 在执行 push() 操作时,除了进行数据的插入,还要触发响应式系统,使得 Vue 能够感知到数据发生了变化。
  • Vue 通过代理数组的原型方法来实现这一功能。
2. 实现代码
// 保存数组原型方法
const arrayProto = Array.prototype;

// 创建一个新的对象,继承自数组的原型
const arrayMethods = Object.create(arrayProto);

// 重写 push 方法
arrayMethods.push = function (...args) {
  console.log('数组发生了 push 操作,新增了元素:', args);
  
  // 调用原始的 push 方法,保证数据正常插入
  const result = arrayProto.push.apply(this, args);
  
  // 模拟 Vue 的响应式通知,触发视图更新
  // 在实际的 Vue 中,这里是调用 Vue 内部的 observer 方法来通知视图更新
  console.log('触发视图更新');
  
  return result;
};

// 创建一个观察者函数,用于给数组的每个元素添加响应式处理
function observeArray(arr) {
  arr.__proto__ = arrayMethods; // 将数组的原型指向我们重写的 arrayMethods
}

// 使用
const arr = [1, 2, 3];
observeArray(arr); // 劫持数组操作

// 使用 push 添加元素
arr.push(4);
arr.push(5);

/* 输出:
  数组发生了 push 操作,新增了元素: [4]
  触发视图更新
  数组发生了 push 操作,新增了元素: [5]
  触发视图更新
*/
3. 代码说明
  • arrayProto:保存数组的原型方法,目的是为了调用原始的 push 方法,保证数组的操作不被破坏。
  • arrayMethods:创建一个新的对象,这个对象继承自数组的原型,并重写了 push 方法。在该方法中,我们先执行原生的 push 操作,再手动触发视图更新(在实际的 Vue 中,是触发依赖的更新)。
  • observeArray:这是一个模拟 Vue 观察数组的方法,它将数组的原型指向我们重写的 arrayMethods,从而使得数组的操作能够被拦截。
4. Vue 2 中的实现原理

在 Vue 2 中,Vue 通过拦截数组的变异方法(如 push)来实现对数组的响应式监听。Vue 重写了这些变异方法,并在这些方法内部调用了依赖的更新通知。

在 Vue 2 中,当我们调用数组的 push 等方法时,Vue 会执行以下操作:

  1. 重写数组变异方法:Vue 重写了 pushpopshiftunshiftsplice 等方法,并在这些方法中插入了依赖收集和派发更新的逻辑。
  2. 触发视图更新:当数组发生变更时,Vue 会触发依赖的更新,进而重新渲染视图。

通过这种方式,Vue 2 可以保证在我们对数组进行增删改操作时,视图也能自动更新。

19.原型和原型链

原型与原型链

1. 原型(Prototype)

在 JavaScript 中,每个函数对象都有一个特殊的属性叫做 prototype,这个属性是一个对象。它的作用是:当我们通过某个构造函数创建一个实例对象时,该实例对象会自动关联到这个 prototype 对象上。通过这个 prototype 对象,实例对象可以共享构造函数的属性和方法。

  • 构造函数:是一个用于创建对象的函数。当你使用 new 关键字来调用一个函数时,它就是一个构造函数。
  • prototype 属性:当一个函数被定义时,系统会自动为它创建一个 prototype 属性,并指向一个对象,这个对象通常叫做“原型对象”。
示例:
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const person1 = new Person('Alice');
person1.greet();  // Hello, my name is Alice

在上面的代码中,Person 是构造函数,它有一个 prototype 属性(即 Person.prototype),这个属性指向一个对象,该对象包含了 greet 方法。通过 new Person('Alice') 创建的 person1,它可以访问 greet 方法,这是因为 person1 的原型指向了 Person.prototype

2. 原型链(Prototype Chain)

原型链是由对象的原型属性(__proto__)连接起来的一系列对象的链条。每个对象都有一个隐藏的属性 __proto__,指向它的构造函数的 prototype。当你访问一个对象的属性时,JavaScript 引擎会首先查找这个对象本身是否有该属性。如果没有,它会继续在该对象的原型(即 __proto__ 指向的对象)中查找,直到找到该属性或到达原型链的末尾(即 null)。

原型链是 JavaScript 实现继承的基础。当一个对象访问某个属性或方法时,会沿着原型链逐级查找,直到找到该属性或方法为止。

示例:
function Animal(type) {
  this.type = type;
}

Animal.prototype.move = function() {
  console.log(`${this.type} is moving`);
};

function Dog(name) {
  this.name = name;
}

// Dog继承Animal的原型
Dog.prototype = new Animal('dog');

const myDog = new Dog('Buddy');
myDog.move();  // dog is moving

在这个例子中,myDogDog 的实例,但是它也继承了 Animal 的方法。这是因为 Dog.prototype 指向了一个 Animal 的实例,而 myDog.__proto__ 指向 Dog.prototype,最终通过原型链找到了 Animalmove 方法。

3. 原型链的查找过程

当访问 myDog.move() 时,浏览器引擎的查找顺序如下:

  1. 首先查找 myDog 自身是否有 move 方法。
  2. 如果没有,则沿着原型链查找 myDog.__proto__,也就是 Dog.prototype
  3. 再沿着 Dog.prototype.__proto__ 查找 Animal.prototype,发现 move 方法并执行。
  4. 如果 Animal.prototype 上没有找到,则继续查找 Object.prototype,这是所有对象的原型的顶层。
  5. 最终,如果原型链的顶层 Object.prototype 也没有找到,返回 undefined
4. 原型链终点

在原型链中,所有对象的原型最终都会指向 Object.prototype,而 Object.prototype.__proto__null,这是原型链的终点。

console.log(Object.prototype.__proto__);  // null
5. 继承中的原型链

当你使用构造函数继承时,实例对象可以继承父类构造函数的 prototype 上的属性和方法。原型链在继承机制中发挥了重要作用,构造函数的 prototype 会成为实例对象的原型,从而实现继承。

示例:
function Animal() {}
Animal.prototype.eat = function() {
  console.log("Eating");
};

function Dog() {}
Dog.prototype = new Animal();

const dog = new Dog();
dog.eat();  // Eating

这里 Dog.prototype 被设置为 Animal 的实例对象,因此 dog 可以访问 Animal.prototype 上的 eat 方法,这就是通过原型链实现的继承。

6. 总结
  • 每个 JavaScript 对象都有一个原型(__proto__),指向它的构造函数的 prototype 对象。
  • 原型链是一系列对象的连接,用于属性和方法的继承查找。
  • 当查找对象的属性时,JavaScript 引擎会沿着原型链逐级查找,直到找到该属性或者到达原型链的终点。
  • 通过原型链可以实现 JavaScript 的继承机制。

20.简述 Vue 的基本原理

Vue 的基本原理可以分为以下几个核心概念:数据响应式系统虚拟 DOM模板编译组件化。这些概念构成了 Vue 的工作机制,使其能够实现高效的、双向绑定的数据驱动视图更新。

1. 数据响应式系统

Vue 的核心是数据响应式系统,它通过“观察者模式”来实现数据和视图的同步更新。当数据发生变化时,视图会自动更新;同样,当视图改变时,数据也会相应更新。

  • Vue 使用 Object.defineProperty()(Vue 2)或 Proxy(Vue 3)来对数据进行“劫持”,即当对象的属性被访问或修改时,Vue 能捕捉到这些操作并通知依赖这个数据的组件进行重新渲染。
数据响应原理
  1. 数据劫持:Vue 通过 Object.definePropertyProxy 劫持数据的读写操作,使得数据变化能够被监听。
  2. 依赖收集:当组件依赖的数据被读取时,Vue 会进行依赖收集,记录哪些组件或 DOM 依赖了这个数据。
  3. 派发更新:当数据发生变化时,Vue 会触发相应的更新机制,通知依赖该数据的组件或 DOM 进行重新渲染。
示例:
const app = new Vue({
  data: {
    message: 'Hello Vue'
  }
});

// 当 message 改变时,Vue 会自动更新页面中的相关部分
app.message = 'Hello World';

2. 虚拟 DOM

Vue 使用虚拟 DOM来进行高效的 DOM 更新。当数据发生变化时,Vue 不会直接操作真实的 DOM,而是先对比“新旧虚拟 DOM”的差异,然后只对那些变化的部分进行更新。这大大减少了直接操作真实 DOM 带来的性能损耗。

工作原理:
  1. 模板编译:Vue 将模板编译成虚拟 DOM。
  2. Diff 算法:当数据变化时,Vue 会通过 Diff 算法比较新旧虚拟 DOM,找出最小的变化范围。
  3. DOM 更新:只更新差异部分,最大限度减少 DOM 操作,提升性能。

3. 模板编译

Vue 提供了基于模板的语法(例如 {{}} 插值和指令)来描述页面的结构,Vue 会将模板编译成虚拟 DOM 的渲染函数。

  • Vue 模板中使用了插值、条件渲染(v-if)、列表渲染(v-for)等特性,编译后生成渲染函数,该渲染函数在数据变化时重新执行,生成新的虚拟 DOM。
示例:
<div id="app">
  <p>{{ message }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});
</script>

在这段代码中,Vue 会将 {{ message }} 转换成渲染函数,当 message 数据改变时,页面会自动更新。

4. 组件化

Vue 提供了组件化开发的支持,使得可以将页面拆分为多个可复用、独立的组件。每个组件封装了自己的模板、数据、逻辑和样式。组件化不仅提高了代码的复用性,还提升了开发和维护的效率。

示例:
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
});

new Vue({
  el: '#app'
});

通过组件化,可以更好地组织代码结构,维护性和扩展性大大提高。

5. 双向数据绑定

Vue 通过 v-model 实现了双向数据绑定,用户在页面上输入内容时,数据模型会自动更新,反过来,数据模型的变化也会实时反映到页面中。

实现原理:
  • Vue 通过 input 事件监听输入框的值变化,当值变化时,自动更新数据模型。
  • 数据模型通过数据响应式机制,反映到视图上。
示例:
<input v-model="message" />
<p>{{ message }}</p>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});
</script>

6. Vue Router 和 Vuex

除了核心部分,Vue 还通过插件系统支持单页面应用(SPA)的路由管理(Vue Router)和状态管理(Vuex),这进一步扩展了 Vue 的应用场景。

  • Vue Router:用于管理页面之间的切换和导航。
  • Vuex:用于管理应用的全局状态,适合大型应用的复杂状态管理。

7. Vue 生命周期

Vue 提供了完整的生命周期钩子函数,开发者可以在这些函数中插入代码,在组件的不同阶段执行特定逻辑。

  • beforeCreate:实例初始化后,数据观测和事件配置还未完成。
  • created:实例已经创建完成,数据观测和事件配置都已完成,但尚未挂载。
  • beforeMount:模板编译/渲染完成,准备挂载到 DOM。
  • mounted:组件挂载到 DOM 中,可以进行 DOM 操作。
  • beforeUpdate:数据更新时调用,DOM 未更新。
  • updated:数据更新后,DOM 也已更新。
  • beforeDestroy:实例销毁之前调用。
  • destroyed:实例销毁后调用。

总结

Vue 是一个渐进式的 JavaScript 框架,其核心是基于数据驱动的视图渲染,虚拟 DOM 的高效更新,以及组件化的开发模式。通过响应式系统、模板编译、虚拟 DOM、组件化以及双向数据绑定,Vue 实现了数据和视图的同步更新,提供了高效且灵活的开发体验。

21.vue-router的原理

1.什么是 vue-router

vue-router 是 Vue.js 官方的路由管理器,主要用于单页面应用 (SPA) 中处理页面路径之间的切换,实现组件的切换与渲染。本质上,它建立了 URL 和组件之间的映射关系,用户在浏览器中访问不同的路径时,路由控制器根据路径匹配对应的组件并展示。

2.vue-router 的实现原理

2.1 Hash 模式
  • 原理: 使用 URL 的 hash(即 # 后面的部分)来模拟路径切换。当 URL 改变时,不会重新加载页面,只会触发浏览器的 hashchange 事件来更新视图。
  • 特点: hash 是锚点标识,不会被发送到服务器,改变 hash 不会触发页面刷新,且 hash 值变化会被记录在浏览器历史记录中。
2.2 History 模式
  • 原理: 利用 HTML5 的 pushState()replaceState() 方法来操作浏览器历史记录。这两个方法允许在不重新加载页面的情况下改变 URL,依旧保留浏览器的前进、后退功能。
  • 特点: URL 看起来是普通的路径,但不会向服务器发送请求。此模式需要服务端进行配置,否则会出现 404 问题。

3.vue-router 使用方式

  1. 安装 vue-router:

    npm install vue-router -S
    
  2. 引入并使用 vue-router:
    main.js 中:

    import Vue from 'vue';
    import VueRouter from 'vue-router';
    Vue.use(VueRouter);
    
  3. 创建路由规则:

    const routes = [
      { path: '/home', component: Home }
    ];
    const router = new VueRouter({ routes });
    
  4. 在 Vue 实例中注册路由:

    new Vue({
      el: '#app',
      router,
      render: h => h(App),
    });
    
  5. App.vue 中使用 <router-view> 渲染组件:

    <template>
      <div>
        <router-view></router-view>
      </div>
    </template>
    

4.vue-router 参数传递

4.1 使用 name 传递参数
  • 在路由配置中给路由指定 name 属性:
    { path: '/user', name: 'user', component: User }
    
  • 通过模板:
    <router-link :to="{ name: 'user', params: { userId: 123 }}">Go to User</router-link>
    
  • 在组件中通过 $route.params.userId 获取参数。
4.2 URL 传参

通过 params 形式传递参数:

{ path: '/params/:newsId/:newsTitle', component: Params }

在 URL 中直接使用 /params/198/newsTitle,在组件中使用 $route.params 获取。

4.3 使用 query 传递参数
<router-link :to="{ name: 'Query', query: { queryId: 123 }}">Query Page</router-link>

通过 $route.query.queryId 获取参数。

5.配置子路由

const routes = [
  {
    path: '/',
    component: ParentComponent,
    children: [
      { path: 'child', component: ChildComponent }
    ]
  }
];

ParentComponent 中使用 <router-view> 渲染子路由的内容。

6.$route 和 $router 的区别

  • $route: 包含当前路由的信息对象(如 pathparamsquery 等)。
  • $router: 是路由实例对象,包含跳转方法(如 pushreplace 等)和钩子函数。

7.$router.push 和 $router.replace 的区别

  • push: 在 history 记录栈中添加一条新记录,后退按钮可以返回之前的页面。
  • replace: 替换当前的历史记录,无法通过后退按钮返回之前的页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端第一深情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值