前端面试题整理

html + css基础

1、CSS 盒子模型,绝对定位和相对定位:

内容(content)、填充(padding)、边界(margin)、 边框(border).

position: absolute;绝对定位:绝对定位是相对于元素最近的已定位的祖先元素(即是设置了绝对定位或者相对定位的祖先元素)。如果元素没有已定位的祖先元素,那么它的位置则是相对于最初的包含块(body)

绝对定位与文档流无关,所以它们可以覆盖页面上其他的元素,可以通过z-index属性来控制这些层的对方顺序。

position: relative;相对定位:相对定位是相对于元素在文档中的初始位置

注意,在使用相对定位时,无论是否进行移动,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。

 

2、清除浮动,什么时候需要清除浮动,清除浮动都有哪些方法

背景不能显示;边框不能撑开;margin设置值不能正确显示

方法一:添加新的元素,应用 clear:both;

<div class="outer">
<div class="div1">1</div>
<div class="div2">2</div>
<div class="div3">3</div>
<div class="clear"></div>
</div>

.outer{ border:1px solid #ccc; background:#fc9; color:#fff; margin:50px auto; padding:50px;}
.div1{ width:80px; height:80px; background:#f00; float:left; }
.div2{ width:80px; height:80px; background:blue; float:left; }
.div3{ width:80px; height:80px; background:sienna; float:left; }
.clear{ clear:both; height:0; line-height:0; font-size:0; }

方法二:父级div定义 overflow:auto;

<div class="outer over-flow">
<div class="div1">1</div>
<div class="div2">2</div>
<div class="div3">3</div>
</div>

.outer{ border:1px solid #ccc; background:#fc9; color:#fff; margin:50px auto; padding:50px;}
.div1{ width:80px; height:80px; background:#f00; float:left; }
.div2{ width:80px; height:80px; background:blue; float:left; }
.div3{ width:80px; height:80px; background:sienna; float:left; }
.over-flow{ overflow:auto; zoom:1;  } //zoom:1;是在处理兼容性问题

方法三:据说最高大上的方法,:after方法,作用于浮动元素的父亲

.outer:after { clear:both;content:'';display:block;width:0;height:0;visibility:hidden; }

3. 如何保持浮层水平垂直居中

(1)利用绝对定位和transform

.children{
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%,-50%);
    background: black;
}

(2) 利用flexbox

.parent{
 justify-content:center;
 align-items:center;
 display: -webkit-flex;
}

(3)已知元素高度,手动计算中间的位置距离

(4) 利用定位与margin: auto;

4. position 和 display 的取值和各自的意思和用法

1、position属性取值:static(默认)、relative、absolute、fixed(定位原点相对于浏览器窗口)、inherit。

2、display属性取值:none(隐藏,不占据空间)、inline、inline-block(行高可变)、block、flex、inherit

5. 样式的层级关系,选择器优先级,样式冲突,以及抽离样式模块怎么写

一个是权重,另一个是共用样式和私用样式

(!important>)id选择器>class选择器(属性选择器/伪类选择器)>标签选择器(伪元素选择器) 同类选择符条件下层级越多的优先级越高。优先级就近原则,同权重情况下样式定义最近者为准

选择器一样的情况下后面的会覆盖前面的属性

 因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。当然,初始化样式会对SEO有一定的影响,但鱼和熊掌不可兼得,但力求影响最小的情况下初始化。

6、CSS3中新增伪类举例

:enabled控制表单控件的可用状态。
:disabled 控制表单控件的禁用状态。
:checked 单选框或复选框被选中。

7、px和em和rem的区别,CSS中link 和@import的区别

像素px是相对于显示器屏幕分辨率而言;em相对于当前对象内文本的字体尺寸;rem相对的是HTML根元素

link属于XHTML标签,而@import完全是css提供的一种方式;link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再加载;用JavaScript控制dom去改变样式的时候,只能使用link标签

8、flex

它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)

JavaScript 基础

1、JavaScript 里有哪些数据类型,解释清楚 null 和 undefined,解释清楚原始数据类型和引用数据类型。比如讲一下 1 和 Number(1)的区别

在es6中大致可以认为有7中数据类型:null 、undefined、string、number、boolean、Symbol、object

null用来表示尚未存在的对象;undefined表示"缺少值",就是此处应该有一个值,但是还没有定义

undefiend\null\boolean\string\number为原始类型;object(包括function、array、Date...)为引用类型

1 是一个原始定义好的的number类型,Number(1)是一个函数类型

2、prototype 是什么东西,原型链的理解,什么时候用 prototype

prototype是函数对象上面预设的对象属性

    function foo(){};
    foo.prototype.z = 3;
    var obj = new foo();
    obj.z // 3

    通过new构造对象(实例)的特点是,obj的原型(prototype)指向了构造器的prototype属性,也就是foo.prototype,而foo.prototype则指向了原始的Object.prototype,Object.prototype也有原型,为null。这就是一整个原型链。

3、JS 怎么实现一个类。怎么实例化这个类

JavaScript使用函数来定义类

创建对象(即类的实例化)使用 new 关键字

比如,上面的foo就可以理解为一个类,obj可以理解为类的一个实例化(对象)

4、理解闭包吗?请讲一讲闭包在实际开发中的作用;闭包建议频繁使用吗?

闭包简单来说就是函数中的函数,也可以把它理解为一种现象,就是说一个函数要访问另外一个目标函数内部的变量,就要在目标函数中再定义一个函数(以此来把作用域链往下延长一段,目的就是为了利用js在找自由变量时,会沿着作用域链一级一级往上找的特点),并将这个定义的函数return出来,供外部使用。在实际开发中,闭包主要是用来封装变量,收敛权限

好处数据隐藏和封装,但是在处理速度和内存消耗方面对脚本性能具有负面影响
 

5、apply()与call()的区别

每个函数都包含两个非继承而来的方法: appy()和call()。这两个方法的用途都是在特定的作用域中调用函数,然后可以设置调用函数的this指向。

call()的使用和apply()基本是一样的,不同的是参数的传递,call()必须明确的传入每一个参数。

6、Javascript类与继承

第一种借助构造函数实现继承;

function Parent1 () {
    this.name = 'parent1';
}
function Child1 () {
    Parent1.call(this); //这里的call用apply也可以
    this.type = 'child1';
}
console.log(new Child1());

第二种是借助原型链实现继承;

function Parent2 () {
    this.name = 'parent2';
    this.play = [1, 2, 3];
}

function Child2 () {
    this.type = 'child2';
}

Child2.prototype = new Parent2(); //通过把Child2的原型指向Parent2来实现继承

第三种 组合方式

function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
}

function Child3 () {
    Parent3.call(this);  //子类里执行父类构造函数
    this.type = 'child3';
}

Child3.prototype = new Parent3(); //子类的原型指向父类

//以下是测试代码
var s3 = new Child3();
var s4 = new Child3();

s3.play.push(4);

console.log(s3.play, s4.play);

7、let、const、var

let:块级作用域;不存在变量声明提前;不能重复定义

const:声明的常量地址是不允许改变

var: js用来声明变量的方法

8、promise

链式操作:

function runAsync(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成');
            resolve('随便什么数据');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

9、async/await

function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Function 2')
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log('Function 3')
}

async function asyncFunArr () {
  fn1()
  await fn2()
  fn3()
}

asyncFunArr()

// output =>
// Function 1
// Function 2
// Function 3

10、写一个方法遍历所有文档树所有节点

非递归:

function traversalUsingNodeIterator(node){
  var iterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT,null,false);
  var node = iterator.nextNode();
  while(node != null){
    console.log(node.tagName);
    node = iterator.nextNode();
  }
}

