如何实现跨域?

1、JSONP:通过动态创建 script,再请求一个带参网址实现跨域通信。
2、document.domain +iframe 跨域:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。
3、location.hash + iframe 跨域:a 欲与 b 跨域相互通信,通过中间页 c 来实现。 三个页面,不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js 访问来通信。
4、window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的 window.name 从外域传递到本地域。
5、postMessage 跨域:可以跨域操作的 window 属性之一。
6、CORS:服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,若要带 cookie 请求,前后端都需要设置。
cors 全称为 Cross Origin Resource Sharing(跨域资源共享)。这种方案对于前端来说没有什么工作量,和正常发送请求写法上没有任何区别,工作量基本都在后端这里。每一次请求,浏览器必须先以 OPTIONS 请求方式发送一个预请求(也不是所有请求都会发送 options),通过预检请求从而获知服务器端对跨源请求支持的 HTTP 方法。在确认服务器允许该跨源请求的情况下,再以实际的 HTTP 请求方法发送那个真正的请求。
6、代理跨域:启一个代理服务器,实现数据的转发
在 dev 开发模式下可以下使用 webpack 的 proxy 使用也是很方便,但这种方法在生产环境是不能使用的。在生产环境中需要使用 nginx 进行反向代理。不管是 proxy 和 nginx 的原理都是一样的,通过搭建一个中转服务器来转发请求规避跨域的问题。
可参考
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

JSONP原理和缺点

JSONP:ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的 js 脚本,利用这个特性,服务端不再返回 JSON 格式的数据,而是返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。
缺点:
JSON 只支持 get,因为 script 标签只能使用 get 请求;
JSONP 需要后端配合返回指定格式的数据

谈谈你对DOM的理解及常用的DOM API

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为 DOM。
节点创建型 api,页面修改型 API,节点查询型 API,节点关系型 api,元素属性型 api,元素样式型 api

说说你对 Vue 的理解

Vue 是一个构建数据驱动的渐进性框架,它的目标是通过 API 实现响应数据绑定和视图更新

说说 Vue 的优缺点

优点:
1、数据驱动视图,对真实 dom 进行抽象出 virtual dom(本质就是一个 js 对象),
并配合 diff 算法、响应式和观察者、异步队列等手段以最小代价更新 dom,渲染
页面
2、组件化,组件用单文件的形式进行代码的组织编写,使得我们可以在一个文
件里编写 html\css(scoped 属性配置 css 隔离)\js 并且配合 Vue-loader 之后,支
持更强大的预处理器等功能
3、强大且丰富的 API 提供一系列的 api 能满足业务开发中各类需求
4、由于采用虚拟 dom,让 Vue ssr 先天就足
5、生命周期钩子函数,选项式的代码组织方式,写熟了还是蛮顺畅的,但仍然
有优化空间(Vue3 composition-api)
6、生态好,社区活跃

缺点:
1、由于底层基于 Object.defineProperty 实现响应式,而这个 api 本身不支持 IE8及以下浏览器
2、csr 的先天不足,首屏性能问题(白屏)
3、由于百度等搜索引擎爬虫无法爬取 js 中的内容,故 spa 先天就对 seo 优化心有余力不足(谷歌的 puppeteer 就挺牛逼的,实现预渲染底层也是用到了这个工具)

什么是虚拟 DOM

虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在 react,vue 等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。
这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom树,那么每次 dom 的更改就变成了 js 对象的属性的更改,这样一来就能查找 js 对象的属性变化要比查询 dom 树的性能开销

请描述下 vue 的生命周期是什么

生命周期就是 vue 从开始创建到销毁的过程,分为四大步(创建,挂载,更新,销毁),每一步又分为两小步,如 beforeCreate,created。beforeCreate前,也就是 new Vue 的时候会初始化事件和生命周期;beforeCreate 和created 之间会挂载 Data,绑定事件;接下来会根据 el 挂载页面元素,如果没有设置 el 则生命周期结束,直到手动挂载;el 挂载结束后,根据templete/outerHTML(el)渲染页面;在 beforeMount 前虚拟 DOM 已经创建完成;之后在 mounted 前,将 vm.$el 替换掉页面元素 el;mounted 将虚拟 dom挂载到真实页面(此时页面已经全部渲染完成);之后发生数据变化时触发 beforeUpdate 和 updated 进行一些操作;最后主动调用销毁函数或者组件自动销毁时 beforeDestroy,手动撤销监听事件,计时器等;destroyed时仅存在 Dom 节点,其他所有东西已自动销毁。
在这里插入图片描述

