前端框架 | 面试总结

二叶草 2020年3月8日16:21:34前端框架评论阅读模式

 

 

 

Angular

Angular是怎么实现双向绑定的?

脏检查机制:

  • Angular 在解析视图模板时, 会找出其中的数据绑定, 以及对应的更新DOM的方式
  • 然后通过 $scope.$watch 将这一绑定注册到当前 $scope 上下文的更新响应操作里。
  • $digest 每一个循环里会从根作用域开始遍历所有的 $scope 注册的 $watch 响应操作
  • 当发现值有变化时,更新DOM数据。
  • 这其中是通过一个dirty为true或false去更新数据。

参考: Angular沉思录(一)数据绑定

angular的路由是怎么实现的?

通过更新hash值

Vue

双向绑定

Vue是怎么实现双向绑定的?

  • 通过Object.defineProperty()进行数据劫持,设置各个属性的setter,getter,在数据变动时触发相应的监听回调。
  • 具体实现:
    • 数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
    • 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
    • 实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图 / 它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
    • mvvm入口函数,整合以上三者

参考: 剖析Vue实现原理 - 如何实现双向绑定mvvm

具体实现

  • 实现数据的双向绑定,首先要对数据进行劫持监听,
  • 我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。
  • 因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。
  • 接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析
  • 将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

因此接下去我们执行以下3个步骤,实现数据的双向绑定:

  • 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
  • 实现一个订阅者Watcher,为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
  • 实现一个解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

参考:vue的双向绑定原理及实现

双向绑定图解

前端框架 | 面试总结

生命周期

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

生命周期图解

前端框架 | 面试总结

参考:详解vue生命周期

Vue的路由是怎么实现的?

  • 通过更新hash值
  • 采用H5的history api
    • history.pushState()
    • history.replaceState()
    • history.back(); history.go()

为什么采用VUE

  • 使用 Virtual DOM
  • 提供了响应式(Reactive)和组件化(Composable)的视图组件。
  • 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
  • 在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染

Vue - template

var vm = new Vue({
  // 选项
  el: "#app",
  data: {
    name: "",
    age: 10
  },
  watch: {
    a: function(val, oldVal) {
      console.log("new: %s, old: %s", val, oldVal);
    },
    // 方法名
    b: "someMethod",
    // 深度 watcher
    c: {
      handler: function(val, oldVal) {
        /* ... */
      },
      deep: true
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
      handler: function(val, oldVal) {
        /* ... */
      },
      immediate: true
    },
    e: [
      function handle1(val, oldVal) {
        /* ... */
      },
      function handle2(val, oldVal) {
        /* ... */
      }
    ],
    // watch vm.e.f's value: {g: 5}
    "e.f": function(val, oldVal) {
      /* ... */
    }
  }
});
<div id="app">
  <p>{{ foo }}</p>
  <!-- 这里的 `foo` 不会更新! -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>

or

<template>
    <div class="messgae">{{msg}}</div>
</template>
<script>
    export default{
        data(){
            return {
                msg:'Hello from vue-loader'
            }
        }
    }
</script>
<style>
.message{
    color:red;
    font-size:36px;
    font-weight:blod;
}
</style>

React

性能问题

性能问题:

关于组件重新渲染的问题:

  • 没有导致state的值发生变化的setState是否会导致重渲染?
  • 组件的state没有变化,并且从父组件接受的props也没有变化,那它就还可能重渲染吗?

    以上两个问题答案分别为【是】和【可能】。解决以上的问题需要shouldComponentUpdate(nextProps,nextState)配合。shouldComponentUpdate只有返回true,componentWillUpdate rendercomponentDidUpdate都会被调用,则组件会被重新渲染。返回false则不会重新渲染

    第一个问题:当本组件调用了setState时输入空参数,或者不改变state,shouldComponentUpdate会被调用。所以组件会被重新渲染,这时可以做如下改进

    //在render函数调用前判断:如果前后state中Number不变,通过return false阻止render调用
    shouldComponentUpdate(nextProps,nextState){
      if(nextState.Number == this.state.Number){
        return false
      }
    }

    第二个问题:当父组件之下有多个子组件,父组件更改了state,会影响其中一个子组件的props,本来只有该子组件被重新渲染,但发现多个子组件都被重新渲染了,这时同样需要在shouldComponentUpdate做一个调整,当props更新时shouldComponentUpdate返回true,否则返回false

    shouldComponentUpdate(nextProps,nextState){
      if(nextProps.number == this.props.number){
        return false
      }
      return true
    }