递归:

function traversal(node){
   //对node的处理
   if(node && node.nodeType === 1){
     console.log(node.tagName);
   }
   var i = 0, childNodes = node.childNodes,item;
   for(; i < childNodes.length ; i++){
     item = childNodes[i];
     if(item.nodeType === 1){
       //递归先序遍历子节点
       traversal(item);
     }
   }
 }

 

 

11、cookie和webstorege

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递

cookie数据还有路径(path)的概念,可以限制。cookie只属于某个路径下、

cookie数据不能超过4K,webStorage可以达到5M或更大

数据的有效期不同

                        sessionStorage:仅在当前的浏览器窗口关闭有效;

                        localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;

                        cookie:只在设置的cookie过期时间之前一直有效,即使窗口和浏览器关闭

作用域不同

                        sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;

                        localStorage:在所有同源窗口都是共享的;

                        cookie:也是在所有同源窗口中共享的

webStorage的API借口使用更方便

webstorege所有对象会转成字符串

12、jsonp和跨域

协议、域名、端口相同则视为同源,域名也可换成IP地址,不同源的页面脚本不能获取对方的数据

在html元素中拥有src属性的元素是可以跨域访问资源,页面中的<script><img><iframe>不受到同源策略限制

JSONP(JSON with padding)的理念就是,与服务器约定好一个回调函数名,服务端接收到请求之后,返回一段Javascript,这段Javascript中调用了约定好的回调函数,并且将数据作为参数进行传递,页面接收到这段Javascript后就会执行这段回调函数,此时数据已传到客户端

