offer收割机-Web前端面试宝典【精编版-4】

在线访问手册: hanxueqing.github.io/Web-Front-e… github地址: github.com/Hanxueqing/…javascript

React—用于构建用户界面的 JavaScript 库

组件

建立组件的方式

一、无状态函数式组件css

建立纯展现组件,只负责根据传入的props来展现,不涉及到要state状态的操做,是一个只带有一个render方法的组件类。html

二、组件是经过React.createClass建立的(ES5)前端

三、React.Componentvue

在es6中直接经过class关键字来建立java

组件其实就是一个构造器,每次使用组件都至关于在实例化组件node

react的组件必须使用render函数来建立组件的虚拟dom结构react

组件须要使用ReactDOM.render方法将其挂载在某一个节点上webpack

组件的首字母必须大写ios

React.Component是以ES6的形式来建立react的组件的,是React目前极为推荐的建立有状态组件的方式,相对于 React.createClass能够更好实现代码复用。将上面React.createClass的形式改成React.Component形式以下:

class Greeting extends React.Component{
   constructor (props) {
       super(props);
       this.state={
            work_list: []
        }

        this.Enter=this.Enter.bind(this); //绑定this
    }
    render() {
        return (
            <div>
                <input type="text" ref="myWork" placeholder="What need to be done?" onKeyUp={this.Enter}/>

                <ul>
                    {
                        this.state.work_list.map(function (textValue) {
                            return <li key={textValue}>{textValue}</li>;
                        })
                    }
                </ul>

            </div>
        );
    }
    Enter(event) {
        var works = this.state.work_list;
        var work = this.refs.myWork.value;
        if (event.keyCode == 13) {
            works.push(work);
            this.setState({work_list: works});
            this.refs.myWork.value = "";
        }


    }
}

复制代码
关于this

React.createClass建立的组件,其每个成员函数的this都有React自动绑定,任什么时候候使用,直接使用this.method便可,函数中的this会被正确设置

React.Component建立的组件,其成员函数不会自动绑定this,须要手动绑定,不然this不能获取当前组件实例对象

React.Component三种手动绑定this的方法

1.在构造函数中绑定

constructor(props) {
       super(props);
       this.Enter = this.Enter.bind(this);
  }
复制代码

2.使用bind绑定

<div onKeyUp={this.Enter.bind(this)}></div> 
复制代码

3.使用arrow function绑定

<div onKeyUp={(event)=>this.Enter(event)}></div> 
复制代码

咱们在实际应用中应该选择哪一种方法来建立组件呢?

  • 只要有可能,尽可能使用无状态组件建立形式
  • 不然(如须要state、生命周期方法等),使用React.Component这种es6形式建立组件

无状态组件与有状态的组件的区别为?

没有状态,没有生命周期,只是简单的接受 props 渲染生成 DOM 结构

无状态组件很是简单,开销很低,若是可能的话尽可能使用无状态组件。

无状态的函数建立的组件是无状态组件,它是一种只负责展现的纯组件

无状态组件可使用纯函数来实现。

const Slide = (props)=>{return (<div>.....</div>)}这就是无状态组件(函数方式定义组件)  能够简写为  const Slide = props =>(<div>......</div>)
复制代码

React父子组件通讯

(1)this.props

(2)ref链

(3)Redux

高阶组件

高阶组件是什么?如何理解?

什么是高级组件?首先你得先了解请求ES6中的class只是语法糖,本质仍是原型继承。可以更好的进行说明,咱们将不会修改组件的代码。而是经过提供一些可以包裹组件的组件, 并经过一些额外的功能来加强组件。这样的组件咱们称之为高阶组件(Higher-Order Component)。

高阶组件(HOC)是React中对组件逻辑进行重用的高级技术。但高阶组件自己并非React API。它只是一种模式,这种模式是由React自身的组合性质必然产生的。

说到高阶组件,就先得说到高阶函数了,高阶函数是至少知足下列条件的函数:

一、接受一个或多个函数做为输入 二、输出一个函数

高阶组件定义

类比高阶函数的定义,高阶组件就是接受一个组件做为参数,在函数中对组件作一系列的处理,随后返回一个新的组件做为返回值。

高阶组件的缺点

高阶组件也有一系列的缺点,首先是被包裹组件的静态方法会消失,这其实也是很好理解的,咱们将组件当作参数传入函数中,返回的已经不是原来的组件,而是一个新的组件,原来的静态方法天然就不存在了。若是须要保留,咱们能够手动将原组件的方法拷贝给新的组件,或者使用hoist-non-react-statics之类的库来进行拷贝。

参考:浅谈React高阶组件

www.jb51.net/article/137…

使用过哪些高阶组件

  1. withRouter高阶组件,能够根据传入的组件生成一个新的组件,而且为新组件添加上router相关的api。

  2. connect 用于链接容器组件与UI组件,connect(mapStateToProps,mapDispatchToProps)(ui组件),当状态改变的时候,容器组件内部由于经过store.subscribe能够监听状态的改变,给ui组件传入新的属性,返回容器组件(智能组件),这个函数返回什么,ui组件props上就会挂载什么,ui组件的属性上就就会有改变状态的方法了,用的话经过this.props.方法名。

虚拟dom

####React高性能的体现:虚拟DOM

在Web开发中咱们总须要将变化的数据实时反应到UI上,这时就须要对DOM进行操做。而复杂或频繁的DOM操做一般是性能瓶颈产生的缘由(如何进行高性能的复杂DOM操做一般是衡量一个前端开发人员技能的重要指标)。

React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时全部的DOM构造都是经过虚拟DOM进行,每当数据变化时,React都会从新构建整个DOM树,而后React将当前整个DOM树和上一次的DOM树进行对比,获得DOM结构的区别,而后仅仅将须要变化的部分进行实际的浏览器DOM更新。并且React可以批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A-B,B-A,React会认为A变成B,而后又从B变成A UI不发生任何变化,而若是经过手动控制,这种逻辑一般是极其复杂的。