nextProps.number == this.props.number不能写成nextProps == this.props,它总返回false因为它们是堆中内存不同的两个对象。

但当组件是对象 数组时,只用等于号判断是不合理的,因为他们存的是引用地址,可以采用深拷贝去更新每一次变化 比如将

  this.setState({
    number: [number[0],2,number[2])
  })

改为

  let number = number.slice(0) // 即深拷贝到新的地址
  this.setState({
    number
  })

关于setState

调用 setState 之后发生了什么?

  • 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
  • 经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。
  • 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。
  • 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

参考: React官方setState

调和过程:

setState通过引发一次组件的更新过程来引发重新绘制; setState调用引起的React的更新生命周期函数4个函数(比修改prop引发的生命周期少一个componentWillReceiveProps函数),这4个函数依次被调用。

  • shouldComponentUpdate 返回true时才会做以下操作
  • componentWillUpdate
  • render 这一步this.state才得到更新
  • componentDidUpdate

setState不会立刻改变React组件中state的值;

不会。修改this.state.number 会引起状态改变 但不会引起UI的更新。所以setState用来调用做UI更新

多次setState函数调用产生的效果会合并。

因为React会将多个this.setState产生的修改放在一个队列里,缓一缓,攒在一起,觉得差不多了再引发一次更新过程。

关于 setState() 这里有三件事情需要知道

  • 不要直接更新状态 如 this.state.number = 1;该方法没调用setState就不会走生命周期
  • 状态更新可能是异步的;所以以下代码错误
    this.setState({
    counter: this.state.counter + this.props.increment,
    });

    正确的方式

    this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
    }));
    • 状态更新合并: 调用setState时,虽然参数只有一个值,但它会与其他state先合并

setState可以接受函数为参数吗?有什么作用?

 this.setState((prevState, props) => {
    return {counter: prevState.counter + props.step};
  });

函数接收到的prevState 和 props保证都是最新的。比如我们多次更改state时有时可能异步的问题,重叠忽略了一些 比如this.setState({number:this.state.number+1})连续调用三次,但三次this.state.number的值一样时,有可能最后并不是加了三次

传入 setState 函数的第二个参数的作用是什么?

该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成

什么时候不能调用setState

  • componentWillUpdate(): 我也不知道这个声明周期函数的意义在哪里,在这个函数内你不能调用setState改变组件状态 render()

什么时候调用setState不会再次调用render

  • componentWillReceiveProps(nextProps): 在这里你可以拿到即将改变的状态,可以在这一步中通过setState方法设置state ,这一步setState不会让重新render()

生命周期

描述一下React组件的各个生命周期函数吗/组件的生命周期有哪些

组件的声明周期有三种阶段
  • 初始化阶段(Mounting)
  • 更新阶段(Updating)
  • 析构阶段(Unmounting)
组件的初始化阶段
  • constructor(): 用于绑定事件以及初始化state(可以通过”fork”props的方式给state赋值)
  • componentWillMount(): 只会在服务端渲染时被调用,你可以在这里同步操作state。由于这个方法始终只执行一次,所以如果在这里定义了setState方法之后,页面永远都只会在加载前更新一次。
  • render(): 这个函数是用来渲染DOM没有错。但它只能用来渲染DOM,请保证它的纯粹性。如果有操作DOM或者和浏览器打交道的一系列操作,请在下一步骤componentDidMount中进行
  • componentDidMount(): 如果你有第三方操作DOM的类库需要初始化(类似于jQuery,Bootstrap的一些组件)操作DOM、或者请求异步数据,都应该放在这个步骤中做
组件更新阶段
  • componentWillReceiveProps(nextProps): 在这里你可以拿到即将改变的状态,可以在这一步中通过setState方法设置state 。这里拿到的this.props还是就的props.
  • shouldComponentUpdate(nextProps, nextState): 这一步骤非常重要,它的返回值决定了接下来的生命周期函数是否会被调用,默认返回true,即都会被调用;你也可以重写这个函数使它返回false。
  • componentWillUpdate(): 我也不知道这个声明周期函数的意义在哪里,在这个函数内你不能调用setState改变组件状态,否则会立即触发另一轮的渲染并且又再一次调用componentWillUpdate,陷入无限循环中。
  • render()
  • componentDidUpdate(): 和componentDidMount类似,在这里执行DOM操作以及发起网络请求
