笔试篇
1.使用CSS实现一个三角形
/* 把上、左、右三条边隐藏掉(颜色设为 transparent) */
#demo {
width: 0;
height: 0;
border-width: 20px;
border-style: solid;
border-color: transparent transparent red transparent;}
2.使用CSS实现一个轮播图
<div class="slide">
<ul class="slide-auto">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
.slide {
position: relative;
margin: auto;
width: 600px;
height: 200px;
text-align: center;
font-family: Arial;
color: #FFF;
overflow: hidden;
}
.slide ul {
margin: 10px 0;
padding: 0;
width: 9999px;
transition: all 0.5s;
}
/*//自动播放*/
.slide .slide-auto {
animation: marginLeft 10.5s infinite;
}
.slide li {
float: left;
width: 600px;
height: 200px;
list-style: none;
line-height: 200px;
font-size: 36px;
}
.slide li:nth-child(1) {
background: #9fa8ef;
}
.slide li:nth-child(2) {
background: #ef9fb1;
}
.slide li:nth-child(3) {
background: #9fefc3;
}
@keyframes marginLeft {
0% {
margin-left: 0;
}
28.5% {
margin-left: 0;
}
33.3% {
margin-left: -600px;
}
62% {
margin-left: -600px;
}
66.7% {
margin-left: -1200px;
}
95.2% {
margin-left: -1200px;
}
100% {
margin-left: 0;
}
}
3.请实现一个深拷贝。
//调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。这就是我们在解这一类题时的核心的方法。
function deepClone(obj) {
// 如果是 值类型 或 null,则直接return
if(typeof obj !== 'object' || obj === null) {
return obj
}
// 定义结果对象
let copy = {}
// 如果对象是数组,则定义结果数组
if(obj.constructor === Array) {
copy = []
}
// 遍历对象的key
for(let key in obj) {
// 如果key是对象的自有属性
if(obj.hasOwnProperty(key)) {
// 递归调用深拷贝方法
copy[key] = deepClone(obj[key])
}
}
return copy
}
4.请实现一个防抖和节流。
防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
防抖原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
适用场景:
按钮提交场景:防止多次提交按钮,只执行最后提交的一次 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
节流原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
适用场景:
拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
缩放场景:监控浏览器resize
动画场景:避免短时间内多次触发动画引起性能问题
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(
throttle(() => {
console.log(1)
}, 500),
1
)
5.找出数组中重复的值
//1.定义一个新数组,遍历源数组,值不在新数组就push进该新数组中:
function unique(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
var arr = [1,'aaa',2,4,5,2,'aaa',5,6,5];
console.log(unique(arr)); // [1, "aaa", 2, 4, 5, 6]
//set和map
var arr = [1,2,1,'aaa',2,'aaa','2'];
var newArr = [...new Set(arr)];
//或者 var newArr = Array.from(new Set(arr))
console.log(newArr) //[1, 2, "aaa", "2"]
6.请对数组进行排序
sort() 函数用于对数组单元从低到高进行排序。
rsort() 函数用于对数组单元从高到低进行排序。
asort() 函数用于对数组单元从低到高进行排序并保持索引关系。
arsort() 函数用于对数组单元从高到低进行排序并保持索引关系。
ksort() 函数用于对数组单元按照键名从低到高进行排序。
krsort() 函数用于对数组单元按照键名从高到低进行排序。
7.实现数组去重和对象数组去重。
数组去重实现的基本原理如下:
① 初始化一个空数组
② 将需要去重处理的数组中的第1项在初始化数组中查找,如果找不到(空数组中肯定找不到),就将该项添加到初始化数组中
③ 将需要去重处理的数组中的第2项在初始化数组中查找,如果找不到,就将该项继续添加到初始化数组中
④ ……
⑤ 将需要去重处理的数组中的第n项在初始化数组中查找,如果找不到,就将该项继续添加到初始化数组中
⑥ 将这个初始化数组返回
双层for循环
function distinct(arr) {
for (let i=0, len=arr.length; i<len; i++) {
for (let j=i+1; j<len; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
// splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
len--;
j--;
}
}
}
return arr;
}
Array.filter() 加 indexOf/includes
//利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,如果不等则说明该元素是重复元素
function distinct(a, b) {
let arr = a.concat(b);
return arr.filter((item, index)=> {
//return arr.indexOf(item) === index
return arr.includes(item)
})
}
ES6 中的 Set 去重
function distinct(array) {
return Array.from(new Set(array));
}
reduce实现对象数组去重
var newArr = arr.reduce(function (prev, cur) {
prev.indexOf(cur) === -1 && prev.push(cur);
return prev;
},[]);
对象数组去重
const responseList = [
{ id: 1, a: 1 },
{ id: 2, a: 2 },
{ id: 3, a: 3 },
{ id: 1, a: 4 },
];
const result = responseList.reduce((acc, cur) => {
const ids = acc.map(item => item.id);
return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]
8.手动实现JSONP跨域
1.创建script标签
2.设置script标签的src属性,以问号传递参数,设置好回调函数callback名称
3.插入到html文本中
4.调用回调函数,res参数就是获取的数据
function jsonp(url, jsonpCallback, success) {
const script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
5.设置 CORS: Access-Control-Allow-Origin:*
postMessage
9.请将’mian shi’输出为:‘ihs naim’
function reverseString(str) {
return str.split("").reverse().join("");
}
reverseString("mian shi");
console.log(reverseString("mian shi"));
//递归
function reverseString(str) {
if (str === "")
return "";
else
return reverseString(str.substr(1)) + str.charAt(0);
}
console.log(reverseString('mian shi'))
Vue篇
10.Vue和React区别,详解?
1、React严格上只针对MVC的view层,Vue则是完全的MVVM模式。
2、虚拟 DOM不一样 vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.而React,每当应用的状态被改变时,全部组件都会重新渲染。
3、组件写法不一样 React推荐的做法是 JSX + inline style, 也就是把HTML和CSS全都写进JavaScript了,即’all in js’; Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,jd写在同一个文件;
4、state对象在react应用中不可变的,需要使用setState方法更新状态;在vue中,state对象不是必须的,数据由data属性在vue对象中管理
5、vue是双向数据绑定,react是单项数据绑定
看法 :
当工程规模比较大时双向数据绑定会很难维护,vue适合不会持续的小型的web应用,使用vue.js能带来短期内较高的开发效率. 否则采用react。
11.vue中v-if和v-show有什么区别?
v-if按照条件是否渲染,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。
v-show是display的block或none,在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。
12.$nextTick是什么?
nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM
13.v-for key的作用是什么?
key的作用主要是为了高效的更新虚拟DOM;
当Vue用 v-for 正在更新已渲染过的元素列表是,它默认用“就地复用”策略
14.常用的事件修饰符?
stop:阻止事件的冒泡
prevent:阻止事件的默认行为
once:只触发一次
self:只触发自己的事件行为时,才会执行
15.Vue全家桶指的是什么?
Vue+Vue-router+Vuex+axios
16.v-if和v-for的优先级?
当v-if和v-for一起使用时,优先使用v-for
17.组件通信方式?
父子组件通信
父组件通过 props 传递数据给子组件,子组件通过 emit 发送事件传递数据给父组件,这两种方式是最常用的父子通信实现办法
<!--父组件中-->
<input :value.sync="value" />
<!--以上写法等同于-->
<input :value="value" @update:value="v => value = v"></comp>
<!--子组件中-->
<script>
this.$emit('update:value', 1)
</script>
兄弟组件通信
对于这种情况可以通过查找父组件中的子组件实现,也就是 this.
p
a
r
e
n
t
.
parent.
parent.children,在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信。
跨多层级组件通信
//假设有父组件 A,然后有一个跨多层级的子组件 B
// 父组件 A
export default {
provide: {
data: 1
}
}
// 子组件 B
export default {
inject: ['data'],
mounted() {
// 无论跨几层都能获得父组件的 data 属性
console.log(this.data) // => 1
}
}
18.vue-loader是什么?使用它的用途有哪些?
Vue Loader 是一个 webpack 的 loader,它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件
用途:解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理
19.Vue的双向数据绑定原理是什么?
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变
20.vuex中有几个核心属性,分别是什么?他们的特点?
五个核心属性:state, getters, mutations, actions, modules
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
详情可参考vuex的官方文档
21.mixins的理解及应用?
vue中提供了一种混合机制–mixins,来分发 Vue组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项
mixins 选项合并
当组件和混入对象含有同名data属性选项时,这些选项将以恰当的方式进行“合并”。比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对,混入对象的方法会被覆盖。
mixins在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了
22.请详细说下你对vue生命周期的理解以及使用场景?
Vue中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程
Vue生命周期总共可以分为8个阶段:创建前后, 载入前后,更新前后,销毁前销毁后
生命周期 | 描述 |
---|---|
beforeCreate | 执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务 |
created | 组件初始化完毕,各种数据可以使用,常用于异步数据获取 |
beforeMount | 未执行渲染、更新,dom未创建 |
mounted | 初始化结束,dom已创建,可用于获取访问数据和dom元素 |
beforeUpdate | 更新前,可用于获取更新前各种状态 |
updated | 更新后,所有状态已是最新 |
beforeDestroy | 销毁前,可用于一些定时器或订阅的取消 |
destroyed | 组件已销毁,作用同上 |
23.第一次加载会触发哪些生命周期?
beforeCreate, created, beforeMount, mounted
24.Vue中过滤器?
过滤器分为:全局过滤器和局部过滤器
全局过滤器的使用:在src下新建一个文件夹,存放自定义的过滤器,之后在入口文件的mian.js中import引入使用即可
1、 当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
2、 一个表达式可以使用多个过滤器。过滤器之间需要用管道符“|”隔开。其执行顺序从左往右
25.你是怎么封装一个组件的?
想要封装好一个组件,必须要熟练掌握的三个技能:1.父组件传值到子组件(props) 2.子组件传值到父组件(
e
m
i
t
)
3.
插
槽
使
用
(
s
l
o
t
)
。
对
于
一
个
独
立
的
组
件
,
p
r
o
p
s
是
用
来
为
组
件
内
部
注
入
核
心
内
容
;
emit)3.插槽使用(slot)。对于一个独立的组件,props是用来为组件内部注入核心内容;
emit)3.插槽使用(slot)。对于一个独立的组件,props是用来为组件内部注入核心内容;emit用来使这个组件通过一些操作来融入其它组件中
首先在components文件夹下创建一个你要封装的组件文件夹,新建一个index.vue,写入组件代码,然后在components下还有一个index.js文件,咱们要在index.js中注册上自己定义的组件
其次如果全局引用就在main.js文件中引入它,局部调用就在需要的页面引入
26.watch发生再哪个生命周期?
mounted
27.谈谈你对 keep-alive 的了解?
keep-alive 可以实现组件的缓存,当组件切换时不会对当前组件进行卸载。常用的2个属性include/exclude ,2个生命周期activated ,deactivated
28.computed 和 watch 的区别和运用的场景?
computed:计算属性,依赖其他属性值,并且computed的值有缓存,只有他依赖的属性值发生改变,下一次获取computed的值时才会从新计算computed的值
watch:更多的是一个观察的作用,类似于某些数据的监听回调,每当监听的数据发生变化时都会执行回调进行后的操作
运用场景:
computed:当我们需要进行数值计算,并依赖其他数据时,应该使用computed,利用其缓存特性,避免每次获值时,都要重新计算
watch:当需要在数据变化时执行异步或开销较大的操作是,应该使用watch,它允许我们执行异步操作,限制我们执行该操作的频率
29.Vuex的严格模式是什么,有什么作用,怎么开启?
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。也就说想要改变状态,必须要使用mutation函数
用户实例化Store的时候传入strict为true就开启了严格模式
30.proxy与Object.defineProperty的优劣势对比?
Proxy 的优势如下:
Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性;
数组新增删除修改时,Proxy可以监听到;
Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty;
对象上定义新属性时,Proxy可以监听到;
Object.defineProperty 的优势如下:
兼容性好,支持 IE9及以上,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
31.Vue3的新特性
重写了虚拟dom的实现,运行时编译,update性能提高,ssr速度提高
核心api支持tree-shaking,能够按需打包
Composition API使用纯函数分隔复用代码
新增三个组件:Fragment、Teleport、Suspense
Custom Renderer API