vue 如何监听键盘事件?

  1. @keyup. 方法
<template>
<input ref="myInput" type="text" value="hello world" autofocus
@keyup.enter="handleKey">
</template>
<script>
export default {
	methods: {
		handleKey(e) {
			console.log(e)
		}
	}
}
</script>

2、addEventListener

<script>
export default {
vmounted() {
	document.addEventListener('keyup', this.handleKey)
},beforeDestroy() {
	document.removeEventListener('keyup', this.handleKey)
},methods: {
	handleKey(e) {
	console.log(e)
	}
	}
}
</script><script>
export default {
	mounted() {
		document.addEventListener('keyup', this.handleKey)
	},beforeDestroy() {
	document.removeEventListener('keyup', this.handleKey)
	},methods: {
		handleKey(e) {
	console.log(e)
	}
	}
}
</script>

watch 怎么深度监听对象变化

deep 设置为 true 就可以监听到对象的变化

let vm = new Vue({
el:"#first", data:{msg:{name:'北京'}}, watch:{
  msg:{
     handler (newMsg,oldMsg){
       console.log(newMsg);
    },immediate:true, deep:true
  }
 }
})

删除数组用 delete 和 Vue.delete 有什么区别

 delete:只是被删除数组成员变为 empty / undefined,其他元素键值不变
 Vue.delete:直接删了数组成员,并改变了数组的键值(对象是响应式的,确保
删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限

watch 和计算属性有什么区别?

通俗来讲,既能用 computed 实现又可以用 watch 监听来实现的功能,推荐用 computed,
重点在于 computed 的缓存功能
computed 计算属性是用来声明式的描述一个值依赖了其它的值,当所依赖的值或者变量
改变时,计算属性也会跟着改变;
watch 监听的是已经在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法。

Vue 双向绑定原理

Vue 数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。利用了
Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)。

v-model 是什么?有什么用呢?

一则语法糖,相当于 v-bind:value=“xxx” 和 @input,意思是绑定了一个 value 属性的值,
子组件可对 value 属性监听,通过$emit(‘input’, xxx)的方式给父组件通讯。自己实现
v-model 方式的组件也是这样的思路。

axios 是什么?怎样使用它?怎么解决跨域的问题?

axios 的是一种异步请求,用法和 ajax 类似,安装 npm install axios --save 即可使用,请
求中包括 get,post,put, patch ,delete 等五种请求方式,解决跨域可以在请求头中添加
Access-Control-Allow-Origin,也可以在 index.js 文件中更改 proxyTable 配置等解决跨域
问题

在 vue 项目中如何引入第三方库(比如 jQuery)?有哪些方法可以做到

1、绝对路径直接引入
在 index.html 中用 script 引入

然后在 webpack 中配置 external
externals: { ‘jquery’: ‘jQuery’ }
在组件中使用时 import
import $ from ‘jquery’ 2 、在 webpack 中配置 alias
resolve: { extensions: [‘.js’, ‘.vue’, ‘.json’], alias: { ‘@’: resolve(‘src’), ‘jquery’: resolve(‘static/jquery-1.12.4.js’) } }
然后在组件中 import
3、在 webpack 中配置 plugins
plugins: [ new webpack.ProvidePlugin({ $: ‘jquery’ }) ]
全局使用,但在使用 eslint 情况下会报错,需要在使用了 $ 的代码前添加 /*
eslint-disable*/ 来去掉 ESLint 的检查。

说说 Vue React angularjs jquery 的区别

参考回答:
JQuery 与另外几者最大的区别是,JQuery 是事件驱动,其他两者是数据驱动。
JQuery 业务逻辑和 UI 更改该混在一起, UI 里面还参杂这交互逻辑,让本来混乱的逻
辑更加混乱。
Angular,Vue 是双向绑定,而 React 不是
其他还有设计理念上的区别等

Vue3.0 里为什么要用 Proxy API 替代 defineProperty API?

