面试题总结
从输入一个 URL 地址到浏览器完成渲染的整个过程
1 输入url并回车
2 浏览器查看当前url是否存在缓存,缓存是否过期
3 dns解析当前url对应的ip
4 根据ip建立tcp连接(三次握手)
5 发送http请求
6 服务器收到请求,并返回响应和内容
7 浏览器解析并渲染页面
8 关闭tcp连接(四次挥手)
什么是事件代理(事件委托) 有什么好处
一、事件委托原理:
不单独给每个子节点设置监听器。而是给其父节点设置事件监听器,然后利用冒泡原理设置每个子节点
二、优点:
减少内存消耗和dom操作,提高性能。因为页面上事件监听器的数量将关系到页面的整体运行速度,操作dom频繁,会引起浏览器重绘和回流
apply call bind 区别
1 三者都可以改变this的指向
2 三者的第一个参数都是this要指向的对象,如果不传,为undefined或则null,则this指向全局window
3 apply是数组,call是参数列表,apply和call是一次性传入参数,而bind可以分为多次传入
4 bind返回绑定this之后的函数,便于稍后调试。而call和apply是立即执行
5 如果bind返回的新函数作为构造函数创建一个新对象,this不再指向传入bind的第一个参数对象,而是指向当前实例
webpack性能优化
一 、exclude/include
我们可以通过exclude/include
配置选项,来过滤掉一些不需要转译的文件,使打包后的文件变的更小exclude
: 指定要排除的文件include
: 指定要包含的文件
const path = require('path');
module.exports = {
//....
module:{
rules:[
{
test:/\.js[x]?$/,
use:['babel-loader'],
include:[path.resolve(__dirname,'src')]
}
]
}
}
二 、cache-loader
在性能开销比较大的loader前,加入cache-loader
,将结果缓存在磁盘中,默认保存在node_modules/.cache/cache-loader
目录下
安装:yarn add cache-loader -D
//...
module.exports = {
module:{
rules:[
{
test:/\.js[x]?$/,
use:['cache-loader','babel-loader']
}
]
}
}
如果只打算给babel-loader设置cache的话。我们也可以不使用cache-loader
,我们可以给babal-loader增加选项cacheDirectory
cacheDirectory
:默认为false,当有设置时,指定的目录将用来缓存loader的执行结果,之后的webpack构建将默认读取缓存
三 、happypack
当有大量的文件需要解析构建的时候,webpack构建慢的问题会显的很严重。happypack
可以同一时刻处理多个任务,发挥cpu电脑的威力happypack
可以让webpack做到这点,它把任务分解给多个子进程并发去执行,子进程处理完后会再发送给主进程
yarn add happypack -D
const Happypack = require('happypack');
module.exports = {
//...
module: {
rules: [
{
test: /\.js[x]?$/,
use: 'Happypack/loader?id=js',
include: [path.resolve(__dirname, 'src')]
},
{
test: /\.css$/,
use: 'Happypack/loader?id=css',
include: [path.resolve(__dirname, 'src')]
}
]
},
plugins: [
new Happypack({
id: 'js', //和rule中的id=js对应
//将之前 rule 中的 loader 在此配置
use: ['babel-loader'] //必须是数组
}),
new Happypack({
id: 'css',//和rule中的id=css对应
use: ['style-loader', 'css-loader','postcss-loader'],
})
]
}
四 、webpack-dev-server(WDS)热更新实现原理
WDS启动服务后,服务端会和客户端建立一个长链接,当文件修改后,服务端会通过长链接向客户端发送一条消息,
客户端收到消息后,会重新请求一个js文件,返回的js文件会调用webpackHotUpdatehmr方法,然后替换掉__webpack_module__中的部分代码
浏览器缓存
一 、强缓存
第一次进入页面,请求服务器,然后服务器应答,浏览器会根据response Header来判断是否对资源进行缓存, 如果响应头中存在,expires,pragma或者cache-control字段,代表这是强缓存,浏览器会把资源存在memory cache,或disk cache中
第二次请求时,浏览器判断请求参数,如果符合强缓存条件就直接返回状态码200,从本地缓存中读取数据,否则把响应参数存在request Header请求头中, 看是否符合协商缓存,符合则返回状态码304,不符合则服务器返回全新资源
expires
值是一个时间戳,服务器返回该请求结果缓存的到期时间(一个格林尼治时间)
有个缺点,就是它判断是否过期是用本地时间来判断的,本地时间是可以自己修改的。
cache-control
cache-control
优先级高于expires
取值为:
public:客户端和服务器端都可以缓存
privite:客户端可以缓存
no-cache:客户端可以缓存,但是是否缓存需要协商缓存来验证
no-store:不使用缓存
max-age:缓存保质期
pragma
取值为no-cache和cache-control
中的no-cache效果一样
二 、缓存位置
存储图像和网页等资源主要缓存在disk cache,操作系统缓存文件等资源大部分都会缓存在memory cache中。具体操作浏览器自动分配,看谁的资源利用率不高就分给谁。
查找浏览器缓存时会按顺序查找: Service Worker-->Memory Cache-->Disk Cache-->Push Cache
1. Service Worker
运行在浏览器背后的独立线程,一般可以用来实现缓存功能,传输协议必须为 HTTPS,因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的
2. Memory Cache
缓存在内存中
3. Disk Cache
缓存在硬盘中,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
4. Push Cache
缓存在会话(Session)中,当以上三种缓存都没有命中时,它才会被使用。一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
三 、协商缓存
协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识来决定是否使用缓存,协商缓存生效返回状态码304,失效返回 状态码200和请求结果
Last-Modified / If-Modified-Since
Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,存在响应头中
If-Modified-Since是客户端再次请求服务器时,存在于请求头中,并且携带上次请求返回的Last-Modified值,服务器收到请求后, 根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,大于If-Modified-Since的字段值则重新返回资源,状态码为 200,否则返回304,代表资源无更新,可继续使用缓存文件
Etag / If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成) 存在于响应头中
If-None-Match是客户端再次发起该请求时,存在于请求头中,并且携带上次请求返回的唯一标识Etag值,服务器收到该请求后,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200
四 、缓存有哪些好处?
1 缓解服务器压力
2 提升性能,打开本地资源肯定会比请求服务器来的快
3 减少带宽消耗
五 、刷新对于强缓存和协商缓存的影响
- 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
- 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
- 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
React中 父子组件生命周期渲染顺序
import React from "react";
import Child from './Child';
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
isChanged: "false"
};
}
UNSAFE_componentWillUpdate() {
console.log("Parent componentWillUpdate");
}
componentDidUpdate() {
console.log("Parent componentDidUpdate");
}
UNSAFE_componentWillMount(){
console.log("Parent componentWillMount");
}
componentDidMount() {
console.log("Parent componentDidMount");
}
render() {
//渲染三个子组建,并提供一个按钮,当按钮被点击时强行update
console.log("Parent Render");
return (
<div>
<Child order="1st" />
<button
onClick={() => {
this.setState({
isChanged: "true"
});
}}
>
Change Sup Data
</button>
<div>{this.state.isChanged}</div>
</div>
);
}
}
export default Parent;
//-------------child
import React from "react";
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
isUpdate: false
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log(this.props.order + " ChildComponent componentWillReceiveProps");
}
UNSAFE_componentWillMount(){
console.log(this.props.order + " ChildComponent componentWillMount");
}
componentDidMount() {
console.log(this.props.order + " ChildComponent componentDidMount");
}
UNSAFE_componentWillUpdate() {
console.log(this.props.order + " ChildComponent componentWillUpdate");
}
componentDidUpdate() {
console.log(this.props.order + " ChildComponent componentDidUpdate");
}
render() {
console.log(this.props.order + " ChildComponent Render");
/*接受一个从父元素传来的props.order*/
return (
<button
onClick={() => {
this.setState({ isUpdate: true });
}}
>
{this.props.order + " button updated? : " + this.state.isUpdate}
<br />
</button>
);
}
}
export default Child;
执行渲染顺序
- Parent componentWillMount
- Parent Render
- ChildComponent componentWillMount
- ChildComponent Render
- ChildComponent componentDidMount
- Parent componentDidMount
如果父组件某一个状态发生变化和子组件无关 执行顺序是什么?
- Parent componentWillUpdate
- Parent Render
- ChildComponent componentWillReceiveProps
- ChildComponent componentWillUpdate
- ChildComponent Render
- ChildComponent componentDidUpdate
- Parent componentDidUpdate
设置shouldComponentUpdate,避免子组件不必要的更新
shouldComponentUpdate(nextProps, nextState){
if(nextState.isUpdate == this.state.isUpdate){
return false
}
return true
}
执行顺序如下:
- Parent componentWillUpdate
- Parent Render
- ChildComponent componentWillReceiveProps
- Parent componentDidUpdate
什么是单页面应用和多页面应用,各自的优缺点是什么?
单页面应用
单页面应用,通俗来说就是只有一个主页面的应用,浏览器一开始要加载所有必须的html,js,css,所有的页面内容都包含在这个主页面中, 但是在写的时候,还是会分开写(页面片段)
多页面应用
多页面应用,就是指一个应用中有多个页面,页面跳转时要刷新整个页面
单页面的优缺点
优点:用户体验好,快,内容的改变不需要重新加载整个页面,对服务器压力小,前后端分离
缺点:不利于seo,导航不可用,要自行实现前进,后退,初次加载耗时多,页面复杂度提高
多页面应用的优缺点
优点:利于seo,导航可用,初次加载耗时低,页面复杂度相对较低
缺点:用户体验一般,内容的改变需要加载整个页面,对服务器压力相对较大
防抖
function throttle(fn,delay=300){
let timer;
return function (){
let args = arguments;
if(timer){
clearInterval(timer);
}
timer = setTimeout(()=>{
fn.apply(this,args);
},delay)
}
}
window.addEventListener('scroll',shake(()=>{
console.log(111)
},10))
节流
function shake(fn,delay=300){
let flag = true;
return ()=>{
if(!flag) return;
flag = false;
timer = setTimeout(()=>{
fn();
flag = true;
},delay)
}
}
window.addEventListener('scroll',throttle(()=>{
console.log(2222)
},10))
flex:1是有哪些属性组成
flex实际上是有
flex-grow
,flex-shrink
,flex-basis
组成
- flex-grow:定义项目的放大比例
- flex-shrink:定义项目的缩小比例
- flex-basis:项目占据的主轴空间,定义在分配多余空间之前,浏览器根据此属性计算主轴是否有多余空间
实现useState方法
import ReactDOM from "react-dom";
let _state;
function _useState(initialValue){
_state = _state||initialValue;
function setState(newState){
_state = newState;
render();
}
return [_state,setState];
}
function App (){
let [count,setCount] = _useState(1);
return (
<div>
<div>{count}</div>
<button onClick={() => { setCount(count + 1); }}>
点击
</button>
</div>
);
}
export default App;
function render() {
ReactDOM.render(<App/>,document.getElementById('root'));}
render();