Vue 基础
MVVM💥💥
本质:一种架构模式(编写代码的方式)。
理解:不需要操作dom的代码编写方式。
特点:(需要记忆)
- 数据驱动视图: 数据变化,页面会自动变化。 (我们不操作
dom) - 数据双向绑定: 页面变化,数据会自动变化。 (我们不操作
dom)
组件
本质:一个
.vue文件,就是一个组件
特点:由三部分组成
template:书写html代码注意:必须有一个根节点
script:书写JS代码style:书写样式代码
插值表达式
作用: 给标签设置内容
语法:{ { 表达式 } }
指令 💥💥
三个特点:
- 所有的指令都是,特殊的 标签属性
- 所有的指令都是,都以
v-开头 - 所有的指令都是,都是等号右侧连接引号,如,
v-xxx="..."
v-bind
作用:给标签设置属性
语法:v-bind:属性名="表达式"
推荐:👍:属性名="表达式"
v-on
作用:绑定事件
语法:v-on:事件名="..."
推荐:👍@事件名="..."
<button @click="少量的代码" > 123 </button>
<button @click="函数名" > 123 </button>
<button @click="函数名(参数)" > 123 </button>事件对象获取
// 函数通过形参获取
<button @click="函数名;"> 123 </button>
// 通过$event传入事件对象
<button @click="函数名(参数1, $event)"> 123 </button>鼠标事件修饰符
作用:给事件增强功能
语法:@鼠标事件.修饰符="..."
种类(常用):
.prevent:阻止默认行为 .stop:阻止事件冒泡
.stop - 阻止事件冒泡
<div @click="divClick">
<button @click.stop="btnClick">点击按钮</button>
</div>- 点击按钮时只会触发
btnClick,不会冒泡到外层div - 阻止事件向上级元素传播
.prevent - 阻止默认行为
<form @submit.prevent="handleSubmit">
<input type="submit" value="提交">
</form>- 阻止表单默认提交行为
- 阻止链接默认跳转(比如
a标签)等其他原生行为
.capture - 使用捕获模式
<div @click.capture="log">
<button @click="btnClick">按钮</button>
</div>- 从外到内捕获事件,而非通常的从内到外冒泡
优先处理最外层的事件监听器
捕获模式的概念
.capture 修饰符使用事件捕获模式,与默认的事件冒泡模式相反。事件捕获是从外层元素向内层元素传递,而事件冒泡是从内层元素向外层元素传递。
事件捕获 vs 事件冒泡 对比
默认事件冒泡模式
<template>
<div @click="outerClick">
外层 div
<button @click="innerClick">内层按钮</button>
</div>
</template>
<script>
export default {
methods: {
outerClick() {
console.log("外层 div 被点击");
},
innerClick() {
console.log("内层按钮被点击");
},
},
};
</script>点击按钮时输出顺序:
- 内层按钮被点击
- 外层
div被点击(事件冒泡)
使用捕获模式
<template>
<div @click.capture="outerClick">
外层 div
<button @click="innerClick">内层按钮</button>
</div>
</template>
<script>
export default {
methods: {
outerClick() {
console.log("外层 div 捕获阶段被触发");
},
innerClick() {
console.log("内层按钮被点击");
},
},
};
</script>点击按钮时输出顺序:
- 外层
div捕获阶段被触发 - 内层按钮被点击
实际应用场景示例 (模态框关闭功能)
<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>实际应用场景示例 (菜单展开/收起)
<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>实际应用场景示例 (修饰符组合使用)
<template>
<div @click.capture.stop="parentHandler">
<button @click="childHandler">按钮</button>
</div>
</template>
<script>
export default {
methods: {
parentHandler() {
console.log("父元素捕获阶段处理,阻止事件继续传播");
},
childHandler() {
console.log("子元素处理"); // 不会被执行
},
},
};
</script>捕获模式的优势
- 提前拦截事件:可以在事件到达目标元素前进行处理
- 统一处理:在外层统一处理某些事件逻辑
- 防止意外触发:在某些情况下避免内层元素的事件被触发 注意事项
捕获阶段发生在冒泡阶段之前.capture可以与其他修饰符组合使用- 需要注意
.stop与.capture的配合使用效果
事件传播的三个阶段
当用户点击一个嵌套元素时,事件传播分为三个阶段:
- 捕获阶段 (
Capture Phase)
- 事件从根元素(
document)开始,逐级向下传播到目标元素 - 优先执行带有
.capture修饰符的事件处理器 - 顺序:
document→body→父元素→子元素... →目标元素
- 目标阶段 (
Target Phase)
- 事件到达实际点击的目标元素
- 执行目标元素上的事件处理器
- 冒泡阶段 (
Bubbling Phase)
- 事件从目标元素开始,逐级向上传播回根元素
- 执行没有
.capture修饰符的事件处理器 - 顺序:
目标元素→子元素→父元素→body→document
.self - 只在当前元素触发
<div @click.self="divClick">
<button @click="btnClick">按钮</button>
</div>- 只有点击
div本身才会触发,点击内部按钮不会触发
.once - 事件只触发一次
<button @click.once="handleOnce">只触发一次</button>- 事件处理器只执行一次,之后被移除
- 适用于需要一次性执行的操作
.passive - 以被动模式添加事件监听器
<div @scroll.passive="onScroll">
<!-- 滚动内容 -->
</div>- 提升移动端滚动性能
- 告诉浏览器不阻止默认滚动行为
修饰符串联使用示例
<!-- 阻止冒泡且阻止默认行为 -->
<a @click.stop.prevent="doSomething">链接</a>
<!-- 只在当前元素触发且只执行一次 -->
<div @click.self.once="handleDiv">内容</div>按键修饰符
作用:给事件增强功能
语法:@键盘事件.修饰符="..."
种类:
.enter:监听回车按键触发 .esc: 监听 Esc 按键触发
键盘事件修饰符补充
<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,标签不显示
区别:
v-show:通过样式display:none控制隐藏v-if:通过创建或销毁, 控制显示与隐藏 场景:
频繁创建,用 v-show,节省性能 不频繁数据,敏感数据,使用 v-if
v-if 和 v-else-if 和 v-else
语法: v-if="布尔值" v-else-if="布尔值" v-else 注意:
v-if、 v-else-if、 v-else 三者之间,不能放入其它标签
v-model 💥💥💥
作用: 数据双向绑定
语法: v-model="变量名"
双向绑定:
- 数据变化,页面自动变化。
- 页面变化,数据自动变化。
场景:💥💥
- 收集用户输入内容
- 回填表单数据
v-model 修饰符
语法:v-model.修饰符="变量名"
种类:
.number: 将收集的数据,转为数字格式,再存给变量 .trim:去除首尾空白字符 .lazy:失去焦点时,才收集数据
v-for
作用:创建多个标签
口诀:需要生成多个谁,就写在谁身上
语法:
v-for="item in 数组" :key="数据.id">v-for="(item, index) in 数组" :key="数据.id"
key 属性 作用: 提高 dom 更新效率
口诀: 💥💥💥 有 id 用 id,没 id 用唯一,没唯一用索引
:style 和 :class👍
作用:给标签添加样式
语法::class="{类名:布尔值}" 为 ture 添加类名, 为 false 去除类名
语法: style="{css 属性名: 值, 小驼峰属性名:值, '连字符属性名': 属性值}"
计算属性
作用: 根据其它的数据, 计算得来一个新的值
优势:
- 只有依赖的数据变化, 才会重新计算
- 计算属性,自带缓存效果
注意 💥:计算属性名称,不能和 data 数据 methods 方法 其它的计算属性同名 语法:
简写写法:只读不改,用简写
computed: {
xxx(){
return ... // 💥💥 必须有返回值
}
}完整写法: 需要对计算属性赋值时,用完整
computed: {
xxx: {
get() { // 💥💥 必须有返回值
return ...
},
set(val) { // 形参 val,是计算属性接收到的值
}
}
}侦听器
简写写法 - 基本数据类型
watch:{
被监听的数据名(){
}
}完整写法 - 引用数据类型
watch: {
被监听的数据名: {
immediate: true,
deep: true,
handler(){
}
}
}组件
组件注册
规范:
- 注册的组件:大驼峰,如
HmButton - 使用组件:小写+连字符, 如
<hm-button> - 文件名:
- 小写+连字符,如
hm-button.vue - 大驼峰,如
HmButton.vue
- 小写+连字符,如
组件通信-父传子
- 父组件内, 在子组件标签上, 写属性
- 子组件内, 通过
props属性接收
组件通信-子传父
- 子组件内, 通过:
$emit(“自定义事件”, 参数1, 参数2) - 父组件内, 子组件标签上,:
@自定义事件名="函数式"
组件通信-单项数据流
💥 Vue 单项数据流:
- 👎 不推荐子组件,直接修改父组件传来的数据
- 👍 让父组件自己修改数据
v-model 语法糖
本质::value 和@input,两个指令二合一
ref 和$refs
作用:
- 获取
dom; - 获取组件实例对象
步骤:
- 绑定 ref 属性:
<标签名 ref="xxx"></标签名>- 通过
$refs.xxx获取:this.$refs.xxx
$nextTcik
作用:等 dom 更新结束, 再执行某些代码
语法:
this.$nextTick(() => {
// dom更新后, 回调函数自动执行
});动态组件
作用:在同一个位置,动态切换,显示不同的组件
步骤:
- 设置
<component :is="组件名称" > - 切换
组件名称变量
插槽
场景:当组件内, 有不确定的标签时, 使用插槽.
种类 3 种:
- 匿名插槽
- 具名插槽
- 作用域插槽
### 1-匿名插槽 场景: 组件内,只有一处有不确定的标签时,使用匿名插槽
步骤:
- 子组件内,使用
<slot></slot>占位 - 父组件内,子组件标签夹着的内容区域,设置传给
slot的内容 语法:
子组件.vue
// 1. 子组件内, 使用 slot 标签占位
<slot>默认值(如果父组件不传任何内容,默认值将显示)</slot>父组件.vue
// 2. 父组件内,子组件标签夹着的内容区域, 传给`slot`内容
<hm-child>
<xxx> 你要传给 slot 区域的内容 </xxx>
</hm-child>2-具名插槽
场景:组件内, 有多处有不确定的标签时
简写:v-slot:xxx 可以简化成#xxx
步骤:
子组件内,使用
<slot/>占位,并给<slot />设置不同的name属性父组件内,子组件标签夹着的内容区域,使用
<template #xxx />,传给slot内容 语法:👍
<slot name="xxx"/>与<template #xxx > 内容 </template>👍
<slot />与<template #default > 内容 </template>示例:
子组件.vue:
<template>
<slot name="xxx">默认值 1</slot>
<slot name="zzz">默认值 2</slot>
</template>父组件.vue:
<hm-child>
<template #xxx>
<span> 你要传给 slot-xxx 区域的内容 </span>
</template>
<template #zzz>
<span> 你要传给 slot-zzz 区域的内容 </span>
</template>
</hm-child>3-作用域插槽
作用:组件内的数据,可以传给父组件使用
步骤:
子组件内,给标签,设置属性
父组件内,通过
#xxx="对象变量",接收数据 语法:<slot name="xxx" yes="确定" no="取消" />和<template #xxx="{yes, no}" /><slot yes="确定" no="取消" />和<template #default="{yes, no}" />注意 💥:
为什么叫作用域插槽? 父组件接收的数据,只能在<template #xxx="{}" />标签范围内使用
路由
路由参数
- 动态路由 :
- 传:
- 改造动态路由,
path: "路径名/:自定义属性名" - 路径传参
- 改造动态路由,
- 接收:
$route.params - 场景: 必传参数
- 传:
- 查询(搜索)字符串:
?aa=xx&bb=yy- 传: 不用改造
- 接:
$route.query - 场景:可选参数
### 路由模式 种类:
hash路由: 带#history路由: 不带#, 需要服务度端支持(配置nginx,否则刷新页面会404)
语法:
new VueRouter({
mode: "history", // 不写 mode 为 hash 路由
});history 路由的 nginx配置:
server {
listen 80;
location / {
# 用于配合 History 使用
try_files $uri $uri/ /index.html;
}
}跨域处理
使用 nginx 处理项目部署后的跨域问题
配置前端项目接口地址,在项目目录下的.env.production文件中配置:
VITE_GLOB_API_URL=/api在 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;
}
}编程式导航
了解-导航种类:
- 声明式导航:
router-link - 编程式导航: 通过
JS跳转
跳转方式
this.$router.push("/路径");
this.$router.back(); // 返回上一页
this.$router.go(-1); // 返回上一页路由传参
// 传查询字符串: ?keyword=123&name=xxx
this.$router.push({ path: "/路径名", query: { 属性名: 值 } });
// 传动态路由: 路径传值
this.$router.push("/路径名/" + 参数值);概念
脚手架
本质:前端脚手架是一个 npm 包
作用:一键生成一个空的项目,已配置好需要的环境,提高程序员开发效率。
问题:
vue2 脚手架,如何使用?
答案
npm i @vue/cli -gvue --versionvue create 项目名称
项目名称,是否可以有中文或特殊符号?能否叫 vue、webpack 之类?推荐命名方式是什么?
答案
- 不可以有中文或特殊符号
- 不可以与使用到的 npm 重名
- 推荐小写+连字符,如 xxx-xx
MVVM💥💥💥💥💥
本质:一种架构模式(编写代码的方式)。
理解:不需要操作 dom 的代码编写方式。
特点:
数据驱动视图: 数据变化,页面会自动变化。 (我们不操作 dom)
数据双向绑定: 页面变化,数据会自动变化。 (我们不操作 dom)
组件化开发
背景:现代前端项目,代码量非常大,需要分不同的文件管理代码。
本质:
- 一个大的页面,会先拆分成不同的小组件。
- 通过组装多个小组件,组合成一个页面。
优势:
- 便于维护
- 便于复用
钩子函数
本质:某个时机,会自动执行的函数
常用的钩子函数:
created: 创建后,发请求,请求数据 mounted: 渲染后,最先获取 dom
扩展问题:
Axios 中有没有钩子函数?
Axios
参数位置:
query用paramsbody用data写
扩展:
ES6 入门指南
面试题:
JS 数据类型:
ES6之前的6种基本数据类型:string- 字符串类型number- 数字类型undefined- 未定义类型null- 空值类型boolean- 布尔类型object- 对象类型 (包含函数(function)、数组(array)、普通对象(object)等子类型)
ES6新增的第7种类型Symbol- 符号类型(ES6新增)
面试题
diff 算法-更新策略 设计目的: 比较新旧虚拟节点,找出需要更新的节点,从而最小化 DOM 操作的次数,提高页面的性能。
策略:
比较同级根元素:
- 如果类型变化,不复用该根元素。该元素及后代元素,全部删除。
- 如果类型没变, 复用该根元素,对比属性,更新
Dom属性。
比较同级兄弟节点:
- 如果不写
key,下标相同的节点,进行对比。根据策略 1, 决定是否复用。 - 如果有写
key,key相同的节点进行对比,根据策略 1, 决定是否复用。
- 如果不写
说一下 Vue 的声明周期
常用:四大阶段、8 个钩子函数
- 创建- 前,后
- 挂载- 前,后
- 更新- 前,后
- 销毁- 前,后
👍 增加使用场景
created发请求mounted最新获取dom,比如加载后立马激活input