参考回答:
响应式优化。
a. defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
Vue2.x 中的响应式实现正是基于 defineProperty 中的 descriptor,对 data 中的属性做了遍
历 + 递归,为每个属性设置了 getter、setter。
这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因,在 Vue 中使用
下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是无法做到 setter 监
听的,这是 defineProperty 的局限性。
b. Proxy API 的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这
就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须
先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
c. 响应式是惰性的
在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要
递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的,这
无疑会有很大的性能消耗。
在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的
处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响
应式,简单的可以说是按需实现响应式,减少性能消耗。
基础用法

在这里插入图片描述

Vue3.0 编译做了哪些

a. 生成 Block tree
Vue.js 2.x 的数据更新并触发重新渲染的粒度是组件级的,单个组件内部 需要遍历该组
件的整个 vnode 树。在 2.0 里,渲染效率的快慢与组件大小成正相关:组件越大,渲染
效率越慢。并且,对于一些静态节点,又无数据更新,这些遍历都是性能浪费。
Vue.js 3.0 做到了通过编译阶段对静态模板的分析,编译生成了 Block tree。 Block tree
是一个将模版基于动态节点指令切割的嵌套区块,每个 区块内部的节点结构是固定的,
每个区块只需要追踪自身包含的动态节点。所以,在 3.0 里,渲染效率不再与模板大小
成正相关,而是与模板中动态节点的数量成正相关

在这里插入图片描述
b. slot 编译优化
Vue.js 2.x 中,如果有一个组件传入了 slot,那么每次父组件更新的时候,会强制使子组
件 update,造成性能的浪费。
Vue.js 3.0 优化了 slot 的生成,使得非动态 slot 中属性的更新只会触发子组件的更新。
动态 slot 指的是在 slot 上面使用 v-if,v-for,动态 slot 名字等会导致 slot 产生运行时动
态变化但是又无法被子组件 track 的操作

c. diff 算法优化

Vue3.0 Composition API 与 React.js 中 Hooks 的异同

a. React.js 中的 Hooks 基本使用
React Hooks 允许你 “勾入” 诸如组件状态和副作用处理等 React 功能中。Hooks 只能
用在函数组件中,并允许我们在不需要创建类的情况下将状态、副作用处理和更多东西
带入组件中。
React 核心团队奉上的采纳策略是不反对类组件,所以你可以升级 React 版本、在新组
件中开始尝试 Hooks,并保持既有组件不做任何更改。
案例

useState 和 useEffect 是 React Hooks 中的一些例子,使得函数组件中也能增加状态和
运行副作用。
我们也可以自定义一个 Hooks,它打开了代码复用性和扩展性的新大门。

b. Vue Composition API 基本使用
Vue Composition API 围绕一个新的组件选项 setup 而创建。setup() 为 Vue 组件提供了
状态、计算值、watcher 和生命周期钩子。
并没有让原来的 API(Options-based API)消失。允许开发者 结合使用新旧两种 API
(向下兼容)

在这里插入图片描述

c. 原理
React hook 底层是基于链表实现,调用的条件是每次组件被 render 的时候都会顺序执行
所有的 hooks。
Vue hook 只会被注册调用一次,Vue 能避开这些麻烦的问题,原因在于它对数据的响
应是基于 proxy 的,对数据直接代理观察。(这种场景下,只要任何一个更改 data 的地
方,相关的 function 或者 template 都会被重新计算,因此避开了 React 可能遇到的性能
上的问题)。
React 中,数据更改的时候,会导致重新 render,重新 render 又会重新把 hooks 重新注
册一次,所以 React 复杂程度会高一些。

Vue3.0 底层是如何变得更快的?

a. diff 方法优化
Vue2.x 中的虚拟 dom 是进行全量的对比。
Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对比的时候,值对比
带有 patch flag 的节点,并且可以通过 flag 的信息得知当前节点要对比的具体内容化。
b. hoistStatic 静态提升
Vue2.x : 无论元素是否参与更新,每次都会重新创建。
Vue3.0 : 对不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用。
c. cacheHandlers 事件侦听器缓存
默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的变化但是因为是同一
个函数,所以没有追踪变化,直接缓存起来复用即可。

vue 要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

