.
1.数据类型有哪几种, 检测方法是什么??
基本数据类型
ES5-------Number/Boolean/String/Undefined/Null
ES6新增—symbol
引用数据类型
Object
检测方法4种
1、Object.prototype.toString.call()
**作用: 可以检测所有数据类型**
**所有数据类型都可以检测,而且非常正确**
语法: Object.prototype.toString.call( 'xxx'/11/[ ] )
返回值: [object Xxx], Xxx 就是对象的类型
2、constructor
**作用: 可以检测基本数据类型和引用数据类型**
**弊端: 把类的原型进行重写, 很有可能把之前的constructor覆盖 检测出来的结果就会不准确**
语法: ("xx")/([ ])/(function(){ }).constructor === String/Array/Function
返回值: true/false
3、instanceOf
**原理: 判断对象类型,基于原型链去判断(obj instanceof Object)**
**左边对象的原型链proto上是否有右边构造函数的proptotype属性**
**作用: 判断左边的对象是否是右边构造函数的实例**
**弊端: 用于引用类型的检测, 对于基本数据类型不生效**
语法: " "/[ ]/true instanceOf String/Array/Boolean
返回值: true/false
4、typeOf
**作用: 用于检测基本数据类型和函数**
**弊端: 引用数据类型(Arrary/function/object)只会返回Object, 不起作用**
语法: typeOf " "/[ ]/xx
返回值: "string"/"boolean"/"object" (无法区分)
2.原型/原型链,请简述prototype、proto constructor三者的关系
每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一 个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对 象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找… 这个操作被委托在整个原型链上
1、prototype:每一个函数都有一个prototype这个属性,而这个属性指向一个对象,这个对象我们叫做原型对象
作用: 节约内存扩展属性和方法可以实现类之间的继承
2 、 proto :每一个对象都有一个_proto_属性_proto_ 指向创建自己的那个构造函数的原型对象对象 可以直接访问 proto 里面的属性和方法
3、constructor:指向创建自己的那个构造函数 ,是原型上的方法
总结: 当我们创建一个构造函数的时候这个构造函数自带了一个prototype属性,而这个属性指向一个 对象,也就是原型对象。 这个原型对象里面有一个constructor构造器,它的作用是指向创建自己的构造函数。除此之外 prototype还可以存放公共的属性和方法。 当我们实例化一个对象的时候(被new调用的时候),这个对象自带了一个 proto 属性,这 个proto 指向创建自己的构造函数的原型对象。可以使用这个原型对象里面的属性和方法
3、普通构造函数constructor与class的区别??
传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。
ES6引入了Class(类)这个概念,通过class关键字可以 定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言
constructor: constructor()方法是类的默认方法,通过new命令生成对象实例 时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的 constructor()方法会被默认添加。
[https://www.jianshu.com/p/86267fab4878
4.跨域出现的原因/解决方法??
原因:由于浏览器的同源策略,即属于不同域的⻚面之间不能相互访问各自的⻚面内容。
哪些情况下产生跨域
1、域名不同,2、 端口号不同,3、协议不同(http/https),4、域名和域名对应ip,5、主域名相同(127.0.01 和 localhost) 多域名匹配一个ip地址 6、子域名不同(一级和二级域名)
解决方法
1、后端代理 - 后端不存在跨域(后端代码脱离浏览器,后端是服务器端)
利用后端(自己公司的后端)去获取接口数据,将数据传给前端
2、jsonp原理** (json with padding) 数据填充的方式
利用浏览器的"漏洞" src不受同源策略的影响,可以请求任何链接 。动态创建script标签,将事先写好的函数名传给服务器,供服务器使用
(1)script标签src属性不存在跨域
(2)get方式–传递函数名 --弊端
(3)callback回调函数(传递函数名)
3、反向代理 proxy webpack配置
“proxy”: {
“/index.php”: {
“target”: “http://qinqin.net”,
“changeOrigin”: true
}
}
4.CORS解决跨域(xhr2) nginx
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出 XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
需要服务器(提供接口的源码里面)添加下面两句话。
header(‘Access-Control-Allow-Origin:*’);
header(‘Access-Control-Allow-Method:POST,GET’);
jsonp是一种非正式传输协议,用于解决跨域问 题流程: 1、创建一个全局函数 2、创建一个script标签 3、给script添加src 4、给src添加回调函数test(callback=test) callback是传给后端的一个参数 5、将script放到⻚面上 6、script请求完成,将自己从⻚面上删除
https://www.cnblogs.com/sdcs/p/8484905.html
5.说一下什么叫闭包原理/优点/缺点/使用场景??
闭包原理:定义在一个函数内部的函数(函数嵌套函数),闭包就是将函数内部和函数外部连接起来的一座桥梁。
打破了作用域链的规则 闭包就是能够读取其他函数内部变量的函数
用途**:a:可以读取函数内部的局部变量 b:让这些变量始终保持在内存当中 **
3注意:由于闭包会使得函数中的变量都被保存在内存当中,内存会消耗很大,所以不能够滥用闭包,否则会造成网⻚性能的问题 闭包
**优点:**1、使用闭包是不会污染全局环境,2、方便进行模块化开发,3、减少形参个数,延长了形参的生命周期,
坏处 1、就是不恰当使用会造成内存泄漏
闭包的不适用于返回闭包的 函数是个特别大的函数,很多高级应用都要依靠闭包实现.
使用场景:1、通过循环给页面上多个dom节点绑定事件 。2、封装私有变量(计数器) 3、:延续局部变量的寿命,4、高阶组件 5、函数防抖
模块化的就是以闭包为基础构建的;
/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null
//借助闭包
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
timer = setTimeOut(fn,delay)
}else{
timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}
5.1 内存泄漏
1、意外的全局变量
2、被遗忘的定时器
3、没有清除的dom应用 ,故要及时清除,
4、滥用闭包
清除内存泄漏方法有两种,一是标记清除,二便是引用计数清除。
5.2 垃圾回收机制
JS的垃圾回收机制是为了以防内存泄漏:标记清除(mark and sweep)、引用计数(reference counting)。
标记清除:垃圾收集器给内存中的所有变量都加上标记,然后去掉环境中的变量以及被环境中的变量引用的变量的标记。在此之后再被加上的标记的变量即为需要回收的变量,因为环境中的变量已经无法访问到这些变量。
引用计数:这种方式常常会引起内存泄漏,低版本的IE使用这种方式。机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一。当该值引用次数为0时就会被回收。
6.promise/generator/async…await??
Promise 是es6新增的,异步编程的一种解决方案,用来取代回调函数和事件,比传统的解决方案——回调函数和事件——更合理和更强大。
有三种状态:pending(进行中)、fulfilled(resolve已成功)和rejected(已失败)。
promise的特点 :
(1)对象的状态不受外界影响。Promise对象代表一个异步操作。
(2)一旦状态设定,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为resolve和从pending变为rejected。只要这两种情况发生,状态就凝固了
promise的的方法
promise实例方法:Promise.prototype.then Promise.prototype.catch*
一:resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
二:reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
三:Promise.prototype.finally
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise的 静态方法 Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve.
Promise.race():将Promise对象数组中最先执行完成的内容通过后面then传出
promise的基本使用
1、通过new promise创建一个promise对象,里面有一个参数,参数是一个回调函数,回调函数中 有2个参数,resolve,reject resolve()当异步执行成功的时候调用的方法,reject()当异步失败的 时候调用的方法。
2、除此之外promise有一个then方法,当成功的时候执行第一个回调函数,当失败 的时候执行第二 个回调函数。第二个回调函数也可以通过promise对象.catch调用
3、Promise.all():当所有的异步代码都执行完毕以后才会执行.then中的操作
4、Promise.race():只要有一个promise执行完毕后就会执行.then操作
https://www.cnblogs.com/moveup/p/9746748.html
promise的三种状态与链式调用: pending - 进行中 fulfilled - 成功 rejected - 失败
https://www.jianshu.com/p/dc61ea153874
async异步能干什么?就是用来修饰函数,使该函数异步执行,不阻碍后续函数的执行 同时我们注意到,async修饰的函数也带有then catch方法,因此,经async修饰的函数也 是一个promise await只能放在async中,且只能修饰promise对象 1. promise的诞生是为了简化函数嵌套调用流程,也便于后续维护 2. async/await定义了异步函数,并在其内部可通过await等待promise对象,阻塞后 续的执行
**await关键字必须在async函数里面 await会阻塞当前直到完成 async返回reject的方法,当抛出异常等同于reject async / await与 Promise的主要区别是 Promise代码完全都是Promise的API(then、catch等等),操作本身的语义反 而不容易看出来, async / await函数的实现最简洁,最符合语义,几乎没有语义不相关的代码 async / await 函数就是 Generator 函数的语法糖
async/await函数的优势
1. 使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要 写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌 套代码
2. 使用aync/await的话,catch能处理JSON.parse错误 promise中不能处理
3. 条件语句也和错误捕获是一样的,在 Async 中也可以像平时一般使用条件语句
[promise的状态处理的原理] :https://www.jianshu.com/p/44a971659696
generator:https://www.jianshu.com/p/83da0901166f
14.async await
await后面有个接口 接口要2S才能完成 接口2S才会执行
await是同步还是异步:await同步 async异步
async和await有两个关键字,一个写在函数外面,一个写在函数里面,函数外面是异步的 函数里面是同步的 调用函数的那一行其实是异步的,下一行
函数里面转成阻塞的
async await promise改写
async函数中
let a=await promise 的a函数
let b=await promise 的b函数
promise.all改写 Promise.allSettled Promise.any
promise实现promise.all的方法
async 使用的时候报错,如何捕获try…catch
var test3 = async function () {
try {
await p1();
await p2();
p3();
} catch (e) {
console.log('p1失败了', e)
}
}
15.$.ajax中async:false的阻塞和await这种阻塞有什么区别
没区别
7、前端堆/栈|深/浅拷贝及方法
1、**堆**--引用类型地址传递
堆:动态分配的内存,大小不定,不会自动释放。
存放引用类型的值 先进后出FILO
引用类型: Object(Arrary, Date, Math,Function, Object)
访问方法: JS不能直接访问内存中的值, 只能操作对象的地址, 所以产生深/浅拷贝问题**
2、**栈**--基本类型值传递
自动分配内存空间,系统自动释放,
存放基本类型的值和引用类型的地址 先进先出FIFO
基本类型: Undefined、Null、Boolean、Number 和 String, Symbol(ES6新增)
访问方法: 按值访问, 直接操作内存中的值
对象浅拷贝在react中用到哪些地方,为什么
为什么在react中要使用浅拷贝
redux中要求:状态是只读的,唯一且不可修改的,reducer必须是一个纯函数
-因为redux中数据不可更改,所以redux中的数据应该要拷贝 返回一个新值
深/浅拷贝针对的是 引用类型
浅拷贝–新旧互相影响,改变的是地址
新值===原值, 只能拷贝一层
数组方法 ` slice截取`, `concat拼接`, `filter过滤` , `map`,
对象方法 ` Object.assign({ },obj)`, `Object.create(obj)`
展开运算符`{…obj}` [...arr]
深拷贝–新旧互不影响,改变的是值
新值=/=原值 , 可以拷贝多层
1、JSON序列化** **JSON.parse( JSON.stringify(obj) )** 对象-->字符串-->对象
这个方式的弊端:
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,不可枚举的不能被复制
可枚举 :可枚举性(enumerable)用来控制所描述的属性,是否将被包括在for...in循环之中。具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性。
- for..in循环
- Object.keys方法
- JSON.stringify方法
2、**原生实现**
递归+浅拷贝
3、 **工具实现**【 第三方封装库 】
loadsh _.cloneDeep(obj)
缺点
4、Immutable
Object.create()
13.es6新特性
let let声明的变量有块作用域 不能重复声明变量,
const 常量声明,一旦声明,常量的值不能改
const定义一个对象,可以修改里面的属性,因为对象是引用类型 改变的是引用地址
解构赋值
扩展运算符 ...
模板字符串
箭头函数 简洁,同时函数体内this对象,就是定义时所在的对象,而不是使用时所在的对象。This不会改变了 一定是匿名函数 不要在最外层定义箭头函数
Symbol类型 独一无二的值
Generators生成器函数 * 函数体内部使用yield表达式将进程分片暂停 next获得结果
class extends 类和继承
promise 异步编程的一种解决方法 三个状态pending-resolve-reject
状态从pending-resolve 成功返回一个promise对象,通过then传递
状态从pending-reject 失败走reject
promise.all 并发 promise.race速发
12,set和map
不说set 数据结构Set new set()存储数据 set.size得到存储的数据长度
has()判断某个值是否存在set中 foreach遍历set
不说map : new map map.set map.get map.delete
都是用来存储数据用的,但是存储的数据格式不同
set 直接存储 任意类型数据
map 存储数据的时候,必须以key,value的形式,
set 使用forEach 遍历的时候,key和value值是一样的
而map 遍历的时候,key就是存进去的对象的key,value就是存在的值
for循环这种写法比较麻烦,因此数组提供内置的forEach方法。
forEach没有返回值,无法中途跳出forEach循环,break命令或return命令都不能奏效。
for...in循环主要是为遍历对象而设计的,不适用于遍历数组**
for...of循环相比上面几种做法,有一些显著的优点。
有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同于forEach方法,它可以与break、continue和return配合使用。
提供了遍历所有数据结构的统一操作接口。
6.宏任务和微任务
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
当指定的事情完成时,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)。
先执行同步代码 微任务放在微任务队列中 宏任务放在宏任务队列中
宏任务和微任务是任务队列是两个任务队列中
同步执行完,执行微任务,再执行宏任务
宏任务 setTimout setInterval
微任务 promise process.nextTick
7.异步操作
回调函数 异步回调 函数作为参数传递给另外有一个函数 将函数内部的值通过回调函数传出来 解决同步的一种方式 多层嵌套 造成回调地狱
事件监听
发布/订阅 系统中有一个信号中心,当某个任务执行完成后向信号中心发布一个消息,其他任务就可以通过这个信号中心去订阅这个信号,从而知道什么时候直接可以开始执行 发布订阅模式和事件监听类似,可以查看消息中心有多少信号,每个信号有多少订阅者
promise
generator(ES6) 异步任务 通过yield关键字可以让任务在需要的地方暂停,每一步的值可以通过next获取
async/await(ES7) await得到的就是async异步返回值,底层原理还是promise中的resolve方法
10.本地存储— 三种存储
首先总的来说,三者都是用于持久化数据存储的手段,都是存储在浏览器端,且同源.
localStorage和sessionStorage都是Web存储,大小5M左右,完全存储在客户端,它们是因为本地存储数据而存在.
cookies也是存储在浏览器端的,大小不超过4k,由服务器端存储在客户端。
localStorage属于永久性存储,数据存储量大,,而sessionStorage属于当会话结束的时候,存储的值会被清空,而cookie是通过设置过期时间expires来存储的。
10.表单只能输入数字
正则 type number
10.es2016-es2020
https://zhuanlan.zhihu.com/p/133658121
https://juejin.im/post/6844904021308735502
String.prototype.matchAll
String.prototype.matchAll比 match() 多得多的信息—它返回的迭代器不仅包括精确的匹配结果,还有全部的正则模式捕获结果。返回的结果更全面
而String.prototype 的 match() 方法仅返回完整的匹配结果,却不会返回特定正则表达式组的信息,
import() 按需获取的动态 import 返回了一个强大的 promise 函数,使得诸如按需引入
BigInt – 任意精度整数 或是在一个数值后添加 n 后缀,来将一个 number 转换为 bigint 类型
Promise.allSettled 它可以用在处理所有的 promise 无论结果是 fulfilled 还是 rejected,无需 catch
Promise.any()
globalThis 手动定义全局this指向,全局this指向:浏览器中它是 window, 在 worker 中它是 self, 在 Node.js 中它是 global
可选链 简化对象属性的长链式访问易出错又不易读
const title = data && data.article && data.article.title // 之前
const title = data?.article?.title // 之后
空值合并运算符 空值合并运算符 ?? 只会在左边的值严格等于 null 或 undefined 时起作用
16.Proxy和Reflect
Proxy用于修改某些操作的默认行为,即对编程语言层面进行修改,属于“元编程”,Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法
let pro = new Proxy(target,handler)
其中 new Proxy相当于创建了一个Proxy实例,target为所要拦截的目标对象,handler也是一个对象,里面定义的是对拦截对象所要进行的拦截方法
Proxy也可以作为其他对象的原型对象使用
上述实例将pro作为obj的原型对象使用,虽然obj本身没有name这个属性,但是根据原型链,会在pro上读取到name属性,之后会执行相对应的拦截操作。
let pro = new Proxy(target,handler);
let obj = Object.create(pro);
Proxy常用的拦截方法**
get(target,name,property)方法,用于拦截某个读取属性的操作,第一个参数为目标对象,第二个参数为属性名称,第三个属性为操作所针对的对象(可选参数)
set(target,name,value,property),用于拦截某个属性的赋值操作,第一个参数为目标对象,第二个参数为属性名,第三个参数为属性值,第四个参数为操作行为所针对的对象(可选参数)
has(target,key),用来拦截对象是否具有某个属性值的操作,第一个参数为目标对象,第二个参数为属性名
Reflect对象 :Reflect设计的目的是为了优化Object的一些操作方法以及合理的返回Object操作返回的结果,对于一些命令式的Object行为,Reflect对象可以将其变为函数式的行为
Reflect(target,name,property) Reflect.has(obj,“name”)
Reflect.get(target,name,property)
12.浏览器优化
减少请求数量
一.图片处理
1、雪碧图 - gulp Base64 使用字体图标来代替图片 - 自定义字体 @font-face{} 在安卓下可以使用webp格式的图片,它
二.减小资源大小 - grunt gulp webpack
1、HTML压缩2、CSS压缩3、JS压缩与混乱4、图片压缩
三.优化网络连接
cdnCDN即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状
用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度
四.优化资源加载
资源加载位置
1、CSS文件放在head中,先外链,后本页
2、JS文件放在body底部,先外链,后本页
3、body中间尽量不写style标签和script标签
资源加载时机
1、异步script标签
2、模块按需加载需要根据路由来加载当前页面需要的业务模块
3、资源懒加载与资源预加载
五.减少重绘回流
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
回流必将引起重绘,而重绘不一定会引起回流。
优化
减少回流和重绘
css3硬件加速(GPU加速)
六.【DOM优化】
1、缓存DOM2、减少DOM深度及DOM数量3、批量操作DOM4、批量操作CSS样式5、在内存中操作DOM6、DOM元素离线更新7、DOM读写分离8、事件代理9、防抖和节流10、及时清理环境
web网络请求的过程 !!!
1、域名解析
2、为将你的信息从pc上传到服务器上,需要IP协议,ASP协议,ospf协议,发起TCP的三次握手
3、建立TCP连接,发起http请求
4、服务器响应http请求
5、服务器端解析HTML代码,请求HTML中的资源
6、断开TCP连接(4次挥手)
7、浏览器将渲染页面呈现给用户
12、请求头
8、Accept
告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
9、Accept-Charset
浏览器告诉服务器自己能接收的字符集。
10、Accept-Encoding
浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)。
11、Accept-Language
浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
12、Authorization
当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,用该头部来回应自己的身份验证信息给WEB服务器。
13、If-Match
如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的动作,获取文档。
14、If-None-Match
如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求的动作,获取文档。
15、If-Modified-Since
如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作(比如返回对象),否则返回代码304,告诉浏览器该对象没有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
16、If-Unmodified-Since
如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)。
17、If-Range /reinge/
浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的ETag 或者自己所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一起使用。
18、Range
浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。例如:Range: bytes=1173546
19、Proxy-Authenticate
代理服务器响应浏览器,要求其提供代理身份验证信息。
20、Proxy-Authorization
浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
21、Host
客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。如Host:rss.sina.com.cn
22、Referer
浏览器向WEB 服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL,例如:Referer:http://www.ecdoer.com/
23、User-Agent
浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN;rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
12 、数据请求设置请求数据格式Content-Type
1.浏览器默认的 application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
2.multipart/form-data
这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,就要让 form 的 enctype 等于这个值
3.application/json
除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify
4.text/xml
相比于JSON,[XML](https://link.jianshu.com/?t=http://www.w3school.com.cn/x.asp)不能更好的适用于数据交换,它包含了太多的包装, 而且它跟大多数编程语言的数据模型不匹配,让大多数程序员感到诧异,XML是面向数据的,JSON是面向对象和结构的,JSON会给程序员一种更加亲切的感觉。
16.怎么携带cookie发送给后端
设置请求头 请求头中携带cookie
8.fetch和axios的区别??
axios是一种对ajax的封装,fetch是一种浏览器原生实现的请求方式,跟ajax对等
Fetch:可以很好的实现分离原则。 也原生支持目前流行的异步流(promise) 模型,但是很多平台都不实现,不能进行复杂的io操作 Fetch携带cookie credentials: “include”
axios怎么取消请求
原生中有一个取消的方法,可以调用XMLHttpRequest对象上的abort方法 在axios拦截器里, 查找axios的文档,发现 可以通过使用CancelToken来取消axios发起的请求
13.static有什么特性
new出来一个实例对象是否带有static属性 static用ES5怎么写
没有创建对象,也能使用属性和调用方法
用来形成静态代码块以优化程序性能。因为只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次
被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。
类第一次加载初始化的时候就去加载static部分,后面可以重新赋值
static用ES5怎么写:静态是通过类名直接调用 class A staticB
直接用A.B 将B绑定在A上
9.回流/重绘 | 防抖/节流??
1、回流:当渲染树中的一部分或者全部因为元素的尺寸、布局、隐藏等改变而需要重新构建的时候,这时 候就会发生回流。 每个⻚面都至少发生一次回流,也就是⻚面第一次加载的时候。 在回流的时候,浏览器会使渲染树中受到影响的元素部分失效,并重新绘制这个部分的渲染树, 完成回流以后,
2、重绘:当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
防抖和节流
3、函数防抖:当事件被触发一段时间后再执行回调,如果在这段时间内事件又被触发,则重新计时。
比如7s内点击100次,我用节流和防抖分别设置了3s,那么实际情况是分别执行了几次
在事件触发时,开始计时,在规定的时间(delay)内,若再次触发事件,将上一次计时(timer)清空,然后重新开始计时。保证只有在规定时间内没有再次触发事件之后,再去执行这个事件。 1次
4、函数节流(throttle):指定时间间隔内,若事件被多次触发,只会执行一次
在事件触发之后,开始计时,在规定的时间(delay)内,若再次触发事件,不对此事件做任何处理。保证在规定时间内只执行一次事件.
10、事件循环EventLoop
Event Loop 即事件循环,是指浏览器或者Node 的一种解决JavaScript 单线程运行时不阻塞的一种机制,
单线程的是所有任务都在主线程上完成,任务太多的时候,页面卡死,
eventLoop可以解决单线程阻塞问题,程序中会有两个线程,一个主线程,一个eventLoop线程,负责主线程和其他进程之间的通信,
遇到I/O的时候,主线程会让eventLoop线程通知对应的程序,主线程的任务会继续往后执行,等I/O程序执行完了,eventLoop线程会把结果返回给主线程,主线程利用回调函数调用结果,完成任务
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
宏任务和微任务的执行顺序
一次事件循环中,先执行宏任务队列里的一个任务,再把微任务队列里的所有任务执行完毕,再去宏任务队列取下一个宏任务执行。
11、同步异步的区别
在js中,所有任务都分为同步任务和异步任务两大类。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
有时候 setTimeout明明写的延时3秒,实际却5,6秒才执行函数,这又是因为什么?
答:setTimeout 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。
浏览器的JS引擎遇到setTimeout,拿走之后不会立即放入异步队列,同步任务执行之后,timer模块会到设置时间之后放到异步队列中。js引擎发现同步队列中没有要执行的东西了,即运行栈空了就从异步队列中读取,然后放到运行栈中执行。所以setTimeout可能会多了等待线程的时间。
这时setTimeout函数体就变成了运行栈中的执行任务,运行栈空了,再监听异步队列中有没有要执行的任务,如果有就继续执行,如此循环,就叫Event Loop。
13、数组扁平化
1. reduce
遍历数组每一项,若值为数组则递归遍历,否则concat。
function flatten(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
reduce是数组的一种方法,它接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce包含两个参数:回调函数,传给total的初始值
// 求数组的各项值相加的和:
arr.reduce((total, item)=> { // total为之前的计算结果,item为数组的各项值
return total + item;
}, 0);
2. toString & split
调用数组的toString方法,将数组变为字符串然后再用split分割还原为数组
function flatten(arr) {
return arr.toString().split(',').map(function(item) {
return Number(item);
})
}
因为split分割后形成的数组的每一项值为字符串,所以需要用一个map方法遍历数组将其每一项转换为数值型
3. join & split
和上面的toString一样,join也可以将数组转换为字符串
function flatten(arr) {
return arr.join(',').split(',').map(function(item) {
return parseInt(item);
})
}
4. 递归
递归的遍历每一项,若为数组则继续遍历,否则concat
function flatten(arr) {
var res = [];
arr.map(item => {
if(Array.isArray(item)) {
res = res.concat(flatten(item));
} else {
res.push(item);
}
});
return res;
}
5. 扩展运算符
es6的扩展运算符能将二维数组变为一维
[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]
根据这个结果我们可以做一个遍历,若arr中含有数组则使用一次扩展运算符,直至没有为止。
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(…arr);
}
return arr;
}
如何扩展数组,让他拥有新的方法
Array.prototype.
class中 super作用是什么
es5 的继承是先创建子类的this,然后将父类的方法添加到子类的this上去;
es6 的继承是创建父类的this对象,然后再对this对象添加方法/属性。
而super方法就是用来创建父类this对象的。
实际上执行的是 super.sport.call(this);
xss攻击和csrf攻击是什么
1、CSRF(Cross-site request forgery):跨站请求伪造。
(1)登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
(2)在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)
、CSRF如何防御
方法一、Token 验证:(用的最多)
(1)服务器发送给客户端一个token;
(2)客户端提交的表单中带着这个token。
(3)如果这个 token 不合法,那么服务器拒绝这个请求。
方法二:隐藏令牌:
把 token 隐藏在 http 的 head头中。
方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。
方法三、Referer 验证:
Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。
2、XSS(Cross Site Scripting):跨域脚本攻击。
XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
最后导致的结果可能是:
盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击
XSS的攻击方式
1、反射型
发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
2、存储型存
储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
XSS的防范措施(encode + 过滤)
XSS的防范措施主要有三个:
1、编码:
对用户输入的数据进行
HTML Entity 编码。
2、过滤:
移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言是,过滤掉一些不安全的内容)移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。
3、校正
避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。备注:我们应该去了解一下DOM Parse
这个概念,它的作用是把文本解析成DOM结构。
比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。
还有一种简洁的答案:
首先是encode,如果是富文本,就白名单。
CSRF 和 XSS 的区别
区别一:
CSRF:需要用户先登录网站A,获取 cookie。XSS:不需要登录。
区别二:(原理的区别)
CSRF:是利用网站A本身的漏洞,去请求网站A的api。XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
3.小程序的逻辑和视图是不是同一个线程
4.类组件和函数组件的区别
5.用过哪些hooks,usememo是什么
7.webapp和小程序有什么区别
首先,微信小程序已经提供了一套 view, data, model, router 层的开发工具,对于开发简单应用,小程序是可以比 webapp 更加快速的。但是实际上微信小程序提供的这一套开发框架,要开发一些复杂应用,是很困难的,因为:小程序不支持 npm 等 package manager无法复用社区中已经很成熟的 web 框架和工具组件只能封装 view 和 style,无法封装行为(handler),行为只能定义在 page 上小程序有 1mb 的限制,所以我们只能将图片之类的静态资源事先放在服务器上注:以上的问题其实还是可以解决的,但是要付出较大的成本。其次,微信小程序是由微信自己来 host,开发者只需要上传就好,而微信 webapp 需要开发者自己 host,还需要注册域名甚至备案才可以调用微信接口以及跟公众号集成。所以微信小程序降低了开发者的门槛。
综上,对于简单的工具型应用,微信小程序可以让开发者加快开发速度,降低发布门槛,这种类型的应用比较适合微信小程序。对于复杂的应用,webapp 是更适合的形式。
8.小程序的性能优化
9.后台管理系统安全怎么处理(token,路由鉴权)
react
1、webPack|前端自动化构建工具??
webpack配置与npm的玩法:Gulp是基于流的前端自动化构建工具,基于流的任务式的工具。 Webpack是一款模块化打包工具,webpack是基于配置的,Webpack 最关键的就是 webpack.config.js 配置文件,架构的好坏都体现在这个配置文件中。
webpack打包react vue,与自己的源代码分离使用splitchunks
webpack热更新:不用刷新浏览器而将新变更的模块替换掉旧的模块。
1.Webpack **
基础配置:1 配置出口entry 2 配置入口output 3 配置loader转换器,4配置插件(plugins) 5、配置别名alias 6配置反向代理 proxy
module.exports = {
entry: './path/to/my/entry/file.js'
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: { // 配置loader转换器,配置插件(plugins)都在module
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
],
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
},
"alias": {
"@": path.join(__dirname,'./src'),
},
"proxy": {
"/index.php": {
"target": "http://qinqin.net",
"changeOrigin": true
}
}
};
(loader , plugin分别什么作用,哪个配置可以把依赖包抽离出来,不打包进去)
【Loader】:用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在buld中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。babel-loader优雅降级配置ES高版本转成低版本
【Plugin】:是用于在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作,
依赖包抽离 const ExtractTextWebapckPlugin= require(“extract-text-webpack-plugin”) module exclude node_modules
排除excloude排除node_modules
我们在工程所在的根目录,新建一个 webpack.config.js,初始化为
var config = {};
export default config;
2、react优缺点
优点: 1 采用Virtual DOM,它并不直接对DOM进行操作,引入了一个叫做虚拟DOM
的概念,安插在javascript逻辑和实际的DOM之间,性能好 2 跨浏览器兼容:虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。 3、一切都是组件:代码更加模块化
,重用代码更容易,可维护性高。 4、单向数据流:redux 实现是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。 5、同构、纯粹的javascript :因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化
。 6、兼容性好:比如使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。 7、JSX语法:为了更加便利的模拟DOM结构,我们使用了JSX语法,可以让我们在JS中编译DOM结构 8、函数式编程:JS的最大特点就是函数式编程,在React中,函数式编程可谓式无处不见
缺点: 1、不适合单独做一个完整的框架:react是视图层框架,大型项目想要一套完整的框架的话,也许还需要引入Flux和route相关的东西。
3、react生命周期
16版本:
- constructor
* super(props)
* 定义state
* 普通函数绑定this
- componentWillMount
* 表示组件即将挂载
* 任务: 为挂载做准备工作
* componentWillMount这个钩子函数在将来会被弃用
* 项目中不使用
- render
* 解析 this.state 和 this.props
* 将jsx型的vdom渲染为对象类型的vdom
* render 中不允许使用 this.setState() 如何你使用了,就会栈 溢出
- componentDidMount
* 表示组件挂载结束
* vdom -> Real dom (可以获取真实dom)
* 项目中
* 1. 数据请求 -> 赋值给state
* 2. 第三方库实例化/DOM操作
更新阶段:
- componentWillReceiveProps
* 用于接收新的props值为参数(nextProps)
* 这个钩子实际作用是判断组件身上的props是否发生改变
* 这个钩子函数也在17版本中被弃用
- shouldComponentUpdate
* 两个参数 nextProps和nextState
* 可以决组件是否要更新渲染 return true/false
* 接收新旧状态,用于作对比(浅对比)
* 一般需要我们手动作深对比
* 这个钩子函数是React组件性能优化的一种方式
- componentWillUpdate
* 表示组件更新前的准备
* 这个钩子函数在未来版本被弃用
- render
* 和初始化阶段的作用一致
- componentDidUpdate
* 参数是接收变化前的属性(preProps)和状态(preState),新值是this.props和this.state
* 对Real dom 作操作
注意:父组件数据更新,子组件会重新运行render,反之不行
- 每次都要运行子组件render,会造成react性能浪费
- 粗略解决方案:在shouldComponentUpdate中,可判断哪些数据改变从而来控制return true/false 继而决定是否需要更新render渲染
- 更好的解决方案:
1.引用PureComponent,让类组件继承(extends)PureComponent 如:class App extends PureComponent{}
2.引用memo,让函数组件写在memo()里 如:const memoApp =memo(function App() {})
3.除了memo之外,更细致的是函数组件中还可以用useCallback(方法)和useMemo(对象和数组)两个钩子阻止组件render
销毁阶段:
- componentWillUnmount
* 销毁组件
* 清除无用实例和事件
错误捕获
- componentDidCatch
* 参数为error和info
* 用户捕获子组件throw的错误,然后显示回退UI
15版本:
相对于16版本,初始化阶段(constructor钩子变成下列两个):
* 定义于初始化props的 getDefalutProps 钩子函数
* 定义于初始化state的 getInitialState 钩子函数
没有错误捕获阶段
17版本:
相对于16版本 少3多2
* 少了componentWillMount,componentWillReceiveProps,componentWillUpdate 3个钩子函数
* 多了static getDerivedStateFromProps钩子函数
一个静态方法,所以不能在这个函数里面使用this,这个函数有两个参数nextProps和prevState,这个函数会返回一个对象用来更新当前的state对象,如果不需要更新可以返回null。简单说就是,可以改变一次状态
* 多了getSnapshotBeforeUpdate钩子函数,这个函数有一个返回值,会作为第三个参数传递给componentDidUpdate钩子
3、Redux 流程 redux的原理就将流程
1、首先我要把关于redux的工具先安装好:如redux,react-redux,redux-thunk,redux-devtools-extension
2、创建store实例,通过createStore来创建,这个实例里接收1个rootReducer和1个中间件执行函数
3、创建分块的数据rootReducer,,通过combineReducers就可以打造rootReducer,里面放分块数据
4、在组件里使用connect高阶组件,接收store里的数据和将actionCreators里的方法绑定到组件上并发送动作到reducer上
5、在reducer中根据action中的type动作类型,判断动作修改数据
redux组成
state 作用:1 用来存储数据和数据管理的 2、更新视图
reducer:reducer是一个纯函数,接受 旧 state 和 action,根据不同的 Action 做出不同的操作并返回新的 state 。
actions:发送动作给reducer, reducer接收动作,判断动作类型修改数据
修改事件后,组件重新做redux事件的订阅
Redux 三大原则
单一数据源 整个应用的 state 被存储在一个 Object tree 中,且只存在于唯一的Store中。
state 是只读的 唯一改变 state 的方法就是触发 action,action 是一个用于描述发生事件的普通对象,视图部分只需要表达想要修改的意图,所有修改都会被集中化处理。
使用纯函数来执行修改 为了实现根据 action 修改 state值,我们就需要编写 Reducer。它是一个纯函数,接收先前的 state 和 action 返回新的 state ,随着应用的变大,你可以将它拆成多个小的 Reducer ,分别独立操作 state tree 中的不同部分。
4.redux-thunk|异步action
好处
可以进行前后端数据交互
缺点
将带有数据请求的action和没有带有数据请求的action混在一起了
缺点解决: 弃用redux-thunk,使用redux-saga
redux-saga可以将异步action和普通action区别开来
5、redux-saga|集中处理异步action
redux-saga可以将异步action和普通action区别开来,控制器与更优雅的异步处理
而redux-saga就是用Generator来处理异步。
redux-saga文档并没有说自己是处理异步的工具,而是说用来处理边际效应(side effects),这里的边际效应你可以理解为程序对外部的操作,比如请求后端,比如操作文件。
redux-saga同样是一个redux中间件,它的定位就是通过集中控制action,起到一个类似于MVC中控制器的效果。
同时它的语法使得复杂异步操作不会像promise那样出现很多then的情况,更容易进行各类测试。
这个东西有它的好处,同样也有它不好的地方,那就是比较复杂,有一定的学习成本。
6、redux与react-reudx的关系
redux是独立的应用状态管理工具。它是可以独立于react之外的。如果我们需要在react当中运用它,那么我们需要手动订阅store的状态变化,来对我们的react组件进行更新。那么react-reudx这个工具,就帮我们实现了这个功能,我们只需对store进行处理,react组件就会有相应的变化。
Redux的核心由三部分组成:Store, Action, Reducer。
Store : 是个对象,贯穿你整个应用的数据都应该存储在这里。
Action: 是个对象,必须包含type这个属性,reducer将根据这个属性值来对store进行相应的处理。除此之外的属性,就是进行这个操作需要的数据。
Reducer: 是个函数。接受两个参数:要修改的数据(state) 和 action对象。根据action.type来决定采用的操作,对state进行修改,最后返回新的state。
总结
Redux: store, action, reducer
store: getState, dispatch, subscribe
combineReducers
createStore
store ️ dispatch ️ action ️ reducer
react-redux:
connect: 将store作为props注入
Provider: 使store在子孙组件的connect中能够获取到
7、react-reudx
-
1. UI组件|显示页面?? React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件 2. 容器组件|负责管理数据/复杂逻辑? UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。 3. Provider组件|容器组件获取state?? 所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。 4. connect()|UI组件+容器组件 React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。
28.DVA与CRA相比的优点??
dva: dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。— 来自官方。相比于cra只是多了内置的redux和redux-saga,帮我们处理了数据流这方面的需求而已。如果只是想要达到这个效果的话,直接在cra中增加dva-core的依赖也是可以做到的。
umi:是一个可插拔的企业级 react 应用框架。umi和cra都是应用框架,可能相比cra来说umi的功能点更多一些,只能说是功能性的话umi要相对来说更胜一筹
22.flux??
flux 是 react 中的类似于 vuex 的公共状态管理方案,它是 Facebook 官方给出的应用架构,利用数据的单向流动的形式对公共状态进行管理。现已不推荐使用。但是为了能更好的理解 Redux 方案,还是有必要熟悉 flux 的工作流程滴~
使用 cnpm i flux -S 的方式进行安装。
flux的组成
flux 是 react 中的类似于 vuex 的公共状态管理方案,它是 Facebook 官方给出的应用架构,利用数据的单向流动的形式对公共状态进行管理。现已不推荐使用。但是为了能更好的理解 Redux 方案,还是有必要熟悉 flux 的工作流程滴~
View:视图层
Action:视图发出的消息
Dispatcher:派发者,用来接收Action,执行回调函数
Store:数据层,存放状态,一旦发生改动
flux 在进行数据更新时,会经历以下几步:
用户与 View 层交互,触发 Action
Action 使用 dispatcher.dispatch 将Action自己的状态发送给dispatcher
dispatcher 通过register注册事件,再通过Action传入的类型来触发对应的 Store 回调进行更新
Store 里进行相应的数据更新,并触发 View 层事件使试图也同步更新
View层 收到信号进行更新
25.React 中 keys 的作用是什么?
key是react用于追踪哪些列表被修改、被添加或者被移出的辅助标识。
在开发过程中,我们需要保证某些元素在同级的元素中key是具有唯一性的特性,在react Diff算法中React会借助元素的key值来判断该元素是新创建的还是移动而来的元素,从而减少元素的不必要的重复渲染。此外,我们还需要借助key值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
react根据key来决定是销毁重新创建组件还是更新组件,原则是:
key相同,组件有所变化,react会只更新组件对应变化的属性。
key不同,组件会销毁之前的组件,将整个组件重新渲染。
30.react新特性和diff算法
render
支持返回这五类:React elements, 数组,Fragments,Portal,String/numbers,boolean/null。基础数据类型
Fiber
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
新的生命周期函数
由于异步渲染的改动,componentWillMount, componentWillReceiveProps,componentWillUpdate 三个函数将被废弃。
由于这是一个很大的改变会影响很多现有的组件,所以需要慢慢的去改。
目前react 16 只是会报warning,在react 17就只能在前面加UNSAFE_ 的前缀来使用
diff算法的作用计算出Virtual DOM中真 正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面
分层对比,把DOM树按层级拆分,简化了 复杂度,只比较同级元素,只匹配相同组件名字的组件,给列表结构的每个单元添加唯一的key 属性,方便比较(为什么不用index,如果用index的话追加在后面不会影响,如果追加到前面会 影响性能)
getDerivedStateFromProps
static getDerivedStateFromProps(props, state)在调用render方法之前调用,无论是在初始安装还是后续更新。它应返回一个对象来更新状态,或者返回null以不更新任何内容。
根据props更新state
这个生命周期可用于替代componentWillReceiveProps
getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate(prevProps, prevState)在最近呈现的输出被提交到例如DOM之前调用。它使组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
hooks
lazy、suspense
lazy需要跟Suspence配合使用。
lazy实际上是帮助我们实现代码分割的功能。
由于有些内容,并不一定要在首屏展示,所以这些资源没有必要一开始就要去获取,那么这些资源就可以动态获取。
这样的话,相当于把不需要首屏展示的代码分割出来,减少首屏代码的体积,提升性能。
Suspence 很像Error Boundary,不同的是Error Boundary是用来捕获错误,显示相应的callback组件。而Suspence是用来捕获还没有加载好的组件,并暂停渲染,显示相应的callback。
8、虚拟dom diff算法
1.虚拟dom是什么
它是一个Object对象模型,用来模拟真实dom节点的结构
2.虚拟dom的使用基本流程(前四步骤)
1.获取数据
2.创建vdom
3. 通过render函数解析jsx,将其转换成 vdom结构
4.将vdom渲染成真实dom
5.数据更改了
6.使用diff算法比对两次vdom,生成patch对象
7.根据key将patch对象渲染到页面中改变的结构上,而其他没有改变的地方是不做任何修改的( 虚拟dom的惰性原则 )
9、setState概述,同步异步
- 两个参数及用法
第一个参数可以是对象或者函数,是更新state 第二个参数获取最新的state,副作用操作,dom操作事件触发声明,数据获取,第三方库实例化
- 同步/异步原理
setState在合成事件和钩子函数中是异步的
在原生事件和setTimeout中是同步的
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。
10、react合成事件
- 合成事件原理
如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent。
当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。
而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
所以当事件触发的时候,对使用统一的分发函数dispatchEvent将指定函数执行。
- 与原生事件的区别
React合成事件一套机制:React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
11、react路由传参
1.params
<Route path='/path/:name' component={Path}/>
<link to="/path/2">xxx</Link>
this.props.history.push({pathname:"/path/" + name});
读取参数用:this.props.match.params.name
优势 : 刷新地址栏,参数依然存在
缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。
2.query
<Route path='/query' component={Query}/>
<Link to={{ path : ' /query' , query : { name : 'sunny' }}}>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
读取参数用: this.props.location.query.name
优势:传参优雅,传递参数可传对象;
缺点:刷新地址栏,参数丢失
3.state
<Route path='/sort ' component={Sort}/>
<Link to={{ path : ' /sort ' , state : { name : 'sunny' }}}>
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
读取参数用: this.props.location.query.state
优缺点同query
4.search
<Route path='/web/departManange ' component={DepartManange}/>
<link to="web/departManange?tenantId=12121212">xxx</Link>
this.props.history.push({pathname:"/web/departManange?tenantId" + row.tenantId});
读取参数用: this.props.location.search
优缺点同params
https://www.jianshu.com/p/ad8cc02b9e6c
27.Mobx|类似双向数据绑定
- 组成部分
actions->state->computed values->Reactions - 工作流
在mobx中, 数据是通过加 @observable 作为可监测的被观察者, 在view层中, 你可以通过添加@observer 将view作为观察者,对数据进行监测, 如果要触发改变数据,则使用@action, 事实上,你可以直接在view层改变数据, 但这种方式不便监控数据,因此不推荐直接改变数据。 而@computed可以用来计算数据, 也可以是计算多个数据之后返回新的数据, 如果其中数据改变, @computed也会触发改变 - 优点
不同于redux的单一数据流, mobx中,你可以同时在各个地方使用同一份state, 也可以在一个页面中使用多个store文件
12.组件通信5种方法
https://www.jianshu.com/p/fb915d9c99c4
1.父–>子
父组件通过向子组件传递 props,子组件得到 props 后进行相应的处理。
2.子–>父
利用回调函数,可以实现子组件向父组件通信:父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信。
3.兄<–>弟
非嵌套组件,就是没有任何包含关系的组件,包括兄弟组件以及不在同一个父级中的非兄弟组件。对于非嵌套组件,可以采用下面两种方式:
1、利用二者共同父组件的 context 对象进行通信
2、使用自定义事件的方式
如果采用组件间共同的父级来进行中转,会增加子组件和父组件之间的耦合度,如果组件层次较深的话,找到二者公共的父组件不是一件容易的事,当然还是那句话,也不是不可以…
这里我们采用自定义事件的方式来实现非嵌套组件间的通信。
4.跨组件通信
所谓跨级组件通信,就是父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:
中间组件层层传递 props
使用 context 对象
对于第一种方式,如果父组件结构较深,那么中间的每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是这些中间组件自己所需要的。不过这种方式也是可行的,当组件层次在三层以内可以采用这种方式,当组件嵌套过深时,采用这种方式就需要斟酌了。
使用 context 是另一种可行的方式,context 相当于一个全局变量,是一个大容器,我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。
使用 context 也很简单,需要满足两个条件:
上级组件要声明自己支持 context,并提供一个函数来返回相应的 context 对象
子组件要声明自己需要使用 context
自己实现组件间的通信还是太难以管理了,因此出现了很多状态管理工具,如 flux、redux 等,使用这些工具使得组件间的通信更容易追踪和管理。
13.组件按需加载
**(实现方式,需要的依赖) **懒加载
1、vue异步组件技术
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载。
但是,这种情况下一个组件生成一个js文件。
2.import()
3.webpack提供的require.ensure()
4.第三方库比如react-loadable
5.lazy-loader
15.组件销毁怎么做 componentWillUnmount
定义state flag:false 作为开关 定义方法if判断是否为1,改变state flag:true
将方法绑定组件上 可以用三目或者短路原则&&控制组件的显示隐藏
14、antv中input输入框三个属性怎么实现
onChange输入框内容改变时候回调 value输入的内容 defaultValue输入框默认值
input默认值在input中绑定value 定义一个state 和input双向数据绑定 做成受控组件 定义一个事件 改变的时候获取e.target.value
14、Hook
1、为什么使用hook
不必写class组件就可以用state和其他的React特性;
自定义hook 实现组件的复用 用useEffects代替生命周期方法 代码更加简洁
2、出现原因3点??
1为了让函数组件拥有类组件的功能。
原因
比如useState、useReducer 状态定义
比如useEffect、useLayoutEffect 生命周期功能
比如useRef useImperativeHandle 替代了类组件的 Ref
2为了优化函数组件,比如useMemo、useCallback
3 函数组件添加新的方法,如
useDebugValue显示自定义hook,自己添加hook类名
自定义hooks来复用状态,
- 优点4点??
让函数组件可以定义状态
让函数组件可以使用生命周期、监听数据
让函数组件可以有Ref的功能(父组件获取子组件)
优化函数组件 useMemo、useCallback
自定义hooks来复用状态,
代码量比类组件更少,更清爽。 - 使用规则2点
不要在循环,条件或嵌套函数中调用 Hook
只在函数组件中使用 Hooks - 常用Hook
useState:定义状态 修改状态
useEffect:相当于componentDidMount , componentDidUpdate ,同useLayoutEffect
componentWillUnmount三个钩子的组合 DOM操作 第三方实例化可以做 清除无用实例和事件
useContext:跨组件通信 createContext创建一个组件 <numContext.Provider value={num}>
useDebugValue:自定义 hook 的标签 方便调试台查看
useMemo:记忆组件 动态缓存 新值和旧值一样,不重新渲染页面,优化作用,类似于shouldComponentUpdate useCallBack
useRef:返回一个可变的ref对象
useImperativeHandle:将组件中的方法放到外面使用 搭配React.forwardRef
5.自定义hook
类似于高阶组件
高阶组件返回的一个类组件,而自定义Hook可以返回任何东西
高阶组件必须传递一个组件作为参数,而自定义Hook不需要
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. “FriendStatus: Online”
useDebugValue(isOnline ? ‘Online’ : ‘Offline’);
return isOnline;
}
可以理解为数据的操作都在hook里进行,而外部只关心自己想要的。我只要数据列表,获取产品钩子(可能并不需要,可通过参数变更从而触发重新获取数据)、删除产品钩子
为了封装方法:节流;
https://www.jianshu.com/p/76901410645a
17.高阶组件HOC
1.名词解释/作用??
使函数复用,可以通过给组件传递方法来复用高阶组件中的函数方法。
高阶组件特点
高阶组件是一个函数
高阶组件接收一个组件作为参数进行使用,且需要在render函数中return返回这个组件
高阶组件的目的是为了: 复用组件,将多个组件都要使用的类似逻辑放在同一个地方进行处理,类似于在Vue中封装cookie以供重复使用
2.常用高阶组件4个??
React.memo()
connect()
provider()
withRouter() // 可以使用
3.有自己封装过吗?
拖拽封装,给组件里面的标签添加方法就可以实现拖拽,并返回标签的x与y坐标。
深对比,深复制封装、正则封装、页面路由跳转、路由数据接收解析。
16、useEffect 第二个参数
第二个参数可以支持数组中有几个函数吗 不可以
依赖性可以是一些props的属性
15、自定义hook封装过组件吗
1.将Hello组件和App组件中共用的逻辑放在统一的自定义hook中写
2.自定义hook,hook名以use开头
3.其他组件通过import引入自定义hook,就可以使用了
23.useEffect 第二个参数
第二个参数可以支持数组中有几个函数吗 不可以
依赖性可以是一些props的属性
16、.是否看过react源码**
看过
17、.vue对比react
相同点:1.都使用了vdom虚拟dom,如果需要改变任何元素的状态,先改vdom,当有变化产生时,会创建新的vdom,通过diff算法对比新旧vdom的差异,只需要渲染差异部分就行
2.都使用组件化
不同点:1.react中有新语法jsx,vue用普通的html
2.vue中父组件和子组件有通信的时候,父组件数据改变引起子组件改变,子组件会重新渲染 如果父子组件没有通信,父组件改变子组件不会渲染
react中不管是否有数据通信,父组件改变子组件都会渲染
3.react:create-react-app vue : vue-cli
4.react中用redux管理状态,state通过setState更新
vue中数据由vuex管理
20.react组件的时候,经常写class组件,class没有的时候怎么写
React.createClass 我们最早使用这个方法来构建一个组件“类”,它接受一个对象为参数,对象中必须声明一个render方法,render返回一个组件实例
21.生命周期 钩子函数用es5怎么定义
protoType.componentDidMount
18.cnnect高阶组件做了什么,cnnect的原理
- connect调用的结果是返回一个高阶组件
- connect方法利用了合并分发的原理来帮助我们完成store内容的获取
- 合并: 将store中的所有数据拿到手
- 分发: 将我们UI需要的数据派发出去
原理 合并分发 :合并的意思是:(我们项目中)redux的数据是集中在一处的
分发的意思是:给的是所有数据中的分块的数据
18.虚拟dom和diff算法
1.虚拟dom是什么
所谓的virtual dom,也就是虚拟节点。它通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点
2.虚拟dom的使用基本流程(前四步骤)
1.获取数据
2.创建vdom
- 通过render函数解析jsx,将其转换成 vdom结构
4.将vdom渲染成真实dom
5.数据更改了
6.使用diff算法比对两次vdom,生成patch对象
7.根据key将patch对象渲染到页面中改变的结构上,而其他没有改变的地方是不做任何修改的( 虚拟dom的惰性原则 )
3.diff算法是什么
Diff算法是用于比较两个新旧vdom树的差异的,比较完之后会得到一个差异对象,我们称之为patch补丁对象
比较后会出现四种情况:
1、此节点是否被移除 -> 添加新的节点
2、属性是否被改变 -> 旧属性改为新属性
3、文本内容被改变-> 旧内容改为新内容
4、节点要被整个替换 -> 结构完全不相同 移除整个替换
4.diff算法运行结束后,返回是什么
返回一个key
18、React 中 keys 的作用是什么?
key是react用于追踪哪些列表被修改、被添加或者被移出的辅助标识。
在开发过程中,我们需要保证某些元素在同级的元素中key是具有唯一性的特性,在react Diff算法中React会借助元素的key值来判断该元素是新创建的还是移动而来的元素,从而减少元素的不必要的重复渲染。此外,我们还需要借助key值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
react根据key来决定是销毁重新创建组件还是更新组件,原则是:
key相同,组件有所变化,react会只更新组件对应变化的属性。
key不同,组件会销毁之前的组件,将整个组件重新渲染。
19、React Native
React Native能在手机上创建原生应用,React在这方面处于领先位置。使用JavaScript, CSS和HTML创建原生移动应用,这是一个重要的革新。Vue社区与阿里合作开发Vue版的React Native——Weex也很不错,但仍处于开发状态且并没经过实际项目的验证。
既拥有Native的用户体验、又保留React的开发效率
React Native与React.js的主要区别还是JSX,它使用XML标记的方式去直接声明界面,将HTML直接嵌入到JavaScript代码中
20.Alibaba fusion design
完整的中后台设计系统解决方案
Alibaba Fusion Design 最初来源于一个阿里巴巴内部的中台项目
antv
npm install @antv/g2
G2 G6关系数据可视化图表分析工具 F2移动端可视化 L7地图空间数据可视化
原理
1.react-router原理
react-router依赖基础 - history
history是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理
老浏览器的history: 主要通过hash来实现,对应createHashHistory
高版本浏览器: 通过html5里面的history,对应createBrowserHistory
node环境下: 主要存储在历史记录memeory里面,对应createMemoryHistory
抽象了一个公共的文件createHistory:
此时的location跟浏览器原生的location是不相同的,最大的区别就在于里面多了key字段,history内部通过key来进行location的操作
原理:
1.1执行URL前进
createBrowserHistory: pushState、replaceState
createHashHistory: location.hash=*** location.replace()
createMemoryHistory: 在内存中进行历史记录的存储
1.2检测URL回退
createBrowserHistory: popstate
createHashHistory: hashchange
createMemoryHistory: 因为是在内存中操作,跟浏览器没有关系,不涉及UI层面的事情,所以可以直接进行历史信息的回退
1.3state的存储
为了维护state的状态,将其存储在sessionStorage里面:
基本原理:实现URL与UI可视化界面的同步。其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成location与components之间的同步问题。
在react-router中最主要的component是Router RouterContext Link,history库起到了中间桥梁的作用。
安装react-router-dom
-Link组件用于点击链接跳转其他页面,没有路由激活
-NavLink 用于有路由激活效果的
1.Switch:表示一次只渲染一个组件
2.Route:路由组件,用于展示一个组件 同router-view
3.Redirect:重定向
4.lazy + Suspense :实现路由懒加载
5.exact:路径完全匹配
6.fallback:组件切换时候的转场组件
2.react原理
1.1setState
setState在合成事件和钩子函数中是异步的
在原生事件和setTimeout中是同步的
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,
第一个参数可以是对象或者函数 是更新state 第二个参数获取最新的state,副作用操作 dom操作事件触发声明 数据获取
1.2 JSX语法的转化
JSX 仅仅是 createElement() 方法的语法糖(简化语法)
JSX 语法被 @babel/preset-react 插件编译为 createElement() 方法
react.createElement()
React 元素:是一个对象,用来描述你希望在屏幕上看到的内容
1.3组件更新机制
setState() 的两个作用: 1. 修改 state 2. 更新组件(UI)
过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
1.4组件性能优化
减轻 state:只存储跟组件渲染相关的数据
避免不必要的重新渲染 : shouldComponentUpdate(nextProps, nextState) 通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染 起到优化作用
1.5纯组件 PureComponent
PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较
纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件
纯组件内部的对比是 shallow compare(浅层对比)
1.6虚拟 DOM 和 Diff 算法
数据改变视图更新
初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。
根据虚拟 DOM 生成真正的 DOM,渲染到页面中。
当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。
与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),生成patch补丁对象,得到需要更新的内容。
最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。
17.connect的实现, 如何在全局中取得store
连接React组件与 Redux store。
connect:connect函数的返回值是一个高阶组件,通过高阶组件来获取store中的数据 connect底层原理:是闭包
mapStateFromProps:从countReducer中解构出num数据,用来获取数据
mapDispatchFromProps:将ActionCreators中的方法绑定到组件上,并且发送action
connect实现原理:
首先connect之所以会成功,是因为Provider组件:
在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
那connect做了些什么呢?
它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
关于它的源码
connect是一个高阶函数,首先传入mapStateFromProps、mapDispatchFromProps,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:
通过props.store获取祖先Component的store
props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
componentWillUnmount时移除注册的事件this.handleChange
TS 是强类型
TS好处:
1强类型
2 不需要去浏览器中浏览效果,就能知道编译错误
静态类型检查可以做到early fail,即你编写的代码即使没有被执行到,一旦你编写代码时发生类型不匹配,语言在编译阶段(解释执行也一样,可以在运行前)即可发现
4、类型就是最好的注释,看类型我们就知道这个是什么
3 即使ts中有编译报错,tsc依旧可以将其编译成js
### 学习TS的基本数据类型
**定类型是为了安全,规矩多就安全**
1 基础数据类型 :number \string\boolean\null\undefined
any 表示任意类型 void 表示空类型,空类型是针对函数的,表示函数没有返回值。返回空
2 内置对象类型 : Array \ Boolean \ HTMLElement
3 自定义类型 : 接口 类 泛型 枚举类型
Webpack
webpack打包react vue,与自己的源代码分离使用splitchunks
webpack热更新:不用刷新浏览器而将新变更的模块替换掉旧的模块。
1.Webpack Loader Plugin
(loader , plugin分别什么作用,哪个配置可以把依赖包抽离出来,不打包进去)
【Loader】:用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在buld中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。babel-loader优雅降级配置ES高版本转成低版本
【Plugin】:是用于在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作,
依赖包抽离 const ExtractTextWebapckPlugin= require(“extract-text-webpack-plugin”) module exclude node_modules
排除excloude排除node_modules
loader的使用很简单:
在webpack.config.js中指定loader。module.rules可以指定多个loader,对项目中的各个loader有个全局概览。
loader是运行在NodeJS中,可以用options对象进行配置。plugin可以为loader带来更多特性。loader可以进行压缩,打包,语言翻译等等。
loader从模板路径解析,npm install node_modules。也可以自定义loader,命名XXX-loader。
语言类的处理器loader:CoffeeScript,TypeScript,ESNext(Bable),Sass,Less,Stylus。任何开发技术栈都可以使用webpack。
webpack常用的loader
-
样式:style-loader、css-loader、less-loader、sass-loader等
-
文件:raw-loader、file-loader 、url-loader等
-
编译:babel-loader、coffee-loader 、ts-loader等
-
校验测试:mocha-loader、jshint-loader 、eslint-loader等
目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。
webpack功能强大,难点在于它的配置文件,webpack4默认不需要配置文件,可以通过mode选项为webpack指定了一些默认的配置,mode分为:development/production,默认是production。
插件可以携带参数,所以在plugins属性传入new实例。
webpack常用的plugin
webpack内置UglifyJsPlugin
,压缩和混淆代码。
webpack内置CommonsChunkPlugin
,提高打包效率,将第三方库和业务代码分开打包。
ProvidePlugin
:自动加载模块,代替require和import
html-webpack-plugin可以根据模板自动生成html代码,并自动引用css和js文件
extract-text-webpack-plugin` 将js文件中引用的样式单独抽离成css文件
DefinePlugin` 编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用。
**【Mode】**可以在config文件里面配置,也可以在CLI参数中配置:webpack --mode=production(一般会选择在CLI,也就是npm scripts里面进行配置)。
在webpack4以下版本,webpack3.XX,通过plugins进行环境变量的配置。
**【resolve】**模块,resolver是个库,帮助webpack找到bundle需要引入的模块代码,打包时,webpack使用enhanced-resolve来解析路径。
2.webpack优化
多进程打包 安装插件thread-loader parallel-webpack HappyPack
多进程压缩 parallel-uglify-plugin terser-webpack-plugin
资源CDN 公用代码提取,使用 CDN 加载
动态polyfill 动态 polyfill 指的是根据不同的浏览器,动态载入需要的 polyfill。
3.版本号上加一个^是什么意思
升级到最新的版本
CSS Less
1.flex布局
(有个布局是:右边是固定宽度,左边是自适应,如何实现,如果是答了flex:1,那么是flex下面的那个属性设置1)
flex是由 flex-grow:1父容器在主轴上还有多少剩余空间,
flex-shrink当父元素的宽度大于所有子元素的宽度的和时(即父元素会有剩余空间),子元素如何分配父元素的剩余空间
flex-basis基准值组成
flex 怎么实现1个盒子垂直居中 如何实现四个盒子水平均匀分布
父容器 display:flex; justify-content: center; align-items: center;
display: flex; justify-content:space-evenly; align-items: center;
2.如果有个导航的吸顶效果,你这边如何实现
设置css:fixed固定定位
判断滚动条滚动的距离大于导航条距顶部的距离,来判断是否实现吸顶,然后addClass添加样式
3.less的hover怎么简写
a{
&:hover {} //这里&代表它的上一级就是a
}
4.css伪类 ::after,before的应用场景
::before和::after必须配合content属性来使用,content用来定义插入的内容,content必须有值,至少是空。默认情况下,伪类元素的display是默认值inline,可以通过设置display:block来改变其显示。
1.清除浮动 : 在浮动元素后面添加一个空的Div标签,然后在设置它的清除浮动要是,使用after伪元素
2.常见消息框 : 伪类content:’ ’ 伪类4条边必须宽度相同,而且其他三条边为transparent 可以通过设置定位元素left,top值为50%,translate(-50%,-50%) 来使任意宽高的元素居中。
div::before{
content:’ ';
3.阴影 : 通过设置before,after不同位置,不同旋转角度,要保证伪类的颜色及z-index
div.outer::before,div.outer::after{content:’’;
z-index:1;
width:50%;
height:3px;
position:absolute;
left:10px;
bottom:7px;
background-color:transparent;
box-shadow:5px 5px 10px rgba(0,0,0,0.5);
-webkit-transform:rotate(-3deg);
4.做出各种图形效果
#star-five:before {
border-bottom: 80px solid red;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
position: absolute;
height: 0;
width: 0;
top: -45px;
left: -65px;
content: ‘’;
transform: rotate(-35deg);
}
#star-five:after {
width: 0;
height: 0;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 70px solid yellow;
top: 7px;
left: -110px;
position: absolute;
display: block;
content: ‘’;
transform: rotate(-70deg);
}
5.弹性盒
display:flex 设置成弹性盒后,里面的子元素在一行显示
flex-direction:row水平 | row-reverse | column垂直 | column-reverse
flex-wrap:nowrap|wrap|wrap-reverse换行,第一行在下方
flex-flow:是flex-direction和flex-wrap的简写形式。默认值为:row nowrap。
justify-content:flex-start|flex-end|center|space-between|space-around; 水平方向
align-items:flex-start|flex-end|center|baseline|stretch; 垂直方向
flex: order排列顺序|flex-grow放大比例|flex-shrink缩小|flex-basis|flex|align-self
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选 flex:1 flex-grow
align-self: auto | flex-start | flex-end | center | baseline | stretch;
6.css权重的计算
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mk0DIgD1-1617721026134)(C:\Users\28913\AppData\Roaming\Typora\typora-user-images\1596852700371.png)]
权重叠加 0,0,0,5 + 0,0,0,5 =0,0,0,10 而不是 0,0, 1, 0, 所以不会存在10个div能赶上一个类选择器的情况
继承的权重是0 1)如果选中了,那么以上面的公式来计权重。谁大听谁的。
2) 如果没有选中,那么权重是0,因为继承的权重为0.
当选择器冲突时,权重高的生效;当权重相同时,写在后头的会把前面的覆盖。
http
1.http2.0,https连接,对称加密非对称加密
https://blog.youkuaiyun.com/qq_33203555/article/details/85211595
http概述:超文本传输协议,是互联网上应用最为广泛的一种网络协议
http的缺点
1.通信使用明文可能会被窃听。
2.不验证通信方的身份可能遭遇伪装。
3.无法证明报文的完整性,可能已遭篡改。
https就是在安全的传输层上发送的http。它在将http报文发送给TCP之前,先将其发送给了一个安全层 ,对其进行加密。http安全层是通过ssl及其现代替代协议TSL来实现的。
https的优点
(1)使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
(2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
https的缺点
但是https因为加了层ssl,所以在效率方面比较低,会使页面加载的时长延长近50%,也会增加10-20%的耗电。
需要安装证书,在一定基础上增加部署费用,并且报文加密解密对数据传递有一点的效率影响。
http/2.0的目标是改善用户加载页面的时候更快
HTTP/2采用二进制格式而非文本格式
HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
对称密钥加密是指加密和解密使用同一个密钥的方式 , 一方通过密钥将信息加密后,把密文传给另一方,另一方通过这个相同的密钥将密文解密,转换成可以理解的明文
非对称加密是加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。
2.WebSocket是否有了解, 和普通接口http有什么区别
socket是传输控制层协议,webSocket是应用层协议
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握 手需要借助HTTP请求完成。
HTTP请求缺点:会导致过多不必要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了一定流量在 相同的头部信息上
然而WebSocket的出现可以弥补这一缺点。在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握 手的动作,然后单独建立一条TCP的通信通道进行数据的传送。
原理:(webSocket)
WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。
- 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
- TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
- 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
- 当收到了连接成功的消息后,通过TCP通道进行传输通信。
WebSocket与HTTP的关系
相同点:
-
都是一样基于TCP的,都是可靠性传输协议。
- 都是应用层协议
不同点:
- WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
- WebSocket是需要握手进行建立连接的。
3.错误调试工具
F12 断点 错误附近输出打印 火狐中的firebug IE开发者工具 Emmet
4http状态码
200 OK 请求成功。一般用于GET与POST请求
3开头 重定向
300 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303 查看其它地址。与301类似。使用GET和POST请求查看
304 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 使用代理。所请求的资源必须通过代理访问
306 已经被废弃的HTTP状态码
307 临时重定向。与302类似。使用GET请求重定向
400 客户端请求的语法错误,服务器无法理解
401 请求要求用户的身份认证
402 保留,将来使用
403 服务器理解请求客户端的请求,但是拒绝执行此请求
404 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 客户端请求中的方法被禁止
406 N服务器无法根据客户端请求的内容特性完成请求
407 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408 服务器等待客户端发送的请求时间过长,超时
409 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411 服务器无法处理客户端发送的不带Content-Length的请求信息
412 客户端请求信息的先决条件错误
413 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414 请求的URI过长(URI通常为网址),服务器无法处理
415 服务器无法处理请求附带的媒体格式
416 客户端请求的范围无效
417 服务器无法满足Expect的请求头信息
500 服务器内部错误,无法完成请求
501 服务器不支持请求的功能,无法完成请求
502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504 充当网关或代理的服务器,未及时从远端服务器获取请求
505 服务器不支持请求的HTTP协议的版本,无法完成处理
web网络请求的过程 !!!
1、域名解析
2、为将你的信息从pc上传到服务器上,需要IP协议,ASP协议,ospf协议,发起TCP的三次握手
3、建立TCP连接,发起http请求
4、服务器响应http请求
5、服务器端解析HTML代码,请求HTML中的资源
6、断开TCP连接(4次挥手)
7、浏览器将渲染页面呈现给用户
前端缓存
HTTP缓存(强缓存、协商缓存)
浏览器缓存(本地缓存、默认缓存)
本地缓存:
WebStorage(localStorage、sessionStorage)
Cookie
WebSql
IndexDB
应用缓存Application Cache
PWA
默认缓存
往返缓存BFCache
浏览器的强缓存和协商缓存
这里说的缓存是指浏览器(客户端)在本地磁盘中对访问过的资源保存的副本文件。
浏览器缓存主要有以下几个优点:
- 减少重复数据请求,避免通过网络再次加载资源,节省流量。
- 降低服务器的压力,提升网站性能。
- 加快客户端加载网页的速度, 提升用户体验。
浏览器缓存分为强缓存和协商缓存,两者有两个比较明显的区别:
- 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
- 在
chrome
中强缓存(虽然没有发出真实的http
请求)的请求状态码返回是200 (from cache)
;而协商缓存如果命中走缓存的话,请求的状态码是304 (not modified)
。 不同浏览器的策略不同,在Fire Fox
中,from cache
状态码是 304.
强缓存(Pragma、Cache-Control、Expires)
强缓存是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间。
Expires 是 http 1.0 的规范,值是一个GMT 格式的时间点字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT 。这个时间点代表资源失效的时间,如果当前的时间戳在这个时间之前,则判定命中缓存。有一个缺点是,失效时间是一个绝对时间,如果服务器时间与客户端时间偏差较大时,就会导致缓存混乱。而服务器的时间跟用户的实际时间是不一样是很正常的,所以 Expires 在实际使用中会带来一些麻烦。
Cache-Control这个字段是 http 1.1 的规范,一般常用该字段的 max-age 值来进行判断,它是一个相对时间,比如 .Cache-Control:max-age=3600 代表资源的有效期是 3600 秒。并且返回头中的 Date 表示消息发送的时间,表示当前资源在 Date ~ Date +3600s 这段时间里都是有效的。不过我在实际使用中常常遇到设置了 max-age 之后,在 max-age 时间内重新访问资源却会返回 304 not modified ,这是由于服务器的时间与本地的时间不同造成的。当然 Cache-Control 还有其他几个值可以设置, 不过相对来说都很少用了:
no-cache 不使用本地缓存。需要使用协商缓存。
no-store直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源, 类似于 network 中的 disabled cache。
public 可以被所有用户缓存,包括终端用户和 cdn 等中间件代理服务器。
private 只能被终端用户的浏览器缓存。
如果 Cache-Control与 Expires 同时存在的话, Cache-Control 的优先级高于 Expires 。
协商缓存(Last-Modified/If-Modified-Since、Etag/If-None-Match)
协商缓存是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
Last-Modified/If-Modified-Since 二者的值都是GMT格式的时间字符串, Last-Modified 标记最后文件修改时间, 下一次请求时,请求头中会带上 If-Modified-Since 值就是 Last-Modified 告诉服务器我本地缓存的文件最后修改的时间,在服务器上根据文件的最后修改时间判断资源是否有变化, 如果文件没有变更则返回 304 Not Modified ,请求不会返回资源内容,浏览器直接使用本地缓存。当服务器返回 304 Not Modified 的响应时,response header 中不会再添加的 Last-Modified 去试图更新本地缓存的 Last-Modified, 因为既然资源没有变化,那么 Last-Modified 也就不会改变;如果资源有变化,就正常返回返回资源内容,新的 Last-Modified 会在 response header 返回,并在下次请求之前更新本地缓存的 Last-Modified,下次请求时,If-Modified-Since会启用更新后的 Last-Modified。
Etag/If-None-Match, 值都是由服务器为每一个资源生成的唯一标识串,只要资源有变化就这个值就会改变。服务器根据文件本身算出一个哈希值并通过 ETag字段返回给浏览器,接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变。与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于在服务器上ETag 重新计算过,response header中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。
为什么要有 Etag
HTTP1.1
中 Etag
的出现主要是为了解决几个 Last-Modified
比较难解决的问题:
- 一些文件也许会周期性的更改,但是内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),
If-Modified-Since
能检查到的粒度是秒级的,使用Etag
就能够保证这种需求下客户端在1秒内能刷新 N 次 cache。 - 某些服务器不能精确的得到文件的最后修改时间。
用户行为对缓存的影响
qq、fire fox 、safari 、chrome 这几个浏览器的访问同一个页面,不同的浏览器在 F5 刷新的时候 ,同一个文件 qq 、fire fox 浏览器会返回 304 Not Nodified
,在请求头中不携带 Expires/Cache-Control
; 而 chrome 和 safari 刷新的时候,会返回 200 from cache
, 没有真正发起请求,走强缓存。可见不同的浏览器反馈是不一致的,所以下面表格中"F5刷新"时 Expires/Cache-Control
会无效我认为是存在一定争议的。而 Ctrl + F5 强制刷新的时候,会暂时禁用强缓存和协商缓存。
开发环境
1.eslint规则
**(你怎么修改某些规则)**https://www.jianshu.com/p/f8d2ef372adf
Eslint 是一个JavaScript验证工具,有了它可以让你的编辑器像ide一样进行一些静态的错误提示功能.
npm install eslint -g
某些文件关闭eslint检测 在文件的最顶端加上注释/eslint-disable/
关闭某一行代码的eslint检查 // eslint-disable-next-line
.eslintrc.json配置rules选项
2.git
(版本回退是什么命令,哪个命令查看已删除的提交commitId)
git-reset 版本回退
git reset --hard xxx 回到上一个版本
git reset --soft xxx 该命令将最近一次提交节点的提交记录回退到暂存区
git reset --mixed xxx 是将最近一次提交节点记录回退到工作区
git log 与 git reflog 查看历史记录(被删除的历史commit ID)
git场景问题
提交暂存区git add 出错 git reset HEAD <文件名> 回退
提交本地仓库 git commit出错 : 1.更改 commit 信息:git commit --amend -m“新提交消息” 2.漏提交 : git add missed-file // missed-file 为遗漏提交文件
git commit --amend --no-edit //–no-edit提交消息不会更改
3.git reset --hard commit_id git log查看提交的版本
git revert是提交一个新的版本
git fetch 将远程主机的更新全部放到本地中
git revert 和 git reset 的区别
(1)git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
(2)git revert是用一次逆向的commit“中和”之前的提交 ,合并的时候回滚的变化不会出现
git reset是之间把某些commit在某个branch上删除 合并时候回滚的commit会被引入
3.nodejs,
1.Node不是一个后端语言,但是它可以做类似后端语言的功能
- Node是使用谷歌V8引擎
- Node是js的一个运行环境
- Node具有非阻塞I/O 特点
- Node采用了Common.js规范
node+koa2 node+express
用于快速构建Node.js项目
node.js 是一个基于 Chrome V8 引擎的 JavaScirpt 运行环境。
Node.js使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效**
Node.js基于commonjs规范
事件驱动: 任务执行,发布者,订阅者,事件驱动 .
异步(非阻塞): 执行某一个任务的同时也可以执行其他任务
同步(阻塞): 执行某一个任务,这个任务如果没有执行完成,其他任务必须等待
I/O: 输入/输出( 数据库操作,文件系统操作等 ) - 服务器的环境
非阻塞I/O模型: 当使用Node.js来实现数据库操作、文件系统等操作时,要进行的异步操作,异步操作的核心传统实现方式就是回调函数和事件。
Node.js的包管理工具npm
优点:Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,异步编程,使其轻量又高效。
缺点:单进程,单线程,只支持单核cpu,不能充分的利用多核cpu服务器。一旦这个进程崩掉,那么整个web服务就崩掉了。
内置模块 http 是用于创建一个能够处理和响应 http 响应的服务
fs 用于对系统文件及目录进行读写操作。
path 提供了一些用于处理文件路径的小工具
Url:帮助我们对提交上来的url进行解析处理
querystring 提供用于解析和格式化 URL 查询字符串的工具。qs.parse() qs.stringify()
4.typescript语法
TypeScript具有类型系统,且是JavaScript的超集。 它可以编译成普通的JavaScript代码。 TypeScript支持任意浏览器,任意环境,任意系统并且是开源的。
npm install -g typescript
tsc --init 生成tsconfig.json配置文件
基础数据类型
* number、string、boolean、null 、undefined
* any 表示任意类型
* void 表示空类型
-
内置对象类型
- Array Boolean
- HTMLElement
- HTMLDivElement
-
自定义类型
-
接口 interface
-
类
-
泛型 未来的类型定义的时候不知道是什么类型,调用的时候才知道
-
枚举 enum类型是对JavaScript标准数据类型的一个补充
-
never类型表示的是那些永不存在的值的类型。
readonly :只读属性,不可修改
sex? :表示sex是一个可传属性,可以有也可以没有
[propName: string]: any; 表示新增的属性可以是任意类型
arr3: Array 数组类型定义
arr2: (number | string)[]
fn (a: number, b: number) : number 函数类型定义
-
5.linux命令
pwd:输入pwd命令,Linux会输出当前目录。
ls命令用来查看目录的内容。
cd命令用来改变所在目录。
cat命令可以用来合并文件,也可以用来在屏幕上显示整个文件的内容。
grep命令的最大功能是在一堆文件中查找一个特定的字符串。
touch命令用来创建新文件
cp命令用来拷贝文件
mv命令用来移动文件
rm命令用来删除文件。
mkdir 创建文件夹创建目录
项目
注册登录是怎么实现的
1.登陆注册要做成受控组件,组件定义state,和表单绑定
2.redux-saga调用数据请求,发送action修改数据,useEffect中dispatch发送数据请求,后端比对用户名是否重复,返回status
3.前端根据返回的信息成功跳转登陆页
4.登陆发送数据请求,数据库对比用户名密码是否正确 根据后端返回的结果进入首页
5.setCookie将用户登录名密码token存cookie中 通过JWT(Json web token)
6.免密登陆 getCookie获取token 发给后端对比 根据返回结果是否自动登陆
7.注册通过Ant Design ,validator中进行表单正则的验证
8.用户体验 注册的时候跳转其他页面的时候给用户提示是否需要跳转,避免因为跳转后导致注册信息没有了 用组件内后置守卫做
如果输入框都没有填信息,不拦截跳转 如果用户输入信息,弹窗提示,点确定,跳转,点取消,不跳转
项目中遇到什么困难,怎么解决的?
1.react中配置二级路由 地址变化 但是界面不更新
使用dva/router中的withRouter高阶组件
2.图表联动怎么实现
我们只需要把当前被选中图表的事件,直接发给其他图表即可,然后判断被选中的图表是哪个作为区分,功能即可实现
onTouchEvent(event) 普通事件传递
3.产品经理要求智能匹配产品
找网上类似功能的网站 查看源码 和主管讨论
-需要一个设计一个投资习惯和风险承受能力测试,
-从后端获取这个客户测试的结果 以及客户平常投资的习惯 生成不同的关键字
-根据关键字从数据库中匹配产品 展示界面
4.后台管理系统遇到遇到什么奇葩的需求
后台管理系统和普通App面向用户的区别
toB和toC的项目
面向企业内部和面向用户的项目的区别
后台管理系统权限比较细 App高并发比较多 做性能优化instance