前端JavaScript高级面试技巧[2]

第5章 虚拟 DOM

  • vdom 是 vue 和 React 的核心
  • vdom 比较独立,使用也比较简单
  • vdom 是 vue 和 React 的核心实现

题目

什么是 vdom,为何要用 vdom?

  • 什么是 vdom
virtual dom , 虚拟 DOM
用 JS 模拟 DOM 结构
DOM 变化的对比,放在 JS 层来做(图灵完备语言)
提高重绘性能 ( DOM 操作是浏览器最耗费性能的操作 )

  • Item 1
  • Item 2
// 用 JS 模拟 DOM 结构
{
  tag: 'ul',
  attrs: {
    id: 'list'
  },
  children: [
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 1']
    },
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 2']
    }
  ]
}
  • 设计一个需求场景
// 1. 将数据展示成一个表格。2. 随便修改一个信息,表格也跟着修改
[
  {
    name: '张三',
    age: '20',
    address: '北京'
  },
  {
    name: '李四',
    age: '21',
    address: '上海'
  },
  {
    name: '王五',
    age: '22',
    address: '广州'
  }
]
  • 用 jQuery 实现
// 渲染函数
function render(data) {
  // 此处省略 N 行
}

// 修改信息
$('#btn-change').click(function () {
  data[1].age = 30;
  data[2].address = '深圳';
  render(data);
});

// 页面加载完成之后,立即执行 render
render(data);
// 渲染函数
function render(data) {
  var $container = $('#container');
  
  // 清空现用内容
  $container.html('');

  // 拼接 table
  var $table = $('');
  $table.append($(''));
  data.forEach(function (item) {
    $table.append($(''));
  });
  
  // 渲染到页面
  $container.append($table);
}
  • 遇到的问题
var div = document.createElement('div');
var item, result = '';
for (item in div) {
  result += ' | ' + item;
}
console.log(result);
DOM 操作是“昂贵”的,js 运行效率高
尽量减少 DOM 操作,而不是“推倒重来”
项目越复杂,影响就越严重
vdom 即可解决这个问题

vdom如何使用,核心 API 有哪些?

  • vdom 是一类技术实现
  • 介绍 snabbdom——一个开源的 v-dom库
    https://github.com/snabbdom/snabbdom
// vdom:用 JS 模拟的 DOM 结构
// vnode:用 JS 模拟的 DOM 节点
// h 函数(参数1=选择器,参数2=事件/样式/属性,参数3=变量或数组) {}
// patch 的两种用法:1.初次渲染。2.再次对比
var container = document.getElementById('container');

var vnode = h('div#container.two.classes', {on: {click: someFn}}, [
  h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),
  ' and this is just normal text',
  h('a', {props: {href: '/foo'}}, 'I\'ll take you places!')
]);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);

var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [
  h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),
  ' and this is still just normal text',
  h('a', {props: {href: '/bar'}}, 'I\'ll take you places!')
]);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
This is bold and this is just normal text I'll take you places!
  • 介绍 snabbdom - h 函数
// snabbdom 简化版
var node = h('ul#list', {}, [
  h('li.item', {}, 'Item 1'),
  h('li.item', {}, 'Item 2')
]);

// 用 JS 模拟出来的 DOM 结构
{
  tag: 'ul',
  attrs: {
    id: 'list'
  },
  children: [
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 1']
    },
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 2']
    }
  ]
}
  • 介绍 snabbdom - patch 函数
// snabbdom 简化版
var node = h('ul#list', {}, [
  h('li.item', {}, 'Item 1'),
  h('li.item', {}, 'Item 2')
]);

var container = document.getElementById('container');
patch(container, vnode);

// 模拟改变
var btnChange = document.getElementById('btn-change');
btnChange.addEventListener('click', function () {
  var newVnode = h('ul#list', {}, [
    h('li.item', {}, 'Item 111'),
    h('li.item', {}, 'Item 222'),
    h('li.item', {}, 'Item 333')
  ]);
  patch(vnode, newVnode);
});
  • 完整使用 snabbdom



  
  
  v-dom


  
  • 重做之前的 demo



  
  
  v-dom


  
    var vnode;
    // 定义渲染函数
    function render(data) {
      var newVnode = h('table', {}, data.map(function (item) {
        var tds = [];
        var i;
        for (i in item) {
          if (item.hasOwnProperty(i)) {
            tds.push(h('td', {}, item[i] + ''));
          }
        }
        return h('tr', {}, tds);
      }));
      
      if (vnode) {
        patch(vnode, newVnode);
      } else {
        patch(container, newVnode);
      }
    
      vnode = newVnode;
    }
  • 核心 API
 h(‘<标签名>’, {…属性…}, […子元素…]);
 h(‘<标签名>’, {…属性…}, ‘….’);
patch(container, vnode);
patch(vnode, newVnode);

了解 diff 算法吗?

  • 什么是 diff 算法
// Linux 里古老的 diff 命令,可以比较两个文本的不同
>diff log1.txt log2.txt
// git diff 版本比较
>git diff ./src/index.js
// 在线 diff 对比器
https://tool.oschina.net/diff/
  • 去繁就简
diff 算法非常复杂,实现难度很大,源码量很大
去繁就简,讲明白核心流程,不关心细节
面试官也大部分都不清楚细节,但是很关心核心流程
去繁就简之后,依然具有很大挑战性,并不简单
  • vdom 为何用 diff 算法
 DOM 操作是“昂贵”的,因此尽量减少 DOM 操作
