css
如何让一个盒子在水平方向和垂直方向都居中
- flex
display:flex; justify-content:center;//水平居中 align-items:center;//垂直居中
- grid
/* 这里引用复用代码 */ .outer { display: grid; } .inner { justify-self: center; /* 水平居中 */ align-self: center; /* 垂直居中 */ }
- absolute+transform
- absolute+calc
- absolute+margin
- absolute+margin:auto
less或者sass相比于css有什么优势?
- 嵌套的样式模块清晰,便于开发人员看起来更清晰
- 样式看起来更加整洁,可读性佳,便于后期的维护
如何做响应式布局或者如何适配
- 百分比布局,相对于父元素设值宽高为百分比
- 媒体查询,利用媒体查询设值不同分辨率下的css样式
- rem响应式布局
-
- 当前页面中元素的rem 单位的样式值都是针对于html 元素的font-size 的值进行动态计算的,所以有两种方法可以达到适配不同屏幕:
-
- 第一种利用媒体查询,在不同分辨率下给 html 的 font-size 赋值。
-
- 第二种利用 js 动态计算赋值,详细代码如下图
- flex弹性布局
css sprite(雪碧图或者精灵图) 有什么优缺点?
- 减少加载网页图片时对服务器的请求次数
- 提高页面的加载速度
- 兼容性好,IE>=8 IE=7(png8)
你知道哪些css3新特性和h5新特性
- h5
-
- 拖拽
-
- canvas
-
- 地理定位
-
- 音频视频audio、video
-
- 本地存储:localStorage - 没有时间限制的数据存储;sessionStorage - 针对一个 session 的数据存储,当用户关闭浏览器窗口后,数据会被删除
-
- 语义化标签header、footer、section、nav、aside、article
标签 描述 header 定义了文档的头部区域 footer 定义了文档的尾部区域 section 定义文档中的节(section、区段) nav 定义文档的导航 aside 定义页面的侧边栏内容 article 定义页面独立的内容区域 detailes 用于描述文档或文档某个部分的细节 summary 标签包含 details 元素的标题 dialog 定义对话框,比如提示框
- 语义化标签header、footer、section、nav、aside、article
- css3
-
- 圆角border-radius
-
- 动画,2d
-
- 线性
-
- 选择器
-
-
- nth-child()
-
-
-
- nth-of-type
-
-
- 盒子阴影
-
-
- box-shadow: 10px 5px 10px #ccc;
-
-
-
- box-shadow:水平阴影 垂直阴影 模糊距离(虚实) 阴影尺寸(影子大小) 阴影颜色 内/外阴影;
-
-
-
- 前两个属性是必须写的。其余的可以省略。
-
-
-
- 外阴影 (outset) 是默认的 但是不能写 , 想要内阴影可以写 inset
-
-
- 透明度
-
-
- rgba
-
-
-
- opacity
-
-
- 背景:线性渐变
-
-
- background-image
值 描述 补充 linear-gradient() 创建一个线性渐变的 "图像"(默认从上到下) radial-gradient() 用径向渐变创建 "图像"。 repeating-linear-gradient() 创建重复的线性渐变"图像" repeating-radial-gradient() 创建重复的径向渐变"图像" inherit 指定背景图像应该从父级元素继承
- background-image
-
-
-
- linear-gradient:线性渐变
1、background-image: linear-gradient(red, yellow, blue);【默认从上到下】
2、background-image: linear-gradient(to bottom right, red , yellow);【从左上到右下】
3、background-image: linear-gradient(45deg, red, green)【从左往右45度】
- linear-gradient:线性渐变
-
-
-
- radial-gradient(red, green, blue) 径向渐变
值 描述 position 定义渐变的位置
center(默认):设置中间为径向渐变圆心的纵坐标值。
top:设置顶部为径向渐变圆心的纵坐标值。 bottom:设置底部为径向渐变圆心的纵坐标值shape 定义圆的类型:
ellipse(默认)指定椭圆形的景象渐变
circle:指定圆形的径向渐变size 定义渐变的大小
farthest-corner(默认)指定径向渐变的半径长度为从圆心到离圆心最远的角
closest-side :指定径向渐变的半径长度为从圆心到离圆心最近的边
closest-corner : 指定径向渐变的半径长度为从圆心到离圆心最近的角
farthest-side :指定径向渐变的半径长度为从圆心到离圆心最远的边
- radial-gradient(red, green, blue) 径向渐变
-
js
js数据类型有哪些?有什么区别
JS的基本数据类型:Undefined、Null、Boolean、Number、String ,sysmbol
Undefined类型只有一个值,即特殊的undefined,声明变量但是没有初始化,这个变量的值就是undefined
Null类型只有一个值null,表示一个空对象指针,正式使用typeof操作符检测null会返回object
Boolean有两个字面值:true和false
Number:用来表示整数和浮点数,还有一种特殊的值即NaN,这个数值用来表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)
String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由单引号(')或双引号(")表示。
三大引用类型
Object类型
Array类型
Function类型
存储空间:基本类型存在栈中,引用类型存在堆上
值传递:基本类型传递的是值,引用类型传递的是地址(引用)
作为函数的参数:基本类型传递的是值,引用类型传递得是地址
谈一下你对作用域的理解
- 作用域是指变量(标识符)的可以使用的区域,作用域决定了代码区块中变量和其他资源的可见性
- 先从当前作用域去查找变量再去他的父级查找变量,这样的一个链条叫作用域链
- 全局作用域:在script标签内任何位置都可以使用的
- 局部作用域:只能在某一个函数里面使用,其他地方都不可以使用
- 块级作用域ES6:{} for if
谈一下你对原型的理解
- 原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享 通过构造函数生成实例对象 时,会自动为实例对象分配原型对象。 而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。
- 每个函数都有一个原型对象
- 原型链的尽头是Object.prototype
Object.prototype.__proto__
===null
什么是闭包(要改,加上闭包的优缺点)
- 闭包是建立在一个函数内部的子函数,由于其能访问到上级作用域的函数,即使上级函数执行完,作用域也不会随之销毁,这是的子函数就是闭包,变拥有了访问上级作用域中的变量权限,即使上级函数执行完后,作用域内的值也不会被销毁
- 本质上闭包就是将函数内部和函数外部链接起来的一座桥梁
如何修改函数的this指向,这些方法之间有什么区别?
可以使用apply、bind、call方法改变this指向(并不会改变函数的作用域)。区别如下:
(1)三者第一个参数都是this要指向的对象,也就是想指定的上下文,上下文就是指调用函数的那个对象(没有就指向全局window);
(2)apply和bind的第二个参数都是数组,call接收多个参数并用逗号隔开;
(3)apply和call只对原函数做改动,bind会返回新的函数(要生效还得再调用一次)。
事件委托或者事件代理的原理是什么?
事件冒泡
- 事件委托就是对于事件处理程序代码的一种优化技巧
- 事件委托就是处理页面事件程序的一种方案,这种方式就是利用冒泡或者捕获机制来完成页面上某一类事件的统一处理
- event对象在冒泡里面是会一直传递给父元素。就可以在父元素的事件函数里面获取到子元素event这样就可以去除,根据结果来执行不同的业务
- 原理:利用冒泡机制以及搭配event对象可以减少事件处理程序的代码
事件冒泡和事件捕获的区别是什么?
- 事件冒泡:从目标事件源一直到window,是自下而上的去触发事件
- 事件捕获:从window到目标事件源,即自上而下的去出发事件
你知道的es6新特性有哪些?
- let和const
- 模板字面量``
- 解构
- for。。。of
- for 。。in
- 展开运算符
...
- 箭头函数
js异步编程方式有几种
- promise
- 回调函数
- async函数
promise有几种状态
- pending:对象初始化状态,异步对象已经创建完毕,但是没收到异步结果
- fulfilled:调用resolve()状态就会变,代表异步处理成功返回结果
- rejected:调用reject状态就会变,代表异步处理失败返回的结果
- 当我们创建一个Promise对象的时候,它的状态默认是pending ,要修改它的状态必须调用resolve方法或者reject方法
const myPromiseObj = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve() }) }) myPromiseObj.then(()=>{}).catch(()=>{})
resolve和reject是两个函数,这两个函数就是promise参数,成功调用resolve,失败调用reject。
js如何浅拷贝和深拷贝一个对象
浅拷贝:Object.assign 或者拓展运算符...
深拷贝:const deepCopiedObj = JONS.parse(JSON.stringify(obj))
- 浅拷贝
let obj ={ name:'张三' age:12, address:{ city:'成都 } }; let o={...obj}
- 深拷贝
let newobj = {}; //深克隆 function deepClone(newobj ,obj){ //先进行浅克隆 object . assign(newobj , obj); //判断是否有对象属性 for (const key in obj) { if(typeof obj[key]==' object' ){ //是一个对象 属性 newobj [key]={}; //使用递归 deepClone (newobj[key], obj [key]); } } }
rem和em和px的区别
px是固定单位,rem和em是响应式单位,1rem的大小取决于根节点html的字体大小(即font-size属性),1em的大小取决于其父节点的字体大小,在移动端我们一般用rem,但是要注意:这时html的fontSize属性应该是动态计算的,而不是写死,一般是把当前可视窗口的宽度除以一个经验值得到的比值作为html的fontSize属性值的。
for循环中break和continue的区别是什么?
- break是跳出当前循环,结束整个循环
- continue是跳出当前这一次的循环,继续下一次的循环
如何用原生js给一个按钮的点击事件绑定两个事件处理函数
addEventListener
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>绑定多个事件</title> <script> window.onload = function () { document.getElementById('btn').addEventListener('click', function (){ alert(1); }, false); document.getElementById('btn').addEventListener('click', function () { alert(2); }, false); } </script> </head> <body><button id="btn">点我</button></body> </html>
js引擎如何实现异步的?
浏览器是多线程的,js引擎只是其中一个线程,
除此之外还有http请求线程,事件处理线程,GUI渲染线程
- js引擎是通过事件循环(Event Loop)实现异步的
- 什么是事件循环
-
- js引擎里面有两个非常重要的部分:执行栈和任务队列
-
- 所有的代码都要加载到执行栈里面执行
-
- 在执行过程中如果发现有异步代码,则js引擎会把异步任务交给浏览器的其他线程去处理,比如在执行的时候遇到接口发送,则js引擎会把它交给http请求线程去处理,然后js引擎继续执行后面的同步任务,但是同时http请求线程也在同时运作,当请求发送成功之后,即数据拿到之后,http请求线程会将我们之前在js里面设置的回调函数推送到js引擎的队列
-
- 当js引擎在执行栈里面把任务清空之后,则会到任务队列里面去寻找有没有待执行的回调函数,有则拿到执行栈里面去执行
-
- 执行完了之后,又会到任务队列里面去寻找有没有待执行的回调,如此循环往复的过程,就是事件循环,理解了事件循环就理解了js引擎如何实现异步
什么是函数柯理化?
通过函数返回函数的方式(闭包),让一个一次性接受多个参数的函数,分解为一次只接受一个参数的若干函数的组合
其作用是为了参数的复用,
function towSum(a,b){ return a + b; } //现在对上面的函数进行柯理化 function towSum(a){ return function(b){ return a + b; } } 或者 const res = (a)=>(b)=>a+b;
微任务和宏任务的区别
- 宏任务:当前调用栈中执行的代码成为宏任务(主代码块,定时器等)
- 微任务:当前宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件,【promise(.then .catch里面的)】
- 宏任务中事件放在callback queue中,有事件触发线程维护,微任务的事件放在微任务队列中。【setTimeout setInterval http请求 】事件
- 运行机制
-
- 在执行栈中执行一个宏任务
-
- 执行过程中遇到微任务,将微任务添加到微任务队列中
-
- 当前宏任务执行完毕,立即执行微任务队列中的任务
-
- 当前微任务队列中的任务执行完毕,检查渲染,gui线程接管渲染
-
- 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。
DOMContentLoaded和load事件的区别
- DOMContentLoaded事件
页面dom加载完成触发,无需等待后续图片等资源
- load事件
整个页面所有资源加载完成之后触发;load一定在DOMContentLoaded之后触发
代码题
var a = {}; var b = {key:"b"}; var c = {key:"c"}; a[b] = "b"; a[c] = "c"; console.log("a[b]",a[b]);//a[b] c
var age = 100; let years = 6; if(age > 12){ let age = 10; var years = age*3; } //问:运行的结果是什么? 报错,不能运行,age和years已经存在了
componentDidMount() { this.setState({ count: this.state.count + 1 }); console.log('1', this.state.count); this.setState({ count: this.state.count + 1 }); console.log('2', this.state.count); setTimeout(() => { this.setState({ count: this.state.count + 1 }); console.log('3', this.state.count); this.setState({ count: this.state.count + 1 }); console.log('4', this.state.count); }, 0); } //打印结果 1234
function Cat() { let showName = function () { console.log(1); } return this; } Cat.showName = function () { console.log(2) }; Cat.prototype.showName = function () { console.log(3) }; var showName = function () { console.log(4) }; function showName() { console.log(5) }; Cat.showName(); showName(); Cat().showName(); showName(); new Cat.showName(); new Cat().showName(); new new Cat().showName(); //打印结果?? 2423
function Cat() { showName = function () { console.log(1); } console.log('this',this) return this; } Cat.showName = function () { console.log(2) }; Cat.prototype.showName = function () { console.log(3) }; var showName = function () { console.log(4) }; function showName() { console.log(5) }; Cat.showName();//2 showName();//4 Cat().showName();//1 showName();//1 new Cat.showName();//2 new Cat().showName();//3 new new Cat().showName();//3 //打印结果,注意跟上一题的区别
- 这段代码有什么问题吗?
this.setState((state,props)=>{ return {total:state.total + props.count} });
- 查看一下代码:如果你在页面中创建了一个React元素,请完成他的组件定义?
<Profile username="sofn"> {user=>user===null ? <Loading/> : <Badge info={user}/>}{" "} </Profile> import React ,{Component} from 'react'; import PropTypes from 'prop-types'; import fetchUser from 'utils'; //fetchUser接收用户名,并返回promise //当得到用户数据的时候返回resolve状态 class Profile extends Component{ //在这里写下你的代码 }
vue
vue组件中watch和computed的区别
- computed
-
- 支持缓存、只有依赖数据发生改变,才会重新计算
-
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- watch
-
- 不支持缓存、数据变,会触发相应的操作
-
- watch支持异步
vue常用的生命周期钩子函数有哪些?分别有什么作用?
钩子函数 | created | mounted | updated | beforeDestory |
执行时机 | 数据初始化完毕之后 | DOM渲染完毕之后 | 数据更新完毕并且DOM更新完毕之后 | 组件卸载之前 |
作用 | 发送请求 | 发送请求、获取DOM等 | 发送请求(注意加上条件判断)、获取DOM等 | 性能优化相关,比如清除定时器、延时器、解绑事件等 |
- beforeCreate()实例在内存中被创建出来,还没有初始化好data和methods属性
- create()实例已经在内存中创建,已经初始化好data和method,此时还没有开始编译模板
- beforeMount()已经完成了模板的编译,还没有挂载带页面中。
- mounted()将编译好的模板挂载到页面指定的容器中显示
- beforeUpdate()状态更新之前执行函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为还没有开始重新渲染dom节点
- updated()此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新渲染好了
- beforeDestroy()实例被销毁之前 destroyed() 销毁完成状态
- destroyed():实例销毁后调用,Vue实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也都会被销毁。组件已经被完全销毁,此时组建中所有data、methods、以及过滤器,指令等,都已经不可用了。
vue如何实现组件通信(组件传值)? 父子 兄弟 复杂组件关系
组件关系 | 父子 | 兄弟 | 复杂组件关系 |
通信方式 | props/绑定自定义事件、$emit | 状态提升、事件总线 | 状态机vuex |
vue如何提取组件的公共逻辑
vuex/mixins
vue项目如何做路由拦截?
全局守卫:
beforeEach//只有进入一个路由的时候就会触发
beforeResolve//当路由解析完成以后去执行
afterEach//全部加载完成以后执行
组件内守卫
beforeRouteEnter 进入组件之前
beforeRouteLeave 离开组件之后
beforeRouteUpdate
vue的响应式原理 Object.defineProperty
vue组件中data里面的数据,在初始渲染之前都会被Object.defineProperty转化成响应式数据,然后这些数据的访问和修改就能够被监听了,每当数据被访问的时候就会触发getter,接着会通知watcher,watcher这是会做一次依赖的收集或者更新,然后watcher会负责通知渲染界面(先转化为虚拟DOM,在映射为真正的DOM)。每当数据被设置的时候会触发setter,接着setter也会通知watcher,watcher在去通知渲染界面
v-if和v-show的区别
- 共同点:v-if 和 v-show 都能实现元素的显示隐藏
- 区别:
- v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁);
- v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;
- v-if 有更高的切换开销,v-show 切换开销小;
- v-if 有配套的 v-else-if 和 v-else,而 v-show 没有
- v-if 可以搭配 template 使用,而 v-show 不行
keep-alive组件有什么作用
keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现
this.$nextTick有什么作用
移动版的updateted
当data中的某个属性改变的时候,这个值并不是立即渲染到页面上,而是先放到watcher队列上(异步),只有当前任务空闲的时候才会去执行watcher队列上的任务。所以导致,改变的数据挂载到dom上会有一定的延迟,这也就导致了,当我们在改变属性值的时候,立即通过dom去拿改变的值时发现拿到的值并不是改变的 值,而是之前的值。
this.$nextTick作用:在下次dom更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获得更新后的dom
Vue实现响应式并不是在数据发生后立即更新DOM,使用vm.$nextTick是在下次DOM更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的DOM。
this.$set有什么作用
- 在组件data中预定义的数据,一定是响应式的,但是我们后续,通过常规的方式(比如someObject.propName = xxx)为data中某些对象添加新的属性时,这些新的属性并不是响应式的,这是可以使用该方法。 this.$set(target,prop,value) 这里的prop属性就是响应式的
vue如何做路由懒加载
路由配置中component属性的值修改为:()=>import(组件路径)
你认为vue 框架和jquery有什么区别?
- 开发思想不同,vue是数据驱动,并且可以组件化开发,jquery则是DOM驱动的,开发效率低
如果要让watch监听器立即执行一次,该如如何处理?
react
shouldComponentUpdate有什么作用?
它是react组件的一个 生命周期钩子函数,每次数据更新时,首先会执行该钩子函数,如果返回true,表示可以更新界面,然后后续的钩子函数才会陆续执行,返回false,则表示不可以更新,后续的钩子函数也不会执行,当然它默认返回true,
我们一般不会直接在组件中使用该钩子函数,而是会选择PureCompopnent来代替Component,在PureComponent里面就利用来该钩子函数来做优化
setState有什么特性
它的作用是更新组件内部状态,可以接受两个参数,参数一个一般是个对象,在底层react会通过Object.assign把传入的对象和原来的state进行合并,当然也可以传入一个函数,这个函数会接受两个形参(一个表示state,一个表示props),并返回个对象,要注意的是,这个函数在执行是会保证之前的state已经更新完毕,最后在把该函数返回的对象和原来的对象进行合并。当新的状态值会基于老的状态值时,我们会倾向于给setState的第一个参数传递一个函数进去。它的第二个参数则是一个回调函数,会在数据更新完毕,并且dom更新完毕时执行。
另外,当我们在react事件体系或者组件生命周期钩子函数里面直接调用setState的时候,它是异步的,其他情况是同步的,比如在setTimeout/setInterval里面调用就是同步的
this.setState({name:'张三'}); this.setState((state,props)=>({})
react列表渲染时,key的作用
- key是react用于找到列表中的元素被修改或被添加或被移除的辅助标识
- 提升渲染性能
react组件之间如何通信
- 父组件传子组件
父组件通过传递props,子组件得到props接受
- 子组件想父组件通信
利用回调函数,可以实现子组件调用该回调函数,便可以向父组件通信
- 兄弟组件 状态提升
- 复杂组件 状态机,redux
解释一下redux核心概念
三大核心概念 :action reducer store
- store:UI唯一数据来源,可以理解为react中的state,store信息的变化会触发视图更新
- action:必须拥有一个type属性,用来描述发生了什么。可以选择携带发生时的数据,如永辉输入的input value。切记:仅仅用来表述发生了什么。
- reducer:计算者,规定了数据的修改规则
-
- 他就是一个函数,接收了两个参数(state,action)
-
- 该函数返回值就是新的数据,会覆盖store中原有的数据,所以在函数体里面就是根据简单的action中的type字段,来返回不同的数据
-
- reducer函数会在通过createStore创建的store的时候,自动执行一次,目的是初始化数据
react常用的生命周期钩子函数有哪些?分别有什么作用
- 挂载阶段:组件数据的初始化,即初始化渲染
-
- constructor:初始化数据
-
- render:根据初始数据进行页面的渲染
-
- componentDidMount:DOM挂载完毕的一个通知
- 运行阶段:和用户交互,改变状态并重绘(最长阶段)
-
- shouldComponentUpdate 不常用,面试要问
-
-
- 默认返回true 表示可以更新
-
-
-
- 如果是false则,表示不可以更新,则后续的钩子函数将不再执行
-
-
- render:根据新的props的state渲染界面
-
- componentDidUpdate:界面渲染完毕的一个通知
-
-
- 在这里我们可以发送请求和获取DOM节点
-
- 卸载阶段:组件使用完毕后,或者不需要存在与页面中,那么将组件移动,执行销毁
-
- componentWillUnmount:组件即将销毁
-
- 一般这里我们可以做清理定时器等,性能优化
react中refs有什么作用?
- 获取DOM和组件实例
redux中间件是什么,有什么作用,你常用的中间件有哪些?
- 什么是中间件
-
- 作用于dispatch派发action开始到reducer接受action之前的一系列插件
- 有什么作用
-
- 强化dispatch方法,并链式处理action
- 常用的中间件
-
- redux-logger:提供日志输出
-
- redux-thunk:处理异步操作
redux三大原则是什么?
- State 是只读的: 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。
- 使用纯函数来执行修改: 为了描述 action 如何改变 state tree ,你需要编写 reducers。Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。
react中容器组件和展示组件有什么区别?
展示组件
- 内部可以包含展示组件和容器组件,通常会包含一些自己的DOM标记和样式(style)
- 通常允许通过this.props.children方式来包含其他组件。
- 对应用程序的其他部分没有依赖关系,例如Flux操作或store。
- 不用关心数据是怎么加载和变动的。
- 只能通过props的方式接收数据和进行回调(callback)操作。
- 很少拥有自己的状态,即使有也是用于展示UI状态的。
- 会被写成函数式组件除非该组件需要自己的状态,生命周期或者做一些性能优化。
容器组件
- 关注应用的是如何工作的
- 内部可以包含容器组件和展示组件,但通常没有任何自己的DOM标记,除了一些包装divs,并且从不具有任何样式。
- 提供数据和行为给其他的展示组件或容器组件。
- 调用Flux操作并将它们作为回调函数提供给展示组件。
- 往往是有状态的,因为它们倾向于作为数据源
- 通常使用高阶组件生成,例如React Redux的connect(),Relay的createContainer(),propvider。
函数组件和类组件有什么区别(16.8以前)?
react16.8以前函数组件只能被动接收外部数据,并且没有自己的生命周期钩子函数,没有state,函数内部没有this可用
你对react新特性hooks有什么了解吗?
Hooks是能够让你在react组件函数内部关联state和生命周期特性的函数,计算属性
hooks特性只能在函数组件中使用
react如何提取组件之间的公共逻辑
- 自定义hooks提取公共组件的逻辑
- redux
- 高阶组件
什么是高阶组件?有什么作用?你常用的高阶组件有哪些
- 什么是高阶组件:高阶组件就是接受一个组件作为参数,在函数中对组件进行一系列的处理,随后返回一个新的组件作为返回值
- 有什么作用:
-
- 代码复用,逻辑抽象
-
- 渲染劫持
-
- state抽象和更改
-
- Props更改
- 常用的高阶组件:widthRouter,Connect,
受控组件和不受控组件有什么区别?
受控组件:通过setState的形式控制输入的值及更新,
非受控组件:即通过DOM的形式更新值,要获取其值可以通过ref的形式去获取
JSX的底层原理是什么?
通过React.createElement创建虚拟DOM 标签
react组件的数据来源有哪些?他们之间有什么区别?
分为外部数据和内部数据
外部数据只读,
react插槽是什么?(或者react中如何实现组件复合?)
位于组件标签之间的内容就是插槽,要显示插槽内容需要在组件内部通过this.props.children渲染
小程序
小程序一个页面有几个文件构成?
小程序怎么跟后台交互
(1)直接用微信官方提供的接口 wx.request接口,跟jquery的ajax差不多
小程序怎么做扫码功能(wx.scanCode),怎么做支付功能(wx.requestPayment)
(1)调用微信提供的现成的接口(wx.开头的),即可,具体参数怎么传参照文档即可
微信里面怎么获取用户的手机号??
(1)简单,直接用微信官方提供的现成组件button即可,具体用法是在button上加一个属性opent-type,方它的值为getPhoneNumber,不知道怎么用的去找微信官文档看看
小程序分包是什么?有什么作用?
小程序的分包总包大小并不超过16M,分八个包,每个包不超过2M
提示:跟vue路由懒加载有类似的作用,提升用户体验度,减少首屏渲染速度
https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html
https://blog.csdn.net/acmdown/article/details/80037660
说一下项目中微信授权登录的流程。前后端是如何去交互的其他
- 调用wx.login()获取临时登录凭证Code,并传给开发者服务器
- 调用auth.code2Session接口,换取用户唯一的OpenID和会话密钥session_key
- 之后开发者服务器可以根据用户标识来自定义登录状态,用于后续业务逻辑中前后端交互时,识别用户身份。
性能优化
从用户在浏览器中输入url,到浏览器渲染出界面,中间发生了什么?
DNS域名解析------》发起http或者https请求------》服务端处理请求-------》浏览器接受请求响应,并渲染界面
如何提高首屏(注意:不是首页,一般你在url中输入一个新的路劲,然后回车,即是一个首屏)的渲染速度
DNS域名解析部分优化
- DNS prefetch(DNS预解析)
在html头部使用link标签进行DNS预解析 <link rel="dns-prefetch" href="//www.baidu.com">
发起请求部分
- 减少请求次数
-
- 采用css sprinte(也叫雪碧图或者精灵图),即:如果有多张比较小的背景图,可以合成一个,然后通过background-position去设置背景,而不用每个都去发送请求
-
- 某些图片编译成可以是用base64格式(webpack中的url-loader会自动把大小小于某些值的图片编译成base64格式的,然后内嵌到js文件中,所以后续在访问这些图片时,不在需要单独发送请求)
-
-
- 何时用base64格式的图片??,可以参考以下几个因素
-
-
-
-
- 这类图片不能与其他图片以CSS Sprite的形式存在,只能独行
-
-
-
-
-
- 这类图片从诞生之日起,基本上很少被更新
-
-
-
-
-
- 这类图片的实际尺寸很小
-
-
-
-
-
- 这类图片在网站中大规模使用
-
-
-
- 合并脚本和样式表,但是要视情况而定,如果合并脚本或者样式表后,包的体积变得非常大,反而会过度延长网络请求时间,从而导致首先渲染更慢
-
- 利用http缓存(新鲜度限值和服务器在验证)
- 减少请求或者服务端响应式时(当然主要是响应),网络数据包的大小
-
- 压缩css/js、图片等
-
- 路由懒加载
- 缩短网络的传输距离,优化网络传输路径
-
- 利用CDN(内容分发网络),一般我们会把静态资源,即很少变化,体积有比较大的资源交给CDN服务商管理
服务端优化:不需要关注
使用单独的图片服务器、使用redis缓存、使用负载均衡等等
浏览器渲染
- css放在body之前,script放在body之后
- ssr(server side render---服务端渲染)
- requestAnimationFrame
对于动画来讲:定时器为什么不是一个好的方案
1. 开发人员并不知道,间隔多少时间出发一个动画何时,间隔时间如果太短,则会损耗浏览器的性能,如果间隔时间太长,则用户就会感觉卡顿。并且定时器自身机制的问题,它的回调函数触发的时机可能并不是你设置的时间,因为他要等到执行栈里面的任务清空之后再去执行优化要达到的目的:间隔时间合适,同时用户不会觉得卡
分析:
第一:间隔时间要合适,你自己手动设置肯定不行,因为延时器或定时器触发的时机本就不精确。
第二:用户不会觉得卡顿,首先我们要搞清楚是什么原因导致的卡顿,所以接下来,我们要解释一下浏览器帧的概念
帧:浏览器中的页面是有一帧一帧绘制出来的,当每秒钟绘制的帧数(FPS--Frame Per Second)大于或等于60的时候,用户是感觉不到卡顿的,换言每一帧的时间控制在1s/60,也就是大约16.6毫秒之内时,用户是感觉不到卡顿的。
在一帧中浏览器大致做了如下事情(有时不一定每一步都会执行):
1. 处理用户的交互
2. js解析执行(例如用户事件处理函数中的逻辑)
3. 调用window.requestAnimationFrame中注册的函数(注意:这个函数其实是上一帧中注册的函数,且如果在这一帧中发现有注册的函数,则该函数一定会被执行)
4. Layout(布局,如果第二次触发,我们就称之为回流)
5. Paint(绘制,如果第二次触发我们就称之为重绘)
6. 如果前五步骤执行完了之后,这一帧还有剩余时间,则调用window.requestIdleCallback中注册的函数
也就是说这前五个步骤(其他我们先暂时不用考虑)执行时间总和要在16.6毫秒内,才能保证用户体验。
结论:我们可以利用requestAnimationFrame实现动画逻辑,并且不需要传递时间,在这里触发动画,一般情况1秒钟也能达到60帧(当然动画里的逻辑也不能太复杂,在这一步,如果执行时间过长也会导致掉帧(1秒钟没有达到60次),从而让用户决定卡顿),具体用法如下(实现一个元素,在水平方向上左右移动的效果)<div style="width:50px;height:50px;background-color: red;" id="myDiv"></div> let originStep = 10; let step = 10;//每次移动的距离 let direction = 1;//移动的方向1表示向右,-1表示向左 let left = 0;//元素的偏移量 const offsetWidth = document.body.offsetWidth;//body的宽度 const el = document.getElementById('myDiv'); const eleWidth = el.offsetWidth;//元素的宽度 const runAnimation = () => {} if(direction === 1){ const remainOffset = offsetWidth - (left + eleWidth);//向右移动时,距离右边距剩余距离 if(remainOffset < step && remainOffset !== 0){ step = remainOffset;//保证向右的移动不会超出右边接线 } }else{ step = originStep;//当向左移动的时候恢复移动速度 } if(left <=0){ direction = 1; }else if(offsetWidth <= left + eleWidth){ direction = -1; } const xOffset = left += step*direction; el.style.transform = `translate(${xOffset}px,0)`; requestAnimationFrame(runAnimation);//在注册回调 } requestAnimationFrame(runAnimation)
requestAnimationFrame执行过后会返回一个id,跟定时器一样,我们可以根据这个id取消对应的requestAnimationFrameconst cancelId = requestAnimationFrame(runAnimation); cancelAnimationFrame(cancelId);
4.requestIdleCallback
SSR(Server Side Render)
webpack
webpack有什么作用?
什么是wepack ?模块化的打包工具,原理是,我们必须向它提供一个入口js文件, 然后webpack会根据这个入口文件生成一张依赖图,然后在堆这张依赖图里面的文件进行转义和打包,最后生产浏览器能够识别的资源文件(css,js)
作用:
- 让项目模块化。让项目模块之间相互独立
- 编译scss和less 和.vue jsx
- 性能优化相关的:压缩和代码拆分,图片转base64格式
- 工程化:自动编译,打包
webpack常用配置项有哪些?(webpack.config.js)
- entry:入口文件
- output:出口文件
- module
module:{ rules:[ test:/\.css$/, use:['style-loader','css-loader'],//从后往前执行 ] }
网络相关
常用的http状态码有哪些,
- 1xx:指示信息–表示请求已接收,继续处理
- 2xx:成功–表示请求已被成功接收、理解、接受
- 3xx:重定向–要完成请求必须进行更进一步的操作
- 4xx:客户端错误–请求有语法错误或请求无法实现
- 5xx:服务器端错误–服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200客户端请求成功
400 //客户端请求有语法错误,不能被服务器所理解
401//请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
403 //服务器收到请求,但是拒绝提供服务
404 //请求资源不存在,eg:输入了错误的 URL
500 //服务器发生不可预期的错误
503//服务器当前不能处理客户端的请求,一段时间后可能恢复正常
- 304:
- 301:资源 永久重定向
http和https的区别
安全:https协议在传输数据的时候,会对数据进行加密,http则是明文传输。
性能:http协议的传输性能更高,因为对于传输同样的数据量来讲,由于https协议需要加密数据,所有最终在线路上,它的数据量要大一点
费用:https需要购买证书,http则免费
端口不同:http端口是80,https端口是443
websokect和http的区别
websokect能够实现客户端和服务端的双向通信,而http则只能是单向通信,由客户端请求服务端,服务端不能主动的向客户端推送数据。
应用场景:
- websokect实时通信,数据的实时更新,
- http简单的客户端请求服务端
业务相关
如何处理权限问题
比如我们的后台管理系统,不同的角色看到的侧边栏是不同的,如何实现?
- 将侧边栏抽象成一个数组,数组里面每一个元素则表示每一个侧边栏,并且在每一个元素里面,我们需要添加一个表示这个角色的字段,以此来区分何种角色能够看到当前侧边栏项,
- 根据当前登录人的角色去过滤当前数组,并返回当前角色能够看到的侧边栏数据
- 最后根据得到的数遍历循环,渲染侧边栏
前端如何实现身份验证
前端现在一般用token实现身份验证,
- 登录过后,后端会给前端返回一个token,然后我们把这个token存到本地存储,然后后续在进到主页之后,基本所有的接口发送时,我们的都要把这个token添加到header里面,
- 后端接收到这个请求之后会先去解析token,如果能够正常的解析,则表示当前登录人没有问题,可以正常返回数据,否则返回401
数据可视化如何实现(echart)
option setOption(option)
如何实现数据的实时更新 websokect
浏览器
什么是跨域?如何解决
- 跨域是浏览器的同源策略导致的问题,当我们的协议、域名和端口号,任意一个不同的时候,就会导致跨域的出现
常见的一个场景,前端请求后端api的时候出现跨域,如何解决?
-
- 前端处理:代理服务器 webpack-dev-server (vue.config.js)
devServer:{ proxy:{ "/api":{ target:"http://localhost:8000" } } }
- 前端处理:代理服务器 webpack-dev-server (vue.config.js)
-
- 后端处理
-
-
- 设置响应头
-
浏览器渲染页面的流程
- GUIx渲染线程负责界面的渲染过程
- 同时解析html/css
- 解析html形成DOM树
- 解析css形成CSS树
- 然后合并DOM和css形成渲染树
- 接下来开始layout(布局,回流)
- 绘制(重绘)
根据这些过程,我们可以知晓在写html的时候,需要注意的事项:(性能优化)
- css放在头部
- js放在尾部,js是由js引擎去编译和执行的,而js引擎和GUI渲染引擎是互斥的
//
- 构建DOM树:浏览器将获取的html文档并解析成DOM树
- CSS解析:处理css标记,构成层叠样式表模型
- 构建渲染树:将DOM和cssom合并为渲染树将会被创建,代表一系列将被渲染的对象
- 渲染树布局:渲染树的每一个元素包含的内容都是计算过的,被称为布局,浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素
- 渲染树绘制:将渲染树的各个节点绘制到屏幕上
重绘和回流有什么区别
- 回流
-
- 当render树中的一部分或者全部因为大小边距等问题发生改变而需要重建的过程叫做回流(改变大小)
- 重绘
-
- 当元素的一部分属性发生变化,如外观背景色不会引起布局变化而需要重新渲染的过程叫做重绘(改变样式)
- 区别
-
- 回流发生条件
当页面布局和几何属性改变时就需要回流。下述情况会发生浏览器回流:
(1)添加或者删除可见的DOM元素;
(2)元素位置改变;
(3)元素尺寸改变——边距、填充、边框、宽度和高度
(4)内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
(5)页面渲染初始化;
(6)浏览器窗口尺寸改变——resize事件发生时;
- 回流发生条件
-
- 重绘发生条件
元素的属性或者样式发生变化。
- 重绘发生条件
6.什么是防抖节流?
- 等我的动作结束之后我们在去接受那个尺寸
- 节流:每隔几秒钟我们再去
防抖和节流有什么区别,如何实现
在高频触发的事件中,可能会导致一些性能问题,比如在PC端浏览器缩放时(resize事件),我们在事件处理函数里面如果操作DOM,则用户缩放的时候,可能会导致浏览器卡顿,这是我们就可以通过防抖节流来控制操作DOM的频率,避免过快的操作DOM,但是这两者的区别在于:
防抖的思路是:设置一个延时器,延时n秒在执行相应的逻辑(即频繁执行可能会影响性能的逻辑),如果在n秒内,再次执行了,则清空延时器,并从当下开始从新设置延时器,n秒后再执行。
节流的思路是:在特定的时间段内,相应的逻辑必须有且只能执行一次。
//防抖 function debunce(func,time){ let timer = null; return function(){ if(timer)clearInterval(timer); timer = setTimeout(()=>{ func.apply(this,arguments) },time); } } //节流 function throttle(func,time){ let preTime = +new Date() return function(){ const curTime = +new Date() if(curTime - preTime >= time){ func.apply(this,arguments); preTime = curTime; } } }
v-model如何实现表单的双向绑定
- 在组件中定义一个内部状态
- 把这个内部状态同input的value属性关联起来
- 给input绑定onchange事件,在事件处理函数里面把用户的输入值和之前定义的内部装那台同步起来
你知道的数据结构有哪些?
- 栈,队列
- 栈先进后出
- 队列:先到先得
- 链表:可以有多个分支
- 二叉树:一个节点最多两个分支
context是什么
是react提供的一种跨组件层级的一个方式
我们在项目开发一般不会用它,会用基于他的redux,
我们创建context react.create.Context
前端登录后,传递id给后端获取数据,这种方式安全吗?你是如何来避免的
get方式修改为post方式
http协议修改为https协议
token过期
- 有感刷新:清空本地存储,弹窗提示用户重定向到登录页面
- 无感刷新
需要后端返回两个token,一个用于身份验证(这里我们称它为auth-token),一个用户刷新token, (这里我们称它为refresh-token),注意:refresh-token的过期时间必须要比auth-token长 ,当后端发现auth-token过期时,会返回401,前端接收之后,立即用refresh-token去换取新的token,
这就是大体的思路,比较简单。
但是这里存在一个问题,即:当我们在用refresh-token去换取
新的token的过程中,如果同时还有其他接口在用auth-token去请求后端数据时,或者是同时并发n个接口,都返回401,应该如何处理,这是一个相对难处理的点。
这个问题的处理思路是:如果发现当前接口返回401,则立即在axios响应拦截器里面
返回一个pending状态的promise(这样页面内部的.then和.catch的逻辑就暂时不会执行)。
同时,把这些返回401的接口缓存到一个队列里面,等到新的token回来之后,在重新发送接口,
并且把promise的状态变成fulfilled。
配置权限的时候,前端如何对留有进行限制(没有权限的用户无法直接通过地址栏访问到其他权限的资源)
- 动态路由 addrouter
请说一下你项目中微信支付流程如何做的
- 调用接口
- wx.requestPayment
为什么使用uni-app,你在项目开发的过程中遇到哪些兼容问题,以及如何解决的
开发效率比原生的开发效率高,数据管理比较方便
- 页面预加载,只有H5和APP端支持, 小程序不支持
- css中给元素设置背景图,HS端支持,小程序不支持(在小程序端用行内样式设置背景图即可)
- css中通配符*在H5端支持,但是小程序不支持
- 在非H5端不能使用re的方式引用内置组件(比如view等)
■如果想为不同的平台写不同的代码,可以使用条件编译
uni-app中要跳转路径,如何提高页面加载效率
使用uni.preloadPage页面预加载,但是该方法有兼容性,只在spp端和H5端有效
如果要让watch监听器立即执行一次,该如如何处理?
我们可以添加 immediate 属性
watch 有三个参数
handler
:其值是一个回调函数。即监听到变化时应该执行的函数
deep
:其值是 true 或 false;确认是否深入监听。
immediate
:其值是 true 或 false,确认是否以当前的初始值执行 handler 的函数
watch: { keyWord: { handler: 'getList', immediate: true } }
项目中如何实现上拉加载,下拉刷新的,如果要自己实现,说一下思路
通过onReachBottom监听到用户上拉到底,然后将数据重置,下拉刷新使用onPullDownRefresh监听用户的动作,将当前页加1之后传进去,当当前页等于总页数的时候提示用户没有数据了
项目是如何打包上线的,打包的时候需要配置些什么内容
react项目打包,在项目路径下,敲npm run buil,就出现了build文件夹
build生成的这些东西要放在服务器root下,可以在pakege.json里,
也能让它充当静态的服务器,敲:npm install -g serve,在敲serve -s build
部署方法:
使用npm run build将项目打包
把打包后地文件件夹中内容放到tomcat中webapps下的root中
开启服务器访问localhost:8080端口即可以看到你的项目内容
不放置在Root目录下的坑:
由于默认path.js【路径:你的react项目名\node_modules\react-scripts\config\path.js】的配置是’/’即对根目录有效,所以如果不放在root下则需要将’/’变成’./’即相对路径有效。(修改代码约在第45行)