组件析构阶段
  • componentWillUnmount(): 主要用于执行一些清理工作,比如取消网络请求,清楚多余的DOM元素等。清除componentDidMount 或 componentDidUpdate中的任务。

参考:各个函数的描述

生命周期图解

前端框架 | 面试总结

渲染机制

  • 要点:合并,diff算法 , 生命周期调用过程

更新渲染过程

  • setState()
  • 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程
  • 调和过程中采用Diff算法,不同节点类型的比较 ,分两种情况:
    • 节点类型不同: 直接删除旧的节点,再新建新的节点
    • 节点类型相同,属性不同: 对比属性,只改变变化了的节点属性
  • 构建 React 元素树并且着手重新渲染整个UI界面
  • shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
  • 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。
  • 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

参考:React 渲染机制解析,深入浅出React(四):虚拟DOM Diff算法解析

其他

在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?

在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。

  function Clock(props) {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }

使用类就允许我们使用其它特性,例如局部状态、生命周期钩子

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

React 中 Element 与 Component 的区别是?

简单而言,React Element 是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 就是利用 JSX 构建的声明式代码片然后被转化为createElement的调用组合。而 React Component 则是可以接收参数输入并且返回某个 React Element 的函数或者类。

React 中的 refs 作用是什么?

Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。 允许您直接访问DOM元素或组件实例

<Input ref = {input => this.Input=input}>

Controlled Component 与 Uncontrolled Component 之间的区别是什么?

React 的核心组成之一就是能够维持内部状态的自治组件,不过当我们引入原生的HTML表单元素时(input,select,textarea 等),我们是否应该将所有的数据托管到 React 组件中还是将其仍然保留在 DOM 元素中呢?这个问题的答案就是受控组件与非受控组件的定义分割。受控组件(Controlled Component)代指那些交由 React 控制并且所有的表单数据统一存放的组件。譬如下面这段代码中username变量值并没有存放到DOM元素中,而是存放在组件状态数据中。任何时候我们需要改变username变量值时,我们应当调用setState函数进行修改。

竟然非受控组件看上去更好实现,我们可以直接从 DOM 中抓取数据,而不需要添加额外的代码。不过实际开发中我们并不提倡使用非受控组件,因为实际情况下我们需要更多的考虑表单验证、选择性的开启或者关闭按钮点击、强制输入格式等功能支持,而此时我们将数据托管到 React 中有助于我们更好地以声明式的方式完成这些功能。引入 React 或者其他 MVVM 框架最初的原因就是为了将我们从繁重的直接操作 DOM 中解放出来。

shouldComponentUpdate 的作用是啥以及为何它这么重要?

shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新。

createElement 与 cloneElement 的区别是什么?

createElement 函数是 JSX 编译之后使用的创建 React Element 的函数,而 cloneElement 则是用于复制某个元素并传入新的 Props。

为什么我们需要使用 React 提供的 Children API 而不是 JavaScript 的 map?

props.children并不一定是数组类型,譬如下面这个元素:

  <Parent>
    <h1>Welcome.</h1>
  </Parent>

如果我们使用props.children.map函数来遍历时会受到异常提示,因为在这种情况下props.children是对象(object)而不是数组(array)。React 当且仅当超过一个子元素的情况下会将props.children设置为数组,就像下面这个代码片:

  <Parent>
    <h1>Welcome.</h1>
    <h2>props.children will now be an array</h2>
  </Parent>

这也就是我们优先选择使用React.Children.map函数的原因,其已经将props.children不同类型的情况考虑在内了。

如果你创建了类似于下面的Twitter元素,那么它相关的类定义是啥样子的?

  <Twitter username="tylermcginnis33">
    {user => (user === null ? <Loading /> : <Badge info={user} />)}
  </Twitter>;



  import React, { Component, PropTypes } from 'react'
  import fetchUser from 'twitter'
  class Twitter extends Component {
    state = {
      user: null,
    }
    static propTypes = {
      username: PropTypes.string.isRequired,
    }
    componentDidMount () {
      fetchUser(this.props.username)
        .then((user) => this.setState({user}))
    }
    render () {
      return this.props.children(this.state.user)
    }
  }

