Skip to content

Vue 基础

MVVM💥💥

本质:一种架构模式(编写代码的方式)。

理解:不需要操作dom的代码编写方式。

特点:(需要记忆)

  1. 数据驱动视图: 数据变化,页面会自动变化。 (我们不操作dom
  2. 数据双向绑定: 页面变化,数据会自动变化。 (我们不操作dom

组件

本质:一个.vue文件,就是一个组件

特点:由三部分组成

  1. template:书写html代码

    注意:必须有一个根节点

  2. script:书写JS代码
  3. style:书写样式代码

插值表达式

作用: 给标签设置内容

语法:{ { 表达式 } }

指令 💥💥

三个特点:

  1. 所有的指令都是,特殊的 标签属性
  2. 所有的指令都是,都以 v-开头
  3. 所有的指令都是,都是等号右侧连接引号,如, v-xxx="..."

v-bind

作用:给标签设置属性

语法:v-bind:属性名="表达式"

推荐:👍:属性名="表达式"

v-on

作用:绑定事件

语法:v-on:事件名="..."

推荐:👍@事件名="..."

js
<button @click="少量的代码" > 123 </button>
<button @click="函数名" > 123 </button>
<button @click="函数名(参数)" > 123 </button>

事件对象获取

vue
// 函数通过形参获取
<button @click="函数名;"> 123 </button>

// 通过$event传入事件对象
<button @click="函数名(参数1, $event)"> 123 </button>

鼠标事件修饰符

作用:给事件增强功能

语法:@鼠标事件.修饰符="..."

种类(常用):

.prevent:阻止默认行为 .stop:阻止事件冒泡

.stop - 阻止事件冒泡

vue
<div @click="divClick">
  <button @click.stop="btnClick">点击按钮</button>
</div>
  • 点击按钮时只会触发 btnClick,不会冒泡到外层 div
  • 阻止事件向上级元素传播

.prevent - 阻止默认行为

vue
<form @submit.prevent="handleSubmit">
  <input type="submit" value="提交">
</form>
  • 阻止表单默认提交行为
  • 阻止链接默认跳转(比如 a 标签)等其他原生行为

.capture - 使用捕获模式

vue
<div @click.capture="log">
  <button @click="btnClick">按钮</button>
</div>
  • 从外到内捕获事件,而非通常的从内到外冒泡
  • 优先处理最外层的事件监听器

捕获模式的概念

.capture 修饰符使用事件捕获模式,与默认的事件冒泡模式相反。事件捕获是从外层元素向内层元素传递,而事件冒泡是从内层元素向外层元素传递。

事件捕获 vs 事件冒泡 对比

默认事件冒泡模式

vue
<template>
  <div @click="outerClick">
    外层 div
    <button @click="innerClick">内层按钮</button>
  </div>
</template>

<script>
export default {
  methods: {
    outerClick() {
      console.log("外层 div 被点击");
    },
    innerClick() {
      console.log("内层按钮被点击");
    },
  },
};
</script>

点击按钮时输出顺序:

  1. 内层按钮被点击
  2. 外层 div 被点击(事件冒泡)

使用捕获模式

vue
<template>
  <div @click.capture="outerClick">
    外层 div
    <button @click="innerClick">内层按钮</button>
  </div>
</template>

<script>
export default {
  methods: {
    outerClick() {
      console.log("外层 div 捕获阶段被触发");
    },
    innerClick() {
      console.log("内层按钮被点击");
    },
  },
};
</script>

点击按钮时输出顺序:

  1. 外层 div 捕获阶段被触发
  2. 内层按钮被点击

实际应用场景示例 (模态框关闭功能)

vue
<template>
  <!-- 点击模态框外部关闭模态框 -->
  <div class="modal-overlay" @click.capture="closeModal">
    <div class="modal-content" @click.stop>
      <h2>模态框标题</h2>
      <p>这里是模态框内容</p>
      <button @click="confirm">确认</button>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    closeModal() {
      console.log("关闭模态框");
      // 关闭逻辑
    },
    confirm() {
      console.log("确认操作");
      // 确认逻辑,不会触发关闭
    },
  },
};
</script>

实际应用场景示例 (菜单展开/收起)

vue
<template>
  <div class="menu-container" @click.capture="closeAllMenus">
    <div class="menu-item">
      <button @click="toggleMenu1">菜单1</button>
      <ul v-show="showMenu1" @click.stop>
        <li>选项1</li>
        <li>选项2</li>
      </ul>
    </div>
    <div class="menu-item">
      <button @click="toggleMenu2">菜单2</button>
      <ul v-show="showMenu2" @click.stop>
        <li>选项A</li>
        <li>选项B</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showMenu1: false,
      showMenu2: false,
    };
  },
  methods: {
    closeAllMenus() {
      this.showMenu1 = false;
      this.showMenu2 = false;
    },
    toggleMenu1() {
      this.showMenu1 = !this.showMenu1;
    },
    toggleMenu2() {
      this.showMenu2 = !this.showMenu2;
    },
  },
};
</script>

