一、 css
1. 手写三角形
.a {
width: 100px;
height: 100px;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-top: 100px solid transparent;
border-bottom: 100px solid pink; //尖尖朝上
box-sizing: border-box;
}
.b {
width: 0px;
height: 0px;
border-width:100px;
border-style:solid;
border-color: transparent transparent transparent red ; // 尖尖朝右
}
2. 重置居中
##### 1.flex
父
display: flex;
justify-content: center;
align-items: center;
##### 2.父 display: flex; 子 margin:auto
##### 3.绝对定位
父:position:relative
子:position:absolute top:50% left:50% transform:translate(-50%,-50%)
父:position:relative
子:position:absolute top:0 left:0 right:0 bottom:0
3. 元素消失
display:none
visibilty:hidden
opcacity:0
height:0,width:0
4. 左侧固定,右侧自适应两栏布局的方法
##### 1.左边浮动,右边margin-left左侧宽度
.父{overflow:hidden;}
.左边{float:left;width:100px}
.右边{margin-left:100px}
##### 2.左边绝对定位,右边margin-left左侧宽度
父{position:relative}
.左边{position: absolute;left: 0;top:0;width:200px;}
.右边{margin-left:100px}
##### 3.双float + calc()计算属性
.父{overflow:hidden;}
.左边{float: left;width:200px;}
.右边{float:left;width:calc(100%-200px);}
##### 4.flex
.父{display: flex;}
.左边{flex:0 0 200px;}
.右边{flex: 1;}
5. 两侧固定,中间自适应
1.浮动+布局,将middle放最下面,left左边浮动,right右边浮动
//只写了关键的代码
.left{ float:left,width:100px}
.right{float:right,width:100px}
.middle {margin:0px 100px;}
2.浮动+calc
//只写了关键的代码
.left,.right,.middle {float:left}
.left,.right{ width:100px}
.middle {width:calc(100% - 200px)}
3.定位
//只写了关键的代码
.box {position:relative;}
.left,.right{ width:100px;position:absolute;top:0px}
.left {left:0px}
.right{right:0px}
.middle {margin:0px 100px}
4.flex
.box {display:flex}
.left,.right{flex:0 0 100px}
.right {flex:2}
6.flex布局
定义:flex是弹性布局,用来给盒模型提交最大的灵活性。设置为flex布局以后,子元素的 float,clear,vertical-aglin属性失效
1. 容器的属性
1.flex-direction: row | row-reverse | column | column-reverse //方向
2.flex-wrap:nowrap | wrap | wrap-reverse //换行
3.flex-flow : row | nowrap // 方向和换行的合写
4.justify-content:flex-start | flex-end | center | space-between | space-around
5.align-item: flex-start | flex-end | center | stretch | baseline
6.align-content:flex-start | flex-end | center | space-between | space-around | stretch
2. 项目的属性(自身)
1.order // 排序 从小到大 默认0
2.flex-grow //放大 默认0 不放大
3.flex-shrink //缩小 默认1 缩小
4.flex-basis //分配多余空间之前,项目占据的空间 默认auto
5 flex: flex-grow flex-shrink basis //3个属性的合写 默认 0 1 auto
6.align-self: auto | flex-start | flex-end | center | basline | stretch // 单个项目与其他项目不一样的对齐方式 默认auto表示继承父元素的align-items
7.文本溢出展示。。。
1.单行文本溢出
overflow:hidden;
text-overflow:ellipsis; // 文本超出的处理方法,属性clip=>裁剪 ellipsis=>点点点
white-space:nowrap;//不换行
2.多行文本溢出
line-height:30px;
overflow:hidden;
display:-webkit-box;
-webkit-line-clamp:2; // 2表示2行
-webkit-box-orient:vertical; //属性规定框的子元素应该被水平或垂直排列。
8.white-space 与 word-wrap的区别
white-space:nowrap //不换行
word-wrap: break-word; // 换行,但不会拆分英文单词
word-break: break-all // 换行,会拆分单词
二 、js
1.this指向的几种类型(核心:谁调用指向谁)
1.普通函数 => window
2.箭头函数 => window
箭头函数能保存函数创建时候的this,而不是调用的
var obj = {
say: function () {
setTimeout(function () {
console.log(this)
});
}
}
obj.say(); // window
var obj = {
say: function () {
setTimeout( ()=> {
console.log(this)
});
}
}
obj.say(); //obj
3.对象里的函数 => 调用的对象
4.构造函数 => new 出的新的函数
为何 React事件要自己绑定 this,几种方式的优缺点
2.如何判断元素类型
typeof
typeof '';// string
typeof 1;// number
intanceof
[] instanceof Array;// true
{} instanceof Object;// true
Object.prototype.toString.call('') ; // [object String]
3.JS 异步解决方案的发展历程以及优缺点
回调-promise-generator-aync/await
4.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
promise构造函数是同步执行的,then方法是异步执行的
5.js的事件执行机制
宏任务 微任务
参考
6.http
状态码 301 302 和307的区别 常见的请求方式 get post put delete put
http请求头有哪些 Request的Header信息
content-type有哪些常见类型
前端缓存机制
浏览器和服务器设置cache-control的区别
1.cache-control是由服务器端设置的,服务器没设置不会走缓存。
2.当服务器端设置了强缓存,浏览器设置no-store,no-cache,max-age=0可以跳过强缓存命中协商换存。
3.强缓存不会向服务器发送请求
参考:HTTP请求头和响应头中cache-control的区别
url输入到展示的过程、tcp三次握手,4次挥手
7. 节流 防抖
节流,执行多次 防抖,执行一次
函数节流:让一个函数不要执行得太频繁,减少一些过快的调用来节流。也就是在一段固定的时间内只触发一次回调函数,即便在这段时间内某个事件多次被触发也只触发回调一次。原理是通过判断是否有延迟调用函数未执行。高频触发事件,定时执行事件,过渡平滑。
//节流
function throttle(fn,wait){
let previos = 0
return function(){
const _this = this,arg = argment
const now = Date.now()
if(now - previos > wait){
fn.apply(_this,arg)
previos = now
}
}
}
函数防抖:将多次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
//防抖
function debounce(fn,delay){
let timer = null
return function(){
clearTimeout(timer)
let _this = this,arg = argment
timer = setTimeout(()=>{
fn.apply(_this,arg)
},delay)
}
}
区别:
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,函数防抖只是在最后一次事件后才触发一次函数。
比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
三、react问题
1.react的diff算法原理
传统diff算法:通过循环递归对节点进行依次比较,计算两棵树差异的时间复杂度为0(n^3)。
react的diff算法:
三大策略
1、tree diff => DOM节点垮层级的移动操作特别少,可以忽略
1.react 通过updateDepth对虚拟dom进行层级控制
2.对数分层比较,两棵树只比较同一层节点比较,若不存在,直接删除
3.只需遍历一次,就可以完成整棵树的比较
若出现了跨级操作,则只会删除和新建节点
2、component diff => 拥有相同类的两个组件,生成相同的树形结构;拥有不同类的连个组件,生成不同的树形结构
1.同一类型的两个组件,按同一层级节点进行比较
2.同一类型的两个组件,若是组件A变成组件B,可能虚拟DOM没有任何变化,只需要用户使用shouldComponentUpdate()来判断是否需要计算
3.不同类型组件,会删除一个,重新创建
3、element diff => 对于同一层级的子节点,通过唯一的id进行区分
节点处于同一层级时候,diff提供了三种方式:删除,插入,移动
2. setState何时同步何时异步?
由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。
大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。
React是怎样控制异步和同步的呢?
在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。
在hanldeClick处理程序中调用了两次setState,但是render只执行了一次。因为React会将多个this.setState产生的修改放在一个队列里进行批延时处理。所以不应该依靠它们的值来计算下一个状态
3.请简述react的事件机制?
react利用了事件委托机制实现事件机制,事件并没有绑定在真实的dom节点上面,是绑定在最外层的docment上。使用一个统一的监听器,所有的事件都由这个监听器统一分发。
组件挂载更新时,会将事件分门别类放进事件池,事件触发根据event找到对应的组件,再组件标识和事件类型找到对应的事件进行监听回调,然后,执行回调函数。
事件绑定中丢失的this
class 组件中,给元素添加事件时,class 的方法默认不会绑定 this,当调用这个方法的时候,this 的值为 undefined。
class Demo extends React.Component {
fun1(){}
render (
)
}
4. 为什么 React组件首字母必须大写
如果传递的是一个字符串,那么在创建虚拟DOM对象时,React会认为这是一个简单的HTML标签,但是这显然不是一个简单的HTML标签,因此去创建一个不存在的标签肯定是会报错的。
如果首字母大写,那么就会当成一个变量传递进去,这个时候React会知道这是一个自定义组件,因此他就不会报错了
5. Redux
- 整个应用的状态管理
2.三个原则
2.1.单一事实来源
Redux 使用 “Store” 将程序的整个状态存储在同一个地方。因此所有组件的状态都存储在 Store 中,并且它们从 Store 本身接收更新。单一状态树可以更容易地跟踪随时间的变化,并调试或检查程序
2.2.状态是只读的
改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示
2.3.使用纯函数进行更改
为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。
3.列出 Redux 的组成
Action – 这是一个用来描述发生了什么事情的对象。
Reducer – 这是一个确定状态将如何变化的地方。
Store – 整个程序的状态/对象树保存在Store中。
View – 只显示 Store 提供的数据。
- Redux 有哪些优点
结果的可预测性 - 由于总是存在一个真实来源,即 store ,因此不存在如何将当前状态与动作和应用的其他部分同步的问题。
可维护性 - 代码变得更容易维护,具有可预测的结果和严格的结构。
服务器端渲染 - 你只需将服务器上创建的 store 传到客户端即可。这对初始渲染非常有用,并且可以优化应用性能,从而提供更好的用户体验。
开发人员工具 - 从操作到状态更改,开发人员可以实时跟踪应用中发生的所有事情。
社区和生态系统
三、vue
1.$nextTick的使用
答:当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。
2.computed 与watch
computed 计算属性 存在缓存,只有它依赖的data数据发生改变它才会改变,应用场景,购物车
watch 属性监听 监听一个值的变化,执行对应的回调,没有缓存,它会影响多个数据,应用场景,搜索框
1.watch观察的名称与data属性名称一致,属性变化,watch函数执行
2.传入两个参数,一个newVal,一个oldVal
3.不需要调用
4.只监听数据值是否 变化,不会监听值的引用地址的改变,要监听引用类型,就需要深度监听了(handler+deep)
区别:
1.功能不同,computed,计算属性,watch监听属性变化,执行回调
2.缓存,computed有缓存,data没变化,调用改属性直接从缓存中取值,watch每次监听都要重新执行
3.return,computed必须return ,watch不是必须
5.Vue 中 强制组件重新渲染的正确方法
简单粗暴的方式:重新加载整个页面
不妥的方式:使用 v-if
较好的方法:使用Vue的内置forceUpdate方法
// 全局
import Vue from 'vue';
Vue.forceUpdate();
// 使用组件实例
export default {
methods: {
methodThatForcesUpdate() {
// ...
this.$forceUpdate();
// ...
}
}
}
最好的方法:在组件上进行 key 更改
2.vue的响应式原理
vue组件在渲染时候会把data里的所有数据遍历,用Object.definePrototype()把所有的protype给打上getter/setter。每个vue组件实例都对应着一个watcher实例,它会把组件渲染过程中的protype作为依赖,依赖项的setter更新时候就会通知watcher,从而触发组件的更新。
3.手写一个数据双向绑定
const data = {}
const input = document.getElementById('input')
const span = document.getElementById('span')
Object.defineProperty(data,'text',{
set(value){
input.value = value
this.value = value
span.innerHTML = value
}
get(){
return value
}
})
input.addEventListen('input',function(e){
data.text = e.target.value
})
obj.text = '111' //
4.双向绑定
Vue的模式是m-v-vm模式,即(model-view-modelView),通过modelView作为中间层(即vm的实例),进行双向数据的绑定与变化。
1.通过建立虚拟dom树document.createDocumentFragment(),方法创建虚拟dom树。
2.一旦被监测的数据改变,会通过Object.defineProperty定义的数据拦截,截取到数据的变化。
3.截取到的数据变化,从而通过订阅——发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据。
4.最后,通过更新虚拟dom的元素值,从而改变最后渲染dom树的值,完成双向绑定
https://segmentfault.com/a/1190000006599500
观察者-订阅者:
Observer 数据监听器,把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty()方法把这些属性全部转成setter、getter方法。当data中的某个属性被访问时,则会调用getter方法,当data中的属性被改变时,则会调用setter方法。
Compile指令解析器,它的作用对每个元素节点的指令进行解析,替换模板数据,并绑定对应的更新函数,初始化相应的订阅。
Watcher 订阅者,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数。Dep 消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法。执行流程如下:
从图中可以看出,当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行解析,初始化视图,并订阅Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。因为VUE使用Object.defineProperty方法来做数据绑定,而这个方法又无法通过兼容性处理,所以Vue 不支持 IE8 以及更低版本浏览器。
5.mvvm
Model:数据层,通过ajax/fetch等api完成客户端和服务器端的Model数据的同步。
View:视图层,是动态模板,展示ViewModel层的数据和状态
把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。
ViewModel:把Model和View关联起来的就是ViewModel,ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。
它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定
优点:
1.分离视图和模型,降低代码耦合,提高视图或逻辑的重用性。
2.提高可测试性
3.自动更新dom
缺点:
1.bug难调试
2.model过大占用性能
3.维护成本高
6.keep-alive
作用:保存组件的渲染状态,是一种抽象组件,会缓存不活动的组件实例而不是销毁。
过程:
1.获取keep-alive包裹的子组件的name和组件实例
2.查看keep-alive的黑白名单,看看该子组件是否需要缓存,不需要直接返回组件实例,缓存继续往下
3.根据组件的ID和tag生成缓存key,并在缓存对象中查找是否已经缓存了该组件。若存在,则直接从缓存获取,而且要更新该key在缓存this.keys中的位置。
4.查看keep-alive缓存设置最大值,若超过,则删除下标为0的数组
5.最后,将组件的keeAlive设置为true
7.vue不能检测哪些属性变化
数组
使用下标更新数组元素
使用赋值方式改变数组长度
使用下标增删数组元素
解决办法:
Vue.set( target, key, value )
vm.items.splice(indexOfItem, 1, newValue)
对象
增删元素
解决办法:
Vue.set(target, propertyName, value);
Vue.delete( target, propertyName/index )
为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作
事实上在 vuex 里面 actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发 mutation 就行。异步竞态怎么处理那是用户自己的事情。vuex 真正限制你的只有 mutation 必须是同步的这一点(在 redux 里面就好像 reducer 必须同步返回下一个状态一样)。同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态(和 reducer 一样),这样 devtools 就可以打个 snapshot 存下来,然后就可以随便 time-travel 了。
8.vue的diff算法
vue与react diff算法的比较
相同点:原始diff算法o(n^3)时间复杂度,循环递归比较每一个节点。现在优化到o(n)时间复杂度,采用先序深度优先遍历。忽略跨级比较,只做同级比较。如果出现跨级操作,则直接删除重新创建一个新的元素。
不同点
1.如果元素className不同,vue认为是不同的元素,会删除重建。react会认为是个同类型节点,只修改节点的属性。
2.同级元素比较,react从左到右比较,vue从两端到中间比较。vue比较更高效。
四、算法
1.去重
[...new Set([1,1,2])]
Array.form(new Set([1,1,2]))
array.filter((item,index,arr)=>arr.indexOf(item) == index)
const obj = {}
array.forEach(item=>{
obj[item] = item
})
Object.keys(obj)
2.回文
str.split('').reverse().join('')
3.最大公共前缀
查找字符串数组中的最长公共前缀
var longestCommonPrefix = function(strs) {
if(strs.length ===0){
return ''
}else if(strs.length ===1){
return strs[0]
}
let index = ''//假设index是公共字符串
for(let i = 0;i
4.[1,[2],[3,[4,7,5],4,3]] 扁平化+排序 + 去重
//方法一:flat(Infinity)无限去扁平化 sort排序 new Set()去重
[...new Set(arr.flat(Infinity).sort((a,b)=>a-b))]
//方法二:toString去扁平化
Array.from(new Set(arr.toString().split(',').sort((a,b)=>a-b)))
5.快排
思路:随便获取一个基准,把它从数组中去除(splice)遍历数组,小于基准的元素放左边数组,大于放右边,循环遍历,最终数组长度为1停止
function quickSort(arr){
if(arr.length <= 1) return arr
const pre = arr.splice(0,1)
let left = [],right = []
arr.forEach(item=>{
if(item < pre){
left.push(item)
}else {
right.push(item)
})
return quickSort(left).concat(pre).concat(quickSort(right))
}
6.冒泡
7.深浅拷贝
1.浅拷贝
1.Object.assign()
Object.assign() 拷贝只是第一层是深拷贝,后面的都是浅拷贝了,所以只能算浅拷贝
const obj = {
a:1,
b:{c:3},
d:4
}
const cloneB = Object.assign({},obj)
// 这里改变 cloneB.a的值对obj.a的值没影响,改变 cloneB.b.c的值会影响obj.b.c
2.slice concat 这两个方法都不会改变原数组(还有join也不会改变原数组)
const ary = [1,3,5]
const cloneAry1 = ary.slice() // ary.slice(0)
const cloneAry2 =[].concat(ary)
2.深拷贝
1.JSON.stringify() JSON.parse()
const obj = { name:"source", child:{ name:"childnm" } }
const str = JSON.stringify(obj)
const cloneDeepObj = JSON.parse(str)
缺点 1.性能低
2.一些类型无法拷贝,如函数,正则等
3.循环引用无法正确解析
2.递归循环
function deepClone(obj){
const cloneObj = Array.isArray(obj) ? [] : {}
// 如果obj为空直接返回 [] 或者 {}
if(obj && typeof obj === 'object'){
for(let key in obj){
if(obj[key] && typeof obj[key] === 'object'){
cloneObj[key] = deepClone(obj[key])
}else {
cloneObj[key] = obj[key]
}
}
}
return cloneObj
}
function deepClone(obj) {
let newObj = null; // 声明一个对象来存储拷贝之后的内容
// 判断数据类型是否是复杂的数据类型,如果是则调用自己,如果不是则直接赋值即可!
// 由于null不可以循环但是他的类型又是object,所以这个需要对null进行判断
if (typeof(obj) == 'object' && obj !== null){
// 声明一个变量用以存储拷贝出来的值,根据参数的具体数据类型声明不同的类型来存储
newObj = Array.isArray(obj)? [] : {};
// 循环obj的每一项,如果里面还有复杂的数据类型的话,则直接利用递归函数再次调用。
for(let i in obj){
newObj[i] = deepClone(obj[i])
}
} else {
newObj = obj
}
return newObj; // 函数没有返回的值的话,则为undefined
}
- lodash库 lodash.cloneDeep()
五、TS
核心原则之一:给值的结构进行类型检查
1.ts的基本数据类型
1.boolean // let a:boolean = true
2.number // let b:number = 123
3.string // let c:string = '123'
4.数组
let d1: number[]=[1,2,3] // 数字数组写法一
let d2: Array = [1,2,3] // 数字数组写法二
5.元祖tuple
let x:[number,string]
x = [1,'qw'] // ok
x = ['qw',1] // Error
6.枚举enum
enum color = {blue,red,yellow}
let g:color = color['blue']
7.any,任意类型 // let f:any = 2
8.void,与any相反,不是任何类型
函数没有返回值就是void
eg:
function fn():void{
console.log(11)
}
申明void只能赋值undefined和null,没啥意义 const a :void = undefined
9.Null/undefined
默认情况下是任何类型的子类
申明了--strictNullChecks后,只能赋值给自己和void
10.never
3中类型:总是异常、没有返回值的函数,箭头函数
任何类型的子类,可以赋值给任何类型
11.object
declare function create(o:obejct | null) :void
create({p:a})// OK
create(null); // OK
create(42); // Error
2.类型断言
1.
const someStr:any = 'hello world'
const strleg:number = (someStr).length
2.some as string
react里面的jsx语法只能使用as这种语法
const someStr:any = 'hello world'
const strleg:number = (someStr as string).length