通过获取当前用户的权限去比对路由表,生成当前用户具有的权限可访问的路由表,通过 router.addRoutes 动态挂载到 router 上。
你可以在后台通过一个 tree 控件或者其它展现形式给每一个页面动态配置权限,之后将这份路由表存储到后端。当用户登录后得到 roles,前端根据roles 去向后端请求可访问的路由表,从而动态生成可访问页面,之后就是 router.addRoutes 动态挂载到 router 上,
只是多了一步将后端返回路由表和本地的组件映射到一起。

const map={
 login:require('login/index').default // 同步的方式
 login:()=>import('login/index')      // 异步的方式
}
//你存在服务端的map类似于
const serviceMap=[
 { path: '/login', component: 'login', hidden: true }
]
//之后遍历这个map,动态生成asyncRoutes
并将 component 替换为 map[component]

高级实现

// 通过递归后台传过来的数据 转成router
function format1(routerList) {
	let children = [];
	let childRouter = {};
	routerList.forEach(child => {
		if (child.userRouters) { //菜单下如果还有子目录 则递归调用
			childRouter = {
				path: child.rPath,
				name: child.rTitle,
				component: resolve => require(['@/views' + child.rComponent], resolve),
				meta: {
					title: child.rTitle,
					icon: child.rIcon
				},
				alwaysShow: true,
				children: format1(child.userRouters)
			}
		} else {
			childRouter = {
				path: child.rPath,
				name: child.rTitle,
				component: resolve => require(['@/views' + child.rComponent], resolve),
				meta: {
					title: child.rTitle,
					icon: child.rIcon
				}
			}
		}
		children.push(childRouter);
	})
	return children;
}

逻辑修改
现在路由层面权限的控制代码都在 @/permission.js 中,如果想修改逻辑,直接在适当的判断逻辑中 next() 释放钩子即可。

指令权限
封装了一个指令权限,能简单快速的实现按钮级别的权限判断。 v-permission

使用

<template>
  <!-- Admin can see this -->
  <el-tag v-permission="['admin']">admin</el-tag>

  <!-- Editor can see this -->
  <el-tag v-permission="['editor']">editor</el-tag>

  <!-- Editor can see this -->
  <el-tag v-permission="['admin','editor']">Both admin or editor can see this</el-tag>
</template>

<script>
// 当然你也可以为了方便使用,将它注册到全局
import permission from '@/directive/permission/index.js' // 权限判断指令
export default{
  directives: { permission }
}
</script>

局限
In some cases it is not suitable to use v-permission, such as element Tab component which can only be achieved by manually setting the v-if.
可以使用全局权限判断函数,用法和指令 v-permission 类似。

<template>
  <el-tab-pane v-if="checkPermission(['admin'])" label="Admin">Admin can see this</el-tab-pane>
  <el-tab-pane v-if="checkPermission(['editor'])" label="Editor">Editor can see this</el-tab-pane>
  <el-tab-pane v-if="checkPermission(['admin','editor'])" label="Admin-OR-Editor">Both admin or editor can see this</el-tab-pane>
</template>

<script>
import checkPermission from '@/utils/permission' // 权限判断函数

export default{
   methods: {
    checkPermission
   }
}
</script>

Ajax

Ajax请求流程

AJAX 创建异步对象 XMLHttpRequest
操作 XMLHttpRequest 对象
(1)设置请求参数(请求方式,请求页面的相对路径,是否异步)
(2)设置回调函数,一个处理服务器响应的函数,使用 onreadystatechange ,类似函数
指针
(3)获取异步对象的 readyState 属性:该属性存有服务器响应的状态信息。每当
readyState 改变时,onreadystatechange 函数就会被执行。
(4)判断响应报文的状态,若为 200 说明服务器正常运行并返回响应数据。
(5)读取响应数据,可以通过 responseText 属性来取回由服务器返回的数据。
返回的状态:
0 - (未初始化)还没有调用 send()方法
1 - (载入)已调用 send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了

假如我有多个请求,我需要让这些 ajax 请求按照某种顺序一次执行,有什么办法呢?如何处理 ajax 跨域?

通过实例化一个 XMLHttpRequest 对象得到一个实例,调用实例的 open 方法为这次 ajax
请求设定相应的 http 方法,相应的地址和是否异步,以异步为例,调用 send 方法,这
个方法可以设定需要发送的报文主体,然后通过监听 readystatechange 事件,通过这个实
例 的 readyState 属性来判断这个 ajax 请求状态,其中分为 0,1,2,3,4 这四种状态(0
未初始化,1 载入/正在发送请求 2 载入完成/数据接收,3 交互/解析数据,4 接收数据完
成),当状态为 4 的时候也就是接受数据完成的时候,这时候可以通过实例的 status 属
性判断这个请求是否成功