JSONP缺点:只支持GET请求,不支持POST或其他的HTTP请求

13、sort排序相关

数组会按照字符的Unicode进行排序(把数组里面当成字符串处理)

sort()方法是使用的冒泡和插入两种方式结合进行排序的

14、数组和对象的深浅拷贝

数组深拷贝:

    遍历复制

    slice(0)巧办法进行修改,返回的是一个副本

    concat()

对象深拷贝:

    JSON.parse(JSON.stringify(a))

    递归调用浅拷贝:

var deepCopy = function(obj) {
  // 只拷贝对象
  if (typeof obj !== 'object') return;
  // 根据obj的类型判断是新建一个数组还是一个对象
  var newObj = obj instanceof Array ? [] : {};
  for (var key in obj) {
    // 遍历obj,并且判断是obj的属性才拷贝
    if (obj.hasOwnProperty(key)) {
      // 判断属性值的类型,如果是对象递归调用深拷贝
      newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
    }
  }
  return newObj;
}

15、String + Array的一些基本操作

string:

substring(start开始位置的索引,end结束位置索引) 截取字符串,截取的位置不包含结束位置的字符,只写start表示从开始位置截取到最后,end>start时,会默认以小的参数为开始位置的索引

slice(start开始位置索引,end结束位置索引)截取字符串,当end>length时,返回空字符串""

substr(start开始位置索引,end需要返回的字符个数)截取字符串,只传入start时,默认从start开始截取到最后一个

charAt(index),返回指定index处的字符,但当index为负数或者大于字符串长度时,返回空字符串""

indexOf(string)该方法返回字符串内第一次出现子字符串的位置。如果没有找到子字符串,则返回-1。

lastIndexOf(string) 倒叙查找

split(separator分隔符,howmany分割返回数量)用于方法用于把一个字符串分割成字符串数组

replace(regexp,replacement)用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串

search(regexp)用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。返回第一个与 regexp 相匹配的子串的起始位置

match(regexp/str) 该方法用于在字符串内检索指定的值,或找到一个或多个正则表达式的匹配,并返回一个存储了该值的数组

toLowerCase/toUpperCase

concat() 该方法用于连接两个或多个字符串

array:

push() 向数组的末尾添加一个或更多元素,并返回新的长度

unshift() 向数组的开头添加一个或更多元素,并返回新的长度

shift() 删除并返回数组的第一个元素

pop() 删除并返回数组的最后一个元素

join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔

toString() 该方法可把数组转换为字符串,返回值与没有参数的 join() 方法返回的字符串相同

concat() 该方法可以连接两个或更多的数组,并返回结果

slice(start,end) 该方法可从已有的数组中返回选定的元素。(返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。),不传参返回整个数组,传入负值(等价于负值+数组长度的位置开始)