在生命周期中的哪一步你应该发起 AJAX 请求?

我们应当将AJAX 请求放到 componentDidMount 函数中执行,主要原因有下:

React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount。如果我们将 AJAX 请求放到 componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。

如果我们将 AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题。

什么是JSX

就是为了把 HTML 模板直接嵌入到 JS 代码里面,这样就做到了模板和组件关联,但是 JS 不支持这种包含 HTML 的语法,所以需要通过工具将 JSX 编译输出成 JS 代码才能使用

props 和 state

  • props是组件的属性,只读
  • state是当前组件的状态,是私有的。

为什么我们利用循环产生的组件中要用上key这个特殊的prop?

有babel转换后React.createElement中的代码可以看出,其它元素之所以不是必须需要key是因为不管组件的state或者props如何变化,这些元素始终占据着React.createElement固定的位置,这个位置就是天然的key。

而由数组创建的组件可能由于动态的操作导致重新渲染时,子组件的位置发生了变化,例如上面用户列表子组件新增一个用户,上面两个用户的位置可能变化为下面这样:

  var element = React.createElement(
    "div",
    null,
    React.createElement("h3",null,"用户列表"),
    [
      React.createElement("div",{ key: 3 },"1:王五"), 
      React.createElement("div",{ key: 1 },"2:张三"), 
      React.createElement("div",{ key: 2 },"3:李四")
    ]
  );

可以看出,数组创建子组件的位置并不固定,动态改变的;这样有了key属性后,react就可以根据key值来判断是否为同一组件。

组件的Render函数在何时被调用

如果单纯、侠义的回答这个问题,毫无疑问Render是在组件 state 发生改变时候被调用。无论是通过 setState 函数改变组件自身的state值,还是继承的 props 属性发生改变都会造成render函数被调用,即使改变的前后值都是一样的。

如果你想手动决定是否调用也没有问题,如果你还记得React的生命周期的话,一定记得有一个boolean shouldComponentUpdate(object nextProps, object nextState)生命周期函数,这个函数的返回值决定了Render是否被调用,默认都返回true,即允许render被调用。如果你对自己的判断能力有自信,你可以重写这个函数,根据参数判断是否应该调用 Render 函数。这也是React其中的一个优化点。

render函数被调用了,DOM就一定被更新了?

这要看更新的是哪一类DOM了。

React组件中存在两类DOM,一类是众所周知的Virtual DOM,相信大家也耳熟能详了;另一类就是浏览器中的真实DOM(Real DOM/Native DOM)。React的Render函数被调用之后,React立即根据props或者state重新创建了一颗Virtual DOM Tree,虽然每一次调用时都重新创建,但因为在内存中创建DOM树其实是非常快且不影响性能的,所以这一步的开销并不大。而Virtual DOM的更新并不意味这Real DOM的更新,接下来的事情也是大家知道的,React采用算法将Virtual DOM和Real DOM进行对比,找出需要更新的最小步骤,此时Real DOM才可能发生修改。每一次的state更改都会使得render函数被调用,但页面的DOM不一定会发生修改

React Native

RN如何与原生iOS交互

RN与IOS的通信

  • React Native用iOS自带的JavaScriptCore作为JS的解析引擎
  • 有两份模块配置表:OC端和JS端分别各有一个bridge,两个bridge都保存了同样一份模块配置表
    • JS调用OC模块方法时,通过bridge里的配置表把模块方法转为模块ID和方法ID传给OC
    • OC通过bridge的模块配置表找到对应的方法执行之

RN在iOS中的操作是: 在IOS中:

  • 取所有模块类 每个模块类都实现了RCTBridgeModule接口 通过runtime接口objcgetClassList或objccopyClassList取出项目里所有类 然后逐个判断是否实现了RCTBridgeModule接口,就可以找到所有模块类,实现在RCTBridgeModuleClassesByModuleID()方法里。
  • 取模块里暴露给JS的方法 可以定义一些能被js访问或者不能被js访问的方法,比如AllowJS为前缀的就可以访问 用runtime方法classgetInstanceMethod取出所有方法名字,提取以RCTExport为前缀的方法