尽管每一次都须要构造完整的虚拟DOM树,可是由于虚拟DOM是内存数据,性能是极高的,部而对实际DOM进行操做的仅仅是Diff分,于是能达到提升性能的目的。这样,在保证性能的同时,开发者将再也不须要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只须要关心在任意一个数据状态下,整个界面是如何Render的。

为何虚拟dom会提高代码性能?

虚拟DOM就是JavaScript对象,就是在没有真实渲染DOM以前作的操做。 真实dom的比对变成了虚拟dom的比对(js对象的比对) 虚拟dom里面比对,涉及到diff算法。 key值 (key值相同dom能够直接进行复用)

react的diff算法实现流程

1.DOM结构发生改变-----直接卸载并从新create 2.DOM结构同样-----不会卸载,可是会update变化的内容 3.全部同一层级的子节点.他们均可以经过key来区分-----同时遵循1.2两点 (其实这个key的存在与否只会影响diff算法的复杂度,换言之,你不加key的状况下,diff算法就会以暴力的方式去根据一二的策略更新,可是你加了key,diff算法会引入一些另外的操做)

React会逐个对节点进行更新,转换到目标节点。而最后插入新的节点,涉及到的DOM操做很是多。diff总共就是移动、删除、增长三个操做,而若是给每一个节点惟一的标识(key),那么React优先采用移动的方式,可以找到正确的位置去插入新的节点

diff算法和fiber算法的区别

diff算法是同步进行更新和比较,必须同步执行完一个操做再进行下一个操做,所耗时间比较长,JavaScript是单线程的,一旦组件开始更新,主线程就一直被React控制,这个时候若是再次执行交互操做,就会卡顿。

React Fiber重构这种方式,渲染过程采用切片的方式,每执行一下子,就歇一下子。若是有优先级更高的任务到来之后呢,就会先去执行,下降页面发生卡顿的可能性,使得React对动画等实时性要求较高的场景体验更好。

如何理解React中key?

keys是什么帮助 React 跟踪哪些项目已更改、添加或从列表中删除。

每一个 keys 在兄弟元素之间是独一无二的。

keys 使处理列表时更加高效,由于 React 可使用子元素上的 keys 快速知道元素是新的仍是在比较树时才被移动。

keys 不只使这个过程更有效率,并且没有 keys ,React 不知道哪一个本地状态对应于移动中的哪一个项目。

例如:数组循环出来三项,每一项前面有一个多选框,假设第一个多选框勾选了,而后我再动态添加新的元素,会发现新添加的元素就会被勾选了,这就是个问题!设置key值,这样的话就能够解决了。
复制代码

####JSX 语法

在vue中,咱们使用render函数来构建组件的dom结构性能较高,由于省去了查找和编译模板的过程,可是在render中利用createElement建立结构的时候代码可读性较低,较为复杂,此时能够利用jsx语法来在render中建立dom,解决这个问题,可是前提是须要使用工具来编译jsx

JSX是一种语法,全称:javascript xml

JSX语法不是必须使用的,可是由于使用了JSX语法以后会下降咱们的开发难度,故而这样的语法又被成为语法糖。

react.js中有React对象,帮助咱们建立组件等功能

HTML 中全部的信息咱们均可以用 JavaScript 对象来表示,可是用 JavaScript 写起来太长了,结构看起来又不清晰,用 XML的方式写起来就方便不少了。

因而 React.js 就把 JavaScript 的语法扩展了一下,让 JavaScript 语言可以支持这种直接在 JavaScript 代码里面编写相似 XML 标签结构的语法,这样写起来就方便不少了编译的过程会把相似 XML 的 JSX 结构转换成 JavaScript 的对象结构

在不使用JSX的时候,须要使用React.createElement来建立组件的dom结构,可是这样的写法虽然不须要编译,可是维护和开发的难度很高,且可读性不好。

所谓的 JSX 其实就是 JavaScript 对象,因此使用 React 和 JSX 的时候必定要通过编译的过程:

JSX代码 — > 使用react构造组件,bable进行编译—> JavaScript对象 — ReactDOM.render()函数进行渲染—>真实DOM元素 —>插入页面

另:

  • JSX就是在js中使用的xml,可是,这里的xml不是真正的xml,只能借鉴了一些xml的语法,例如:

最外层必须有根节点、标签必须闭合

  • jsx借鉴xml的语法而不是html的语法缘由:xml要比html严谨,编译更方便

webpack中,是借助loader完成的jsx代码的转化,仍是babel?

在vue中,借助webpack提供的vue-loader来帮助咱们作一些转化,让vue代码能够在浏览器中执行。 react中没有react-loader来进行代码的转化,而是采用babel里面babel-preset-react来实现的。

调用setState以后,发生了什么?

constructor(props){
	super(props);
	this.state = {
		age:1
	}
}
复制代码

经过调用this.setState去更新this.state,不能直接操做this.state,请把它当成不可变的。 调用setState更新this.state,他不是立刻就会生效的,他是异步的。因此不要认为调用完setState后能够立马获取到最新的值。

多个顺序执行的setState不是同步的一个接着一个的执行,会加入一个异步队列,而后最后一块儿执行,即批处理。

setState是异步的,致使获取dom可能拿的仍是以前的内容,因此咱们须要在setState第二个参数(回调函数)中获取更新后的新的内容。

this.setState((prevState)=>({
	age:++prevState.age
}),()=>{
	console.log(this.state.age) //获取更新后的最新的值
});
复制代码

React实现异步请求

redux中间件

一般状况下,action只是一个对象,不能包含异步操做

redux-thunk中间件

redux-thunk原理:

-能够接受一个返回函数的actionCreators,若是这个actionCreators返回的是一个函数,就执行它,若是不是,就按照原来的next(action)执行

-若是不安装redux-thunk中间件,actionCreators只能返回一个对象

-安装了redux-thunk中间件以后,actionCreators能够返回一个函数了,在这个函数里面能够写异步操做的代码

-redux中间件,建立出来的action在到达reducer之间,加强了dispatch的派发功能