splice() 参数1 添加/删除 的起始位置,参数2 要删除的项目数量。如果设置为 0,则不会删除项目,参数3 向数组添加的新项目。(可以添加多个)

reverse() 该方法用于颠倒数组中元素的顺序。并返回颠倒顺序后的数组

sort() 该方法用于对数组的元素进行排序,会直接对Array进行修改,它返回的结果仍是当前Array

reduce() 把结果继续和序列的下一个元素做累积计算,[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

map(fuc) 数组中的每一个值都进行fuc,返回对应的新数组,

filter() 和map()不同的是,传入的函数依次作用于每个元素后,根据返回值是true还是false决定保留还是丢弃该元素

16、数组去重的方法

function unique(arr) {
    const res = new Map();
    return arr.filter((a) => !res.has(a) && res.set(a, 1))

    // 或者
    return Array.from(new Set(arr))
}

17、冒泡和捕获 

事件流描述的是从页面中接受事件的顺序

冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点

捕获:不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件

16、事件委托和事件绑定

不需要为每一个元素都添加监听事件而是通过委托给父元素来处理

可以方便动态添加元素。不需要再为新添加的元素重新绑定事件

17、箭头函数的this指向问题

普通函数中的this表示调用此函数时的对象

 箭头函数中会往上寻找this,直到找到所代表的this为止

18、变量提升和函数提升

在ES6之前,JavaScript没有块级作用域(一对花括号{}即为一个块级作用域),只有全局作用域和函数作用域。变量提升即将变量声明提升到它所在作用域的最开始的部分

console.log(global); // undefined
var global = 'global';
console.log(global); // global

js中创建函数有两种方式:函数声明式和函数字面量式。只有函数声明才存在函数提升!

console.log(f1); // function f1() {}   
console.log(f2); // undefined  
function f1() {}
var f2 = function() {}

19、高阶函数

JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

function add(x, y, f) {
    return f(x) + f(y);
}
add(-5, 6, Math.abs);

map,reduce,filter,sort都是高阶函数

20、setTimeout、setInterval和requestAnimationFrame

setTimeout()(设置某个时间后执行某个动作,表示延时执行某个动作)
setInterval()(设置每隔多久执行一次某个动作,它是循环的,如果想重复执行使用该方法)
requestAnimationFrame()(不需要设置时间间隔)

三个方法在做动画时会经常用到

21、模块化开发

通行的js模块规范主要有两种:CommonJSAMD

ES6:

export:它是用来定义模块的,可以导出对象、函数、类、字符串等等:

export和export default的区别就是:前者导出的东西需要在导入的时候加{},而后者则不需要

import导入的也可以通过as改名

动态引入:其实就是基于promise语法,根据promise机制来实现动态引入。有了这种机制就可以实现按需加载

// 1.html
<script type="module">
    import('./1.js').then(res =>{
        console.log(res.a); // 1
    });
</script>

22、引起内存泄漏的原因

(1)、创建意外的全局变量;

function leaks(){
        a = 3;
 }

(2)、element元素被闭包引用,因此无法减少对element的引用数

function assignHandler(){
        let element = document.getElementById("someElement");
        element.onclick = function () {
            alert(element.id);
        };
    }

解决:

function assignHandler(){
        let element = document.getElementById("someElement");
        let id = element.id;
        element.onclick = function () {
            alert(id);
        };
        element = null;
    }

(3)、循环引用 在引用计数策略下会导致内存泄漏,标记清除不会

function fn() {
 var a = {};
 var b = {};
 a.pro = b;
 b.pro = a;
} 
fn();

在引用计数策略下,a和b的引用次数不为0,不会被垃圾回收器回收内存。如果fn函数被大量调用,就会造成内存泄漏

(4)、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露

<div id="myDiv">
    <input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
        btn.onclick = null;// 按钮存在事件未处理
        document.getElementById("myDiv").innerHTML = "Processing...";
    }
</script>

23、Map和Set

map:初始化map并通过key来取value

var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95

set:存储的是一组不重复的key.

var s=new Set([1,2,3,4,4]);  //Set(4) {1, 2, 3, 4}