JS ---- Objective-C

  • 调用Objective-C提供出来的某个方法ABC,将JS代码填充进入

进入JS Bridge

  • 把上一步的调用,分解为模块名(ModuleName),方法名(MethodName),对callback进行一些处理,放到一个信息队列里面(MessageQueue)。
  • 进入MessageQueue,用上一步的callback生成对应的CallbackID。拿到上一步中MessageQueue的模块配置表的ModuleName和MethodName转为ModuleID和MethodID。 离开JS Bridge
  • 把上述步骤得到的ModuleID,MethodId,CallbackID和其他参数argus传给OC

进入OC Bridge

  • OC接收到消息,通过模块配置表拿到对应的模块和方法。 在这一步,OC Bridge已经初始化了。 在OC Bridge初始化的时候,每一个模块生成了对应的实例,模块上的每一个方法也都生成了对应的RCTModuleMethod对象。我们就可以同上前面拿到的ModuleID,MethodId,CallbackID和其他参数 进行操作
  • RCTModuleMethod对JS传过来的每一个参数进行处理。 在这一步中 OC会把JS的参数类型转为自己OC对应的参数类型,还会生成一个block,等执行完js的时候再来执行 我没写过ios,但我的理解是这个block类似于js中的单层回调 block:类似js的单层回调 离开OC Bridge
  • OC模块方法调用完,执行block回调。

返回过来的时候,我们刚已经记录了block callbackID 和 callbackID对应的callback方法,这样 我们就可以按原路返回到js端

runtime接口可以理解为一个运行时的事件,运行后就可以得到所有类 如 objcgetClassList或objccopyClassList

与iOS交互图解

前端框架 | 面试总结参考: React Native通信机制详解

RN如何与原生Android交互

  • 在Android系统上已经有了实现。就是WebView。然而React-Native与WebView并没有一点关系。React-Native实现一套新的webview
  • JavascriptModuleRegistry:Js层模块注册表,负责将所有JavaScriptModule注册到CatalystInstance,通过Java动态代理调用到Js。
  • java通过注册表调用到CatalystInstance实例,透过ReactBridge的jni,调用到Onload.cpp中的callFunction,最后通过javascriptCore,调用BatchedBridge.js,根据参数{moduleID,methodID}require相应Js模块执行
  • js -> Java:JS不主动传递数据调用Java。在需要调用调Java模块方法时,会把参数{moduleID,methodID}等数据存在MessageQueue中,等待Java的事件触发,再把MessageQueue中的{moduleID,methodID}返回给Java,再根据模块注册表找到相应模块处理。

Flex布局

Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。

容器的属性

  • flex-direction 属性决定主轴的方向(即项目的排列方向)。
    • row(默认值):主轴为水平方向,起点在左端。
    • row-reverse:主轴为水平方向,起点在右端。
    • column:主轴为垂直方向,起点在上沿。
    • column-reverse:主轴为垂直方向,起点在下沿。
  • flex-wrap 属性定义,如果一条轴线排不下,如何换行。
    • nowrap(默认):不换行。
    • wrap:换行,第一行在上方。
    • wrap-reverse:换行,第一行在下方。
  • justify-content 属性定义了项目在主轴上的对齐方式。
    • flex-start(默认值):左对齐
    • flex-end:右对齐
    • center: 居中
    • space-between:两端对齐,项目之间的间隔都相等。
    • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
  • align-items 属性定义项目在交叉轴上如何对齐。
    • flex-start:交叉轴的起点对齐。
    • flex-end:交叉轴的终点对齐。
    • center:交叉轴的中点对齐。
    • baseline: 项目的第一行文字的基线对齐。
    • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
  • align-content 属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
    • flex-start:与交叉轴的起点对齐。
    • flex-end:与交叉轴的终点对齐。
    • center:与交叉轴的中点对齐。
    • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
    • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
    • stretch(默认值):轴线占满整个交叉轴。