####refs的做用业务场景?

经过ref对dom、组件进行标记,在组件内部经过this.refs获取到以后,进行操做

<ul ref='content'><li>国内新闻</li></ul>
...
this.refs.content.style.display = this.state.isMenuShow?'block':'none'
复制代码

ref用于给组件作标记,例如获取图片的宽度与高度。

非受控组件,input输入框,获取输入框中的数据,能够经过ref作标记。

<input ref={el=>this.input = el}/>
复制代码

ref是一个函数,为何?

避免组件之间数据相互被引用,形成内存泄漏

class Test extends React.Component{
	componentDidMount(){
		console.log(this.el);
	}
	render(){
		//react在销毁组件的时候会帮助咱们清空掉ref的相关引用,这样能够防止内存泄漏等一系列问题。
		return <div ref={el=>this.el=el}></div>
	}
}
复制代码

受控组件与非受控组件的区别

受控组件与非受控组件是相对于表单而言。

受控组件: 受到数据的控制。组件的变化依靠数据的变化,数据变化了,页面也会跟着变化了。输入框受到数据的控制,数据只要不变化,input框输什么都不行,一旦使用数据,从状态中直接获取。

<input value={this.state.value}/>
复制代码

非受控组件: 直接操做dom,不作数据的绑定。经过refs来获取dom上的内容进行相关的操做。

<input ref={el=>this.el=el}/> //不须要react组件作管理
复制代码

数据驱动是react核心。

实现监听滚动高度

class Test extends React.Component{
	constructor(props){
		super(props);
		this.handleWidowScroll = this.handleWidowScroll.bind(this);
	}
	handleWidowScroll(){
		this.setState({
			top:document.body.scrollTop
		})
	}
	componentDidMount(){//绑定监听事件
		window.addEventListener("scroll",this.handleWindowScroll);
	}
	componentWillUnmount(){//移除监听事件
		window.removeEventListener("scroll",this.handleWindowScroll);
	}
}
复制代码

####React中data为何返回一个函数

为了防止组件与组件之间的数据共享,让做用域独立,data是函数内部返回一个对象,让每一个组件或者实例能够维护一份被返回对象的独立的拷贝。

路由

react-router4的核心思想是什么?

路由也变成组件了,因此它是很是灵活的(NavLink Route)。 vue中的路由须要单独的配置 vue-router。

react-router的两种模式是什么?

hashHistory # 不须要后端服务器的配置 browserHistory / 须要后端服务器的配置 (后端人员不清楚路由重定向等相关的概念)

hash路由的实现原理

经过onhashchange事件监听路由的改变,一旦路由改变,这个事件就会被执行,就能够拿到更改后的哈希值,经过更改后的哈希值就可让咱们的页面进行一个关联,一旦路由发生改变了,整个页面状态就会发生改变,可是整个页面是没有发生任何http请求的,整个页面处于一种无刷新状态。

  • hash模式背后的原理是onhashchange事件,能够在window对象上监听这个事件:
window.onhashchange = function(event) {
    console.log(event.oldURL, event.newURL);
    let hash = loaction.hash  //经过location对象来获取hash地址
    console.log(hash)    // "#/notebooks/260827/list"#号开始
}
复制代码

由于hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退均可以用 ,这样一来,尽管浏览器没有请求服务器,可是页面状态和url一一关联起来,后来人们给它起了一个霸气的名字叫前端路由,成为了单页应用标配。

spa单页应用:根据页面地址的不一样来实现组件之间的切换,整个页面处于一种无刷新状态。

history路由

随着history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片断,而history api则给了前端彻底的自由

history api能够分为两大部分:切换和修改 【切换路由/修改路由】

(1)切换历史状态

包括括back、forwardgo三个方法,对应浏览器的前进,后退,跳转操做

history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
复制代码

(2)修改历史状态

包括 了pushState、replaceState两个方法,这两个方法接收三个参数:stateObj,title,url。

两种模式的区别是什么?

在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,因此没有问题。可是在history下,你能够自由的修改path,当刷新时,若是服务器中没有相应的响应或者资源,会分分钟刷出一个404来,须要后端人员去作一个配置。

生命周期

react的生命周期函数

【初始化阶段】:

(1)getDefaultProps:实例化组件以后,组件的getDefaultProps钩子函数会执行

这个钩子函数的目的是为组件的实例挂载默认的属性

这个钩子函数只会执行一次,也就是说,只在第一次实例化的时候执行,建立出全部实例共享的默认属性,后面再实例化的时候,不会执行getDefaultProps,直接使用已有的共享的默认属性

理论上来讲,写成函数返回对象的方式,是为了防止实例共享,可是react专门为了让实例共享,只能让这个函数只执行一次

组件间共享默认属性会减小内存空间的浪费,并且也不须要担忧某一个实例更改属性后其余的实例也会更改的问题,由于组件不能本身更改属性,并且默认属性的优先级低。

(2)getInitialState:为实例挂载初始状态,且每次实例化都会执行,也就是说,每个组件实例都拥有本身独立的状态。

(3)componentWillMount:执行componentWillMount,至关于Vue里的created+beforeMount,这里是在渲染以前最后一次更改数据的机会,在这里更改的话是不会触发render的从新执行。

(4)render:渲染dom

render()方法必须是一个纯函数,他不该该改变state,也不能直接和浏览器进行交互,应该将事件放在其余生命周期函数中。 若是shouldComponentUpdate()返回falserender()不会被调用。

(5)componentDidMount:至关于Vue里的mounted,多用于操做真实dom

【运行中阶段】

当组件mount到页面中以后,就进入了运行中阶段,在这里有5个钩子函数,可是这5个函数只有在数据(属性、状态)发送改变的时候才会执行

(1)componentWillReceiveProps(nextProps,nextState)

当父组件给子组件传入的属性改变的时候,子组件的这个函数才会执行。初始化props时候不会主动执行

当执行的时候,函数接收的参数是子组件接收到的新参数,这个时候,新参数尚未同步到this.props上,多用于判断新属性和原有属性的变化后更改组件的状态。