框架基础

1、vue数据绑定原理

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的

var vm = new Vue({
    data: {
        obj: {
            a: 1
        }
    },
    created: function () {
        console.log(this.obj);
    }
});

每个数据属性有两个相对应的get和set方法,因为vue是通过Object.defineProperty()来实现数据劫持的(Object.defineProperty( )可以来控制一个对象属性的一些特有操作,比如读写权、是否可以枚举)

了解详细

2、vue父子组件和兄弟组件的通信问题

父组件到子组件通信:使用props

子组件到父组件的通信:可以在子组件中使用$emit触发事件的值传出去,父组件通过事件监听,获取数据

    可以由子组件内部主动传数据给父组件,由父组件监听接收(在子组件中定义一个click点击事件来触发自定义事件$emit,然后       在父组件监听)

    也可以通过父组件决定子组件什么时候传值给父组件,然后再监听接收(是在父组件中定义一个click点击事件,在组件中通过     refs获取实例方法来直接触发事件,然后在父组件中监听)

兄弟组件之间的通信:

    创建一个新Vue的实例,让各个兄弟共用同一个事件机制

     传递数据方,通过事件触发$emit(方法名,传递的数据)

     接收数据方,在mounted()钩子函数(挂载实例)中 触发事件$on(方法名,callback(接收数据的数据)),此时callback函数中的          this已经发生了改变,可以使用箭头函数

//建立一个空的Vue实例,将通信事件挂载在该实例上
//emptyVue.js
import Vue from 'vue'
export default new Vue()
//兄弟组件a:childa.vue
<template>
  <div>
    <span>A组件->{{msg}}</span>
    <input type="button" value="把a组件数据传给b" @click ="send">
  </div>
</template>
<script>
import vmson from "./emptyVue"
export default {
  data(){
    return {
      msg:"我是a组件的数据"
    }
  },
  methods:{
    send:function(){
      vmson.$emit("aevent",this.msg)
    }
  }
}
</script>
//兄弟组件b:childb.vue
<template>
   <div>
    <span>b组件,a传的的数据为->{{msg}}</span>
  </div>
</template>
<script>
import vmson from "./emptyVue"
export default {
 data(){
    return {
      msg:""
    }
  },
  mounted(){
    vmson.$on("aevent",(val)=>{//监听事件aevent,回调函数要使用箭头函数;
      console.log(val);//打印结果:我是a组件的数据
      this.msg = val;
    })
  }
}
</script>
//父组件:parent.vue
<template>
  <div>
    <childa><childa>
    <childb></childb>
  </div>
</template>
<script>
import childa from "./childa";
import childb from "./childb";
export default {
  data(){
    return{
      msg:""
    }
  },
  components:{
    childa,
    childb
  },
}
</script>

3、vuex的原理

  • store是怎么注册的?

vuex在vue 的生命周期中的初始化钩子前插入一段 Vuex 初始化代码。给 Vue 的实例注入一个 $store 的属性,我们在 Vue 的组件中可以通过 this.$store.xxx 访问到 Vuex 的各种数据和状态

  • mutation,commit 是怎么实现的?

registerMutation 是对 store 的 mutation 的初始化,它接受 4 个参数,store为当前 Store 实例,type为 mutation 的 key,handler 为 mutation 执行的回调函数,path 为当前模块的路径。mutation 的作用就是同步修改当前模块的 state ,函数首先通过 type 拿到对应的 mutation 对象数组, 然后把一个 mutation 的包装函数 push 到这个数组中,这个函数接收一个参数 payload,这个就是我们在定义 mutation 的时候接收的额外参数。这个函数执行的时候会调用 mutation 的回调函数,并通过 getNestedState(store.state, path) 方法得到当前模块的 state,和 playload 一起作为回调函数的参数

commit 支持 3 个参数,type 表示 mutation 的类型,payload 表示额外的参数,根据 type 去查找对应的 mutation,如果找不到,则输出一条错误信息,否则遍历这个 type 对应的 mutation 对象数组,执行 handler(payload) 方法,这个方法就是之前定义的 wrappedMutationHandler(handler),执行它就相当于执行了 registerMutation 注册的回调函数

  • 辅助函数是怎么实现的?