如何更新jsbundle

  • 我们打好包jsbundle文件放到远程服务器
  • 启动React Native, 检查sdcard是否有jsbundle文件, 如果没有调用setBundleAssetName(该方法存在React Native 启动入口)加载asset目录的jsbundle, 同时启动线程下载远程jsbundle文件到sdcard目录.
  • 待下次启动时, sdcard是有jsbundle文件的, 加载的就是最新的jsbundle文件.

Hot Replacement/请简述 code push 的原理?

code push 调用 react native 的打包命令,将当前环境的非 native 代码全量打包成一个 bundle 文件,然后上传到微软云服务器(Windows Azure)。在 app 中启动页(或 splash 页)编写请求更新的代码(请求包含了本地版本,hashCode、appToken 等信息),微软服务端对比本地 js bundle 版本和微软服务器的版本,如果本地版本低,就下载新的 js bundle 下来后实现更新(code push 框架实现)。

CodePush 可以进行实时的推送代码更新:

  • 直接对用户部署代码更新
  • 管理 Alpha,Beta 和生产环境应用
  • 支持 React Native 和 Cordova
  • 支持JavaScript 文件与图片资源的更新

首屏优化

  • 首屏耗时=react native上下文初始化耗时+首屏视图渲染耗时
  • 根据生命周期,有初始阶段,更新阶段和卸载组件阶段
  • 在初始阶段首先渲染loading视图,获取数据后,通过改变状态(state),触发视图的再次渲染,在屏幕绘制出视图。
  • 可以通过控制台打印的方式或者日志输出的方式去监听时间
  • 加上缓存,RN有一个AsyncStorage类似localStorage,第一次进入的时候加上缓存,再下次进入的时候不进入loading,而是直接获取上一次缓存数据,在做网络请求
  • shouldComponentUpdate中判断缓存数据与网络响应数据的差别

参考:探索react native首屏渲染最佳实践

RN的优缺点

React Native相对于原生的ios和Android有哪些优势?

  • 跳过App Store审核,远程更新代码,提高迭代频率和效率,既有Native的体验,又保留React的开发效率。
  • 性能媲美原生APP
  • 使用JavaScript编码,只要学习这一种语言
  • 绝大部分代码安卓和IOS都能共用
  • 组件式开发,代码重用性很高
  • 跟编写网页一般,修改代码后即可自动刷新,不需要慢慢编译,节省很多编译等待时间
  • 支持APP热更新,更新无需重新安装APP

缺点

内存占用相对较高 对于不熟悉前端开发的人员上手比较慢,不能真正意义上做到跨平台,使用后,对app体积增加。

各个框架比较

React 和 Vue 有许多相似之处

  • 它们都是JavaScript的UI框架
  • 使用 Virtual DOM
  • 提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。
  • 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。

追踪变化/更新DOM/视图渲染/Vue和React的设计思想有什么区别

简述:vue是采用双向绑定去做数据更新,视图渲染。而react是单向的,通过对比状态差异更新。

vue: (可以对着图片想象)
  • 对数据进行劫持监听,当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter
  • 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
  • 实现一个订阅者Watcher,为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
  • 实现一个解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
react:
  • setState()
  • 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程
  • 调和过程后有一个新的UI树,这个UI树需要跟旧的UI树做对比。 采用Diff算法,不同节点类型的比较 ,分两种情况:
    • 节点类型不同: 直接删除旧的节点,再新建新的节点
    • 节点类型相同,属性不同: 对比属性,只改变变化了的节点属性
  • 构建 React 元素树并且着手重新渲染整个UI界面
  • shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
  • 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。
  • 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

数据绑定

  • vue 是 双向绑定
  • react 是 单向的

数据流

  • vue
    • 向父组件发送事件,我们可以调用实例中内置的 $emit 方法
    • 通过 v-on 监听这个事件
    • 父子组件之间的数据通信是通过Prop和自定义事件实现的
    • 使用 events 向父组件发送消息:
    • 非父子组件可以使用订阅/发布模式实现(类似于Angualr中的非父子指令之间的通信)
  • React
    • 父与子之间的数据通信是通过props属性就行传递的
    • 子与父之间的数据通信可以通过父组件定义事件,子组件触发父组件中的事件时,通过实参的形式来改变父组件中的数据来通信
    • 非父子组件之间的通信:React中在处理非父子组件之间的通信时,简单的,嵌套不深的非父子组件(如:兄弟组件)可以仍然使用上一节非父子组件之间通信中的事件函数,传形参的方式来实现。深层的话可以用redux管理