(2)接下来就会执行shouldComponentUpdate(nextProps,nextState),这个函数的做用:当属性或状态发生改变后控制组件是否要更新,提升性能,返回true就更新,不然不更新,默认返回true。

接收nextProp、nextState,根据根据新属性状态和原属性状态做出对比、判断后控制是否更新

若是shouldComponentUpdate()返回falsecomponentWillUpdate,rendercomponentDidUpdate不会被调用。

(3)componentWillUpdate,在这里,组件立刻就要从新render了,多作一些准备工做,千万千万,不要在这里修改状态,不然会死循环 至关于Vue中的beforeUpdate

(4)render,从新渲染dom

(5)componentDidUpdate,在这里,新的dom结构已经诞生了,至关于Vue里的updated

【销毁阶段】

当组件被销毁以前的一刹那,会触发componentWillUnmount,临死前的挣扎

至关于Vue里的beforeDestroy,因此说通常会作一些善后的事情,例如使定时器无效,取消网络请求或清理在componentDidMount中建立的任何监听。

image

为何Vue中有destroyed,而react却没有componentDidUnmount

Vue在调用$destroy方法的时候就会执行beforeDestroy,而后组件被销毁,这个时候组件的dom结构还存在于页面结构中,也就说若是想要对残留的dom结构进行处理必须在destroyed处理,可是react执行完componentWillUnmount以后把事件、数据、dom都所有处理掉了,因此根本不须要其余的钩子函数了

React中怎么样就算组件被销毁:

  1. 当父组件从渲染这个子组件变成不渲染这个子组件的时候,子组件至关于被销毁
  2. 调用ReactDOM.unmountComponentAtNode(node) 方法来将某节点中的组件销毁

哪一个生命周期里面发送ajax?

AJAX请求应该在componentDidMount生命周期事件中。

如何避免ajax数据从新获取?

将全部的数据存储在redux中进行管理,既能够解决该问题。

为何不把请求数据的操做写在componentWillMount中而写在componentDidMount中?

(1)此钩子函数在16版本中会被频繁调用:15.X版本用的是diff算法,不会被频繁调用,而React下一代调和算法Fiber会经过开始或中止渲染的方式优化应用性能,其会影响到comonentWillMount的触发次数,对于componentWillMount这个生命周期的调用次数就会变得不肯定。React可能会屡次频繁调用componentWillMount,若是咱们将ajax请求放到componentWillMount函数中,那么显而易见就会被触发屡次,天然也就不是好的选择。

(2)componentWillMount()将在React将来版本(官方说法 17.0)中被弃用。为了不反作用和其余的订阅,官方都建议使用componentDidMount()代替。这个方法是用于在服务器渲染上的惟一方法。

componentWillReceiveProps调用时机?

初始化父组件第一次将数据传递给子组件的时候不会去执行,只有属性props改变的时候,子组件的钩子函数才会触发执行。

受控组件与非受控组件的区别

受控组件:受到数据控制,例如表单元素,当输入框中的内容发生改变的时候,使其更改组件。数据驱动的理念,提倡内部的一些数据最好与组件的状态进行关联。

父组件能够将本身的属性传递给子组件,子组件经过this.props调用。

非受控组件;不会受到数据(state)的控制,由DOM自己进行管理,输入框的内容发生改变了,直接经过ref进行标记,而后直接获取使用便可。

Redux

你会把数据统一放入到redux中管理,仍是共享数据放在redux中管理?

把全部的数据放入到redux中管理。(props,state) 项目一旦有问题,能够直接定位问题点。 组件扩展的时候,后续涉及到传递的问题。原本的话,本身使用数据,可是后来公用,还须要考虑如何传递。

redux中存储数据能够存储至少5G以上的数据。 目的就是方便数据统一,好管理。

React链接Redux用到什么?

react-redux辅助工具

核心组件:Provider提供者,属性上经过store将数据派给容器组件,connect用于链接容器组件与UI组件。

引入provider,哪一个地方须要用到状态和属性,就包裹一下,而且一旦有状态改变,就会监听到,而且将最新的状态返回给UI组件。

ReactDOM.render(
    <Provider store = {store}>
        <Router>
            <App />
        </Router>
    </Provider>, document.getElementById('root'));
复制代码

connect()(UI组件) ==》返回一个容器组件

这个方法参数是state=>store.getState()

这个方法返回什么,UI组件的属性上就是有什么

当状态改变的时候,容器组件就会监听状态的变化,而且把更新后的状态经过属性的方法传递给UI组件

由于容器组件已经帮助咱们实现了store.subscribe方法的订阅,这时候就不须要constructor函数和监听函数,容器组件就会自动订阅状态的变化,UI组件经过this.props来获取函数中返回的state,这时候当咱们对state进行操做的时候,状态就会改变,视图从新渲染,componentWillReceiveProps这个钩子函数就会执行,实现了对状态改变的事实监听。

connect中有两个参数,一个是映射状态到组件属性(mapStateToProps),一个是映射方法到组件属性(mapDispatchToProps),最终内部返回一个容器组件帮助咱们作监听操做,一旦状态更改,UI组件就会从新渲染。

connect(mapStateToProps,mapDispatchToProps)(ui组件)

容器组件内部帮你作了 store.subscribe() 状态变化 ==> 容器组件监听状态改变了 ==> 经过属性的方式给ui组件传递

store.getState()的状态转化为展现组件的props

当咱们须要挂载不少方法的时候咱们能够将之简写

首先咱们引入bindActionCreators

import {bindActionCreators} from "redux"
复制代码

而后咱们使用bindActionCreators将全部操做状态的方法所有取出来绑定到UI组件的属性上,使用的时候直接经过this.props取便可。

//actionCreators很纯粹了,须要建立action而后返回action便可!
//ui组件的属性上就就会有改变状态的方法了,用的话经过this.props.方法名
const mapDispatchToProps = dispatch=>{
    return bindActionCreators(actionsCreators,dispatch)
}
connect(mapStateToProps,mapDispatchToProps)(UI组件)
复制代码