找出本次 DOM 必须更新的节点来更新,其他的不更新
这个“找出”的过程,就需要 diff 算法
  • diff 算法的实现流程
1.patch(container, vnode);
2.patch(vnode, newVnode);
  1. 虚拟 DOM 如何变成的真实 DOM

  • Item 1
// 用 JS 模拟 DOM 结构
{
  tag: 'ul',
  attrs: {
    id: 'list'
  },
  children: [
    {
      tag: 'li',
      attrs: {className: 'item'},
      children: ['Item 1']
    }
  ]
}
// vnode 参数就是类似上方的结构
function createElement(vnode) {
  var tag = vnode.tag;
  var attrs = vnode.attrs || {};
  var children = vnode.children || [];
  if (!tag) {
    return null;
  }
  
  var elem = document.createElement(tag);

  var attrName;
  for (attrName in attrs) {
    if (attrs.hasOwnProperty(attrName)) {
      elem.setAttribute(attrName, attrs[attrName]);
    }
  }
  
  children.forEach(function (childVnode) {
    // 递归调用 createElement 创建子元素
    elem.appendChild(createElement(childVnode));
  });
  return elem;
}
虚拟 DOM 和真实 DOM 有对应关系
  1. 新的虚拟 DOM 和旧的 DOM 比较
function updateChildren(vnode, newVnode) {
  var children = vnode.children || [];
  var newChildren = newVnode.children || [];
  
  children.forEach(function (child, index) {
    var newChild = newChildren[index];
    if (newChild == null) {
      return
    }
    if (child.tag === newChild.tag) {
      updateChildren(child, newChild);
    } else {
      replaceNode(child, newChild);
    }
  });
}

解答

  • 知道什么是 diff 算法,是 linux 的基础命令
  • vdom 中应用 diff 算法是为了找出需要更新的节点
  • 实现,patch(container, vnode) 和 patch(vnode, newVnode)
  • 核心逻辑,createElement 和 updateChildren

第6章 MVVM

题目

之前使用 jquery 和现在使用 Vue 或 React 框架的区别?

  • jQuery 实现 todo-list
    • vue 实现 todo-list
    • {{ item }}

    jQuery 和框架的区别

    数据和视图分离 - 解耦
    以数据驱动视图 - 封装DOM 操作
    

    你如何理解MVVM?- 联系 View 和 Model

    View 可以通过 事件绑定 的方式影响 Model
    Model 可以通过 数据绑定 的方式影响 View
    

    第7章 组件化和 React

    • 是否做过 React 开发?
    • React 以及组件化的一些核心概念
    • 实现流程
    //>todo
    //>——index.js
    import React, { Component } from 'react'
    import Input from './input/index.js'
    import List from './list/index.js'
    
    // class Component {
    //     constructor(props) {
    
    //     }
    //     renderComponent() {
    //         const prevVnode = this._vnode
    //         const newVnode = this.render()
    //         patch(prevVnode, newVnode)
    //         this._vnode = newVnode
    //     }
    // }
    
    class Todo extends Component {
        constructor(props) {
            super(props)
            this.state = {
                list: ['a', 'b']
            }
        }
        render() {
            return (
                
    ) /* React.createElement( "div", null, React.createElement(Input, { addTitle: this.addTitle.bind(this) }), React.createElement(List, { data: this.state.list }) ); */ // React.createElement(List, { data: this.state.list }) // var list = new List({ data: this.state.list }) // var vnode = list.render() } addTitle(title) { const currentList = this.state.list this.setState({ list: currentList.concat(title) } // , () => { // // console.log(this.state.list) // this.renderComponent() // } ) } } export default Todo
    //>todo
    //>——list
    //>————index.js
    import React, { Component } from 'react'
    
    class List extends Component {
        constructor(props) {
            super(props)
        }
        render() {
            const list = this.props.data
            return (
                
      { list.map((item, index) => { return
    • {item}
    • }) }
    ) /* React.createElement( "ul", null, list.map((item, index) => { return React.createElement( "li", { key: index }, item ); }) ); */ } } export default List
    //>todo
    //>——input
    //>————index.js
    import React, { Component } from 'react'
    
    class Input extends Component {
        constructor(props) {
            super(props)
            this.state = {
                title: ''
            }
        }
        render() {
            return (
                
    ) } changeHandle(event) { this.setState({ title: event.target.value }) } clickHandle() { const title = this.state.title const addTitle = this.props.addTitle addTitle(title) // 重点!!! this.setState({ title: '' }) } } export default Input

    对组件化的理解?

    • 组件的封装
    视图
    数据
    变化逻辑(数据驱动视图变化)
    
    • 组件的复用
    props 传递
    复用
    

    JSX 是什么?

    • JSX 语法
    html 形式
    引入 JS 变量和表达式
    if…else…
    循环
    style 和 className
    事件
    JSX 语法根本无法被浏览器所解析
    那么它如何在浏览器运行?
    
    • JSX 解析成 JS
    • 独立的标准
    JSX 是 React 引入的,但不是 React 独有的
    React 已经将它作为一个独立标准开放,其他项目也可用
    React.createElement 是可以自定义修改的
    说明:本身功能已经完备;和其他标准监控和扩展性没问题
    另:有机会录制《1000行代码实现React》,就用 JSX 标准
    

    JSX 和 vdom 什么关系?

    简述 React 的 setState?

    简述自己如何比较 React 和 Vue?

    你可能感兴趣的:(前端JavaScript高级面试技巧[2])

    nameageaddress
    ' + item.name + '' + item.age + '' + item.address + '