vue的组件通信
1、props 父子之间2、自定义事件 $$emit3、全局事件总线 是什么 为什么vm 怎么做4、vuex 共享状态工具5、pubsub 消息的订阅和发布6、插槽 默认·具名·作用域7、v-model 父子双向同步8、.sync 父子双向同步9、$和$children及$refs 10、$和$listeners 对组件进行二次封装11、provide和inject 提供和注入
元素水平居中的4种方式
1.flex 盒子当中只能有一个元素,给父元素加
display:flex
justify-content: center;
align-items: center;
2. 给子元素
2.1 定位1 盒子必须有宽高
position: absolute; 绝对定位
left: 50%;
top: 50%;
margin-left: -盒子宽一半;
margin-top:-盒子高一半
2.2 定位2 盒子必须有宽高
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
2.3 定位3 无敌
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
c3动画8个属性:
animation-delay 规定动画开始的延迟。animation-direction 定动画是向前播放、向后播放还是交替播放。animation-duration 规定动画完成一个周期应花费的时间。animation-fill-mode 规定元素在不播放动画时的样式(在开始前、结束后,或两者同时)。animation-iteration-count 规定动画应播放的次数。animation-name 规定 @keyframes 动画的名称。animation-play-state 规定动画是运行还是暂停。animation-timing-function 规定动画的速度曲线。
简写的是
animation
text开头 font color line都是可以继承的
小程序封装组件两步:
1.考虑结构(考虑结构不要关系数据)把结构搭好,捞过来
2.再考虑数据,不变的写死就行,变化的就要考虑数据从哪来
先把静态结构捞过来,变化的数据都是爹给儿子传,就是使用的人在传。props
根据功能考虑结构,把结构列出来。考虑动态和静态数据,看数据是变化还是不变化。
***配置代理服务器:把浏览器给目标服务器发送的请求转化为服务器往服务器发请求。
变量或者常量与运算符组成的式子叫表达式,特点是有值的。
子绝父不相 相对的是初始包含块.
**重排重绘
操作真实dom 会导致效率低下,原因是因为会引起dom树重新渲染
dom树重新渲染:重排和重绘 假设你改变了元素的位置和大小,那么这个渲染的过程就牵扯到重排 这个重排的消耗是很大的
假设你只是改了元素的颜色 背景色 字体颜色 那么这个渲染的过程牵扯到重绘 比重排的消耗要小,效率要好
频繁操作真实dom导致 一定会重绘,但是不一定重排 如果重排了,那么就一定重绘
1.vuex
vuex是vue的集中(共享)状态管理工具,可以用来做组件通信
适用于所有场合
vuex的核心概念一共是6个: state mutations actions getters modules plugins
1、state是存储状态数据
2、mutations 是直接修改state状态数据
mutations当中能不能写异步? /面试题/
可以写异步,但是不建议,因为浏览器安装vue-devtools工具,在监视数据变化的时候就是监视
mutations函数调用,如果写了异步,那么监视的时候就监视不到了,数据的改变,就没有根据了
3、actions 当中一般都是异步请求的逻辑
1)、和组件进行连接 组件当中通过dispatch
2)、提交mutations,让mutations去修改数据
4、getters 简化state的数据操作,和组件当中computed的get一致
2.async和await
async代表异步的意思
async函数代表异步函数的意思
异步函数内部一般都会有异步操作,但是不是必须
async函数返回值不看return 返回的一定是promise对象
但是async函数返回的promise对象成功和失败看return
return的结果分两大类情况
非promise值和promise值
如果return的是非promise值,async函数返回的promise就是成功的,成功的结果就是return的值
如果return的是promise值,async函数返回的promise的成功和失败要看 return的promise的成功和失败
如果return的promise是成功的,那么async函数返回的promise就是成功,成功的结果就是return的promise成功的结果
如果return的promise是失败的,那么async函数返回的promise就是失败,失败的原因就是return的promise失败的原因
注意:如果一旦抛出异常,就失败,失败的原因就是抛出的异常
-
async函数当中写不写return要看后期你需要不需要根据请求成功失败做不同的事情来决定
ES6模块化
引入语法 1、引入暴露的整个对象 import * as xxx from './xxx.js' xxx代表的就是暴露时候暴露出来的对象 2、默认暴露的数据引入方式 import { default as xxx } from './xxx.js' default 是关键字,必须取别名 简写为 import xxx from './xxx.js' 3、分别暴露的数据引入方式 import { a as xxx, b as yyy } from './xxx.js' 通过as可以取别名 4、统一暴露的数据引入方式和分别暴露的数据引入方式相同
3.要想搞明白深浅拷贝,必须先搞明白拷贝
什么是拷贝? 全称叫数据拷贝,必须在内存当中出现一个新的空间才叫拷贝
深浅拷贝是建立在拷贝的基础上,拷贝说的是对象数据本身地址,不同 深浅拷贝说的是对象内部的属性数据地址是否相同
对象里面属性没有地址出现,言外之意全是基本值,那么浅拷贝即可 对象里面属性有地址出现,言外之意里面属性值也是对象数据,那么此时才有必要考虑深拷贝 如果我们需求拷贝出来的对象内部属性和之前的对象内部属性地址不一样,那么就要深拷贝 一句话,深拷贝只针对对象内部的对象
4.页面什么更新
数据改变,页面发生变化 数据的改变是同步的,但是数据发生改变引起的页面改变是异步的 此时如果立马去获取页面上的真是dom,拿到一个undefined
基于上述原因,vue给了nextTick,让我们可以等待页面更新完成之后去做我们需要真是dom去做的操作 必须在nextTick回调的内部去拿真是dom,才有可能,否则拿不到报错
nextTick能不能用updated去替代? 效果是对的,但是效率是低下的 因为nextTick只是等待最近的这一次更新完成 而updated是页面上以后只要有更新,就都执行
5.es6模块化
暴露语法
无论什么暴露,暴露最终出去的都是一个对象
默认暴露:暴露出去的是以default为属性,以default后面的值为值的一个对象
分别暴露:暴露出去的是以分别暴露的变量为属性组成的对象
统一暴露:暴露出去的就是我们export后面的对象
注意:前面的两个对象不是我们自己写的,而最后一个对象是我们自己写的
引入语法
1、引入暴露的整个对象
import * as xxx from './xxx.js' xxx代表的就是暴露时候暴露出来的对象
2、默认暴露的数据引入方式
import { default as xxx } from './xxx.js'
default 是关键字,必须取别名
简写为 import xxx from './xxx.js'
3、分别暴露的数据引入方式
import { a as xxx, b as yyy } from './xxx.js'
通过as可以取别名
4、统一暴露的数据引入方式和分别暴露的数据引入方式相同
6.scoped的作用及深度作用选择器(面试点)
scoped的作用
scoped本意是作用域的意思,在style身上添加scoped是在限制这个style的样式作用域
如果style当中没有添加scoped,那么内部所写的样式会影响其它组件 如果其它组件当中也有和这个组件一样的选择器,那么选中的元素也会被影响到(整个页面)
如果style当中添加scoped,那么内部所写的样式被限定了作用域 在style当中添加了scoped会让样式作用域被限定在本组件内部和子组件的根标签 如果子组件的根标签和样式的选择器一样,那么也能被影响到
原理是什么(怎么做到的)
如果style添加scoped,那么它是靠添加标识数据来限定作用域的添加了scoped,本组件的所有元素以及子组件的根标签,最后解析完都会添加一个data-v-xxxx的一个标识数据,只要有这个标识数据,就代表样式,可以影响到这些区域
虽然这样能确定style当中的样式会影响到哪,但是还要保证元素的选择器是正确的,才能生效
总结:想让一个元素有样式必须符合两个条件
1、scoped能够作用到,也就是添加标识数据2、样式的选择器必须能选择到
具体实现其实就是用css选择器来限定样式,把标识数据作为元素的一个特殊属性如果不添加scoped,样式最终是h2:{ color:hotpink}
如果加了scoped,样式最终是
h2[data-v-38fc2d66] { color: hotpink;}
有些组件标签添加样式就可以生效,有些组件添加样式就不能生效
生效的样式,我们样式的作用域一定是作用到了 恰好选择器选中的就是组件的根标签没生效的样式,我们样式的作用域一定是没作用到 选择器选中的根本不是组件的根标签,而是子组件根标签内部的元素
如何解决?
1、把不生效的样式不加scoped,重新写一个style
样式是没有作用域限定的,也就是说子组件里面不管什么元素,只要选择器符合就可以生效
一般使用的比较少,因为在使用的时候需要限定区域,不能让整个页面所有相关的元素,全部改变
2、在加scoped的style中使用深度作用选择器 ***************
让选择器发生改变,继续往子组件根标签内部的元素身上去选
深度作用选择器,在vue2当中去使用的时候和vue3当中使用的时候是不一样de
vue3中,我们需要用 :deep(需要的样式选择器)
vue2中: 采用在需要的选择器前面 ::v-deep
原生的写法:要用父元素选择器 >>> 需要的选择器 一般不用
深度作用选择器最终也是通过改变样式选择器来达到目标的
如果加了scoped没有使用深度作用选择器,那么我们的样式
h2[data-v-38fc2d66] { color: hotpink; }
如果加了scoped也使用了深度作用选择器,那么我们的样式
[data-v-38fc2d66] h2 { color: hotpink; }
一句话,如果不用深度选择器,那么标识数据属性选择器是在最右边的,和自己的选择器组成交集选择器,就选择不到 如果使用深度选择器,那么标识数据属性选择器是在左边的,和自己的选择器组成后代选择器,就可以被选择到
7.跨域
http请求分为两大类请求:普通请求和ajax请求
普通请求不会跨域 (特点是刷新页面) ajax才会出现跨域 (局部更新,不刷新页面)
为什么跨域:
违背浏览器上的同源策略,就称作跨域 只有浏览器才会出现跨域
什么是同源
协议 ip 端口三者一致叫同源 (域名就是ip和端口) 如果有一个不一样,就不同源(异源)
怎么解决?
1、jsonp 2、cors头
3、代理服务器
1、当我们运行的项目的时候npm run serve,项目都会先进行打包,打包后再去运行
2、我们的webpack在配置的时候装了一个插件,webpack-dev-server,它本质是一个服务器 我们运行起来项目之后,在本地其实起了一个服务器,它会在浏览器发送请求的时候处理,返回我们 打包的资源(dist) 注意:没有webpackdevserver的时候dist是可以打包看到的,配置了以后 dist打包后就会放在内存当中,我们看不到
3、当打包完成后,我们输入localhost:8080,回车,其实是发了一个请求给本地服务器,本地服务器接收到请求后 把dist当中的index.html返回给浏览器,浏览器会把内容进行渲染,我们就看到眼前的页面
4、页面当中如果有发送ajax请求,那么此时,我们如果直接写的是目标服务器的地址,此时必然出现跨域 因为我们目前站在本地的地址通过浏览器往另外一个真实服务器发请求,必然违背浏览器同源策略。所以浏览器往目标 服务器去发送ajax请求,一般都会出现跨域。
5、通过代理解决跨域其实就是一句话,把浏览器给目标服务器发送的请求转化为服务器往服务器发请求 设置代理要明白下面步子: 1、如果我们写代码的时候,发送请求直接写的目标地址,协议 ip 端口都有,那么就是给目标服务器发请求(跨域) 2、如果我们在写代码的时候,发送请求目标地址没有写协议、ip和端口,只写了路径,默认是给本地发的,不会跨域 前面两步做完之后,我们是把浏览器往目标服务器发的请求转化浏览器往本地服务器发的请求,此时会出现404
3、通过配置本地开发服务器的代理,解决跨域
在安装webpack-dev-server的时候,这个服务器会自动安装一个中间件,http-proxy-middleware 这个中间件就有转发请求的功能
配置代理本质其实是在配置这个中间件,这个中间件不配,那么相当于没用,配置了,这个中间件就生效了(看门狗)
请求已经从给目标服务器转化到了给本地服务器,我们只需要告诉看门狗,哪些是需要转发的 那么我们后期只要是发给本地的请求,看门狗都去检查,如果检查到有需要转发的请求,就会转发请求
Vue的配置代理写法
devServer:{
//看门狗 proxy: { "/api": { // /api代表转发标识,本质是路径的开头 // 这个标识有可能本身路径里就存在,也有可能是自己加的 // 只要往本地发的请求,都要去检查路径是不是以转发标识开头,如果是就转发
target: "http://localhost:3000", // 目标,代表要转发的目标服务器地址,没有路径,只有协议 IP 端口 // 路径发往本地的请求当中已经有了
pathRewrite: {"^/api" : ""} // 路径重写,要看最终目标服务器的路径当中有没有标识,如果本来就有这个标识 // 那么就不需要重写,如果本身,没有是自己加的,那么要重写掉这个标识 } } }
8.权限管理
给面试官说:菜单权限目的是不同用户登录进来看到的菜单不一样,菜单的不同是因为路由器里面注册的路由不同引起的,而路由器注册路由的不同又是因为用户所携带的用户信息不同所导致的。
权限管理包括权限数据管理(后面)和权限控制两部分
权限控制又包含路由权限(菜单权限)控制* 和 按钮权限控制(后面)
菜单权限控制的目的是让不同的用户登录成功看到的菜单是不一样的
菜单权限控制流程 (重要)
1、菜单的显示是根据注册的路由遍历而来的2、不同的用户要显示不同的菜单,那么也就是说不同的用户登录后路由注册是不一样的3、不同的用户登录的时候,需要给我携带不同的路由相关的权限信息数据,为了让我们可以根据不同的权限信息数据,来决定注册不同的路由4、不同的用户登录,注册的路由不一样,那么我们之前的路由是不能用的,因为已经写死了导致目前不管是什么用户,携带的是什么路由权限信息,最终看到的都是一样的菜单
5、既然路由写死是不能用,那么我们就要把路由给修改为三大类路由 常量路由(静态路由): 所有的用户都可以看到的菜单路由 异步路由(动态路由): 根据用户登录返回的路由权限信息决定动态注册的路由 任意路由(最终返回404):这个路由是用户随意输入路径的时候要匹配的,最终重定向到404 (这个路由在注册的时候,必须是注册在最后一个)6、用户登录成功的时候,除了name和avatar以外的其余三个数组数据:buttons,roles,routes,对应的 就是我们要的权限信息数据,其中routes就是不同用户给我们的不同动态路由相关的信息,routes本质是 字符串数组,这里面的字符串是和当年我们配路由的时候的name值对应的
7、用户登录成功获取到用户信息的时候,我们需要使用这个用户对应的路由权路限信息数据routes,从我们配的所有的动态路由数组当中,过滤出当前这个用户所需要注册到路由器里面的路由数组
8、我们需要把用户过滤出来的自己的动态路由,通过路由器的api addRoute动态的添加注册到路由器,还有任意路由
9、我们还需要根据注册的动态路由构造菜单遍历的时候需要的路由数组,让侧边栏动态生成菜单
10、用户退出登录的时候,其实我们应该要把路由器的路由重置成静态路由 1、把路由器当中所有的路由找到并删除 2、把staticRoutes再重新添加注册到路由器当中
注意:重置路由和不重置路由不会造成菜单的不同,但是会造成路由跳转的不同
11、当用户使用普通用户登录,商品管理只有一个二级路由的时候,那么这个二级路由会展示成一级路由菜单 如果用户此时切换为超级用户登录admin,发现超级用户的商品管理也只有这个二级路由菜单了,刷新之后 正常了
12、白板bug 在路由前置守卫当中获取用户信息成功之后,我们不能无条件放行next(),这样的话,是不会认识到新添加的路由 所以,任意路由是不会匹配到,进不了404,而改为next(to),这样的话会让我们重新去跳转一次,它就可以认识 新添加的路由了,可以进入404
权限数据管理
菜单权限控制是要根据用户登录返回的权限数据信息routes来决定的
那么权限数据是如何添加给每个用户的呢? 这些就是权限数据管理要做的
准备,拷贝完成版的view api 和 router
只要是说权限,权限数据必须包含下面三个数据包含三种 用户 角色 和 权限
而且都是多对多的关系(数据库当中表的关系)
他们三个之间 用户 和 角色 有分配和被分配的关系 角色 和 权限 有分配和被分配的关系 用户 和 权限 没有直接关系
用户管理串讲 1、搜索查询 需要两个数据 2、给用户分配角色当中的checkbox组的操作、及全选、全不选和不确定状态
角色管理串讲 编辑模式和查看模式,及修改取消后如何显示原数据(需要缓存原数据) 给角色授权的树形结构,数据类型 数据类型必须指定label和children 如果数据当中没有这两个,需要通过props指定label和children分别代表 当前数据的哪个属性
权限管理串讲 之前我们要想显示一个新的菜单,直接配置路由组件,和对应的路由,那么菜单当中就会出现新的
现在我们要想要显示一个菜单,必须保证用户先要有对应的菜单权限信息数据、然后再去创建对应的路由组件和路由
如何添加菜单 1、先要给用户添加权限数据,添加一级菜单权限数据、二及菜单权限数据及按钮权限数据 2、再去创建对应的路由组件和配置相关路由 注意:创建好的新的菜单权限数据,只是给权限数据的表当中添加了一个新的数据,这个新的数据对于admin来说 会自动的授权给用户,而如果是普通用户,那么我们需要在admin登录后,手动给用户角色进行授权,才能显示
按钮级别权限的控制 按钮级别的权限依赖于菜单级别权限,没有菜单权限是不可能有按钮权限的 自定义插件实现自定义指令v-has来对用户是否有这个按钮权限进行鉴权
无论是菜单级别的权限还是按钮级别的权限, 首先第一步:要添加对应的权限数据 第二步:要给用户对应的角色进行授权(除了admin) 第三步:对对应的权限进行鉴权(控制) 菜单有菜单的控制(全局前置守卫 + 动态添加注册路由),按钮有按钮的控制(自定义插件自定义指令)
9.组件通信高级
1>、props
props是vue当中最基础的也是用的最多的组件间通信方式
props适用于父子之间
父给子如果传递的是非函数数据 那就是父给子传数据
父给子如果传递的是函数数据 就是子给父传数据
props子组件接收(声明接收属性):接收属性的方式3种
1、数组 不能限定数据
props:['a']
2、对象的简单写法 只能限定数据类型
props:{
a:Array
}
3、对象的复杂写法 都能限定 validater(){}
props:{
a:{
type:Number,
default:100,
validater(value){
return value > 5 && value < 10
}
}
}
路由配置props 简化路由参数的写法
1、布尔值 true 代表只会将路由的params参数映射为组件内属性(组件内部接收使用),但是不能映射query参数 没啥用
props:['a']
2、对象 可以传递额外的数据 没啥用
3、函数 可以映射任何数据作为组件的属性
下面这个例子是把 路由穿的params参数a映射为组件的属性 通过props去接收使用即可
还把路由穿的query参数b映射为组件的属性,通过props接收使用
还可以映射额外的一些数据,比如username,也可以在组件内部props接收使用
props(route) {
return {a:route.params.a,b:route.query.b,username:'zhaoliying'}
}
2>、自定义事件*********
事件分为:原生dom事件和自定义事件两大类
事件三要素:事件源 事件类型 回调函数,事件源就是添加事件的元素/组件
1、原生dom事件
事件类型:有限个
回调函数:
回头再调的函数(当条件达到的时候,再去调用),三大特点:1、自己定义2、自己没调、3最终执行
回调函数调用是谁调的?
浏览器(系统)内核 会驱动事件源,去调用回调函数。简称是系统调的
回调函数调用的时候传递的默认参数是什么? $event
e/event 称作叫事件对象
浏览器在这一次触发事件的时候,会把事件相关的所有信息,封装为一个对象,在调用回调的时 候,传递给回调的第一个形参,称作叫事件对象
//回调函数
<button @click="this.test">
methods:{
test(event){}
}
//函数调用
<button @click="test($event)">
等价于
<button @click = "
function($event){
test($event)
}
">
methods:{
test(event){}
}
2、自定义事件
事件类型:无数个
回调函数:
自己定义的
回调函数调用是谁调的?
自己调用的 this.$emit()
回调函数调用的时候传递的默认参数是什么?$event
自己传递 传递的是什么就是什么 不传递就是undefined
3、原生dom事件在html标签和组件标签身上使用的区别
1、在html标签身上原生dom事件就是原生dom事件,该怎么玩就怎么玩
2、在组件标签身上即使是原生dom事件的名字,也会变为自定义事件
vue2当中所有的事件在,组件标签身上,默认都是自定义事件
如果想让这个事件还是原生dom事件,需要用到一个新的事件描述符.native
本质,这个原生dom事件是添加给子组件的根标签,形成事件委派
4、自定义事件在html标签和组件标签身上使用的区别
1、如果是一个自定义事件用在html标签身上,无意义
2、如果是一个自定义事件用在组件标签身上,该怎么玩怎么玩。内部去触发
在外部是没办法触发的,this是不对的
5、自定义事件的做法
适用场景:子向父
在父组件当中 我们可以看到子组件标签(本质就是子组件对象),因此我们可以给这个标签绑定事件,事件回调就留在父组件
在子组件当中,我们可以触发自己身上绑定的事件,本质还是在调用回调函数,通过函数传参把子组件的数据传递给父组件
3>、全局事件总线
全局事件总线是vue当中任意组件间通信方式
全局事件总线本质是一个对象
全局事件总线需要满足两个条件才能作为总线使用
·所有的组件对象都能找到它
·必须能调用$和$emit
vue当中能符合这两个条件的: 一个是vm 一个是组件对象(vc代表VueComponent)
最终选择vm作为事件总线去使用
1、vc太多了 拿谁不确定
2、vc可能销毁
3、如果专门创建一个vc作为全局事件总线使用,又浪费内存
安装总线 Vue.prototype.$bus = this(vm) 写在beforCreate
在接收数据的组件当中,找到总线,给总线绑定事件,事件的回调就留在了本组件
在发送数据的组件当中,找到总线,触发总线身上绑定的事件,本质就是调用函数,通过传参把数据传递给其它组件
10.rem适配
搜狐、唯品会的移动端页面用的此方案
布局视口宽度 / 10 = 1rem的字体大小
(最终我们要做的动态的拿到布局视口宽度 / 10 求出1rem的字体大小,设置给html即可)
适配方法
无论什么设备,无论什么设计稿 直接把设计稿宽度作为10rem
设计稿宽度是10rem,那么设备这边宽度也得是10rem 才能等比 我们只需要把设备宽度/10 作为根元素字体大小即可
书写元素样式的方法
设计稿看作是10rem,设计稿的宽度我们是知道的,这样可以求出设计稿的rem值为多少px 在写样式的时候,设计稿上div标注的px/设计稿1rem的px值,就可以得到盒子在设计稿上占的rem值 在手机这边布局的时候,直接写这个rem值即可得到等比
淘宝移动端页面用的方案 适配方法:
无论什么设备,无论什么设计稿 在设计稿上直接把100px作为1rem
这样设计稿的总rem值我们是可以得到,然后可以把这个rem值作为设备总的宽度rem值 我们只需要把设备宽度/得到的rem值 作为根元素字体大小即可
书写元素样式的方法: 在写样式的时候,设计稿设计值/100,即得到设备上该写的rem值
唯品会适配方案:
优点:可以在计算设备的1rem字体的时候,直接除以10,比较简单 缺点:我们在去计算元素在设计稿所占的rem值(后期要在设备上所写的rem值), 就比较麻烦,小数也比较多
淘宝的适配方案:
优点:设计稿这边100px做为1rem,后期求元素在设计稿这边的rem(就是我们要在设备上写的rem值) 就很好求,直接小数点往前走两位
缺点:计算设备的1rem字体的时候,比较复杂,也可能出现很多小数
11. 转真实数组
1...扩展运算符
let b = [...aNodes];
console.log(b);
2、Array.from()
let c = Array.from(aNodes);
console.log(c);
3、slice
let d = [].slice.call(aNodes);
console.log(d);
12.权限管理
13.continue和break
-
continue: 跳出本次循环,继续下一次循环。
-
break: 跳出当层循环。
注意:如果有多层循环嵌套,在内层中break,仅会跳出内层循环,外层循环依然照常执行。
break案例: 一共10个馒头,准备要吃第五个时,发现吃不下了,后面的也就不吃了
for(var i=1;i<=10;i++){ if(i == 5){ break; // 跳出当层for循环 } console.log("吃第"+i+"个馒头<br/>"); }
continue案例:一共10个饺子,当看到第五个饺子没熟。这个饺子就不吃了,接着吃剩下的。
for(var i=1;i<=10;i++){ if(i == 5){ continue; // 继续下一次循环,意味着下面代码不会执行 } console.log("吃第"+i+"个饺子<br/>"); }
14.在导入模块时,使用 *
和 name
有以下区别:
-
使用
*
导入:通过import * as moduleName from 'module'
的方式导入整个模块,并将模块中的所有导出内容作为一个对象存储在moduleName
中。你可以通过moduleName
来访问模块中的所有导出内容。import * as utils from './utils'; console.log(utils.foo); // 访问 utils 模块中导出的 foo 变量 console.log(utils.bar()); // 调用 utils 模块中导出的 bar 函数
使用
*
导入的方式适用于需要访问模块中多个导出内容的情况,可以避免命名冲突。 -
使用
name
导入:通过import { exportName } from 'module'
的方式只导入模块中特定的导出内容,并将其赋值给指定的变量名。你可以直接使用指定的变量名来访问该导出内容。import { foo, bar } from './utils'; console.log(foo); // 直接访问导入的 foo 变量 console.log(bar()); // 直接调用导入的 bar 函数
使用
name
导入的方式适用于只需要访问模块中特定导出内容的情况,可以提高代码的可读性和维护性。
总结来说,使用 *
导入整个模块适用于需要访问多个导出内容的情况,而使用 name
导入特定导出内容则更加直观和简洁。可以提高代码的可读性和灵活性。
15.foreach在循环的时候不能使用break和continue打断
16.vue中注册全局组件的方式
<script setup> import HelloWorld from './components/HelloWorld.vue'; // 注册全局组件 app.component('hello-world', HelloWorld); </script>
在上面的示例中,我们通过 app.component
方法将名为 HelloWorld
的组件注册为全局组件,并指定了组件的名称为 'hello-world'
。
然后,在其他组件中就可以直接使用 <hello-world></hello-world>
标签来引用这个全局组件了。
需要注意的是,app.component
方法需要在创建 Vue 应用实例之前调用,通常是在入口文件(如 main.js
)中进行注册。
另外,如果你希望在单个组件中注册一个全局组件,也可以使用 defineComponent
函数来定义组件,并在组件选项中使用 global
属性将其注册为全局组件。例如:
复制代码 <script setup> import { defineComponent } from 'vue'; import HelloWorld from './components/HelloWorld.vue'; export default defineComponent({ global: { components: { 'hello-world': HelloWorld } } }); </script>
Vuex 的 state
数据是存储在 Vuex 的状态存储库中的,可以在整个应用程序中共享和访问。
17.全局路由守卫
vue-router有三种全局路由守卫
守卫 | 函数 | 说明 | 功能 |
---|---|---|---|
全局前置路由守卫 | beforeEach | 在路由改变之前 | 鉴权,登录状态核对,打点等相关操作 |
局部路由守卫 | beforeResolve | 在导航被确认之前1. 所有的组件内守卫执行完毕2. 异步路由组件已经解析完成 | beforeResolve 方法可以确保路由组件及组件内回调被执行完成后再执行渲染可以避免在渲染页面之前异步组件还未加载完成的情况所以这个回调更多的是vue内部被回调 |
全局后置路由守卫 | afterEach | 在路由跳转完成后执行的回调注意: 此时导航已经完成,无法通过这个回调来改变导航所以它不是守卫函数,只是一个钩子函数 | 比如页面滚动到指定位置、更新页面标题等 |
// 全局前置守卫 router.beforeEach(async (to, from) => { if ( // 检查用户是否已登录 !token && // ❗️ 避免无限重定向 to.name !== 'Login' ) { // 将用户重定向到登录页面 return { name: 'Login' } } })
局部路由守卫
beforeEnter
是局部路由守卫,该守卫只有在进入路由时触发,不会在 params
、query
或 hash
改变时触发
const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { return false }, }, ]
也可以将一个函数数组传递给 beforeEnter
以方便在不同路由中复用守卫函数
// 在本例中removeQueryParams被复用了两次 const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: [removeQueryParams, removeHash], }, { path: '/about', component: UserDetails, beforeEnter: [removeQueryParams], }, ]
组件内守卫
组件内守卫指的是在路由组件
内直接定义的导航守卫函数
也就是说组件内守卫只能在路由组件中使用,在渲染到<router-view />
的子组件中是不存在组件内守卫的
守卫 | 说明 |
---|---|
beforeRouteEnter 组件内路由开始守卫 | 异步组件加载完毕,组件实例化之前不可以访问组件实例 this |
beforeRouteUpdate | 当前路由改变,但是该组件被复用时调用如/users/1 和 /users/2 之间之间进行跳转的时候可以访问组件实例 this |
beforeRouteLeave 离开当前组件的路由守卫 | 离开该组件的时候可以访问组件实例 this |
beforeRouteEnter
是唯一一个支持给 next
传递回调的守卫
next
是一个函数,在next
被回调的时候,会传入组件实例作为next
方法的参数
beforeRouteEnter (to, from, next) { next(vm => { // vm对应的就是组件实例 }) }
离开守卫 通常用来预防用户在离开界面时没有保存修改
beforeRouteLeave (to, from) { const answer = window.confirm('是否确认离开') // 通过返回false,来取消离开操作 if (!answer) return false }
完整的导航解析流程完整的导航解析流程
18.call-apply-bind区别
作用:都是改变函数内的this指向
call-apply作用:执行函数并改变函数内this的指向。
区别:call参数是一个一个传递,而apply是传递参数数组。
bind:改变函数内this的指向,参数也是一个一个传递。
(1)执行方式不同:
call和apply是改变后页面加载之后就立即执行,是同步代码。
bind是异步代码,改变后不会立即执行;而是返回一个新的函数。
(2)传参方式不同:
call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。
apply可以使用数组的方式传入的,只要是数组方式就可以使用剩余参数的方式传入。
19.http和https有什么不同?
两者都是超文本传输协议
HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。https则是具有安全性的ssl加密传输协议。http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
20.
1.同源的情况下,a页面和b页面可以直接去获取对方的内容
contentWindow.document
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <h2>我是a页面</h2> <iframe id="bframe" src="./b.html" frameborder="0"></iframe> <button>点击获取b页面内容</button> <script> window.onload = function () { let btn = document.querySelector("button"); btn.onclick = function () { let bframe = document.getElementById("bframe"); let title = bframe.contentWindow.document.querySelector("h2").innerHTML; console.log("a中获取到b页面的内容", title); }; }; </script> </body> </html>
2.不同源情况下,父子页面如何通信
-
window.addEventListener("message", function (event) {})
接受消息 -
window.postMessage(data, targetOrigin)
发送消息 -
父页面给子页面发送数据的时候
-
需要拿到iframe的标签,然后通过
contentWindow.postMessage
去发给子页面,第二个参数填写子页面的域名地址 -
父页面接受数据直接使用window去绑定事件
window.onmessage
-
-
子页面给父页面发送数据的时候
-
需要拿到父页面的window,通过
window.parent.postMessage
去给父页面发数据,第二个参数填写父父页面的域名地址 -
子页面接受数据直接使用window去绑定事件
window.onmessage
-
21.vue2中data为什么是函数
防止多个组件实例对象之间共用一个data,产生数据污染。
Object是引用数据类型,如果不用function 返回,每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了。
22.let、const、var的区别
(1)块级作用域: 块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES5中的两个问题:内层变量可能覆盖外层变量用来计数的循环变量泄露为全局变量
(2)变量提升: var存在变量提升,let和const不存在变量提升,即在变量只能在声明之后使用,否在会报错。
(3)给全局添加属性: 浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
(4)重复声明: var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。
(5)暂时性死区: 在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。
(6)初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。
(7)指针指向: let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。
23.React函数式组件 子给父传递数据
React用的是17版本
首先,父组件需要向子组件传递一个函数,然后,子组件通过props获取函数并附上参数,最后,父组件通过函数拿到子组件传递的值。