Redux的组成

redux有四个组成部分:

store:用来存储数据

reducer:真正的来管理数据

actionCreator:建立action,交由reducer处理

view: 用来使用数据,在这里,通常用react组件来充当

image

何时用redux?

若是你不知道是否须要 Redux,那就是不须要它

只有遇到 React 实在解决不了的问题,你才须要 Redux

简单说,若是你的UI层很是简单,没有不少互动,Redux 就是没必要要的,用了反而增长复杂性。

  • 用户的使用方式很是简单
  • 用户之间没有协做
  • 不须要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

须要使用redux的项目:

  • 用户的使用方式复杂
  • 不一样身份的用户有不一样的使用方式(好比普通用户和管理员)
  • 多个用户之间能够协做
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

从组件层面考虑,什么样子的须要redux:

  • 某个组件的状态,须要共享
  • 某个状态须要在任何地方均可以拿到
  • 一个组件须要改变全局状态
  • 一个组件须要改变另外一个组件的状态

redux的设计思想:

  1. Web 应用是一个状态机,视图与状态是一一对应的。

  2. 全部的状态,保存在一个对象里面(惟一数据源)。

Redux的流程

Redux的流程:

  1. 建立store:

    从redux工具中取出createStore去生成一个store。

  2. 建立一个reducer,而后将其传入到createStore中辅助store的建立。

    reducer是一个纯函数,接收当前状态和action,返回一个状态,返回什么,store的状态就是什么,须要注意的是,不能直接操做当前状态,而是须要返回一个新的状态。

    想要给store建立默认状态其实就是给reducer一个参数建立默认值。

  3. 组件经过调用store.getState方法来使用store中的state,挂载在了本身的状态上。

  4. 组件产生用户操做,调用actionCreator的方法建立一个action,利用store.dispatch方法传递给reducer

  5. reducer对action上的标示性信息作出判断后对新状态进行处理,而后返回新状态,这个时候store的数据就会发生改变, reducer返回什么状态,store.getState就能够获取什么状态。

  6. 咱们能够在组件中,利用store.subscribe方法去订阅数据的变化,也就是能够传入一个函数,当数据变化的时候,传入的函数会执行,在这个函数中让组件去获取最新的状态。

reducer是一个纯函数?你对纯函数是怎么理解的?

reducer是state最终格式的肯定。它是一个纯函数,也就是说,只要传入参数相同,返回计算获得的下一个 state 就必定相同。没有特殊状况、没有反作用,没有 API 请求、没有变量修改,单纯执行计算。 reducer对传入的action进行判断,而后返回一个经过判断后的state,这就是reducer的所有职责

Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是一样的输入,一定获得一样的输出。

纯函数是函数式编程的概念,必须遵照如下一些约束。

不得改写参数

不能调用系统 I/O 的API

不能调用Date.now()或者Math.random()等不纯的方法,由于每次会获得不同的结果

(1)只要是一样的输入,一定获得一个一样的输出。

(2)千万不能更改以前的状态,必需要返回一个新状态

(3)里面不能有不纯的操做,例如Math.random(),new Date(),io操做

redux中间件的原理是什么?如何理解?

一般状况下,action只是一个对象,不能包含异步操做,这致使了不少建立action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了以后,能够经过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提升,能够方便于测试、复用,同时actionCreator还集成了异步操做中不一样的action派发机制,减小编码过程当中的代码量。

redux中间件就是指action到达store之间。store.dispatch(action)方法将action派发给了store 而且咱们的action只能是一个对象,需求的时候,就须要考虑到一些异步逻辑放在哪里去实现? 采用中间件以后,action就能够是一个函数的形式了,而且会把函数式的action转成对象,在传递给store.

dispatch一个action以后,到达reducer以前,进行一些额外的操做,就须要用到middleware。你能够利用 Redux middleware 来进行日志记录、建立崩溃报告、调用异步接口或者路由等等。

换言之,redux的中间件都是对store.dispatch()的加强

redux有哪些中间件?

作异步的操做在action里面去实现!须要安装redux中间件 redux-thunk redux-saga (基于配置文件 es7 async await) redux-promise

redux-thunk原理

Redux-thunk是一个Redux中间件,位于 Action与 Strore中间,简单的说,他就是对store.dispatch进行了一次升级,他经过改造store.dispatch,可使得store.dispatch能够接受函数做为参数。

能够看出来redux-thunk最重要的思想,就是能够接受一个返回函数的action creator。若是这个action creator 返回的是一个函数,就执行它,若是不是,就按照原来的next(action)执行。 正由于这个action creator能够返回一个函数,那么就能够在这个函数中执行一些异步的操做。

React项目相关

项目中遇到哪些问题?如何解决?

用于轮播图组件的远程数据已经请求回来了,而且也已经实例化完毕了。发现navbar能滑,可是滑不过去的现象

缘由:由于咱们在ComponentDidMount中请求数据,这个操做是异步操做,不会阻止后续代码,因此咱们一边执行请求数据的代码一边实例化,数据还在请求中的时候,实例化已经开始执行,等数据回来的时候实例化已经早就结束了。

方法一:放入在componentDidUpdate钩子函数里面

问题:当页面中的无关数据改变的时候一样会走这个钩子函数,那就会致使它从新执行。

因此咱们给Swiper的实例化起一个别名

在componentDidUpdate这个函数中if语句判断它是否存在,若是不存在再去实例化,存在的话就不须要再去执行实例化操做。

//在这个钩子函数里面  就能够获取到因数据改变致使的虚拟dom从新渲染完成的真实dom结构了
    componentDidUpdate(){
        if(!this.swiper)this.initSwiper()  //数据可能还在请求当中,可是这个实例化操做已经完毕了。等后续数据来了,实例化提早早就结束了。
    }
复制代码

方法二:会发现上面的方案会多写一个钩子函数,可不能够在componentDidmount里面实现此功能呢?