实际应用场景示例 (修饰符组合使用)

vue
<template>
  <div @click.capture.stop="parentHandler">
    <button @click="childHandler">按钮</button>
  </div>
</template>

<script>
export default {
  methods: {
    parentHandler() {
      console.log("父元素捕获阶段处理,阻止事件继续传播");
    },
    childHandler() {
      console.log("子元素处理"); // 不会被执行
    },
  },
};
</script>

捕获模式的优势

  1. 提前拦截事件:可以在事件到达目标元素前进行处理
  2. 统一处理:在外层统一处理某些事件逻辑
  3. 防止意外触发:在某些情况下避免内层元素的事件被触发 注意事项
  4. 捕获阶段发生在冒泡阶段之前
  5. .capture 可以与其他修饰符组合使用
  6. 需要注意 .stop.capture 的配合使用效果

事件传播的三个阶段

当用户点击一个嵌套元素时,事件传播分为三个阶段:

  1. 捕获阶段 (Capture Phase)
  • 事件从根元素(document)开始,逐级向下传播到目标元素
  • 优先执行带有 .capture 修饰符的事件处理器
  • 顺序:documentbody父元素子元素... → 目标元素
  1. 目标阶段 (Target Phase)
  • 事件到达实际点击的目标元素
  • 执行目标元素上的事件处理器
  1. 冒泡阶段 (Bubbling Phase)
  • 事件从目标元素开始,逐级向上传播回根元素
  • 执行没有 .capture 修饰符的事件处理器
  • 顺序:目标元素子元素父元素bodydocument

.self - 只在当前元素触发

vue
<div @click.self="divClick">
  <button @click="btnClick">按钮</button>
</div>
  • 只有点击 div 本身才会触发,点击内部按钮不会触发

.once - 事件只触发一次

vue
<button @click.once="handleOnce">只触发一次</button>
  • 事件处理器只执行一次,之后被移除
  • 适用于需要一次性执行的操作

.passive - 以被动模式添加事件监听器

vue
<div @scroll.passive="onScroll">
  <!-- 滚动内容 -->
</div>
  • 提升移动端滚动性能
  • 告诉浏览器不阻止默认滚动行为

修饰符串联使用示例

vue
<!-- 阻止冒泡且阻止默认行为 -->
<a @click.stop.prevent="doSomething">链接</a>

<!-- 只在当前元素触发且只执行一次 -->
<div @click.self.once="handleDiv">内容</div>

按键修饰符

作用:给事件增强功能

语法:@键盘事件.修饰符="..."

种类:

.enter:监听回车按键触发 .esc: 监听 Esc 按键触发

键盘事件修饰符补充

vue
<template>
  <!-- 回车键触发 -->
  <input @keyup.enter="submit" />

  <!-- 空格键触发 -->
  <input @keydown.space="handleSpace" />

  <!-- 修饰键  Ctrl+C -->
  <input @keyup.ctrl.67="copy" />

  <!-- 系统修饰键 -->
  <button @click.ctrl="doSomething">Ctrl+点击</button>
</template>

v-if 和 v-show

作用:控制标签的显示和隐藏

语法:v-if="布尔值"。 布尔为 true,标签显示。 为 false,标签不显示

区别:

  1. v-show:通过样式 display:none 控制隐藏
  2. v-if:通过创建或销毁, 控制显示与隐藏 场景:

频繁创建,用 v-show,节省性能 不频繁数据,敏感数据,使用 v-if

v-if 和 v-else-if 和 v-else

语法: v-if="布尔值" v-else-if="布尔值" v-else 注意:

v-ifv-else-ifv-else 三者之间,不能放入其它标签

v-model 💥💥💥

作用: 数据双向绑定

语法: v-model="变量名"

双向绑定:

  1. 数据变化,页面自动变化。
  2. 页面变化,数据自动变化。

场景:💥💥

  1. 收集用户输入内容
  2. 回填表单数据

v-model 修饰符

语法:v-model.修饰符="变量名"

种类:

.number: 将收集的数据,转为数字格式,再存给变量 .trim:去除首尾空白字符 .lazy:失去焦点时,才收集数据

v-for

作用:创建多个标签

口诀:需要生成多个谁,就写在谁身上

语法:

v-for="item in 数组" :key="数据.id" > v-for="(item, index) in 数组" :key="数据.id"

key 属性 作用: 提高 dom 更新效率

口诀: 💥💥💥 有 idid,没 id 用唯一,没唯一用索引