var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
		if (xhr.status == 200) {
			console.log(xhr.responseText);
		}
	}
}

使 ajax 请求按照队列顺序执行,通过调用递归函数:
//按顺序执行多个 ajax 命令,因为数量不定,所以采用递归

function send(action, arg2) {
	//将多个命令按顺序封装成数组对象,递归执行
	//利用了 deferred 对象控制回调函数的特点
	$.when(send_action(action[0], arg2))
		.done(function() {
			//前一个 ajax 回调函数完毕之后判断队列长度
			if (action.length > 1) {
				//队列长度大于 1,则弹出第一个,继续递归执行该队列
				action.shift();
				send(action, arg2);
			}
		}).fail(function() {
			//队列中元素请求失败后的逻辑
			//
			//重试发送
			//send(action, arg2);
			//
			//忽略错误进行下个
			//if (action.length > 1) {
			//队列长度大于 1,则弹出第一个,继续递归执行该队列
			// action.shift();
			// send(action, arg2);
			//}
		});
}
//处理每个命令的 ajax 请求以及回调函数
function send_action(command, arg2) {
	var dtd = $.Deferred(); //定义 deferred 对象
	$.post("url", {
		command: command
		arg2: arg2
	}).done(function(json) {
		json = $.parseJSON(json);
		//每次请求回调函数的处理逻辑
		//
		//
		//
		//逻辑结束
		dtd.resolve();
	}).fail(function() {
		//ajax 请求失败的逻辑
		dtd.reject();
	});
	return dtd.promise(); //返回 Deferred 对象的 promise,防止在外

请写出原生的ajax

Ajax 能够在不重新加载整个页面的情况下与服务器交换数据并更新部分网页内容,实现
局部刷新,大大降低了资源的浪费,是一门用于快速创建动态网页的技术,ajax 的使用
分为四部分:
1、创建 XMLHttpRequest 对象 var xhr = new XMLHttpRequest();
2、向服务器发送请求,使用 xmlHttpRequest 对象的 open 和 send 方法,
3、监听状态变化,执行相应回调函数

var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
		if (xhr.status == 200) {
			console.log(xhr.responseText);
		}
	}
}

如何实现一个 ajax 请求?如果我想发出两个有顺序的 ajax 需要怎么做?

AJAX 创建异步对象 XMLHttpRequest
操作 XMLHttpRequest 对象
(1)设置请求参数(请求方式,请求页面的相对路径,是否异步)
(2)设置回调函数,一个处理服务器响应的函数,使用 onreadystatechange ,类似函数
指针
(3)获取异步对象的 readyState 属性:该属性存有服务器响应的状态信息。每当
readyState 改变时,onreadystatechange 函数就会被执行。
(4)判断响应报文的状态,若为 200 说明服务器正常运行并返回响应数据。
(5)读取响应数据,可以通过 responseText 属性来取回由服务器返回的数据。
发出两个有顺序的 ajax,可以用回调函数,也可以使用 Promise.then 或者 async 等。

Fetch 和 Ajax 比有什么优缺点?

promise 方便异步,在不想用 jQuery 的情况下,相比原生的 ajax,也比较好

vue 在 created 和 mounted 这两个生命周期中请求数据有什么区别呢?

看实际情况,一般在 created(或 beforeRouter) 里面就可以,如果涉及到需要页面加载完成之后的话就用 mounted。
在 created 的时候,视图中的 html 并没有渲染出来,所以此时如果直接去操作 html 的 dom节点,一定找不到相关的元素
而在 mounted 中,由于此时 html 已经渲染出来了,所以可以直接操作 dom 节点,此时 document.getelementById 即可生效了

说说你对 proxy 的理解

vue 的数据劫持有两个缺点: 1、无法监听通过索引修改数组的值的变化
2、无法监听 object 也就是对象的值的变化
所以 vue2.x 中才会有$set 属性的存在
proxy 是 es6 中推出的新 api,可以弥补以上两个缺点,所以 vue3.x 版本用 proxy 替换
object.defineproperty

Logo

前往低代码交流专区

更多推荐