将实例化操做写在获取数据的回调函数里

componentDidMount(){
        //请求数据 更改navs
        this.props.getNavs(()=>{
            this.initSwiper()  
        })
    }
复制代码

在store/home/actionCreators文件中让getNavs接收这个回调函数,在数据请求结束后执行callback回调函数。

import {Get} from "../../modules/axios-utils"
import {GET_NAV_INFO} from "./const"
export default {
    getNavs(callback){
        return dispatch=>{
            Get({
                url:"/sk/navs"
            }).then(res=>{
                let navs = res.data.data.object_list
                dispatch({ type: GET_NAV_INFO,navs})
                callback && callback() 
            })
        }
    }
}
复制代码

咱们跳转页面的时候它会屡次请求数据,因此咱们须要在componentDidMount这个钩子函数中判断redux里面navs是否存在,存在就不须要再发送请求了,这时候从别的页面再跳转回首页就不会重复请求数据,可是数据划不动,因此咱们须要在函数中再次执行Swiper初始化操做。

let {navs} = this.props;
        if(navs){
            this.initSwiper()
            return false;
        }
复制代码

reducer中的深拷贝与浅拷贝

咱们进行改变状态操做时,componentWillReceiveProps()这个函数没有被触发,说明视图没有检测到状态的改变。这时候咱们来到reducer.js这个文件中,查看执行添加操做的函数,咱们经过

let new_state = {...prevState}
复制代码

这句代码将prevState解构出来赋值给new_stat,,咱们往new_state中的todos数组push一个新内容,并无返回新的状态,那是由于当咱们对new_state这个数组进行操做的时候,会影响到以前的prevState中的todos,由于todos是个引用类型,它和new_state中的todos指向同一块内存空间,因此当咱们执行push操做的时候至关于更改了以前的状态。在redux中规定,千万不能对以前的状态进行任何操做,必需要返回一个新状态,内部以此为依据来判断到底有没有新的状态产生,根据以前状态与新状态的地址比较,更改以后的地址跟以前的地址是同一个的话,就说明没有产生新状态。因此即使咱们操做的是new_state中的todos,实际上咱们更改的也是prevState中的todos,因此不会有新的状态产生。

因此咱们要使用深拷贝,拷贝出来一份同样的数组,而且这个新数组的引用地址和以前的引用地址彻底不一样。

ew_state.todos = new_state.todos.slice();
复制代码

深拷贝浅拷贝有什么区别?

1. 浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。

2. 深拷贝: 建立一个新的对象和数组,将原对象的各项属性的“值”(数组的全部元素)拷贝过来,是“值”而不是“引用”

react,jquey,vue是否能够共存在一个项目中?

能够存在,互不干扰。

<div></div>
  <div id="react"></div>
  <div id="vue"></div>
复制代码

ReactDOM.render(,document.getElementById("react")); new Vue({el:"#vue",router,store});

服务端渲染SSR

什么是服务端渲染? 核心在于方便seo优化

后端先调用数据库,得到数据以后,将数据和页面元素进行拼装,组合成完整的html页面,再直接返回给浏览器,以便用户浏览。 例如:www.cnblogs.com/cate/design

什么是客户端渲染? 分担到客户端

数据由浏览器经过ajax动态得到,再经过js将数据填充到dom元素上最终展现到网页中,这样的过程就叫作客户端渲染。 例如:m.maizuo.com/v5/#/films/…

服务端渲染与客户端渲染区别?

客户端渲染不利于SEO搜索引擎优化 服务端渲染是能够被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的 服务端渲染对SEO友好,通过服务端渲染的页面,在网络传输的时候,传输的是一个真实的页面,因此爬虫就会对这个页面中的关键数据进行分析、收录。 服务端渲染缺点就是 对服务器压力比较大 客户端渲染减轻了服务器端的渲染压力,可以实现先后端分离开发 客户端渲染缺点就是 对SEO至关的不友好

主流UI开发框架

你都使用过哪些UI开发框架(类库)?

VUE:Vant、Element、Mint UI、iView

React:Ant Design

移动端:Ant Design Mobile

PC端:Bootstrap、Ant Design

混合开发:MUI

有没有用一些脚手架的搭建?,脚手架有什么做用?

Vue-cli create-react-app wepy-cli

包含基础的依赖库,只须要 npm install就能够安装,快速搭建项目。

Mint-UI用到哪些模块,怎么用,在vue里怎么注册,引入

引入:

(1)完整引入

(2)按需引入

image

移动端

移动端开发

编写移动端时的四个步骤

(1)添加mate声明

在编辑器中输入mate:vp按tab键

<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
复制代码

(2)写移动端必须用怪异盒模型,box-sizing:border-box;

使用弹性盒布局:display:flex;

(3)根据设计图设置单位,rem相对于根元素。

Iphone5:宽度640px;

html:font-size:31.25vw;

31.25vw=100px=1rem;

Iphone6:宽度750px;

html:font-size:26.67vw;

26.675vw=100px=1rem;

(4)编写一屏页面:

html,body{height:100%;}

移动设备单位

ppi/dpi (每英寸所拥有像素点的数量)

​ dpr > 设备像素比 (物理像素、逻辑像素)

​ 逻辑像素就是css设置的像素

​ 物理像素就是设备显示的像素

​ dpr == 物理像素 / 逻辑像素

​ dpr 通常考虑的值 > 2或者3

​ 若是移动端设计图的宽度为750/640 > 选择的dpr为2

​ 若是移动端设计图的宽度为1080 > 选择的dpr为3

​ 例:

​ 若是设计图为640px

​ dpr为2

​ 若是从ps中量出元素宽度为300px;

​ 在css里面设置的为 300px / dpr(2) == 150px;

页面布局有哪几种方式

**固定布局:**以像素做为页面的基本单位,无论设备屏幕及浏览器宽度,只设计一套尺寸;

**可切换的固定布局:**一样以像素做为页面单位,参考主流设备尺寸,设计几套不一样宽度的布局。经过识别的屏幕尺寸或浏览器宽度,选择最合适的那套宽度布局;