mapState在调用了 normalizeMap 函数后,把传入的 states 转换成由 {key, val} 对象构成的数组,接着调用 forEach 方法遍历这个数组,构造一个新的对象,这个新对象每个元素都返回一个新的函数 mappedState,函数对 val 的类型判断,如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的 state 和 getters 作为参数,返回值作为 mappedState 的返回值;否则直接把 this.$store.state[val] 作为 mappedState 的返回值

4、vuex的action和mutation的异步操作和同步操作问题

 

 

5、vue的事件监听

可以使用 v-on 指令来绑定并监听 DOM 事件。绑定的内容可以是一个当前实例上的方法 (后面无需跟括号) 或一个内联表达式。如果提供的是一个方法,则原生的 DOM event 会被作为第一个参数传入,同时这个 event 会带有 targetVM属性,指向触发该事件的相应的 ViewModel

6、vue-router获取自定义参数

设置好路由配置

router.map({
  '/history/:deviceId/:dataId': {
    name: 'history', // give the route a name
    component: { ... }
  }
})

在v-link中传递参数

    <a v-link="{ name: 'history', params: { deviceId: deviceId, dataId: dataId }}">history</a>

7、vue-router的go相关

router.go(n) 
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

router.push(location) 
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

router.replace(location) 
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。


8、vue组件设计相关

页面上把每个独立可以交互的区域视为一个组件

每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护

页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面

 

9、模块化的工具

vue-cli

10、webpack打包优化

查看项目打包
webpack外部扩展
总结需要抽离的公共依赖
使用CDN引入资源
配置webpack.conf.js

11、mvvm的好处

(1)、数据一致性,因为MVVM采用ViewMode进行数据的自动更新,数据变化时无需进行各种传递,并且可以做到各个Fragment之间数据共享
(2)、解耦性,将View和Mode解耦,View层真正只负责UI相关的,Mode负责数据相关的,不会造成庞大的控制逻辑,特别逻辑多了会出现超级Activity
(3)、利于单元测试,因为View层和Mode层是解耦的,所以可以单独对View和Mode逻辑进行单元测试

12、lodash相关

Lodash是一套工具库,它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数,其中部分是目前ECMAScript尚未制订的规范,但同时被业界所认可的辅助函数

13、vue、angular7和react的对比

1 vue

优点: 入门非常容易,资料丰富,框架功能完善,加入非常多的特性,例如,if, for, async,为开发者节省很多垃圾代码。模板支持html和jsx,支持自定义指令,方便操作dom的一致行为。

缺点: 插件引入不够规范,代码提示变弱,不利于维护。开发规范不清晰,项目庞大之后组织结构混乱,不利于新人接手旧的项目。

 

 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

·         可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

·         独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。

·         可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

2 react

优点: 框架功能强大,非常灵活,组建非常规范,jsx功能强大,生态不错。文档也不错。

缺点:无数个 回调,无数个选择表达式,this绑定,没有if没有for指令,更没有父子状态同步,需要自己写回调同步状态,这简直就是垃圾中的战斗机。因为太灵活,写代码也是毫无逻辑,乱写一通,项目混乱堪比vue更甚。维护起来,让人直接放弃。

3 angular7

优点: 组织结构清晰,组建结构规范,官方支持各种技术,不用去找。依赖注入强大的无解,保证代码运行的可控性。对ts深度集成,更是如虎添翼。框架搭建完毕,之后组建拼接,非常高效。

缺点: 入门太难,有些技术不行的人真的很难。

 

 

 

 

总结: 如果你最擅长react,不用再考虑什么,就用react,如果你最擅长vue,也不用考虑,就选vue吧,不要被框架束缚。好与不好都由使用者决定。至于,angular,如果你的项目可能要好几年的迭代,那不用犹豫了,就选angular,这是你入坑的唯一理由,因为需要维护,不想加班熬夜的修bug,就用anglar。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值