HTML
1.行内元素,块元素,空元素有哪些
行内:span input img
块元素:div footer header section p h1....h6
空元素:br hr
元素之间的转换问题:display:inline inline-block block
inline:转换成行内元素 不独占一行 不能设置宽高 大小被内容撑开
inline-block:可以设置宽高,不独占一行
block:块元素 独占一行 可以设置宽高
2.页面导入样式时 link和@import有什么区别
①link兼容性比@import好
②link是一个标签,@import不是标签
③加载顺序不同,浏览器先加载的是link标签 后加载@import
3.title与h1区别 b与strong区别 i与em的区别
定义:
title:概括了网站信息,可以告诉搜索引擎或者用户关于这个网站的内容主题是什么
h1:文章主题内容,告诉蜘蛛我们的网站内容最主要是什么
区别:
title他是显示的网页标题上,h1是显示在网页内容上*
对于seo优化来说,title比h1更加的重要
场景:网站的logo都是用h1标签包裹的
b与strong区别
定义
b是实体标签,用来给文字加粗的
strong是逻辑标签,用来加强字符语气的
区别:
b标签只有加粗的样式,没有实际含义*
strong表示标签内字符比较重要,用以强调的*
题外话:为了符合css3的规范 b尽量少用 改用strong就行了
i和em区别
定义
i是实体标签,用来做文字倾斜的
em是逻辑标签 用来强调文字内容的
区别
i只是一个倾斜标签,没有实际含义
em表示标签内字符重要,用于强调的
场景:i更多用在字体图表,em用在术语上
4.img标签的title和alt的区别
区别一:
title是鼠标移入图片会显示的文字,alt是图片加载失败显示的文字
区别二:
在seo的层面上,蜘蛛抓取不到图片的内容,所以在写img标签的时候为了增加seo的效果要加入alt属性来描述这张图是什么内容或者关键词
5.png jpg gif这些图片格式解释一下 分别什么时候用
png:无损压缩,同样内容体积要比jpg/jpeg大,适合做小图标
jpg:采用压缩算法,有一点失真,比png要小 适合做中大型的图片
gif:一般是做动图的
webp:同时支持有损或者无损压缩,相同质量的图片webp具有更小的体积,兼容性不是特别好
CSS
1.介绍一下css的盒子模型
css的盒子模型有哪些:标准盒子模型和ie 怪异盒子模型
css的盒子模型区别:
标准盒子模型:包括margin border padding content
怪异盒子模型:包括 margin和content (内容区包括border+padding+content)
通过css可以转换盒子模型 box-sizing:border-box cntent-box
2.line-height和height的区别
heigth就是元素的高度值 line-height是每一行文字的高,如果文字换行则整个盒子高度会增大(行数*行高)
3.css选择符有哪些?哪些属性可以继承
选择符:
通配选择符 *
id选择器 #
类选择器 .
标签选择器 div....
相邻选择器+ 下一个兄弟
后代选择器 ul li 中间空格 ul li + li 可以选择ul中所有li然后排除第一个li
子元素选择器 >
属性选择器
哪些属性可以继承?
文字系列:font-size、color、line-height、text--align....
哪些不能继承?
border、padding、margin....
4.css优先级算法如何计算
优先级比较:!import>内联>id>class>标签>通配
css权重计算:
第一:内联样式 style 权重值:1000
第二:id选择器 权重值:100
第三:类选择器 权重值:10
第四:标签和伪元素选择器 权重值:1
第五:通配 权重值:0
5.用css画一个三角形
用border边框去画和transparent透明属性 想要箭头指向哪 就把哪个位置的边框设置为不透明 其他都设置为透明即可 下面是指向下的三角形
{
height:0,
width:0,
border-top:100px solid red transparent
border-left:100px solid red transparent
border-right:100px solid red transparent
border-bottom:100px solid red
}
6.一个盒子不给宽高如何水平垂直居中
<div class=father>
<div class='box'>
</div>
方式一:弹性盒子 父盒子开启弹性盒子 并设置align-item:center和justify-content:center
.father{
display:flex
align-item:center
justify-content:center
width:300px
height:300px
}
方式二:定位
.father{
position:relative
}
.box{
position:abslute,
left:50%
top:50%
transfrom:translateX(-50%)
transfrom:translateY(-50%)
}
7.display有哪些值,说明他们的作用
none:隐藏 只是隐藏 但是还会再dom中
block:转换成块元素
inline:转换成行内元素
inline-block:转换成行内块元素
8.对BFC块级格式化上下文的理解
BFC就是页面上一个隔离的独立容器,容器里的子元素不会影响到外面的元素
如何触发BFC:
float的值非none
overflovw的值非visible(hidden等)
display的值为inline-block , table-cell...
position的值为absoute 或者 fixed
9.清除浮动的方式
1.触发BFC
设置浮动 overflow:hidden display:inline-block position:abslute fixed
2.添加clearFix类名 添加伪类
clearFix:after{
content:‘’,
clear:both
display:block
}
10.在网页中应该使用奇数还是偶数的字体?为什么? 现在问的不多了 1%左右
偶数:让文字在浏览器上表现更好看
另外说明:ui给设计图一般都是偶数的,这样不管布局还是转换px也好 方便一点
11.有几种定位?分别是根据什么定位的?有什么作用?
static 默认值 没有定位
fixed 固定定位 根据可视窗口定位(浏览器的窗口)
relative 相对于自身定位 不脱离文档流
absolute 相对于第一个有relative的父元素,脱离文档流
relative和absolute区别
区别一
relative不脱离文档流 是占位的
bsolute脱离文档流
区别二
relative相对于自身
absolute相对于第一个有relative的父元素
区别三
relative left和right都行写了的话 值存在left top和bottom只存在top
absolute 的left right top bottom可以同时存在
12.写一个左中右布局沾满屏幕,其中左、右两块固定宽200 中间自适用,要求先加载中间块、写出结构及样式
双飞翼布局 使用浮动来做的 中间width100% 自适用大小 左和右固定200 开启浮动之后,元素会在一行显示,而我们将中间设置了100%宽 就会将其他的挤到下面去了 注意是挤下去的。 我们可以设置margin值 正值向右 负值向左 。我们就可以将左盒子margin-left:-100% 右盒子margine-left:200px 自身宽度 然后中间盒子 里面添加一个main盒子 给main盒子设置padding 不能直接给center盒子设置padding
<div class="center">
<div class="main">中</div>
</div>
<div class="left">左</div>
<div class="right">右</div>
.center{
float: left;
height: 100px;
width: 100%;
background-color: pink;
}
.left{
margin-left: -100%;
float: left;
height: 100px;
width: 200px;
background-color: red;
}
.right{
float: left;
height: 100px;
width: 200px;
background-color: blue;
margin-left: -200px;
}
.main{
padding: 0 200px;
}
13.什么是CSS reset
重置CSS的样式的
14.css sprite是什么,有什么优缺点
sprite雪碧图 也叫精灵图
将多个小图片合并成一个大图片 ,设置背景图的位置 来达到显示想要的小图的效果 设置bacground-position: xxx xxx 属性来移动背景图
优点: 减少了http请求的次数 提升了性能 只需要加载一次就可以了
缺点: 维护比较差(例如图片位置或内容发生修改)
15.display:none与visibility:hidden的区别 涉及重绘和回流
display:none
1.是不占位隐藏
visibility:hidden
1.是占位隐藏
为什么一个是占位一个是不占位的? 聊一聊原理 和浏览器渲染机制有关
在输入url之后 浏览器会向服务器发请求获取代码,得到代码后会走html解析器-》里面会执行js解析器 dom解析器 css解析器 (dom解析和css解析是单独 分别进行的) 在dom和css解析完了之后 将dom和css树合并 执行render (render里执行布局layout-绘制paint-合并) layout 确定位置(这个时候页面上还没有东西 ) paint绘制后才会将东西展示出来
visibility:hidden为什么会占用位置呢?
在因为他会在dom解析的时候解析div css解析时候解析样式 因为解析了dom了 他只是对css进行了一个隐藏 dom是存在的
display:none在html为什么不占位呢?
dom会解析div css也会解析displa:none 只不过 没有把div和样式合二为一
2.重绘和回流的问题
display:none和visibility:hidden都会产生一次重绘,display:none还会产生一次回流
产生和回流一定:会造成重绘,但是重绘不一定会造成回流
产生回流的情况:改变元素的位置(left,top、、、、),显示隐藏元素....
产生重绘的情况:样式改变、换皮肤
16.重绘和回流的问题
回流一定会造成重绘,但重绘不一定会造成回流
产生回流的情况:改变元素的位置,显示隐藏元素等
产生重绘的情况:改变样式
17.opacity和rgba区别
共同性:都能实现透明效果
1.opacity取值范围是0-1之间 0表示完全透明,1表示不透明
2.rgba r代表红色 g表示绿色 b表示蓝色 取值0-255 a表示透明度0-1之间
区别:
opacity会继承父元素的opacity属性
而rgba设置的元素后代元素不会继承不透明属性
javascript
1.延迟加载js的方式有哪些
延迟加载:async 和 defer script标签上加上
例如<script src='script.js' async/defer >
async 和defer区别
defer:等html全部解析完成了 才会执行js代码,而且是顺序执行js脚本的
async:async的脚本和html解析是同步的 不是顺次执行js脚本(谁先加载完谁先执行) 如果脚本之间存在依赖关系 绝对不能用async
2.js数据类型有哪些
基本类型:string numbuer boolean undefined null ES6新增的symbol bigint比较有争议
引用类型:object
3.数据类型的考题
隐式类型转换:
布尔+number 会转成number
string+true 字符串和任何相加 其他都会转换为字符串(连接形式)
undefined+1 NaN NaN是一个数值类型 但是不是一个具体的数 除了字符串和数值类型相加,都会将其他的类型变成数值类型
typeof null Object
typeof undefined undefined
typeof NaN Nubmer
4.null和undefined的区别
最开始设置javascript的时候 先设计的null 是借鉴了java
但是null在转换成数值的时候会被转换成0,不容易发现问题,后来就设计了undefined 为了填补之前的坑 undefined转换成数值时NaN
具体区别:
null表示一个空对象指针,转换为数值时为0,undefined转换为数值时为NaN
5.==和===有什么不同
==比较的是值
sring== number || boolean || number ... 都会通过valueOf隐士转换
===除了比较值还比较类型
扩展:
null==undefined true
string==number 会将string转换成number比较
boolean==number 会将boolean转成number比较
object==string||number...会将object转成基本类型比较
6.js微任务和宏任务
考察js是怎么执行的和执行顺序
1.js是单线程的语言 为什么是单线程的? 比如我有两个操作 一个操作是创建一个新的节点,另一个操作是对这个节点进行操作,如果js是多线程的话,他有可能在操作节点的时候,这个节点还没有创建出来 就会报错
js代码执行过程:先执行同步的代码,同步的执行完了之后就会进入事件循环,事件循环中包含宏任务和微任务,先执行微任务,微任务执行完了之后再执行第一条宏任务,第一条宏任务执行完了之后然后查看微任务队列里面是否还有,如果有就执行微任务队列里的任务,微任务队列执行完了之后再执行宏任务里的下一条任务,依次类推。就是在每次执行宏任务之前都要保证清空了所有的微任务(要执行宏任务的前提是清空了所有的微任务),
同步的任务执行完了 才会执行时间循环的内容
异步任务有哪些:ajax请求 定时器、事件...
微任务:promise.then promise里的任务是同步的.then才是微任务
宏任务:setTimeout
7.js的作用域考题 *** 初级遇到概率可能到80%
1.除了函数外,js是没有块级作用域的
2.作用域链:内部可以访问外部的变量,外部不能访问内部的变量。如果内部有 优先查找内部,内部没有就查找外部 逐一向上查找
3.注意声明变量是用var 还是没有写 没有写的话其实是window.xxx var = a = b =10 b就被声明到了全局了
4.注意:js有变量提升的机制(变量悬挂声明)
5.优先级:声明变量优先级>声明普通函数(function a (){} 如果var a = function(){} 这个属于声明a)>函数参数>变量提升
面试时候怎么看
1.看本层作用域有没有此变量【注意变量提升】
2.注意:js除了函数外没有块级作用域
3.声明函数是不看写的时候的顺序的
比如 console.log(a) var a = 10 function a() {} 这个时候打印的是a这个函数
function b(a){
console.log(a)
var a = 10
}
b(3)
//打印 3
function b(a){
console.log(a)
var a = 10
function a(){}
}
b(3)
//打印a函数
考题一
function c (){
var b = 1;
function a (){
console.log(b)
var b = 2
console.log(b)
}
a()
console.log(b)
}
c()
输出undefined 2 1
考题二
var name = 'a'
(function(){//var name
if(typeof name == 'undefined'){
var name ='b',
console.log('111'+name);
}else{
console.log('222'+name);
}
})()
//注意 js中除了函数是没有块级作用域的 if(){这里的var name 会永远悬挂在函数内部} 即使这里是false。他如果不走的话是指不执行 但是会一直悬挂
//111b
考题三
function fun(a){
var a = 10
function a(){}
console.log(a)
}
fun(100)
//打印10
考题四
function fun(){
a = 10
console.log(a)//10
var a =20
console.log(a)//20
}
//这个是再次赋值 var a声明了a 然后a=10 如果本层作用域声明了 a 并且用了a=xxx这些都是再次赋值
8.js对象考题
1.对象是通过new操作符构件出来的
2对象注意:引用类型 比较的是地址
3.对象里的key都是字符串 如果Obj[对象]=‘xxx’ 会自动将这个对象转换为字符串 [object Object]
4.对象如何查找属性|方法 沿着原型链查找
现在对象本身找,找不到去构造函数中找,对象原型中找,构造函数原型中找,对象上一层原型中去查找
考题一
var a = {}
var b = {
key:'a'
}
var c = {
key:'a'
}
a[b]='123'
a[c]='456'
console.log(a[b]) //输出456
b和c是对象 会被转换成字符串[object Object] 然后后者覆盖前者
9.js作用域+this指向+原型考题
考题一***很典型
function Foo(){
getName = function(){console.log(1)}
return this
}
Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
console.log(5)
}
Foo.getName()
getName()
Foo().getName()
getName()
new Foo().getName()
//输出2 4 1 1 3
Foo.getName() //直接上面的2
getName()//var声明的优先级最高 执行的是var的那个 只要有var getName存在 那个函数就不会执行
Foo().getName()//这个Foo执行了 里面的getName没有 就挂载在window上了 返回的是也是window 然后windiw上有刚刚执行的挂载的getName 所以打印1
getName()// 这里直接从window上找 打印1
new Foo().getName() //这里 new Foo()返回的实例 先在对象上找 没找到,再去对象的原型上去找
找到了 打印3 它不会走Foo.getName 因为这个只是在Foo这个对象上的 它既不在Foo的原型上 也不在实例的原型上
考题二
var o ={
a:10
b:{
fn:function(){
console.log(this.a)
console.log(this)
}
}
}
o.b.fn()
//打印 undefined b:{}这个对象
隐式调用 this指向b b这个对象里没有a 所以是undefined
考题三
var length = 10
function fn(){
return this.length + 1
}
var obj = {
length:5
test1:function(){
return fn()
}
}
obj.test2 = fn
console.log(obj.test1())
console.log(fn() === obj.test2()))
console.log( obj.test1() == obj.test2() )
///11 false false
obj.test1() 返回了fn 会找到全局中的fn 并执行 这个是独立调用 所以是11
后面 11 === 6
11 ===6
10.new关键字做了什么***高频
1.创建了一个全新的对象
2.将这个新对象的原型指向构造函数的原型
3.将this指向新创建的对象(将空对象作为构造函数的上下文)
4.如果函数没有返回其他对象,表达式就会返回这个新对象。返回基本类型的话 会忽略基本类型 返回引用类型的话 返回值就是返回的对象
自己实现
function create(fun,...args){
let obj = {}
Object.setPrototypeOf(obj,fn.prototype)
let result = fn.apply(obj,args)
return result instanceof Object ? result : obj
}
function Fun(age,name){
this.age=age
this.name=name
}
console.log(create(Fun,18,'张三'))
11.this的指向
this是在函数执行的时候才确定绑定的是谁,同一个函数调用方式不同 this指向也不同
1.默认绑定:独立函数调用 this指向window 独立调用:没有obj.xxx 没有obj.call() 直接foo()
var obj={
name:‘xxx’,
foo:function(){
console.log(this)
}
}
var bar = obj.foo bar()这样也是独立调用 obj.foo 并没有调用
2.隐式绑定:通过某个对象来调用,将函数作为对象的方法来调用的 不管怎么变 只要式对象主体调用的 就是隐式绑定 谁调用this就指向谁
3.显示绑定 call()和apply()方法 还有bind
bind调用指定this的优先级高于默认调用
4.new一个对象 this指向新的实例对象
优先级问题:
默认规则优先级式最低的
显示绑定优先级高于隐士绑定
new绑定优先级高于隐士绑定
new高于显示显示绑定 new关键字不能和apply和call一起使用 只能和bind一起使用
总结
new>显示(call apply bind)>隐式(obj.xxx)>默认(独立调用)
12.call和apply区别还有bind
call传参的时候 多个参数之间用,号分割
apply传参的时候 将多个参数放在一个数组
他们都是将函数的this指定后 然后执行函数
bind()方法
当我们给一个对象指定this的时候 调用call和apply方法只会修改我们调用的那一次的this,如果想要多次修改,就需要每次调用call和apply方法
可以使用bind方法进行显示绑定this 他会返回一个函数 这个函数就是改变了this为我们指定的对象的函数,然后直接调用返回的函数就可以了
13.判断一个数据是不是数组有哪些方法
1.Array.isArray()方法
2.arr instanceof Array[可写可不写 不要写typeof]
3.Object.prototype.toString.call(arr).indexOf('Array')>-1
4.Array.prototype.isPrototyoeOf(arr)
5.arr.constructor.toString().indexOf('Array')>-1
14.slice是干嘛的 splice是否会改变原数组
slice是截取数组的 返回一个新数组 不会影响原数组 从第几个下标截取到第几个下标 [1,3),可以写负数表示从后面数
splice功能有:插入、删除、替换等 会改变原数组 返回的是删除的元素
第一个参数表示从哪开始 第二个参数表示删除几个 第三个参数表示替换的内容
15.数组的方法
pop()删除并返回最后一个元素
shift()删除并返回第一个元素
push()向数组末尾添加一个或者多个元素,并返回新的长度
unshift()表示开头添加一个或多个新的元素,并返回新的长度
slice()截取元素到一个数组返回 不会影响原数组
splice()插入删除替换元素 会对原数组产生影响 返回删除的元素
不常用的:
sort()排序 会改变原数组
reverse()点到数组中元素的顺序 会改变原数组
concat()连接两个或多个数组并返回结果
join()把数组的所有元素放入一个字符串,通过指定的分隔符进行分隔
高级的 需要迭代器的方法
for...in 用于遍历对象 里面的item是key 如果遍历数组 拿到的是索引值
for...of遍历的东西必须是可迭代的,对象是不可迭代的,所以不能遍历对象。他就是迭代器的一个语法糖。字符串 数组 Map Set arguments对象 NodeList集合都是可迭代对象
Array.from()方法 可以将可迭代的对象转换成数组
数组的高阶函数
filter((item,index,arr)=>{})过滤 将返回结果为true的item放到一个数组中返回
map()映射 将return的结果组成一个数组返回
forEach() 迭代遍历 没有返回值
reduce((preval,item)=>{},val)统计累加 preval为上一次执行的返回值 val是初始值 没有指定val的话默认是0
find(item=>{})返回reutrn为true时的元素
findIndex(()=>{})返回reutrn为true时的元素的索引值
16.字符串的相关方法
length属性 获取字符串长度
chartAt()返回指定位置的字符
cancat()和+一样
indexof()检索一个字符串中是否有指定的内容 有就返回第一次出现的索引 没有就返回-1 可以指定第二个参数指定开始查找的位置
lastIndexof()从后往前找
slice()不影响原字符串 将截取到的内容返回
substring()和slice类似 只是不能接收负数 写负数的话默认为0
split()根据字符串截取 返回一个数组
17.js数组去重
一般写个三种
1.使用set数据结构 new set()返回的时一个对象
Array.form( new set(arr) ) 或者 ...new set(arr)
封装函数
function unique(arr){
return ...[new set(arr)]
}
2.使用indexOf
function unique(arr){
let arr2 = []
for(let i = 0 ;i <arr.length; i++){
if(arr2.indexOf(arr[i])==-1){
arr2.push[arr[i]]
}
}
return arr2
}
3.sort排序的方式
function uniqre(arr) {
arr = arr.sort()
let arr2 = []
for(let i = 0 ; i<arr.length; i++){
if(arr[i]!==arr[i+1]){
arr2.push(arr[i])
}
}
return arr2
}
18.var和let的面试题
let arr = []
for(var i = 0 ; i<10; i++){
arr[i] = function(){
console.log(i)
}
}
arr[3]()
arr[4]()
//打印 10 10
let arr = []
for(let i = 0 ; i<10; i++){
arr[i] = function(){
console.log(i)
}
}
arr[3]()
arr[4]()
//打印 3 4
因为var没有块级作用域 var每次都是声明在全局的
就相当于
{
var i = 0
console.log(i)
}
{
var i = 1
console.log(i)
}
{
var i = 2
console.log(i)
}
.
.
.
每次后面的都覆盖掉前面的了 所以不管打印哪个都是找到全局的i 是10
而let有自己的块级作用域
{
let i = 0
console.log(i)
}
{
let i = 1
console.log(i)
}
{
let i = 2
console.log(i)
}
.
.
.
他每次在自己的作用域里能找到i 所以打印的是 3 和4
19.找出多维数组中每一项的最大值组成一个新的数组
function fn(arr){
let newArr = []
arr.forEach(item=>{
newArr.push(Math.max(...item))
})
return newArr
}
//用map
function fn(arr){
return arr.map(item=>{
return Math.max(...item)
})
}
20.给字符串新增方法实现某些功能
String.prototype.addPrefix=addPrefix
function addPrefix(str){
return this+str
}
console.log('hello'.addPrefix('world'))
21.找出字符串中出现最多次数的字符以及次数
let str = 'asd;orkwpqkpqkfwlkjqwflmfqw'
let obj = {}
for(let i = 0 ; i<str.length ; i++){
let chat = str.charAt(i)
if(obj[chat]){
obj[chat]++
}else{
obj[chat]=1
}
}
let max = 0
for(let item in obj){
if(max<obj[item]){
max = obj[item]
}
}
for(let item in obj){
if(obj[item]===max){
console.log('出现次数最多的是'+item,'出现的次数是'+max)
}
}
//注意 需要使用字符串的charAt()方法来获取到每个字符
遍历对象用for in for(item in obj)里面的item是key
用了三次遍历 第一次遍历 创建一个新的对象,遍历字符串 如果对象中有这个字符的 他的值就+1 没有就添加这个字符 值为1
获取到字符以及出现次数的对象后 再遍历对象 找到值最大的数
再遍历对象 找到值最大的对应的key是谁
22闭包***高频
1.闭包是什么?
函数之间有嵌套关系,内部函数中使用到了外部函数的变量,在内部函数定义执行的时候就形成了闭包(执行外部函数/内部函数创建)
2.闭包可以解决什么问题?[优点]
2.1优点就是可以让内部函数访问到外部函数的局部变量
2.2 就比如我的页面上有四个li,想要点击每个li的时候使用它自己的下标,就使用循环给每个li绑定点击事件。根据js的事件循环机制,是先执行同步再执行异步的,这个时候onclick就是最后执行,i已经执行完了,所以点击每个li结果都是最后的结果值,这个时候就可以使用闭包,将i驻入到内存中就可以解决问题了
let lis = document.getElementByTageName('li')
for(let i =0 ; i<lis.length ; i++){
(function(i){
lis[i].addEventListen('click',()=>{
alert(i)
})
})(i)
}
3.闭包的缺点?
变量会驻留在内存中,造成内存损耗问题
解决方式:把闭包的函数设置为null
4.项目中哪些地方用过闭包/闭包的应用 2.2
setTimeout传递参数的时候
function fun(param){
return function(){
alert(param)
}
}
var f = fun(1)
setTimeout(f,1000)
23.原型链***高频
1.为什么有原型/解决了什么问题?
对象共享属性和共享方法
2.谁有原型
函数拥有:prototype
对象有:__proto__
对象的隐式原型指向他的构造函数的显示原型
3.对象查找属性或方法的顺序
当我们想要获取到某个对象的属性的时候,如果它本身没有,就会到他的隐式原型里去查找如果有就返回,没有就会继续向它的原型的原型中去查找,一直查找到Obje的原型(Object是顶级原型)是个null 如果还没有找到就返回is not defined ,这个查找顺序形成的链条就叫做原型链
4.原型链
是什么?就是把原型串联起来
原型链的最顶端是null
24.js继承有哪些方式
方式一:ES6的calss和extends关键字
class parents {
constructor(){
this.age='18'
}
}
class children extends parents{
constructor{
super()
this.name='张三'
}
}
let o1 = new Child()
console.log(o1.name,o1.age)
方式二:原型链继承,给构造函数的原型中添加属性或者方法,实例对象中可以直接访问
缺点是:继承来的属性打印时候是看不到的,继承来的属性如果是引用类型的话是会相互影响的,实例化对象的时候无法传递参数
(数据共享)因为引用类型的属性 指向的是同一块地址。非引用类型的属性不会相互影响,因为非引用类型的属性,在父类找到之后会将他添加到自己的属性中,相当于给本类添加了一个新属性
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.running = function(){
console.log('xxx')
}
let p1 = new Person('why',18)
方式三:借用构造函数实现继承
缺点:父类函数至少会被调用两次 子类的原型上面会多出来一些属性,但这些属性是没有必要存在的,不能数据共享
function Person(name,age){
this.name=name
this.age=age
}
function Student(name,age,friends,son){
Person.call(this,name.age.friends)
this.sno=111
}
方式四:组合式继承 将原型继承和构造函数继承结合起来 解决同时实现数据共享和传参的问题
function parent(){
this.age = 100
}
function child(){
parent.call(this)
this.name='张三'
}
child.prototype=new Parent()
child.constructor = child
25.call,apply,bind的区别
他们都是用来可以改变函数体内this指向的
call和apply会立即执行,只会改变执行的这一次的this指向
bind是将改变了的this的函数返回 不会立即指向 需要手动调用
call传递参数用逗号隔开 apply传递参数时放在一个数组中 bind传递参数也是逗号隔开
应用场景
日常情况下用call就可以了
用apply的场景 比如想要使用Math.max判断数组中的最大值的时候,不能直接将数组作为参数传递,需要一个一个写,这个时候就可以使用apply 指定this为null 第二个参数传入数组即可
let arr = [1,2,3,4]
Math.max().apply(null,arr)
使用bind的场景 绑定点击事件的时候,想要改变回调里面this的指向,如果使用call和apply会立即被执行一次,没有点击也会执行。使用bind就不会立即执行了
val btn = document.getElementById('btn')
val his = document.getElementById('hs')
btn.onclick=function(){
console.log(this.id)
}.bind(h1s)
26.apply call bind手写
call
Function.prototype.myCall=function(thisBind,...args){
thisBind = thisBind?Object(thisBind):window
thisBind.fn = this
let result = thisBind.fn(...args)
delete thisBind.fn
return result
}
apply
Function.prototype.myApply=(thisBind,args)=>{
thisBind = thisBind?Object(thisBind):window
thisBind.fn = this
let result = ''
if(!args){
result = thisBind.fn()
}else{
result = thisBind.fn(...args)
}
delete thisBind.fn
return result
}
bind
Function.prototype.myBind = function(thisBind,...bindArgs){
thisBind = thisBind?Object(thisBind):window
thisBind.fn = this
return function(...newArgs){
let args = [...bindArgs,...newArgs]
return thisBind.fn(...args)
}
}
27.sort背后的原理
默认是根据uniqued编码排序的
之前是插入排序和快排
现在是冒泡排序
用法 回调里的a,b其实就是代表的数组的前一项和后一项 底层用的是冒泡排序
arr.sort(function(a,b){
return a-b//升序
})
arr.sort(function(a,b){
return b-a//降序
})
//如果数组里是对象,想要根据对象里的某个属性来排序
let arr = [{name:'xxx',age:1},{name:'xxx',age:5},{name:'xxx',age:2},{name:'xxx',age:6}]
// let result = arr.sort((a,b)=>b-a)
let result = arr.sort((a,b)=>{
return a.age-b.age
})
console.log(result)
28.深拷贝和浅拷贝以及引用赋值 ***
共同点:复制
引用赋值:指向同一个对象,相互之间会有影响
浅拷贝:只是浅层的拷贝(只拷贝最外面一层),内部引用类型的数据发生改变时时,依然会相互影响
实现方式:展开运算符,自己遍历实现
数组还可以通过一些方法来实现浅拷贝
比如:
map return item
filter return true
slice不指定开始也不指定结束
自己实现
function deepClone(obj){
const newObj = {}
for(let key in obj){
newObj[key] = obj[key]
}
return newObj
}
深拷贝:两个对象之间不再有任何关系,不会相互影响
实现方式
JSON.parse(JSON.stringfy(obj)) 如果对象中有函数的话无法对函数进行深拷贝的 因为json.stringfy不会对函数进行处理
自己实现深拷贝 【递归】
function copyObj (obj){
let newObj = Array.isArray(obj)?[]:{}
for(let key in obj ){
if(typeof obj[key] =='object'){
newObj[key] = copy(obj[key])
}else{
newObj[key] = obj[key]
}
}
return newObj
}
实际工作中都是使用封装好的工具函数来实现的
29.深拷贝和浅拷贝的应用场景
深拷贝:
就比如我在一个点击事件里将data中的数据作为参数过去了,然后回调中对这个数据进行了修改,会导致原本的数据也发生变化,这里就需要使用到深拷贝
30.loaclstorage、sessionstorage、cookie的区别
公共点:在客户端存放数据
区别:
1.有效期 localstorage不手动清除会一直存在 sessionstorage关闭浏览器窗口会被清除
cookie可以设置过期时间,只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效
2.loacl和session storage不能设置过期时间
3.存储大小限制
cookie不能超过4k
其他两个不能超过5M
根据不同的浏览器存储的大小时不同的
31节流和防抖函数
防抖:事件在触发的过程中不会去执行事件的函数,知道事件触发完之后等待一定的时间,并且在这个时间内没有发生新的事件,就会再等待后触发函数。如果在等待期间触发了事件,这个等待的事件就会重置。他在固定的事件内只会触发一次
场景:输入框搜索联想的功能 浏览器的resize缩放事件
节流:当事件触发的时候会执行事件相应的函数,如果这个事件会被频繁的触发,那么节流函数就会按照一定的频率来执行,不管中间触发多少次这个事件,执行函数的频繁次数总是固定的
场景:监听页面的滚动事件,鼠标移动事件,抢购的场景
防抖的实现
function debounce(fn,delay){
let timer = null
const _debounce = function(...args){
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fn().apply(this,args)
},delay)
}
return _debounce
}
节流的实现
function throttle(fn,interval){
let lastTime = 0
const _throttle = function(){
const nowTime = new Date().getTime()
const remainTime = interval - (nowTime - lastTime)
if(remainTime<=0){
fn()
lastTime = nowTime
}
}
return _throttle
}
H5C3
1.什么是语义化标签
header
footer
section
bottom
nav...
优点:易读性和维护性更好,seo成分会更好,蜘蛛抓取更好
缺点:低版本的浏览器不兼容 ie8不兼容HTML5标签的 可以通过html5shiv.js去处理
2.::before和:after中双冒号和单冒号有什么区别?解释一下这两个伪元素的作用
1.区别
:是伪类、::是伪元素 ===>是为了做区分
2.是什么?作用
元素before之前、元素after之后
作用:清除浮动,样式布局上也有作用
3.如何关闭ios键盘首字母自动大写
设置autocapitalize为off
<input type='text' autocapitalize='off'>
4.怎么让Chrome支持小于12px大小的文字
chrome默认字体大小是16px 每个浏览器默认字体大小可能都不一样 最小值是12
采用css3的缩放
span{
-webkit-transform:sacle(1.6)
}
5.rem和em区别
1em等于父元素font-size的大小
1rem等于根标签font-size的大小
6.ios系统中元素被触摸时产生的半透明灰色遮罩怎么去掉
a,button,input.textrea{
-webkit-tap-highlight-color:rgba(0,0,0,0)
}
7.webkit表单输入框placeholder的颜色值能改变吗?
input :: -webkit-input-placeholder{
color:red
}
8.禁止ios长按时触发系统的菜单,禁止ios和android长按时下载图片,禁止ios和android用户选中文字
禁止ios长按时触发系统的菜单
html,body{
touch-callout:none
-webkit-touch-callout:none
user-select:none//禁止长按选中
-webkit-user-select:none//兼容
}
9.自适用 /适配**
淘宝无限适配【移动端】:淘宝无限适配+布局单位使用rem
自己实现 监听浏览器窗口尺寸变化的事件resize 当窗口变化的时候 动态设置根标签的字体大小,一般设置为当前窗口宽度的十分之一。 这个函数立马就需要调用一次。在我们写样式的时候,可以使用一些插件来写尺寸大小
function flexible(){
let doc = document.documentElement//获取根标签结点
let w =doc.clientWidth/10 //获取窗口的宽度 除以 10
doc.style.fontSize = w + 'px'
}
flexible()
window.onresize = function(){
flexible()
}
10.响应式 其实问的不是特别多
1.响应式是什么?
一个URL可以响应多端
2.语法结构 使用@meida媒体查询
@media only screen and (max-width:1000px){
ul li:last-child{
display:none
}
}
only : 可以排除不支持媒体查询的浏览器
screen:设备类型
3.响应式图片【性能优化】
srcset属性用于设置不同屏幕密度下,image自动加载不同的图片
在<picture>中使用<source>来设置不同屏幕显示的图片
<picture>
<source srcset='1.jpg' media='(min-width:1000px)'>
<source srcset='2.jpg' media='(min-width:700px)'>
<img srcset='3.jpg' >
</picture>
11.布局方案
1.什么情况下采用响应式布局
数据不是特别多,用户量不是特别大,纯展示类项目适合响应式布局
例如:公司的官网 专题页面
特别追求性能的项目,不太适合响应式,因为如果添加了很多的响应式就会造成加载速度变慢
2.pc+移动端 应该左什么样的布局方案
注意:访问量还可以或者比较大、类似于淘宝网
pc是一套,pc会加入一点点响应式
移动端是一套,会使用自适用的布局方式
3.pc的设计图不匹配
ui给的是1980宽
笔记本电脑是1280的
ui图的宽度和电脑的宽度不对应该怎么办?
1.把ui图进行等比缩放,缩放成和电脑一样的尺寸
2.换1980的电脑
4.移动端的设计图
宽度一般都是750的
因为750设计图/2就是375,正好是iphone6的尺寸,我们要把iphone6作为基准点
10.scale的作用
11.动画
ES6面试题
1.var 、let 、 const区别***高频
共同点:都是可以声明变量的
区别:
1.var具有变量提升的机制 let和const没有变量提升
2.使用var可以多次声明同一个变量,let和const不行
3.var和let是声明变量的,const是声明常量的 var和let声明的变量可以再次赋值,const不能再次赋值
4.var没有自身的作用域 let和const有自身的作用域
正常开发中基本上都是使用const 因为性能最好,遇到需要重新赋值的再给改成let
2.用你知道的方法合并下列对象,进行写多个答案
const a = {a:1,b:4}
const b = {b:2,c:3}
1.ES6新增的方法 用于合并对象 Object.assign(a,b)
2.ES6新增的扩展运算符 let obj2 = {...a,...b}
3.自己封装方法
function extend(a,b){
for(let key in b){
a[key]=b[key]
}
return a
}
3.箭头函数和普通函数有什么区别***高频
1.this指向的问题 箭头函数中的this在箭头函数定义的时候就决定了,而且是不可修改的(call apply bind)
普通函数根据函数的调用方式不同,this指向不同
箭头函数中的this指向它定义时,外层第一个普通函数的this
2.箭头函数不能new 不能当作构造函数
3.箭头函数没有prototype
4.箭头函数没有arguments
4.promise有几种状态?后面一堆问题*********高频
1.promise有几种状态
三种状态pending fulfilled rejected
2.为了解决什么问题的?
为了解决axios请求回调地狱的问题,当想要发送多个请求,而每个请求都需要在上一个请求成功之后再去发送,就需要在axios.then里继续发。就会一直在.then里发 再走.then 就产生了回调地狱的问题 维护性差 可读性差
axios('').then(axios().then(axios.then()))
promise可以让异步请求写成同步代码
用了promise之后 就可以直接在then的后面 再.then
Promise.resolve('').then().then().then() 但是这样的可维护性还是很差 易读性差 一直.then也很乱
后面就出现了generator 解决了promise的(.then.then)问题
使用*修饰的方法 就会返回一个generator 通过返回的generator调用next()方法 就会执行函数内第一个yield的代码 再次调用next方法 就会执行下一个yield的代码 这样还是不够简洁
* getData(){
yield axios('xxx')
yield axios('yyy')
}
const result = getData()
result.next()
result.next()
又出现了async await 使代码更加的简洁 便于维护
await等待 异步请求成功的结果
发展史
pormise 解决axios回调地狱问题 -->generator 解决promise代码难以维护的问题 --->async await 使promise和generator的语法糖
5.find和filter的区别
区别
1.filter返回一个新的数组 find返回找到的第一个内容
2.find匹配到第一个即返回 filter返回整体(每一个匹配到的都返回)
6.some和every区别
some只要有一个符合要求就返回true every是所有都满足要求才返回true
webpack
webpack 是模块化打包工具
安装webpack
cnpm install webpack -g
cnpm install webpack-cli -g 全局安装
测试版本
webpack -v
webpack-cli -v
webpack的核心概念
入口entry:默认使src/index.js
出口output:默认是dist/main.js
loader
插件plugin
模式mode
浏览器兼容性
环境environment
多入口配置 单出口
entry:['./src/a.js','/src/b/js']写成数组形式
output:{
path:path.resolve(__dirname,'dist'),
filename:'main.js'
}
多入口多出口
entry:{ 写成对象形式 key作为文件名
a:'./src/a.js',
b:'./src/b.js'
}
output:{ //不指定出口的名字 只指定路径
path:path.resolve(__dirname,'dist'),
}
loader
GIT
VUE
1.vue2生命周期有哪些***重点 各种各样
1.有哪些生命周期
系统自带的有八个:beforeCreate created beforeMount mounted beforeUpdata Unpdated beforeDestory destroyed
加入keep-alive 会多两个 actived deactived
2.一旦进入到页面或者组件,会执行哪些生命周期 顺序
beforeCreate、created、beforeMount、mounted
3.在哪个阶段有$el,在哪个阶段有$data $el表示根节点 $data表示数据
created阶段有的$data mounted阶段有的$el
4.如果加入keep-alive会多两个声明周期
actived deactived
5.如果加入了keep-alive 第一次进入组件会执行哪些生命周期?
beforeCreate、created、beforeMount、mounted、actived
6.如果加入了keep-alvie第二次或者第n次进入组件会执行哪些生命周期
actived
原理:执行顺序和你写的顺序没有关系
vue的构造函数中就指定了函数的调用顺序,和$el以及$data在什么时候挂载
2.谈谈你对keep-alive的了解
1.是什么?
是vue系统自带的一个组件,功能是用来缓存组件的,可以提升性能
2.使用场景
比如:首页进入到详情页,如果用户在首页每次点击都是相同的,那么详情页就没必要多次发送请求了,直接缓存起来就可以了,如果不是点击的同一个,那么就直接请求
3.v-if和v-show的区别
共同点:都可以让组件展示或者消失掉
区别:
1.展示形式不同
v-if是创建一个dom结点,v-if为false的时候 页面上就不存在这个dom结点
v-show是添加一个属性 diaplay 设置为none,无理是true还是false 他都是存在的
2.使用场景不同
初次加载v-if要比v-show性能要好
频繁切换 v-show要比v-if好,创建和删除的开销太大了,显示和隐藏开销较小
4.v-if和v-for优先级
v-for的优先级要比v-if优先级高 ---是在源码中体现的
不建议将v-for和v-if写在一个标签里面
5.ref是什么
用来获取dom的
6.nextTick是什么
是什么?
用户获取更新后的dom内容的,它的回调函数会在页面dom更新完成之后才会调用
为什么要使用nextTick?
因为vue中的数据更新是异步的,数据更新应变视图变化不是实时的。使用nextTick可以保证用户定义的逻辑在是视图更新之后执行
使用场景?
需要在视图更新之后,
比如页面上有一个按钮,按钮上的文字是从data中获取的,点击按钮的时候修改data中的数据,下面紧接着就操作这个按钮结点的dom获取按钮上的文字,这个时候获取到的就是更新前的内容,
原理?
nextTick会在每次事件循环结束之后执行
7.scoped原理
1.作用:
让样式只在本组件生效,不影响其他组件
如果想要style样式在只在本组件内生效,可以在style标签上添加scoped,添加了scoped属性之后,样式只会作用到组件的根标签上
2.原理
标签中添加了scoped属性后,会组件的根节点添加一个data-v-xxx一串数字的自定义属性,然后css根据属性选择器添加样式
3.如果想要影响到组件内部的属性可以给父元素添加 (样式穿透)
less:/deep/
sass:/deep/
stylus:/deep/ 或者 >>>
8.vue组件之间如何通信
1.props
适用范围:父子之间
传递非函数就是将数据传递给子组件
传递一个函数就是父亲想要子组件的数据
特殊:路由配置props
props:true 组件可以接收params参数
props:{}对象写法,传递静态参数
props:(route)=》{}函数形式 可以自己定义传递的数据
2.自定义事件
$on $emit('事件名称',参数)
父子之间通信
3.全局事件总线$bus
所有场合都可以使用
使用:new Vue的时候 在beforeCreated阶段 Vue.prototpye.$bus=this 在vue的原型上添加$bus 并指向vue的实例化对象vm
为什么是挂载在vm身上? 因为要所有组件都能看到这个对象,并且能够使用$on和$emit方法
4.vuex
将数据进行集中管理,所有的组件都可以访问到
五个核心概念:state mutations actions getters modules
5.pubsubjs 消息订阅与发布
6.slot插槽
默认插槽:<slot></slot>中间包裹的东西就等待着父组件使用的时候传递过来 父组件使用<template>标签包裹需要传递的东西,默认插槽只能有一个
具名插槽:有名字的插槽叫具名插槽<slot name='abc'> <template slot='abc'>
作用域插槽:数据是由父组件传递个子组件展示的,而子组件展示数据的过程中数据结构又由父组件说了算
7.v-model
适用范围:父子数据双向同步,并且子组件中有表单类元素
v-model就是:value='msg' @input='msg=$event.target.value'的简写
8. .sync修饰符实现父子组件数据同步
适用范围:父子之间数据同步,一般用于不带表单项的组件
:money.sync 就是 :money='money' @updata:money='money=$event'的简写
:属性名.sync
9.$attrs和$listeners
主要用于对一个组件进行二次封装
$attrs父组件传递来的所有属性组成的对象,除了class style属性和使用props声明接收的属性
$listeners父组件传递来的所有自定义事件方法组成的对象
可以使用v-bind一次性把父组件传来的属性添加给子组件
可以通过v-on一次性把父组件传递过来的事件监听添加给子组件
10.$children $parent和ref 可以直接获取组件实例调用组件的方法
1.父传子
props 全局事件总线 vuex 获取组件实例调用子组件的方法 $attrs和$listeners
2.子传父
$emit props传递过来一个函数 全局事件总线 vuex $parent获取所有父组件实例调用方法
3.兄弟组件之间
vuex 全局事件总线 携带params参数
9.computed、methods、watch有什么区别
1.computed vs methods
computed是有缓存的 methods没有缓存 computed性能要比methods好
2.computed和watch区别
watch是监听,数据或者路由发生了改变才会执行
computed计算某一个属性的改变,如果某一个值改变了,计算属性会监听到进行返回
watch是当前监听数据发生了改变才会执行
computed只能使用同步,watch当中是可以同步也可以使用异步
监听路由
watch(){
'$route':{
}
}
10.computed、methods、watch原理
11.props和data优先级谁高
props==》methods==》data==》computed==》watch
12.vuex面试题
1.vuex中有哪些属性
state:存放公共数据
mutations:直接操控数据的,同步修改
actions:发送异步请求,提交mutitons
getters:计算属性
modules:模块化的
2.mutations和actions区别
mutations都是同步事务
actions可以包含异步操作
3.vuex是单向数据流还是双向数据流
单向的 可以用vuex中的state数据 但是不能修改 怎么证明?改不了,只能通过mutations修改
4.vuex如何持久化存储
vuex的数据不是持久化的
使用localstorage cookie在mutations中提交修改的同时将数据存储到本地,然后将state中的数据初始化的时候 读取本地存储的数据
使用vuex-persist插件 这个其实也是存储到本地的
13.vue设置代理
vue项目打包完之后出现空白页 是什么情况 看一下那个视频
打包完路径不对 在vue项目打包那个课程里
14.跨域问题
跨域是浏览器的一个安全策略,不同源的地址发送ajax请求的时候就会产生跨域,同源指的是的协议,ip地址,端口号只要有一个不一样就不是同源的。可以通过设置代理的方式来解决跨域,因为跨域是只在浏览器和服务器端产生的,服务器和服务器之间是没有跨域问题的,我们就可以使用devserver在本地开启一个小型的服务器,这个服务器和我们本地的地址是同源的,我们在发送请求的时候就会被本地的服务器拦截下来,然后再转发给远端的服务区,这样就可以解决开发中的跨域问题了。因为项目在打包后,vue的脚手架就被拆除了,所以代理也就不在了,所以这种方式只能解决开发中的跨域问题,生产环境的跨域问题需要使用nginx配置反向代理进行解决
jsonp方式解决跨域,可以将要请求的地址写在script标签的src属性上,他的src属性是不受跨域限制的。但是这样只能发送get请求
14.vue项目打包出现空白页怎么解决?为什么会出现空白页
打包时候默认路径是/需要改成./
vue.config.js文件中 module.exports={
publicPath:'./',
}
new Router({mode:hash}) 路由模式改成hash模式
项目上线要求是history模式该怎么办
前端如果自己测试项目 改成hash看看有没有问题,如果没有问题 再改成history模式 让后端去处理 重定向
15.代理和环境变量
vue-cli的环境变量里可以看
在项目的根目录新建文件
生产环境.env.development
开发环境.env.production
可以在环境配置文件里配置环境变量 然后再axios 对当前的环境进行判断,给不同的环境指定不同的地址
process.env.VUE_APP_ENV==='dev'生产环境
16.vue路由的面试题
1.vue路由模式有哪些?
默认是hash 可以修改为history
区别:1.hash模式的url后面会带# 而history模式不会携带
2.跳转请求不同
hash模式下如果路由不存在的时候不会发送请求
history模式下如果路由不存在的时候会发送请求
3.打包后前端自测要使用hash模式,如果使用history会出现空白页
2.介绍一下SPA以及SPA有什么缺点
SPA是什么?单页面应用,一个项目只有一个html文件。
优点:
用户操作页面变化只是在组件之间进行跳转,用户体验会很好
缺点:
1.SEO优化不好
2.性能不是特别好,第一次加载比较慢
3.vue路径传值
query参数:/home?a=1 显式的,url中可以看到 不占位
传:$router.push({path:'xxx',query:{a:1}})
或者$router.push(`xxxxx/?id=${}`)
接:$route.query.a
params参数:/home 隐式的 url中看不到 路由中需要 使用:xxx来占位
传:$router.push({
name:'xxx',
params:{
a:1
}
})
或者 path:'xxxx/:xxx'
或者$router.push(`xxxxx/${abc}`)
接:$route.params.a
4.query参数和params参数的区别
1.query参数会在url路径中显式而params不显示
2.query参数不需要占位,params参数需要在路由中占位
5.路由导航守卫有哪些
全局、路由独享、组件内
1.全局:beforEach、beforeResolve、afterEach(to,from,next)
2.路由独享:beforeEnter
3.组件内:beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave
应用场景:比如想要跳转到订单页面,如果登录了就跳转到订单页面 如果没有登录就跳转到登录。已经登录了还想跳转登录就拦截
6.vue动态路由 其实就是通过路径传递参数
应用场景
一般是一些详情页
17.Vue源码-数据代理
proxyData(){
for(let key in this.$data){
Object.definePrototype(this,key,{
get(){
return this.$data[key]
}
set(val){
this.$data[key]=value
}
})
}
}
数据代理是什么:data对象中的所有属性的操作都由VM对象来代理操作
优点:简化编码,可以直接通过vm对象就可以方便的操作data中的数据
如何实现的:
1.通过Object.definePrototype给vm添加与data对象的属性对应的属性
2.所有添加的属性都包含get/set方法
3.在get/set方法中去操作data中对应的属性
分两个部分去说,一个是对数据代理的理解,再去说原理
数据代理就是通过vm直接操作data中的属性数据,比如 原本应该是this._data.name来操作name 属性,在有了数据代理之后可以直接使用this.name来操作。这样可以简化代码。他的原理就是通过definePrototype方法 给vm添加与data所有属性对应的属性,并且给属性指定get和set方法,在get方法中读取data中的属性值返回,在读取属性的时候自动调用。在set方法中,将最新的name值赋给data的name属性,当修改属性的时候自动调用
19.响应式数据原理/数据绑定原理
搞清楚两个问题?
如何知道data的属性值变化了
如何知道当前这个数据变化要更新哪些节点?
vue实现数据绑定的2个重要技术
数据劫持:通过defineProperty来简直data中所有层次属性数据的编号,一旦变化就准备去更新界面
订阅者-发布者模式:能知道界面上哪些节点需要更新,并更新所有相关节点
watcher是订阅者 observer是发布者 dep是订阅器
observer:
给data中所有层次的属性都添加了getter和setter/添加数据劫持,并且为data中的每个属性创建一个对应的dep对象
dep:
订阅器 与data中属性一一对应的对象
watcher:
订阅者 用于更新节点的回调函数
在vue初始化的时候他会使用递归和definedPrototpye 给所有的属性都添加了get和set方法,同时创建一个dep对象,dep与data中的属性是一一对应的,然后会进行模版解析,解析每一个模版语法都会创建一个watcher,watcher里面绑定了用于更新节点的回调函数,并且watcher与需要更新的节点是一一对应的。然后将watcher添加到dep里面去
当属性数据发生变化时,set方法就会调用,这个时候observer就会通知dep属性发生了变化,然后dep就会通知相关的watcher去更新它对应的节点
17.Vue源码-模版解析
class Vue{
constructor(options){
this.$data=options.data
this$el=document.querySelector(options.el)
this.compile(this.$el)
}
}
compile(node){node}{
node.childNodes.forEach((item,index)=>{
if(item.nodeType == 1){
this.compile(this.$el)
}else if(item.nodeType == 3){
let reg = /\{\{(.*?)\}\}/g
let text = item.textContent
item.textContent = text.replace(reg,(match,vmKey)=>{
vmKey = vmKey.trim()
return this.$data[vmKey]
})
}
})
}
//他会通过拿到的根节点,然后通过childNodes方法获取到所有的子节点,进行循环遍历。然后对每一项使用nodeType方法进行判断,如果是3就代表是文本节点,就通过replac正则替换,把花括号里的内容替换为data中对应的内容,如果是一就递归调用自己
18.Vue源码-添加事件
在模版解析的时候
compile(node){
node.childNodes.forEach((item,index)=>{
if(item.nodeType===1){
//等于1的时候 说明是标签节点,事件只能绑定在标签节点上
//判断有没有事件属性
if(item.getAttribute('click')){
//获取事件名称
let vmkey = item.getAttribute('@click').trim()
item.addEventListener('click',()=>{
//调用回调方法
this.$options.methods[vmkey]()
})
}
}
})
}
//在模版解析判断子节点类型的时候,如果是标签节点,就尝试获取标签节点上的事件(比如点击事件等),如果有的话,就使用addEventListen绑定相应的事件,在事件回调中,调用$options.methods[getAttribute(click)]相应的方法
20.vue源码-视图更新
模版解析的时候
let watchEvent = {}
if(item.nodeType==3){
....
let text = item.textContent
//给节点赋值
item.textConten = text.replace(reg,(match,vmkey)=>{
vmkey = vmkey.trim()
if(this.hasOwnProperty(vmkey)){
let watcher = new Watch(this,vmkey,item,'textContent')
if(this.$watchEvent[vmkey]){
this.$watchEvent[vmkey].push(watcher)
}else{
this.$watchEvent[vmkey]=[]
this.$watchEvent[vmkey].push(watcher)
}
}
})
}
class Watch{
constructor(vm,key,node,attr){
//对象
this.vm=vm
//属性名称
this.key=key
//节点
this.node=node
//改变文本节点内容的字符串
this.attr.attr
}
update(){
this.node[this.attr] = this.vm[this.key]
}
}
observe(){
for(let key in this.$data){
let value = this.$data[key]
let that = this
Object.definePrototype(this.$data,key,{
get(){
return value
}
set(val){
value = val
if(that.$watchEvent[key]){
that.$watchEvent[key].froEach((item,index)=>{
item.update()
})
}
}
})
}
}
21.v-model数据双向绑定的原理
双向数据绑定其实有两个方向,一个是从data-页面的绑定,还有一个是从页面到data的绑定。
拿v-model和input输入框来举个例子
从data-页面就是 当data中的msg发生变化了,页面上input的内容也会发生变化,这个是由于数据绑定的存在,一旦更新msg 输入框就会自动更新
另一个方向是 从页面-data 当在输入框输入内容的时候,他内部其实是给input输入框绑定了一个@input事件监听,在回调函数中 会读取最新的value的值保存到data的msg属性上,而msg属性发生变化了又会触发一个数据绑定
22.diff算法
功能:提升性能
虚拟dom===》就是把dom数据化,其实就是数据
snabbdom
h函数:生成虚拟节点
patch函数 将新老节点进行替换
新老节点替换的规则
1.如果新老节点不是同一个节点名称,就是暴力删除旧的节点,创建新的节点
2.只能同级比较,不能跨层比较。如果跨层那么就暴力删除旧的,创建新的节点
3.如果是相同结点又分为很多情况
新节点有没有children
如果新节点没有children ,那就证明新节点是文本,那直接把旧节点替换成新的文本
如果新节点有children
新的有children,旧的也有children ---最复杂情况 **diff核心** 看下面
新的有children,旧的没有children==》把旧的内容删除清空,添加新的
***注意:如果要体形性能,一定要加入key,key是唯一标识,在更改前后确定是不是同一节点
diff算法核心
1.旧前 和 新前 头和头比较
匹配到了:旧的指针++ 新的指针++
2.旧后 和 新后 尾和尾比较
匹配到了:旧的指针-- 新的指针--
3.旧前 和 新后 头和尾比较
匹配到了:旧前的指针++,新后的指针--
4.旧后 和 新前 尾和头比较
匹配到了:旧后的指针--,新前的指针++
5.以上都不满足条件==》查找
到新的里面去找旧的中第一个的值,如果找到了,就将新的里的第一个结点添加到页面上,然后将新的指针++,然后到旧的结点里去找刚刚添加到页面中的那个新结点的值,如果找到了,就将旧的里面找到的那个结点设置为undefined
6.创建或者删除
到最后如果新的还剩就创建,旧的如果还有就删除
**注意:如果旧的是undefined 就会让指针继续++
21.手写diff算法---生成虚拟dom
h函数
h('ul',{},[
h('li',{key:'b'},'b')
])
function h(sel,data,params){
if(typeof params =='string'){
//h函数的第三个参数是字符串类型【意味着他没有子元素】
return vnode(sel,data,undefined,params,undefined)
}else if(Array.isArray(params)){
let children = []
for(let item of params){
children.push(item)
}
return vnode(sel,data,children,undefined,undefined)
}
}
function vnode(sel,data,children,text,elm){
return {
sel,
data,
children,
text,
elm
}
}
22.diff算法核心---patch函数
将旧的真是dom转换成虚拟dom再和新的虚拟dom进行对比
export default function(oldVnode,newVnod){
//1.将真实的oldVnode转换为虚拟dom
if(oldVnod.sel==undefined){
//没有sel就证明是非虚拟结点,就让他变成虚拟结点
oldVnode=vnode(
oldVnode.target.toLowerCase,//sel
{},
[],
undefined,
oldVnode//elm
)
}
//2.判断旧的虚拟结点和新的虚拟结点是不是同一个结点
if(oldVnode.sel === newVnoe.sel){
//是同一个结点 会有很多判断条件
patchVnode(oldVnode,newVnode)
}else{//不是同一个,就暴力删除旧的,创建插入新的结点
//把新的虚拟结点创建为dom结点
let newVondeElm = createElement(newVnode)
//获取旧的虚拟结点 .elm就是真正结点
let oldVnodeElm = oldVnode.elm
//创建新的结点
if(newVnodeElm){
oldVnode.parentNode.insertBefore(newVnodeElm,oldVnode)
}
//删除旧结点
oldVnodeElm.parentNode.removeChild(oldVnodeElm)
}
}
//vnode为新结点 就是要创建的结点
function createElement(vnode){
//创建dom结点
let domNode = document.createElement(vnode.sel)
//判断有没有子节点children 是不是为undefined
if(vnode.children==undefined){
//没有子节点,就是文本结点
domNode.innerText = vnode.text
}else if(Array.isArray(vnode.children)){
//新的结点有子节点 并且是数组
//递归 调用
for(let child of vnode.children){
let childDom = createElement(child)
domNode.appendChild(childDom)
}
}
}
//补充elm属性
vnode.elm=domNode
}
// 是同一个结点的情况下
function patchVnode(oldVnode,newVnode){
//判断新节点有没有children
if(newVnode.children == undefined){//没有子节点
if(newVnode.text!==oldVnode.text){
//新旧结点文本内容不是一样的
oldVnode.elm.innerText = newVnode.text
}
}else{ //新的有子节点
if(oldVnode.children !== undefined){
//新的有子节点 旧的有子节点 diff核心
updataChilde()
}else{
//新的有子节点 旧的没有子节点
//将就节点的内容清空
oldVnode.elm.innerHTML = ''
//遍历新的子节点,创建dom元素添加到页面中
for(let child of newVnode.children){
let childDom = createElement(child)
oldVnode.elm.appendChild(childDom)
}
}
}
}
//diff算法核心
//参数一:真实dom结点 参数二:旧的虚拟结点 参数三:新的虚拟结点
updataChilde(parentElm,oldVCh,newCh){
let oldStartIdx = 0 旧前的指针
let oldEndIdx = oldCh.length-1//旧后的指针
Let newStartIdx = 0 //新前的指针
let newEndIdx = newCh.length-1 //新后的指针
let oldStartVnode=oldCh[0] //旧前虚拟结点
let oldEndVnode = oldCh[oldEndIdx]//旧后虚拟结点
let newStartVnode = newCh[0]//新前虚拟结点
let newEndVnode = newCh[newEndIdx]//新后虚拟结点
while(oldStartIdx<=oldEndIdx&&newStartIdx<=newEndIdx){
if(oldStartVnode ==undefined){
oldStartVnode = oldCh[++oldStartIdx]
}
if(oldEndVnode==undefined){
oldEndVnode = oldCh[--oldEndIdx]
}
if( sameVnode(oldStartVode,newStartVnode)){
patchVnode(oldStartVode,newStartVnode)
if(newStartVnode) newStartVnode.elm=oldStartVnode?.elm
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
}else if( sameVnode(oldEndVnode,newEndVnode)){
patchVnode(oldEndVode,newEndVnode)
if(newEndVnode) newEndVnode.elm=oldEndVnode?.elm
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--newEndIdx]
}else if( sameVnode(oldStartVnode,newEndVnode)){
patchVnode(oldStartVode,newEndVnode)
if(newEndVnode) newEndVnode.elm=oldStartVnode?.elm
//把旧前指定的结点移动到旧后指向的结点的后面 parentElm.insertBefore(oldStartVnode.elm,oldEndVnode.elm.nextSibling)
oldStartVnode = oldCh[++oldStartIdx]
newEndVnode = newCh[--newEndIdx]
}else if( sameVnode(oldEndVnode,newStartVnode)){
patchVnode(oldEndVnode,newStartVnode)
if(newStartVnode) newStartVnode.elm=oldEndVnode?.elm
//把旧前指定的结点移动到旧后指向的结点的后面 parentElm.insertBefore(oldEndVnode.elm,oldStartVnode.elm)
oldEndVnode = oldCh[--oldEndIdx]
newStartVnode = newCh[++newStartIdx]
}else{
//第五种情况,都不满足查找
const keyMap = {}
for(let i = oldStartIdx ; i<=oldEndIdx ; i++){
const key = oldCh[i]?.key
if(key) keyMap[key] = i
}
let idxInOld = keyMap[newStartVnode.key]
if(idxInOld){
const elmMove = oldCh[idxInOld]
patchVnode(elmMove,newStartVnode)
oldCh[idxInOld] = undefined
parentElm.insertBefore(elmMove.elm,oldStartVnode)
}else{
parentElm.insertBefore(createElement(newStartVnode),oldStartVode)
}
newStartVnode = newCh[++newStartIdx]
}
}
//while循环结束 只有两种情况 新增和删除
if(oldStartIdx>oldEndIdx){
const before = newCh[newEndIdx+1]?newCh[newEndIdx+1].elm:null
for(let i=newStartIdx ; i<=newEndIdx ;i++){
parentElm.insertBefore(createElement(newCh[i]),before)
}
}else{
for(let i=oldStartIdx ; i<=oldEndIdx;i++){
parentElm.removeChild(oldCh[i].elm)
}
}
}
//判断两个虚拟结点是否为同一个结点
function sameVnode(vNode1.vNode2){
}
23.什么是mvvm 了解 认识
model-view-modelView的简写
view:视图【dom】 页面中展示的内容
model:模型【数据层:vue中的data数据】
性能优化
1.vue性能优化
1.keep-alive缓存组件
一个是首页,一个点击列表进入详情页,如果连续进入的是同一个详情页 就缓存起来,不是同一个详情页再重新发送请求
2.路由懒加载
原理?
3.内容使用
v-if 和v-show
computed、watch、methods
4.Object.freeze:冻结对象
纯展示类接口数据,冻结就可以了
5.使用ui组件按需引入
2.加载优化
1.http请求
能不能减少(能不能合并)
2.使用精灵图
3.script标签的位置
4.link标签(css引入)
3.图片优化
1.图片拦截在
原理?
2.响应式图片 使用img的srcset属性
3.使用字体图标
4.webp代替其他格式
5.精灵图
4.渲染优化
1.减少重绘和回流
2.改变位置使用transform
3.动画尽量用requestAnimationFrame,不要用定时器
5.首屏优化
1.懒加载
2.长列表 返回十万条,一次放十条,触底再加载10条
3.项目的html文件、css文件、图片、js文件压缩打包
总结性能优化
做过哪些性能优化?
v-if和v-for不能连用 如果连用的话 不管v-if是否成立 他都会执行一遍v-for
页面采用keep-alive缓存组件
合理使用v-if和v-show
key保证唯一
使用路由懒加载
节流防抖
第三方模块按需引入
图片懒加载
使用精灵图
对代码进行压缩
页面样式兼容
1.遇到过哪些兼容问题?
1.ios键盘首字母大写的问题
<input type='text' autocapitalize='off'>
2.ios日期转换NAN问题
具体就是,new Date('2020-11-12 00:00:00')在ios中会为NAN
解决方案是:用new Date('2020/11/12 00:00:00')的日期格式
3.移动端使用click事件有300ms延迟
加入一个meta标签禁止缩放即可 meta:user-scalabel=no
4.移动端点击穿透问题,怎么解决?
阻止默认行为:e.preventDefault()
5.部分安卓机上input的placeholder偏上
input{
line-height:normal
}
跨域
1.jsonp跨域
2.代理
打包后无效,需要.ENV文件解决
3.http和https的区别
1.端口不同
http:80端口
https:443端口
2.https比http更加安全
https会校验证书
web安全篇---问的概率特别低 了解一下
1.XSS攻击
用户输入的文本框,需要替换某些特殊字符(<> ...)
2.SQL注入
用户输入的文本框中不可以有特殊符合,比如引号 空格等
其他类
1.token
token是后端生成的
token+cookie 前端判断是否过期
token+localStorage 后端判断是否过期 过期了给我返回一个code 前端进行判断,如果是过期了 就跳转登录页重新登录
2.SEO
搜索引擎优化
1.网站一定要多页面 页面越多越好
2.title描述、关键字
3.图片,音频,视频的标签属性特别关键
4.网站不能出现死链接
补充
如何封装组件
使用$attr接收到父组件的所有属性绑定给子组件,然后使用$listener接收父组件传递的事件绑定给子组件 然后使用插槽自定义展示的内容
computed里的函数能否接收参数,里面的值能否双向绑定
我的理解是不能接受参数 因为他(){}写成函数形式 只是简写的方式
vue的事件修饰符
.stop 停止冒泡 .prevent 组织默认事件 .onec只触发一次
vue是怎么进行依赖收集的
data中的每一个属性都有一个dep,用来存放我们所依赖的watchr,当属性变化后通知自己对应的watcher去更新,默认在渲染的时候(获取响应式数据) ,此时就会调用dep.depend方法触发属性收集依赖,当属性发生变化的时候触发watcher 通过dep.notify()方法
输入url地址发生了什么
1.浏览器查找域名对应的ip地址
2.浏览器向web服务器发送一个HTTP请求
3.服务器重定向
4.浏览器跟踪重定向地址
5.服务器处理请求
6.服务器返回一个HTTP响应
7.浏览器进入DOM树构件
8.浏览器发送请求获取HTML中的资源
9.浏览器显示完成页面
10.浏览器发送异步请求
主要有url解析 dns查询 tcp连接 http请求 响应请求和页面渲染
url解析首先会判断你输入的是不是一个合法的url还是一个待搜索的关键字 进行对应操作
dns查询获取到域名对应的服务器ip地址
然后开始tcp连接,首先要三次握手建立tcp连接接着浏览器会发送http请求到目标服务器,服务器收到浏览器的请求后会进行逻辑操作之后返回一个http响应,响应完了之后当页面关闭,tcp再经过四次握手断开连接 。最后就是页面渲染,浏览器拿到服务器响应的资源,先对资源进行解析,比如重定向,存储cookie,解压gzip等,再根据不同的资源采用不同的解析方式,解析html构件dom树 解析css生成css规则树,合并dom树和css规则树,生产render树,布局render树 绘制页面像素信息
首先会先判断输入的是不是一个合法的url还是一个待搜索的关键字,然后进行相应的处理。如果是合法的url dns就会查询到域名所对应的服务器的ip地址,然后开始tcp连接,连接了之后浏览器会向服务器发送一个http请求,服务器收到请求后会进行逻辑操作后返回http响应。浏览器会将响应的数据进行解析,然后渲染到页面上
webpack是什么 怎么使用户 如何优化
webpack是js打包模块化的工具
优点:1.专注于处理模块化的项目,能够做到开箱即用,一步到位
2.通过plugin扩展,完整好用又不失灵活
3.使用场景不局限于web开发
import和require的区别
reuquire是运行时动态加载的,import是静态编译的
require输出的是一个值的拷贝,import模块输出的是值的引用
如何计算白屏和首屏时间
白屏是指输入网址回车-》浏览器开始显示内容的时间
首屏时间是指输入网址回车-》浏览器首屏内容渲染完成的时间
白屏时间:在head标签的最后获取当前时间,然后用这个时间去减去导航时间
首屏事件:在script标签加载完后面获取当前时间,然后减去导航时间
vuex原理
vuex是一个状态管理模式,每一个vuex应用的核心就是store,store基本上就是一个容器,他包含着你应用中大部分的状态
vuex的状态存储是响应式的,当vue组件从store读取状态的时候,若store中的状态发生变化,那么相应的组件也会得到动心 改变store中状态的唯一途径就是mutation 提交修改
echarts
路由传参怎么进行加密
可以使用一些加密工具将参数进行加密 ,服务器端接收到再进行解密
md5
git常用指令
http请求头里都有什么属性
:path请求路径
:method 请求方法
host 发送的服务器主机名和端口号
connection 客户端告诉服务器当前的tcp连接的状态
accept:告诉服务端客户端接收什么类型的数据
js中循环性能最高的是哪个
for循环
promise的api
promise.resolve
promise.reject
promise.all 传入一个数组,数组里是promise的实例 如果传入的不是promise实例 会被转成promise.resolve(xxx) 如果数组里的promise全部都是成功的,就会将所有成功的结果放在数组里返回,如果有一个失败,那么整个promise.all就是失败的 就会返回第一个失败的promise
promise.allSettled方法 它和all一样 不过他会将所有返回的结果都放到一个数组中
promise.race方法 传入一个数组 只要有一个是成功的 就是成功的并作为结果返回 如果成功之前有一个先失败的 就返回失败的那个
promise.any方法 至少等到有一个成功的才返回,如果全失败的话会等到所有的都执行完了之后咋偶倒catch中
promise是同步还是异步
promise本身是同步的,.then是异步的 promise返回一个promise
data为什么是一个函数
主要是为了提高组件的复用性,因为对象都是引用类型的,如果多个组件引用同一个对象的时候,一个组件改变这个对象中的数据,其他的组件也会受到影响。而使用函数的方式,可以保证每次都是返回一个新的对象,组件之间不会相互影响
Object.create和nwe Object有什么区别
Object.create没有继承Object原型上的属性和方法
而new Object的对象 继承了Object原型上的属性和方法
说几个ES6新增的方法
let const 模版字符串 箭头函数 函数的默认参数 可以直接在对象中写变量
Object.keys()方法
Object.assign() 将多个对象的属性和方法合并到目标对象上面 第一个参数是目标对象 后面都是源对象 是浅拷贝
Array.from()
Object.setPrototypeof()
Object.getPrototypeof()
string.startsWith()
string.endWith()
for of循环
promise
set和map
class类
vue首屏加载优化
1、把不常改变的库放到 index.html 中,通过 cdn 引入2、Vue 路由的懒加载
3、不生成 map 文件
4、Vue 组件尽量不要全局引入
5、使用更轻量级的工具库 6、开启 gzip 压缩
7、首页单独做服务端渲染
动态组件
使用<component> 标签 使用:is添加组件名 控制is的值就可以动态切换组件
vue的核心是什么
组件化和双向数据绑定
vue的优缺点
优点:
数据驱动
组件化
轻量级
SPA单页面应用
入门比较简单一些
缺点:
不支持ie8一下浏览器
比较吃内存(每个组件都会实例化一个vue实例,实例的属性和方法很多)
定义在data里的对象,实例化的时候都会遍历转换成响应式数据,然而有的响应式数据我们并不会用到 造成性能上的浪费
C3增加了哪些特性
圆角border-radius
盒子模型box-sizing
阴影box-shadow盒子阴影 text-shadow 文字阴影
过渡transition
2D转换 transform
flex弹性布局
媒体查询@media
自定义动画@keyframs animation
背景 background-size background-orgin background-clip
在数组中如何查找一个数
includes方法 indexof 和lastIndexof
如何实现数组的平铺
flat方法
cookie的字段
httponly:只允许https去传递这个cookie
sameSite:
maxage:设置过期时间 --可以用以删除cookie
webpack
使用过哪些webpackloader
cssloader
lessloader
urlloader:判断一个文件是否可以转换成dataUrl的格式
babel-loader
什么是伪数组
有length属性并且有索引值的对象 就可以被称为伪数组
如何将伪数组转换为真实数组
三点运算符
Array.from()方法
通过.call调用数组的slice,splice,concat
如何遍历伪数组
转换成数组再进行遍历
arr.prototype.forEach(call,xxxx)
ajax
ajax可以在不重新加载整个网页的情况下,对网页的部分内容进行更新。无刷新更新内容
axios是基于promise封装的 他会自动将数据转换为json格式
什么是原型
每一个构造函数都有一个prototype属性,他指向一个对象。该构造函数的实例可以共享这个prototype对象中所有的属性和方法
异步编程的实现方式
回调函数 多个回调函数嵌套会造成会回调地狱问题
promise 可以链式调用
generator
async函数
伪类和伪元素
:hover :active :first-child 伪类
::before ::after 伪元素
代码评审
透明属性
opicaty 可以被继承
rgba 不会被继承
对vue的理解
数据驱动视图,用来做单页面应用的,双向数据绑定 使用虚拟dom diff算法]
vue是一个渐进式的js框架 他是以核心库加插件的形式 他的 最大的特点就是mvvm模式
代码简洁 体积小
mvvm模式是什么
model 数据层
vive model 连接model和view之间的一个桥梁
view是视图层
当model层的数据发生改变的时候 view-model通知view的界面发生改变 当view界面上的数据发生改变的时候 也需要通过view-model通知我们的model把数据改变
你对渐进式框架的理解:我只我想用和能用到的部分 vue不会一次性让我们接收他的全部
封装组件的思路
根据项目需要,如果是单一组件就没有必要去封装了。如果需要复用的话,首先会去考虑他的复用性,看后端的数据接口,来封装一些属性和方法
http和https区别
https是有状态的 因为要校验证书
http是无状态
http是明文的 https是加密的
https更加安全
http的三次握手:浏览器-服务器 服务器接收到 目的:确定是不是我的目标服务器
MVVM框架的理解
model数据层
view-model连接层
view视图层
model数据层的数据发生变化的时候,会通过view-model通知view数据发生变化了,然后改变页面上的显示
反之亦然
h5和c3的新特性
h5新增的语义化标签
比如footer header nav main section article aside等有利于seo'优化
h5新特性标签 :音频标签audio 视频标签video canvas画布 svg 地理位置 拖放api webWorker另外加载js文件
webStorage 本地存储方式
WebSocket 通信建立长链接
c3新特性
flex 选择器 标准盒子模型和怪异盒子模型 阴影 box-shadow text-shadow transition过渡 transform animation 媒体查询
引入了flex布局模式,引入了
flex
为父盒子设置flex之后 子元素的float
flex-grow:表示在flex容器中分配剩余空间的比例
flex-shrink指定flex元素的收缩规则
flex-basis指定flex元素在主轴方向上初始大小
实现左侧固定右侧自适应
flex-grow:1
全局守卫和局部和组件内守卫谁先运行
先走全局再走局部
移动端的兼容问题
ios和安卓兼容
好像for in是一个 有些问题 他会遍历原型上的东西 性能差
ios某种机型上 会出现双重循环的问题for in
有什么要问我的
几个面试
用的是什么技术
this遇到的坑
代码规范
怎么判断这段代码好还是不好
outline设置边框和borde区别
outline的效果是跟随元素的focus二自动出现的,不会影响元素的尺寸或位置
box-shadow 的参数
水平阴影的位置 垂直阴影的位置 模糊距离 阴影的尺寸 阴影的颜色 将外部阴影改为内部阴影
css的引入放在header script放在body里 为什么
避免操作dom失效 js会阻塞html的解析,将js放在后面可以减少浏览器空白页的显示时间
如果css放在后面的话,可能会出现布局混乱的情况 直到css加载完成
for in和for of
for in 会遍历整个原型链 性能非常差 不推荐使用 他不能遍历数组
for of 可以遍历数组和对象 遍历数组会把下标作为对应的属性值
forEach可以跳出循环吗
break只能跳出原生的循环
forEach可以使用return代替break
0.1+0.2为什么不等于0.3 要怎么处理
小数运算会转成二进制,会有精度丢失的问题
可以*100然后运算 运算完成之后再除以100
事件修饰符
v-model的 .trim
@click的 .stop .prevent .once .async
v-bind :
vue组件按需加载和普通加载 写法的区别
普通加载就是引入后 components 写 按需加载是使用import
import是es6新增的函数 他会返回一个promise
vue里的transiton过渡 动画
1.使用transiton标签将需要过渡的标签包裹起来
2.给transiton的过渡动画起个名字
3.设置name-enter name-enter-to name-enter-active的效果
.name-enter是开始时候的样式
.name-enter-to是最终完成后的样式
.name-enter-active是设置过渡的效果
前端帐号密码传输加密
树形结构
后端返回的数据是没有层级的,需要递归来处理成树形的数据
每一条数据都有一个id和pid 顶级数据的pid为0 ,二级数据的pid是上一层级数据的id
function treeTo(list ,root){
let arr = []
list.forEach(item=>{
if(item.id===root){
const children = treeTo(list,item.id)
if(children.length){
item.children = children
}
arr.push(itme)
}
})
return arr
}
项目问题
移动端遇到的困难,后面是怎么解决的
ios上 new Date(2021-08-11 12:00:00) ios上通过new Date获取时间的时候 如果日期用的是-链接 会显示NaN 需要改成/链接
android部分机型使用heightl和line-height设置垂直居中,但是显示会偏高的问题,这是因为有些android机型有自己的默认的line-height 需要将line-height:normal在通过padding去设置居中就可以了
移动端点击会有300ms的点击延迟 这是因为手机系统要识别是单机还是双击的 解决:在meta标签中设置禁止缩放就可以了
项目中做过比较复杂的封装有哪些
有封装过一个分页器组件
需要传递的参数:当前页,总条数,每页的数量,连续页数
上一页,1,...,当前页,...总页数,下一页,总条数
难点:需要计算...的起始点和结束点
当总页数小于连续页数的时候,第一页就是1 最后就是总页数
当总页数大于连续页的时候,start是当前页减去连续页/2向下取整,end是当前页+连续页/2向下取整
然后要进行修正,因为start可能小于1或者end大于总页数了
start<1时 算出这个差值 1-staret start+dis end+dis
end>total 算出插值 total-end start-=dis end-=dis
基于elment-ui的为树形结构 封装成一个组件,主要是对后端传来的数据 进行处理,添加children节点 转换成树形结构
pc端遇到比较难的技术/比较坑的地方
使用swiper插件没有轮播效果
将new Swiper的时候放在nextTick中,还需要使用watch监听轮播的数据是否已经到了
使用编程式导航路由传参,连续点击两次会报错 重写router原型上的push方法,在里面使用catch捕获一下错误
使用代理模式绑定点击跳转模块
给父节点绑定点击事件,当点击子节点的时候,回调中就会有event对象,就可以知道点击的是哪个对象。传参是通过data-xxx分别为一级二级三级导航添加对应的属性名,然后event.target中就会dataset对象 里面可以得到对应的属性值
三级分类列表功能
使用编程式导航代替声明式导航,利用事件委托event.target,然后利用自定义的data标签属性保存参数携带过去,通过event.target中dataset对象里获取 然后 使用节流处理鼠标在三级分类上的移动操作
发送请求是在哪个阶段
created 如果在mounted的话 就会再页面已经挂载完成了 再发送一次请求,会导致页面多更新一次
支付模块
使用qrcode将后端返回的支付url转成二维码,然后在页面上面展示。同时开启一个定时器,轮询向服务器发送请求查询支付结果,支付成功就清除定时器 关闭弹窗。用户点击取消 也清除定时器
动态路由是怎么使用的/权限管理
后端会返回该用户的权限列表 然后对这个权限列表进行遍历,将他能访问的页面 在路由守卫中使用router.addRoutes()方法 添加到静态路由中去,然后动态遍历显示
使用router.addRoutes()方法 我们在导航守卫中对不同的用户进行判断,将他们已有的路由权限使用addRouters添加进去就可以了。他添加的路由不是动态的,所以还需要读取添加后的所有路由,然后遍历去显示
功能管理用的是mixin混合,处理按钮是否能够点击
如何画一个扇形
画三角形一样 多了圆角属性
什么是事件代理
事件冒泡是从里向外
事件捕获是从外向里 先事件捕获 再事件冒泡
默认情况下 事件是在冒泡阶段触发 e.target
将子的事件委托给父处理
事件代理就是将子集的事件委托给父亲,他是利用了事件捕获和事件冒泡的机制,比如ul下有四个li 给ul绑定事件,点击li的时候也会触发,可以通过e.target来获取点击的节点
重绘和回流
回流:页面中的元素的结构或者尺寸发生改变,浏览器会重新渲染部分或者全部文档的过程就叫做重绘
浏览器窗口发生变化 页面首次渲染 添加删除节点等
重绘:当页面中元素只改变样式,不影响位置,浏览器会重新将样式赋予给元素 这个过程就交重绘
背景色 visibility
性能:回流性能消耗比重绘大 因为重绘值影响他自己
避免:css避免设置多层内两样式
js:避免频繁操作dom
git
git checkout -b <分支名>从当前节点创建分支
git branch 列举所有分支
git checkout <分支名> 单纯的切换到某个分支
git branch -D <分支名> 删掉特定的分支
git merge <分支名> 合并分支
git merge --about 放弃这次合并
git push提交
git pull拉取
公司问题
公司人数,业务方面
黑马头条
项目介绍
黑马头条移动端是一款 IT 咨询移动 web 应用,有着和今日头条类似的资讯浏览体验,主要功能包含, 讯列表、标签页切换,文章举报,频道管理、离线频道,文章详情、阅读记忆,关注功能、点赞功能、评论功能、回复评论、搜索功能、登录功能、个人中心、编辑资料、小智同学,并且可以打包成一款,APP 应用,火气结合 h5+Dcloud 打包成一款体验较好的手机应用
组织架构
vant组件库 vue-cli脚手架
人员配置
产品经理:1,确定需求以及给出产品原型图。 项目经理:1 人,项目管理。
前端团队:2 人,根据产品经理给出的原型图制作静态页面。后端团队:2 人,根据项目经理分配的任务完成产品功能。测试团队:0 人,前后端开发人员联调解决。
运维团队:1 人,项目的发布以及维护,其中小程序的上线和发布由前端团队完成。
登录注册模块
1、引入 vant 组件库,配置页面 rem 适配,完成移动页面适配2、使用 veux 完成对本地存储中的 token 进行统一管理
3、表单校验,检测用户信息是否符合项目要求
4、统一封装登录请求 api
技术亮点
表单验证,token,token过期及统一添加
文章列表模块
业务实现思路
1、页面基础布局
2、展示频道列表
3、展示文章列表
4、上拉加载更多
5, 时间格式处理
6,优化缓存组件及阅读记忆
7,图片懒加载
技术亮点
上拉加载更多, 下拉刷新, 标签列表,图片懒加载,缓存组件抽离
更多的操作
业务思路
1、登录后通过 Vuex 状态,更新频道与文章
2, 更多操作组件准备,以及实现不感兴趣
3、通过组件通讯,实现举报文章功能
技术亮点
Vuex,组件传值,文章删除
频道管理
业务思路
1、准备组件,并且完成页面基本布局
2、通过 API 渲染频道列表数据
3、重构我的频道 API,实现兼容本地存储
4、实现删除,添加我的频道 API 选项
技术亮点
频道渲染,本地存储,组件抽离,API 封装优化
搜索中心
业务思路
1、完成搜索中心基础布局
2、结合本地存储,实现搜索记录
3、wacth 监听本地搜索关键字,实现联想词条,并结合函数防抖实现搜索
优化
单独对搜索结果,上拉加载进行独立封装
技术亮点
搜索联想,watch 监听,函数防抖,API 独立封装
文章详情
1、封装接口数据,完成基本结构渲染,以及文章记忆
2、结合 Vuex 管理本地存储状态,实现关注与取消及点赞与不喜欢
3、上拉加载数据,实现评论列表数据渲染
4、根据 API 接口文档,实现回复与评论
文章记忆,点赞与取消,上拉加载数据,封装独立 API 接口
个人中心
1、利用 Vuex 实现退出登录,并且清除 token 信息2、结合 vant 对应组件,实现基础布局
3、使用 file 组件实现对头像上传
Vuex, 个人信息修改,退出登录,token 操作
项目介绍话术
首先描述自己所做项目有哪些功能模块,然后描述其中单个模块有哪些功能, 再对其中的一个单独功能进行详细描述,中间可以穿插一下遇到的技术问题,循 环往复,和面试官保持平等对话。
举例:
我在上家单位最近做的项目是一款移动端项目,主要有文章列表,搜索模 块,个人设置等,我在其中负责,首页数据渲染,个人中心设置,其中在个人设置 模块中,使用 socket.io 实现了在线客服咨询功能,并且该项目整体开发完毕之后,使用 Gzip 打包优化项目,并且利用 HbuilderX 打包成了一款混合是 app
开发中遇到的问题
1.如何做到 webpack 热更新
2.什么是长缓存,在 webpack 中如何做到长缓存优化?
3.什么是 Tree-shaking?css 可以 Tree-shaking 吗?
4.webpack 引入 css 报错 cannot resolve module ‘style’?
5.webpack 局部安装,命令报错(不是内部命令)
6.路由发生了变化,但是页面数据无法实现刷新
7.为何执行了 npm run bulid 打包后的 index.html 页面打开成了空白
8.项目打包后,index.html 文件无法打开
9.如何只在当前页面中覆盖 Ui 组件库中的样式
10.打包后生成很大的.map 文件
11.fastClick 的 300ms 延迟解决方案
12.路由懒加载
13.如何去做性能测试的?
14.打包项目后,出现文件,图片,背景图资源等不存在或者路径错误的问题
15.本地开发项目中,遇到跨域问题,该如何解决
16.实现 Vue 路由拦截浏览器需求,进行一系列操作,如保存草稿等
17.如何控制指令的编译优先级
18.Watch 监听 object 时,为什么需要开启深度监听
19.Vue 中数组和对象已经更新,但是视图无法实现更新
20.Computed 中返回一个数组,但是交换两个元素的位置不能触发 computed 的更新
黑马头条项目
使用rem适配方案
使用postcss-pxtorem和lib-filexible做适配
lib-filexible用于设置rem的基准值的
postcss-pxtorem是postcss的一款插件 用于将单位转换为rem
axios二次封装
封装了哪些东西?
baseURL地址
timeOut过期时间
Progress展示进度条,在请求拦截器里面开启,响应拦截器里关闭
在响应拦截器中对出现的错误进行统一的处理,服务器的错误就直接弹窗告诉用户,如果是token的错误,就判断类型,是过期了还是什么问题,如果是过期了就跳转到登录页面重新登录。
axios请求回来的数据都会默认在最外面加上了一层data,所以在响应拦截器中直接将data结构出来进行返回
在请求拦截器中,将token携带在请求头上面
登录模块
表单验证 :使用的就是vant组件库中表单校验的功能,利用正则表达式来进行校验
token的持久化存储:在vuex中,获取到用户返回的token,提交到state修改的时候 将token使用localstorage存储到本地,然后在state中的token初始化为从本地读取的
文章列表模块
使用list组件,实现下拉刷新和滑动到底部自动加载更多的功能
下拉刷新是在下拉刷新的时候 再发一次请求,然后把新的数据放到文章数组的前面
滑动底部加载更多是添加到后面
使用过滤器处理时间
频道列表的模块
后端返回的数据中只有我已经关注的列表,和全部的频道列表
剩下的列表就通过filter过滤属性 计算得到
频道本地化存储
搜索模块
搜索建议联想,搜索建议中相同的文字高亮显示
使用防抖函数
点击联想跳转到搜索结果
文章详情模块
使用params传参
使用keep-alive缓存组件
图片预览
个人信息编辑
token的无感刷新
统一处理页面的访问权限
人资管理项目
登录模块
使用rules校验函数,对手机号和密码的格式实现校验
使用token作为用户登录的唯一标识 并且存储在localstorage中 通过vuex统一管理token 并且实现token持久化
axios的请求拦截器中注入token
使用vue-router 的路由前置守卫 队token的统一检测和拦截登录
技术亮点:token localstorage vue-router axios
主页模块
封装action获取用户资料 用户登录后
使用vuex的getters属性
封装action 实现用户退出登录 调用commit方法 清除vuex中保存的tolen信息 和localstorage中的token
根据后端检测token返回的状态码,在相应拦截器中对失效的token信息实现拦截
技术亮点:vuex token 请求拦截器
组织架构模块
利用el-tree组件实现结构层渲染 结合slot插槽渲染数据
返回的tree数据 封装函数来将他转换成树形的
使用async await对部门中的数据进行增删改
技术亮点:
tree树形组件基本使用以及动态处理数据
员工管理模块
实现员工的升序降序排列
使用xlsx插件 通过注册的方式拓展为全局插件 实现对表格数据的批量导出
技术亮点:excel组件导出数据
员工详情
使用qrcode生产二维码插件
项目中哪些模块是你负责的
阅头条项目中:
登录注册模块,首页模块,详情页模块,搜索模块
蕾宝COS系统:
登录注册,首页,权限处理模块
乐购商城:
登录注册,购物车,订单详情,首页
自我介绍
面试官你好,我叫徐子轩,我面试的岗位是前端开发,我从20年3月开始从事前端开发职业,到现在已经有将近两年的工作经验了。我掌握的前端技能有 布局方面html div css H5 C3 代码管理工具用的是git 前端开发工具用的是vue
项目方面:第一个是电商项目 我负责的模块有订单,支付,购物车,登录,登录包括短信验证登录和密码登录首页
第二个是 和这个电商配套的后台管理的项目,主要包括商品管理模块
第三个是 一个是公司自己使用的后台管理项目,使用的是vue+element ui