**弹性布局(百分比布局):**以百分比做为页面的基本单位,能够适应必定范围内全部尺寸的设备屏幕及浏览器宽度,并能完美利用有效空间展示最佳效果;

**混合布局:**同弹性布局相似,能够适应必定范围内全部尺寸的设备屏幕及浏览器宽度,并能完美利用有效空间展示最佳效果;只是混合像素、和百分比两种单位做为页面单位。

**布局响应:**对页面进行响应式的设计实现,须要对相同内容进行不一样宽度的布局设计,有两种方式:pc优先(从pc端开始向下设计);

移动优先(从移动端向上设计);不管基于那种模式的设计,要兼容全部设备,布局响应时不可避免地须要对模块布局作一些变化(发生布局改变的临界点称之为断点)

####什么是响应式设计?响应式设计的基本原理是什么

响应式是指根据不一样设备浏览器分辨率或尺寸来展现不一样页面结构、行为、表现的设计方式。

响应式设计的基本原理是经过媒体查询检测不一样的设备屏幕尺寸作处理。

####说说对于移动端页面部局你都知道哪几种布局方案,并说明其各自特色及实现的方法

移动端布局经常使用的有100%布局,等比缩放布局,或是混合布局。

百分比布局也称做流式布局,通常适用一些流式页面的布局;

等比缩放布局能够利用rem或vw等方式来实现;

rem

rem和em的区别

rem(font size of the root element)是指相对于根元素 (html)的字体大小的单位

em是相对于父元素

rem方法的封装

document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px"

window.onresize = function(){
    document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px"
}
复制代码

px和em的区别

px和em都是长度单位,区别是:px的值是固定的,指定是多少就是多少,计算比较容易。em得值不是固定的,而且em会继承父级元素的字体大小。

浏览器的默认字体高都是16px。因此未经调整的浏览器都符合: 1em=16px。那么12px=0.75em, 10px=0.625em。

弹性盒

####Flex弹性盒布局

Flex容器:采用 Flex 布局的元素的父元素;

Flex项目:采用 Flex 布局的元素的父元素的子元素;

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫作main start,结束位置叫作main end;交叉轴的开始位置叫作cross start,结束位置叫作cross end。

项目默认沿主轴排列。单个项目占据的主轴空间叫作main size,占据的交叉轴空间叫作cross size。

不兼容全部浏览器,不适合写pc端,主要用于移动端。

弹性元素只能影响子元素,没法影响全部的后代元素。

弹性布局属于css3只兼容高版本浏览器

Flex垂直居中

flex:弹性盒

justify-content 项目在主轴上的对齐方式

参数:center 居中/space-between 两端对齐/space-around均分对齐

align-items 项目在交叉轴上的对齐方式

参数:center 居中

flex-direction 决定主轴的方向

row X轴/column Y轴

参考:Flex 布局教程:语法篇

www.ruanyifeng.com/blog/2015/0…

布局方式

媒体查询如何实现?

媒体查询 @media screen and (max-width:300px){ …} 最大宽度

Bootstrap12栅格系统

Row col col-md-4 col-md-4 col-md-4

在Bootstrap框架当中,每列基本被分为12格,要使用栅格系统须要在该<div>标签当中设置class="container",而对于每一行则用<div class="row">包着,内部因为有12格,所以能够结合具体状况分配比例,举例:

<div class="container">
    <!-- 定义栅格系统 -->
    <div class="row">
        <!-- 定义一行 -->
        <div class="col-md-4">
            <!-- 定义了三列,每列占3格 -->
            <img src="timg.jpg" width="300px">
        </div>
        <div class="col-md-4">
            <img src="timg.jpg" width="300px">
        </div>
        <div class="col-md-4">
            <img src="timg.jpg" width="300px">
        </div>
    </div>
    <div class="row">
        <!-- 定义了4列,分别占六、三、二、1格 -->
        <div class="col-md-6">
            <img src="timg.jpg" width="300px">
        </div>
        <div class="col-md-3">
            <img src="timg.jpg" width="300px">
        </div>
        <div class="col-md-2">
            <img src="timg.jpg" width="300px">
        </div>
        <div class="col-md-1">
            <img src="timg.jpg" width="300px">
        </div>
    </div>
</div>

复制代码

Grid网格布局

网格布局(Grid)是最强大的 CSS 布局方案。

它将网页划分红一个个网格,能够任意组合不一样的网格,作出各类各样的布局。之前,只能经过复杂的 CSS 框架达到的效果,如今浏览器内置了。

image

参考:CSS Grid 网格布局教程

www.ruanyifeng.com/blog/2019/0…

趋势:flex和grid使布局更简单

www.cnblogs.com/hsprout/p/8…

混合开发&小程序

混合开发模式

咱们知道混合开发的模式如今主要分为两种,H5工程师利用某些工具如DCLOUD产品、codorva+phonegap等等来开发一个外嵌native壳子的混合app。 还有就是应用比较普遍的,有native开发工程师和H5工程师一块儿写做开发的应用,在native的webview里嵌入H5页面,固然只是部分界面这么作,这样作的好处就是效率高,开发成本和维护成本都比较低,较为轻量,可是有一个问题不可避免的会出现,就是js和native的交互。

进入域后根据不一样的状况显示不一样的页面(PC/MOBILE)

不少状况下,一个应用会有PC和移动端两个版本,而这两个版本由于差异大,内容多,因此不能用响应式开发可是单独开发,而域名只有一个,用户进入域后直接返回对应设备的应用,作法主要有两种:

1. 前端判断并跳转
    进入一个应用或者一个空白页面后,经过navigator.userAgent来判断用户访问的设备类型,进行跳转

2. 后端判断并响应对应的应用
    用户地址栏进入域的时候,服务器能接收到请求头上包含的userAgent信息,判断以后返回对应
复制代码

小程序开发的形式