:style 和 :class👍

作用:给标签添加样式

语法::class="{类名:布尔值}"ture 添加类名, 为 false 去除类名

语法: style="{css 属性名: 值, 小驼峰属性名:值, '连字符属性名': 属性值}"

计算属性

作用: 根据其它的数据, 计算得来一个新的值

优势:

  1. 只有依赖的数据变化, 才会重新计算
  2. 计算属性,自带缓存效果

注意 💥:计算属性名称,不能和 data 数据 methods 方法 其它的计算属性同名 语法:

简写写法:只读不改,用简写

js
computed: {
    xxx(){
      return ... // 💥💥 必须有返回值
    }
}

完整写法: 需要对计算属性赋值时,用完整

js
computed: {
    xxx: {
        get() { // 💥💥 必须有返回值
            return ...
        },
        set(val) { // 形参 val,是计算属性接收到的值
        }
    }
}

侦听器

简写写法 - 基本数据类型

js
watch:{
    被监听的数据名(){

    }
}

完整写法 - 引用数据类型

js
watch: {
    被监听的数据名: {
        immediate: true,
        deep: true,
        handler(){
        }
    }
}

组件

组件注册

规范:

  1. 注册的组件:大驼峰,如 HmButton
  2. 使用组件:小写+连字符, 如<hm-button>
  3. 文件名:
    1. 小写+连字符,如 hm-button.vue
    2. 大驼峰,如 HmButton.vue

组件通信-父传子

  1. 父组件内, 在子组件标签上, 写属性
  2. 子组件内, 通过 props 属性接收

组件通信-子传父

  1. 子组件内, 通过:$emit(“自定义事件”, 参数1, 参数2)
  2. 父组件内, 子组件标签上,: @自定义事件名="函数式"

组件通信-单项数据流

💥 Vue 单项数据流:

  1. 👎 不推荐子组件,直接修改父组件传来的数据
  2. 👍 让父组件自己修改数据

v-model 语法糖

本质::value@input,两个指令二合一

ref 和$refs

作用:

  1. 获取 dom
  2. 获取组件实例对象

步骤:

  1. 绑定 ref 属性: <标签名 ref="xxx"></标签名>
  2. 通过$refs.xxx获取: this.$refs.xxx

$nextTcik

作用:等 dom 更新结束, 再执行某些代码

语法:

js
this.$nextTick(() => {
  // dom更新后, 回调函数自动执行
});

动态组件

作用:在同一个位置,动态切换,显示不同的组件

步骤:

  1. 设置 <component :is="组件名称" >
  2. 切换组件名称 变量

插槽

场景:当组件内, 有不确定的标签时, 使用插槽.

种类 3 种:

  1. 匿名插槽
  2. 具名插槽
  3. 作用域插槽

###  1-匿名插槽 场景: 组件内,只有一处有不确定的标签时,使用匿名插槽

步骤:

  1. 子组件内,使用<slot></slot>占位
  2. 父组件内,子组件标签夹着的内容区域,设置传给 slot 的内容 语法:

子组件.vue

ts
// 1. 子组件内, 使用 slot 标签占位
<slot>默认值(如果父组件不传任何内容,默认值将显示)</slot>

父组件.vue

ts
// 2. 父组件内,子组件标签夹着的内容区域, 传给`slot`内容
<hm-child>
  <xxx> 你要传给 slot 区域的内容 </xxx>
</hm-child>

2-具名插槽

场景:组件内, 有多处有不确定的标签时

简写:v-slot:xxx 可以简化成#xxx

步骤:

  1. 子组件内,使用<slot/>占位,并给<slot /> 设置不同的 name 属性

  2. 父组件内,子组件标签夹着的内容区域,使用<template #xxx />,传给 slot 内容 语法:

  3. 👍 <slot name="xxx"/><template #xxx > 内容 </template>

  4. 👍 <slot /><template #default > 内容 </template> 示例:

子组件.vue

vue
<template>
  <slot name="xxx">默认值 1</slot>
  <slot name="zzz">默认值 2</slot>
</template>

父组件.vue

js
<hm-child>
<template #xxx>
<span> 你要传给 slot-xxx 区域的内容 </span>
</template>

<template #zzz>
<span> 你要传给 slot-zzz 区域的内容 </span>
</template>
</hm-child>

3-作用域插槽

作用:组件内的数据,可以传给父组件使用

步骤:

  1. 子组件内,给标签,设置属性

  2. 父组件内,通过#xxx="对象变量",接收数据 语法:

  3. <slot name="xxx" yes="确定" no="取消" /><template #xxx="{yes, no}" />

  4. <slot yes="确定" no="取消" /><template #default="{yes, no}" /> 注意 💥:

为什么叫作用域插槽? 父组件接收的数据,只能在<template #xxx="{}" />标签范围内使用

路由

路由参数

  1. 动态路由 :
    1. 传:
      1. 改造动态路由, path: "路径名/:自定义属性名"
      2. 路径传参
    2. 接收: $route.params
    3. 场景: 必传参数
  2. 查询(搜索)字符串: ?aa=xx&bb=yy
    1. 传: 不用改造
    2. 接: $route.query
    3. 场景:可选参数

### 路由模式 种类:

  1. hash 路由: 带#
  2. history 路由: 不带#, 需要服务度端支持(配置nginx,否则刷新页面会404)

语法:

js
new VueRouter({
  mode: "history", // 不写 mode 为 hash 路由
});

history 路由的 nginx配置:

nginx
server {
  listen 80;
  location / {
    # 用于配合 History 使用
    try_files $uri $uri/ /index.html;
  }
}

跨域处理

使用 nginx 处理项目部署后的跨域问题

配置前端项目接口地址,在项目目录下的.env.production文件中配置:

bash
VITE_GLOB_API_URL=/api

nginx 配置请求转发到后台

nginx
server {
  listen       80;
  server_name  localhost;
  # 接口代理,用于解决跨域问题
  location /api {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # 后台接口地址
    proxy_pass http://110.110.1.1:8080/api; 
    rewrite "^/api/(.*)$" /$1 break; 
    proxy_redirect default;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  }
}

编程式导航

了解-导航种类:

  1. 声明式导航: router-link
  2. 编程式导航: 通过 JS 跳转

跳转方式

js
this.$router.push("/路径");

this.$router.back(); // 返回上一页

this.$router.go(-1); // 返回上一页

路由传参

js
// 传查询字符串:  ?keyword=123&name=xxx
this.$router.push({ path: "/路径名", query: { 属性名: 值 } });

// 传动态路由: 路径传值
this.$router.push("/路径名/" + 参数值);

概念

脚手架

本质:前端脚手架是一个 npm

作用:一键生成一个空的项目,已配置好需要的环境,提高程序员开发效率。

问题:

vue2 脚手架,如何使用?

答案

  • npm i @vue/cli -g
  • vue --version
  • vue create 项目名称

项目名称,是否可以有中文或特殊符号?能否叫 vue、webpack 之类?推荐命名方式是什么?

答案

  • 不可以有中文或特殊符号
  • 不可以与使用到的 npm 重名
  • 推荐小写+连字符,如 xxx-xx

MVVM💥💥💥💥💥

本质:一种架构模式(编写代码的方式)。

理解:不需要操作 dom 的代码编写方式。

特点:

数据驱动视图: 数据变化,页面会自动变化。 (我们不操作 dom

数据双向绑定: 页面变化,数据会自动变化。 (我们不操作 dom

组件化开发

背景:现代前端项目,代码量非常大,需要分不同的文件管理代码。

本质:

  1. 一个大的页面,会先拆分成不同的小组件。
  2. 通过组装多个小组件,组合成一个页面。

优势:

  1. 便于维护
  2. 便于复用

钩子函数

本质:某个时机,会自动执行的函数

常用的钩子函数:

created: 创建后,发请求,请求数据 mounted: 渲染后,最先获取 dom

扩展问题:

Axios 中有没有钩子函数?

Axios

参数位置:

  1. queryparams
  2. bodydata

扩展:

ES6 入门指南

面试题:

JS 数据类型:

  1. ES6 之前的 6 种基本数据类型:
    • string - 字符串类型
    • number - 数字类型
    • undefined - 未定义类型
    • null - 空值类型
    • boolean - 布尔类型
    • object - 对象类型 (包含函数(function)、数组(array)、普通对象(object)等子类型)
  2. ES6 新增的第 7 种类型 Symbol - 符号类型(ES6 新增)

面试题

diff 算法-更新策略 设计目的: 比较新旧虚拟节点,找出需要更新的节点,从而最小化 DOM 操作的次数,提高页面的性能。

策略:

  1. 比较同级根元素:

    1. 如果类型变化,不复用该根元素。该元素及后代元素,全部删除。
    2. 如果类型没变, 复用该根元素,对比属性,更新 Dom属性。
  2. 比较同级兄弟节点:

    1. 如果不写 key,下标相同的节点,进行对比。根据策略 1, 决定是否复用。
    2. 如果有写 keykey 相同的节点进行对比,根据策略 1, 决定是否复用。

说一下 Vue 的声明周期

常用:四大阶段、8 个钩子函数

  1. 创建- 前,后
  2. 挂载- 前,后
  3. 更新- 前,后
  4. 销毁- 前,后

👍 增加使用场景

  1. created 发请求
  2. mounted 最新获取 dom,比如加载后立马激活 input