编写方式 Template vs jsx

  • vue是用模板,Vue鼓励你去写近似常规HTML的模板
  • React推荐你所有的模板通用JavaScript的语法扩展——JSX书写

状态管理

  • vue 数据由data属性在Vue对象中进行管理
  • react react 采用state去管理它的状态,state对象在React应用中是不可变的,意味着它不能被直接改变(这也许不一定正确)。在React中你需要使用setState()方法去更新状态

Vue和React的有什么优劣

Vue的优势(性能方面)

  • vue在更新数据时会定位到某个状态如何就去修改它,因为这个监听从一开始就已经被绑定。而react是当一个节点类型发生变化,该节点包含节点下所有子节点均会被删除。 > 在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法。同时你可能会需要使用不可变的数据结构来使得你的组件更容易被优化。然而,使用 PureComponent 和 shouldComponentUpdate 时,需要保证该组件的整个子树的渲染输出都是由该组件的 props 所决定的。如果不符合这个情况,那么此类优化就会导致难以察觉的渲染结果不一致。这使得 React 中的组件优化伴随着相当的心智负担。而Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。你可以理解为每一个组件都已经自动获得了 shouldComponentUpdate,并且没有上述的子树问题限制。
  • 双向绑定:这样开发过程快速找到某个值,实时反应用户输入的场合会非常方便

React的优势

  • 有Facebook背景。像 React Native,React VR这种量级的生态圈是很重要的考量;
  • 社区活跃,大量react周边产物,如蚂蚁金福的ant design;
  • 高度组件化,我可以一层一层地拆 很清晰
  • React 采用虚拟DOM,js操作比DOM操作快多了在大数据的情况下,React的运行效率是最高的 > 基于v-dom的UI框架: 采用virtual dom render + diff进行必要的DOM渲染。这样的好处是我们可以采用一种比重置innerHtml 更高效的方法更新DOM元素。在过往我们写的DOM元素是需要调出DOM然后再对它操作,因此导致页面reflow或repaint,而react是所有元素置于v-dom,即完完全全在JavaScript中曲操作DOM,这样开销就小了很多。

学习成本

vue对新手更友好,react相对来说学习路线更陡;个人觉得,react写起来更优雅。

  • AngularJS适用于CRUD操作的web应用,不适用与游戏、图形编辑器这种DOM操作频繁的web应用。目前功能比较齐全。
  • react可以拓展到服务端、移动端native部分,支持跨平台,比如React Native,React VR
  • vue最轻量、还可以用于业务场景非常轻的页面中(不用全家桶也不错)
  • 状态:MVVM模型的有angular和vue 实行的是数据双向绑定。react是采用单向数据流。
  • 组织:组件化能提高开发维护效率,目前对三个框架,React组件化最好
  • 效率
    • Angular的运行效率是最低的,因为它用到了脏检查,在应用运行的过程中,需要不断地操作 DOM,会造成明显的卡顿。
    • React 采用虚拟DOM,js操作比DOM操作快多了在大数据的情况下,React的运行效率是最高的
    • 组件化会提高效率,具体到每个组件的实现,MVVM代码量少一些
    • 开发效率
    • 运行效率

单向数据绑定和双向数据绑定

  • 单向数据绑定:复杂应用来说这是实施统一的状态管理
  • 双向数据绑定:实时反应用户输入的场合会非常方便

如何选择框架

开发的业务需求,模块需求(组件化这块),开发效率,运行效率,和框架的使用场景.团队成员对框架的熟悉程度

 

 

 

 

 

本文来源于:前端框架 | 面试总结-变化吧门户
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
二叶草
Go语言接口规则 前端框架

Go语言接口规则

Go语言接口规则 接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口对应的全部方法签名。就表示它 "实现" 了该接口,无须在该类型上显式声明实现了哪个接口。对应方法,是指有相同名称、参数...
Go语言中处理 HTTP 服务器 前端框架

Go语言中处理 HTTP 服务器

1 概述 包 net/http 提供了HTTP服务器端和客户端的实现。本文说明关于服务器端的部分。 快速开始: package main import (   "log"   "net/http" )...

发表评论