目前小程序开发主要有三种形式:原生、wepy、mpvue,其中wepy是腾讯的开源项目;mpvue是美团开源的一个开发小程序的框架,全称mini program vue(基于vue.js的小程序),vue开发者使用了这个框架后,开发小程序的效率将获得很大的提高。

wepy与mpvue如何选择?

image

参考:使用mpvue开发微信小程序——原生微信小程序、mpvue、wepy对比

blog.csdn.net/fabulous111…

微信小程序里面各个部分是干什么的

这些文件能够分为四类,分别是以js、wxml、wxss和json结尾的文件。

以js结尾的文件,通常状况下是负责功能的,好比,点击一个按钮,按钮就会变颜色。

以wxml为后缀的文件,通常状况下负责布局,好比,把按钮放在屏幕的上方,仍是放在屏幕的正中间。

以wxss为后缀的文件,是负责渲染的功能,好比,按钮是什么颜色,是正方形仍是圆形。

以json为后缀的文件,这里能够暂时理解为更改屏幕上方的标题的,也就是说明页面的顶部标题。

参考:微信小程序里面各个部分是干什么的

www.jianshu.com/p/3863f8b75…

你常常浏览哪些技术网站

  • 酷壳
  • 掘金
  • stackoverflow(国外)
  • 思否SegmentFault

前端开发的优化问题

性能优化

简述一下你对web性能优化的方案?

一、尽可能减小 HTTP 请求

二、使用浏览器缓存

三、使用压缩组件

四、图片、JS的预载入

五、将脚本放在底部

六、将样式文件放在页面顶部

七、使用外部的JS和CS

如何优化项目

(1) 减小http请求次数:CSS Sprites(雪碧图), JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。

(2) 前端模板 JS+数据,减小因为HTML标签致使的带宽浪费,前端用变量保存AJAX请求结果,每次操做本地变量,不用请求,减小请求次数

(3) 用innerHTML代替DOM操做,减小DOM操做次数,优化javascript性能。

(4) 当须要设置的样式不少时设置className而不是直接操做style。

(5) 少用全局变量、缓存DOM节点查找的结果。减小IO读取操做。

(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。

(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。

(8) 避免在页面的主体布局中使用table,table要等其中的内容彻底下载以后才会显示出来,显示比div+css布局慢。

雅虎14条性能优化原则

1. 尽量的减小 HTTP 的请求数 content
2. 使用 CDN(Content Delivery Network) server
3. 添加 Expires 头(或者 Cache-control ) server
4. Gzip 组件 server
5. 将 CSS 样式放在页面的上方 css
6. 将脚本移动到底部(包括内联的) javascript
7. 避免使用 CSS 中的 Expressions css
8. 将 JavaScript 和 CSS 独立成外部文件 javascript css
9. 减小 DNS 查询 content
10. 压缩 JavaScript 和 CSS (包括内联的) javascript css
11. 避免重定向 server
12. 移除重复的脚本 javascript
13. 配置实体标签(ETags) css
14. 使 AJAX 缓存

你如何对网站的文件和资源进行优化?

  1. 文件合并:目的是减小http请求
  2. 文件压缩:目的是直接减小文件下载的体积
  3. 使用CDN(内容分发网络来托管资源)
  4. 缓存的使用:而且多个域名来提供缓存
  5. GZIP压缩JS和CSS文件

参考:你如何对网站的文件和资源进行优化?

blog.csdn.net/xujie_0311/…

SEO优化

一个单页应用程序SEO友好吗?

单页应用实际是把视图(View)渲染从Server交给浏览器,Server只提供JSON格式数据,视图和内容都是经过本地JavaScript来组织和渲染。而搜索搜索引擎抓取的内容,须要有完整的HTML和内容,单页应用架构的站点,并不能很好的支持搜索。

参考:单页应用SEO浅谈

www.chinaz.com/web/2014/12…

一个单页应用程序SEO友好吗?

baijiahao.baidu.com/s?id=160547…

网页自身SEO优化(如何让网站被搜索引擎搜索到)

  1. 页面主题优化

实事求是的写下本身网站的名字,网站的名字要合理,最好包含网站的主要内容。

  1. 页面头部优化

页面头部指的是代码中部分,具体一点就是中的“Description(描述)”和“Keywords(关键字)”两部分

  1. 超连接优化

(1)采用纯文本连接,少用,最好是别用Flash动画设置连接,由于搜索引擎没法识别Flash上的文字.

(2)按规范书写超连接,这个title属性,它既能够起到提示访客的做用,也可让搜索引擎知道它要去哪里.

(3)最好别使用图片热点连接,理由和第一点差很少。

  1. 图片优化(alt属性,title属性)

网络爬虫对title和alt友好,网络优化时必须写。

  1. 为网站制做一个“网站地图”

  2. PageRank(pr值,友情连接)

  3. 静态页面与动态页面

  4. 避免大“体积”的页面

  5. 最重要的一点!合理的代码结构

框架性能优化

react性能优化

  • 使用生产环境production版本的react.js

  • 重写shouldComponentUpdate来避免没必要要的dom操做,一旦返回true ,组件更新操做;返回false,就不会更新,节省性能。

  • 使用key来帮助React识别列表中全部子组件的最小变化

  • PureComponent 纯组件 ,自带shouldComponentUpdate,能够对props进行浅比较,不会比较对象这些东西。 发现后面的props与前面的props同样,就不会进行render了。

    class Test extends React.PureComponent{     <div><Test a={10}/></div>
    	constructor(props){
    		super(props);
    	}
    	render(){
    		return <div>hello...{this.props.a}</div>
    	}
    }
    复制代码

VUE性能优化

像VUE这种单页面应用,若是没有应用懒加载,运用webpack打包后的文件将会异常的大,形成进入首页时,须要加载的内容过多,时间过长,会出现长时间的白屏,即便作了loading也是不利于用户体验,而运用懒加载则能够将页面进行划分,须要的时候加载页面,能够有效的分担首页所承担的加载压力,减小首页加载用时,简单说就是:进入首页不用一次加载过多资源形成的用时时长。

相关文章
相关标签/搜索