Vue2知识点(包含js、html、css)

JS

数据双向绑定原理(响应式)

vue采用了发布-订阅模式。
内部结合了Object.defineProperty这个ES5的新特性(ie8浏览器不支持)

Object.defineProperty(obj, prop, descriptor) 方法会直接给对象定义一个新属性或者修改现有属性,并返回此对象。

处理过程:

  1. 对vue传入的数据,通过数据监听器Observer进行数据监听
  2. 为其动态添加getset方法。当数据变化的时候,就会触发对应的set方法
  3. 随后触发watcher
  4. 接着进行虚拟dom对比
  5. 执行render,视图进行更新。

缺点:

  1. 需要对data对象的每一个属性进行遍历劫持,性能消耗大
  2. 由于 Vue 会在初始化实例时对 property 执行getter/setter 转化,所以property 必须在最开始时在 data 对象上存在才能让 Vue 将它转换为响应式的。
  3. vue不能检测到利用索引直接设置一个数组项, 例如:vm.items[indexOfItem] = newValue
    也不能检测到修改数组的长度,例如:vm.items.length = newLength

哪些数组的方法是响应式的:

  • push pop shift unshift splice sort resourse 是响应式的,还有$set() 拦截了数组的原型,重写了数组的原型
  • 通过下标索引的方法,也可通过去实现响应式
    this.letters.splice(0, 1, 'bbbbbb')
    this.$set(this.letters, 0, 'bbbbbb') // set(要修改的对象, 索引值, 修改后的值)

补:

  • watcher是用来订阅数据的变化,并执行相应操作,从而更新视图
  • render函数是把模板解析成Vnode(虚拟DOM)节点参数:obj:要定义属性的对象。 prop:要定义或修改的属性的名称,descriptor:要定义或修改的属性描述符
  • get: 负责获取值,它不带任何参数, set: 负责设置值,在它的函数体中,return是无效的.
  • 实现数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动将最新值通知订阅者
key的作用

1、主要是用在vue的虚拟Dom算法,新旧虚拟dom对比时作为唯一标识ID
2、vue为了高效的渲染元素,会恢复已有的元素(key值没有改变)而不是从头开始进行渲染,同理,改变某一个元素的key值会使该元素重新被渲染。
来源:尚硅谷bibililivue视频
图片来源:尚硅谷bibililivue视频截图

当页面的数据发生变化时,Diff算法只会比较同一层级的节点:

  • 如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
  • 如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。

补:
元素类型nodetype

  • 元素document.getElementById('div')
  • 属性document.getElementById('div').getAttributeNode('name')
  • 文本document.getElementByTagName('td')[0].firstChild
  • 注释
  • 文本
  • 另一个版本(尚硅谷)
    1。虚拟DOM中key的作用:

      key是虚拟DOM对象的标识,当数据发生变化,Vue会根据[新数据]生成[新的虚拟DOM]随后Vue进行[新虚拟DOM]与[旧虚拟DOM] 的差异比较
    

    2.对比规则:

      (1).旧虚拟DOM中找到了与与新虚拟DOM相同的key:
      a.若虚拟DOM中内容没变,直接使用之前的真实DOM
      b.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
      (2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
    

    3。用index作为key可能会引发的问题:

      1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==>界面效果没问题,但效率低。
      2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
    

    4。开发中如何选择key?

      1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
      2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示.
      使用index作为key是没有问题的。
    
diff算法
  1. 首先比较两个虚拟dom是否在同层级
    • 是否在同一层
    • 是否有相同的父级
  2. diff是采用先序、优先深度遍历的方式进行节点比较的,即,当比较某个节点时,如果该节点存在子节点,那么会优先比较他的子节点,直到所有子节点全部比较完成,才会开始去比较改节点的下一个同层级节点。
    在这里插入图片描述
    遍历的顺序为:1、2、3、4、5、6、7、8、9

diff算法的优化策略:四种命中查找,四个指针

  1. 旧前与新前(先比开头,后插入和删除节点的这种情况)
  2. 旧后与新后(比结尾,前插入或删除的情况)
  3. 旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
  4. 旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)

数据类型

数据类型&类型检测
  • 基本数据类型:number string undefined null? boolean typeof()
  • 引用(Object)数据类型:
function、Array、Date  
Object.prototype.toString.call()  &&  hasOwnPropertype()  // 检测某个对象是否含有指定的自身属性

symbol(ES6新增):类似一种构造函数。
特点:
1.唯一性:两个symbol有相同值但不相等
2.隐藏性:for…in无法遍历

  • typeof:对于null及数组、对象,typeof均检测出为object
  • [1,2] instanceof Array:可以左边放你要判断的内容,右边放类型来进行JS类型判断,只能用来判断复杂数据类型 instanceof 是用于检测构造函数(右边)的prototype属性是否出现在某个实例对象(左边)的原型链上。
  • Object.prototype.toString.call(1):toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,基本上所有对象的类型都可以通过这个方法获取到。

引:

  • undefined和null的区别

      null --------定义但为空
      undefined ---应该有值,缺少值,但未定义
    
    • undefined:( 4 ,变量与对象没有赋值,函数入参出参)
      1. 变量被声明了,但没有赋值时,就等于undefined。
      2. 对象中没有赋值的属性,该属性的值为undefined。
      3. 调用函数时,应该提供的参数没有提供,该参数等于undefined。
      4. 函数没有返回值时,默认返回undefined。
    • null:(2)
      1. 作为函数的参数,表示该参数不是对象。
      2. 作为对象原型链的终点
        当用Object.prototype._proto_取原型时,当不存在时需要用一个值去表示,undefined为不存在,也相当于原型就是undefined。所以用null来表示比较合适。
        Object.getPrototypeOf(XXX)返回对象的原型

引:

  • 数据类型转化
    • 隐式转换
      • 隐式类型转换:== != - * / % *= /= -= %= 先转数据类型再进行比较
      • 数字与字符串进行运算
        • 加法运算:结果会转换为字符串。
        • 除加法以外的运算转换为数字。
    • 强制转换:Number() parseInt() parseFloat()
      1. Number:
        如果是undefined,返回NaN
        如果是Boolean,分别转换为0,1
        如果是null,返回0
      2. ParseInt&ParseFloat
        从第一个字符开始解析每一个字符,一直解析到字符串的末尾,或者遇到一个无效的浮点数字字符为止。——字符串的第一个小数点有效,第二个小数点无效
栈和堆:
  • 栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型:String,Number,Boolean,Null,Undefined,BigInt,简单的数据段,占据固定大小的空间
  • 堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,(Function,Array,Object),指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。

区别:

  • 栈:所有在方法中定义的变量都是放在栈内存中,随着方法的执行结束,这个方法的内存栈也自然销毁。
    • 优点:存取速度比堆快,仅次于直接位于CPU中的寄存器,数据可以共享;
    • 缺点:存在栈中的数据大小与生存期是确定的,缺乏灵活性。
  • 堆:堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。创建对象是为了反复利用,这个对象将被保存到运行时数据区。
深拷贝和浅拷贝的区别

https://juejin.cn/post/6844903929705136141
发生在引用类型中

  • 浅拷贝(引用拷贝):它与原来的变量仍然指向同一个地址,两者之间相互影响
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
//Object.assign()是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并。
let obj2 = Object.assign({}, obj1);   
let obj2= {... obj1} // 扩展运算符实现
  • 深拷贝(值拷贝):拷贝的过程中,开辟了独立的空间,这个对象指向这个地址,与原来的对象互不干扰。
    • JSON.parse(JSON.stringify()):有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况。
    • for in 遍历 递归 *
    function clone(target) {
        if (typeof target === 'object') {
        	let cloneTarget = Array.isArray(target) ? [] : {} 
         	for (const key in target) { 
          		cloneTarget[key] = clone(target[key])
            } 
            return cloneTarget
        } else {
        	return target
         }
    }   
    
原型&原型链
提出这个概念(why)
  • 同一个函数new出来的两个实例,都会开辟出一个新的堆区,当判断这两个实例方法时会发现不相等
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.eat = function() {
    console.log(age + "岁的" + name + "在吃饭。");
  }
}

let p1 = new Person("jsliang", 24);
let p2 = new Person("jsliang", 24);

console.log(p1.eat === p2.eat); // false
  • 这样会导致内存不足,需要建立一个能够溯源的链条,类似共享库的对象;实例向上可以找到自己的构造函数
function Person(name) {
  this.name = name;
}

// 通过构造函数的 Person 的 prototype 属性找到 Person 的原型对象
Person.prototype.eat = function() {
  console.log("吃饭");
}

let p1 = new Person("大雄", 24);
let p2 = new Person("静香", 24);

console.log(p1.eat === p2.eat); // true
what
  • 原型

    • 所有引用类型都有(除null外)一个__proto__(隐式原型)属性,指向它构造函数的prototype

    :引用类型:
    Array、Object、Date、Function、null(null值表示一个空对象指针,而这也是typeof检测null返回object的原因)

    var a = [1,2,3];
    a.__proto__ === Array.prototype; // true
    
    • 所有函数都有一个prototype(原型)属性,指向函数的原型对象
    function Person(age) {
        this.age = age       
    }
    Person.prototype.name = 'kavin'
    var person1 = new Person()
    var person2 = new Person()
    person1.name === person2.name === 'kavin'
    

    prototype属性值都为一个普通对象

  • 原型链

    • 当访问一个对象的某个属性时,会先在对象本身属性上查找,如果没有找到,则会去它的__proto__上查找,即它的构造函数的prototype,如果还没有找到就会在构造函数prototype__proto__中查找,这样一层一层向上查找就会形成一个链式结构,称为原型链。
      image

    1.一直往上层查找,直到到null还没有找到,则返回undefined

    null作为对象原型链的终点

    2.Object.prototype.__proto__ === null(为了避免死循环而设置)
    3.所有从原型或更高级原型中的得到、执行的方法,其中的this在执行时,指向当前这个触发事件执行的对象

this指向
  1. 构造函数(new)的this指向实例化后的那个对象
    第一步:创建一个Object对实例。
    第二步:将构造函数的执行对象赋给新生成的这个实例。
    第三步:执行构造函数中的代码。
    第四步:返回新生成的实例。


    new时做了什么
    1. 创建一个新对象
    2. this指向这个新对象
    3. 执行代码给this赋值
    4. return this
    如果调用新对象没有的属性时,js会延原型链向上逐层查找

    function Person(age,name){
       this.age = age 
       console.log(this) // this分别指向Person的实例对象p
    }
    var p = new Person(18,"xiaoming")
    

2.普通函数的this指向调用该函数的那个对象

// 在ES6严格模式中禁止this指向全局对象
"use strict";
function f() {
    console.log(this);
}
f() //log:undefined
window.f() //log:window{}

3.在事件中this指向发生事件的元素本身(btn)
4.箭头函数的this指向父作用域的this

  • 箭头函数中的this永远都只看它所属的作用域的this,无法通过bind/call/apply来修改;不能做为构造函数

5.setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的this关键字会指向window(或全局)对象。

call、apply、bind区别
  • call将第一个参数指向this,当为null、undefined时,默认指向window
  • apply与call相似,但apply传递给方法的参数是数组的形式,只接受两个参数,一个是改变this的对象,另一个是参数数组
  • call和apply在改变方法的this指向时,会同时执行方法;而bind不会立即执行方法,参数会传给返回的方法
es6
序号具体分类
1let、const、symbol数据类型
2模板字符串字符串
3扩展运算符数组
4解构赋值对象
5箭头函数函数
6取消变量提升,添加块级作用域语法
7promise、async await、filter(),map(),forEach(),find()高阶函数等

引:

  • 数组去重
    [...new Set(arr)]
  • 数组方法:
    push()// 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
    pop()// 方法用于删除并返回数组的最后一个元素。
    shift()// 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
    unshift()// 方法可向数组的开头添加一个或更多元素,并返回新的长度。
    sort()//(排序)
    reverse() //(反转数组)
    concat()// 方法用于连接两个或多个数组
    slice()//(数组截取) arr.slice(start,end);
    splice() //方法向/从数组中添加/删除项目,然后返回被删除的项目。(该方法会改变原始数组) arr.splice(index,howmany,item1,.....,itemX)
    forEach() //不返回、map()返回新数组、filter()
    reduce() Array.from() find() includes()
    
  • map和forEach
    1. map会返回一个新数组,forEach返回undefined,都不对原数组产生影响 ,只是调用原数组,但在回调函数中原数据都可以被修改。
    2. map速度比forEach(快70%)
    3. map()会分配内存空间存储新数组并返回,forEach()不会返回数据
      使用场景:
    • map适合改变数据值的时候。不仅更快,而且返回一个新数组。还可以组合filter(), reduce()等组合使用
    • forEach适用于不改变数据的时候,而只是取值po

生命周期

vue的生命周期
    就是Vue实例一生过程——从最开始的数据监听、模板编译、将实例挂载到DOM并在数据变化时更新DOM、销毁等。在这过程中有8个钩子的函数,在不同阶段可以添加自己的代码
  • vue对象核心的四个阶段 八个钩子函数:
    1. 数据挂载:把传入data属性的内容,赋给vue对象
      前后分别的钩子函数是:beforeCreatecreated
    2. 模板渲染:把vue对象中data渲染到dom对象上。
      前后分别的钩子函数是:beforeMountmounted
    3. 组件更新:当渲染到DOM上的数据发生变化时,会触发组件的更新。
      前后分别的钩子函数是:beforeUpdateupdated
    4. 组件销毁:
      前后分别的钩子函数是:beforeDestroy、`destroyed
keep-alive标签的作用
    keep-alive可以缓存组件的状态,避免了组件的频繁创建和销毁。
    减少加载时间及性能消耗,提高用户体验性
  • 特性:
    • 一般结合路由和动态组件一起用于缓存组件
    • 提供includeexclude属性。两者都支持字符串和正则表达式,
      1. include名称匹配的组件会被缓存,
      2. exclude表示任何名称匹配的组件都不会被缓存,其中exclude的优先级比include高;
      3. 对应两个钩子函数activateddeactivated,当组件被激活时,触发钩子函数activated,当组件被移除时,触发钩子函数deactivated
  • 钩子函数的执行顺序:
    • 当使用keep-alive时:

        钩子触发的顺序是:created -> mounted -> activated -> deactivated
      
    • 当使用keep-alive,并且再次进入了缓存页面的情况:

          activated -> deactivated
      

使用<keep-alive>会将数据保留在内存中,

如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务

Vue 的父组件和子组件生命周期钩子函数执行顺序

Vue 的父子组件钩子函数的执行顺序可以归类为4个部分:

  1. 第一部分:首次加载渲染

    父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
    
  2. 第二部分:子组件更新

    父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
    
  3. 第三部分:父组件更新过程
    不会影响子组件

    父 beforeUpdate -> 父 updated
    
  4. 第四部分:销毁过程

    父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
    
nextTick:
    在下次 DOM 更新循环结束之后立即执行延迟回调。用来获取更新后的 DOM。
  • 视图更新之后,基于新的视图进行操作。
  • 第三方插件,在vue生成的某些dom动态发生变化时重新应用该插件。
promise
    把复杂的异步代码改成同步——解决回调地狱
    promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
  • 基本使用
  1. Promise的构造函数接收一个为函数的参数,有两个参数:resolve,reject,分别表示异步操作执行成功、异步执行失败后的回调函数。
  2. 当promise运行有了结果,再执行.then()中的异步操作。当执行失败时也可以通过promise.catch()调用;
  • 方法:
  • Promise.all():当所有的异步代码都执行完毕以后才会执行.then中的操作
  • Promise.finally() : 不管 Promise 对象最后状态如何,都会执行的操作
  • Promise.race():只要有一个promise执行完毕后就会执行.then操作
Promise、nextTick、setTimeout的执行顺序
执行顺序:同步代码 > nextTick > Promise > setTimeout
  • 同步&异步:

    • 同步:立即执行且必须执行。单位时间内,只会做一件事。如果中途出现错误,就会导致主线程阻塞,从而导致页面报错。
    • 异步:并非是当下立即需要执行的,就会使用异步;比如当满足某个条件时才会执行。
  • 宏任务&微任务: 微任务 > 宏任务

    • 宏任务:script、setTimeout / setInterval

      • setTimeOut并不是直接把回掉函数放进异步队列中,而是在定时器的时间到了之后,把回掉函数放到执行异步队列中。如果此时这个队列已经有很多任务了,那就排在他们的后面。这也就解释了setTimeOut为什么不能精准执行的问题
    • 微任务:Promise、nextTick

      • new Promise是同步任务,会被放到主进程中去立即执行。而.then()函数是异步任务,当promise状态结束时,就会立即放进异步队列中去
      • nextTick会在一个事件循环结束之后,下一个事件循环开始之前进行回调的调用
      • nextTick其实也是使用Promise来处理回调,但是会将 nextTick 中的回调全部塞到 Promise.then 的回调之前,而同级微任务的情况下,是按顺序来进行执行的,这就让 nextTick 永远都在 Promise.then 之前执行。
  • 调用栈 & 任务队列:先入后出

    • 当有函数需要执行而分配给浏览器引擎时,引擎会按一定顺序将函数进行排队
    • 排到该函数后,该函数推入到 JavaScript 主线程中的调用栈
    • 该函数执行完毕后,出栈
  • Event Loop事件循环

    当同步代码执行完后,就会开启Event Loop,不断去循环回调队列,看队列中是否有回调函数需要被执行

    • 工作原理:
      1. 同步代码执行完后,开启 Event Loop,其中分为微任务队列和宏任务队列
      2. 检查微任务队列中是否有回调函数,且调用栈是否为空,若是则把该函数从队列中推入到调用栈执行
      3. 检查宏任务队列中是否有回调函数,且调用栈是否为空,若是则把该函数从队列中推入到调用栈执行
      4. 直到微任务和宏任务队列中均为空,则开启下一个循环,从第2步开始一直反复

概念

36.如何利用webpack来优化前端性能?(提高性能和体验)

https://juejin.cn/post/6844904071736852487

用webpack优化前端性能是指----优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
  • 压缩代码:删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPluginParallelUglifyPlugin来压缩JS文件,利用cssnano(css-loader?minimize)来压缩css
  • 兼容高版本node
  • 提取公共代码
  • 利用CDN加速:在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
  • 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
26.路由传参2种方式params与query
  • params: 取值this.$route.params.id

    • params比query严格只能用name不能用path,不然会直接无视掉params中的内容
    • 需要写成动态路由'/login/:username:age',不然在第一次点击实现跳转后再刷新页面是,不能取到传过来的值。但是这样就不会类似于 post 请求,他会把接收到的参数替换作为地址
    • 假如传入参数为:params: { username: 'admin'},那么最终访问的地址为:http://localhost:8080/#/home/admin
      编程式:
    data:{
      username: '',
      age:''
    },
    login() {
      ...
      this.$router.push({
        name: 'home', //注意使用 params 时,只能使用 name , 不能使用 path
        params: { username: this.username , age: this.age },
      })
    }
    // 动态路由
    const routes = [
      {
        path: '/home/:username/:age',
        name: '/home',
        component: Home
      }
    

    声明式:

    <router-link :to="{ name: 'home', params: { username: username } }">
    
  • query:取值this.$route.query.id

    • query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(http://localhost:8080/#/?name=xx)。

    编程式:

    data:{
      username: ''
    },
    login() {
        this.$router.push({
            path: '/home',
            query: { username: this.username },
         })
    }
    

    申明式:

    <router-link :to="{ path: '/home', query: { username: username } }">
    
28.内存泄漏
动态开辟的空间在使用完毕后未释放,结果导致一直占据该内存。

原因:

  • setInterval定时器如果没有取消会一直存在与内存中
  • 不合理的使用闭包
  • 单例,原因单例的静态特性使得其生命周期和应用的生命周期一样长
  • 使用未声明的变量,使其成为全局变量会一直存在内存中
21.闭包
  • 闭包就是能够读取父函数内部变量的函数
  • 就是函数嵌套,子函数要在外部访问父函数的局部变量,通过在子函数中使用父元素的局部变量,将子函数作为父函数的返回值。
  • 缺点:使变量始终保持在内存当中,增长变量生命周期,不会被垃圾回收机制回收;内存会消耗很大(内存泄漏)。
var fn = function(){
    var count = 0;
    return function(){
        return count++;
    }
}

引用:节流防抖

引:
垃圾回收机制:
1.工作原理:
是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
2.工作流程

  • 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记
  • 去掉环境中的变量以及被环境中的变量引用的变量的标记。
  • 再被加上标记的会被视为准备删除的变量。
  • 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
防抖节流:
  • 防抖:函数连续多次触发,只执行最后一次。-------防止抖动,以免把一次事件误认为多次 防抖重在清零 clearTimeout(timer)
    • 应用
      • 富文本编辑器自动保存,当无任何更改操作一秒后进行保存
      • 登录、发送数据等按钮避免用户点击太快,以致于发送了多次请求,需要防
    function debounce (f, wait) {
      let timer
      return (...args) => {
        clearTimeout(timer)   //   !!!
        timer = setTimeout(() => {
          f(...args)
        }, wait)
      }
    }
    
  • 节流:函数连续多次触发,在某个时间间隔内只执行一次 。 控制事件发生的频率,如控制为1s发生一次 节流重在开关锁 timer=null
    • 应用
      • 滚动事件中触发请求,不希望在滚动时一直发送请求,而是隔一定时间发起一次
      • 调整浏览器窗口大小时,resize 次数过于频繁,此时需要一次到位
    function throttle (f, wait) {
      let timer
      return (...args) => {
        if (timer) { return }
        timer = setTimeout(() => {
          f(...args)
          timer = null    //   !!!
        }, wait)
      }
    }
    

为什么用闭包:如果把timer放在内部函数中,每次执行内部的函数,都会创建一个新的timer变量。

5.MVVM
  • 将mvc中Controller演变成mvvm中的viewModel。
  • mvvm主要解决了mvc模式下大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
  • mvvm: View(代表UI 组件)负责将数据模型转化成UI 展现出来,ViewModel是一个同步View 和Model的桥梁(用来存放于页面相关的数据)
面向对象
面向对象就是一种思想,任何事物都可以看作是一个对象。
对象:对象是一个容器,封装了属性{key:value}和方法{function:()},属性是对象的状态,方法是对象的行为
  • 主要思想:
    1. 封装:就是模块化,把不同的功能,封装在不同的模块里。把内部的实现隐藏起来,然后只暴露必要的方法让外部调用。
    2. 继承:在不修改原类的情况下,给现存的类添加新特性。
    3. 多态:相当于对于同一个接口类型,由多种不同的实现方式。
  • 特点:
    1. 代码复用性
    2. 代码开发模块化,便于维护。
    3. 代码的可靠性灵活性
    4. 代码的可读性和可扩展性

面向过程:按照我们分析好的步骤,按照步骤解决问题。(更注重过程实现) ++优点++:性能好,++缺点++:不易维护
面向对象:以对象功能来划分问题,而不是步骤。(关注对象的功能) ++优点++:低耦合,灵活,易于维护,++缺点++:性能比面向过程低

watch和computed的区别
  • computer:通过属性计算而得来的属性 有缓存没异步
    1. 当它依赖的属性值发生改变,下一次获取computed的值时会重新计算,当没有改变时,会从缓存中读取
    2. computed中的函数必须用return返回
    3. 可以设置getter和setter

使用场景:多对一&&一对一,比如购物车商品的结算

  • watch:属性监听 没缓存有异步
    1. watch是依赖data中的属性,当data中的属性发生改变的时候,watch中的函数就会立即执行
    2. watch中的函数有两个参数,前者是newVal,后者是oldVal。可以没有return
    3. watch想要监听引用类型数据的变化,需要进行深度监听( deep:true );首次加载做监听( immediate:true

使用场景:一对多,比如搜索框

浏览器工作原理
实现http协议的通讯
  • 浏览器的主要组件:
    1. 用户界面 - 包括地址栏、 前进/后退按钮、 书签菜单等。除了浏览器主窗口显示的请求的页面外,其他显示的各个部分都属于用户界面。其他显示的各个部分都属于用户界面。
    2. 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
    3. 渲染引擎 - 负责显示请求的内容。 如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
    4. 网络 - 用于网络调用,如 HTTP 请求。 其接口与平台无关,为所有平台提供底层实现。
    5. 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。 其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
    6. JavaScript 解释器 - 用于解析和执行 JavaScript 代码。
    7. 数据存储 - 这是持久层。 浏览器需要在硬盘上保存各种数据,例如 Cookie。 新的 HTML 规范(HTML5) 定义了“网络数据库” ,这是一个完整的浏览器内数据库。
      image
      https://www.cnblogs.com/xiaohuochai/p/9174471.html
cookie和webstorage区别
  1. sessionStorage数据在浏览器关闭后自动删除
  2. localStorage长期存储数据;当对cookie做持久化处理时也可。

设置cookie持久化:
设置cookie的生命周期setMaxAge,并给出一个以秒为单位的时间。将最大时效设为0则为删除。

  1. sessionStorage和localStorage的存储空间更大(5M),cookie(4k);
  2. sessionStorage和localStorage有比cookie更多丰富易用的接口,和各自独立的存储空间;
  3. cookie在浏览器和服务器间来回传递,每次http请求都会携带cookie;sessionStorage和localStorage不会
data是个函数

引用类型如果不用function返回,每个组件的data都是内存的同一个地址,一个数据改变了其他也改变了;使用函数使每个实例维护一份独立的地址

什么是作用域链?

内部环境通过作用域链来访问外部环境的属性和方法,外部环境不能访问内部环境的任何属性和方法。只能通过定义函数来延长作用域。


CSS

css3新特性
  1. 新的选择器
    • 属性选择器:[title] [class='demo']
    • 结构伪类选择器
      • nth-child(n)n可为数字、公式、关键字
      • nth-of-type(n)根据类型选择
    • 伪类选择器 ::before ::after
  2. 转换transform
    • 2D
      • translate(x,y)
      • rotate(45deg)
      • scale(x,y)
    • 3D
      • translate3d(x,y,z)
      • rotateX(45deg)
      • rotateY(45deg)
      • rotateZ(45deg)
  3. 动画animation
  4. 浏览器私有前缀
    • -moz-:firefox浏览器
    • -ms-:ie浏览器
    • -webkit-:safari、chrome浏览器
    • -o-:Opera
css盒模型
盒模型:分为内容(content)、内填充(padding)、外边界(margin)、边框(border)四个部分

通过 box-sizing 来改变盒子模型,它有两个选项

  1. border-box 设置的width = 左右padding + 左右border + content真正宽度width
  2. content-box 默认值 内容真正宽度 = 设置的宽度
flex布局:
  • 采用Flex布局的元素,称为Flex容器(flex container)。它的所有子元素自动成为容器成员,称为Flex项目(flex item)
  • 容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。
    • 主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;
    • 交叉轴的开始位置叫做cross start,结束位置叫做cross end。项目默认沿主轴排列
  • 元素的属性:
    • flex-wrap:是否换行
    • justify-content:主轴上的对齐方式
    • align-items:交叉轴上的对齐方式
    • flex-direction:设置主轴的方向
    • column-reverse:主轴的起点位置
为元素设置默认样式
为了兼容不同了浏览器,不同浏览器的样式不同。
1.clientWidth, offsetWidth, scrollWidth
  • offsetWidth
    • box-sizing:border 的时候: offsetWidth 其实就等于 dom 元素的 width
    • box-sizing:content 的时候: offsetWidth= width + 左border + 右border + 左padding + 右padding

注意width指内容区(content)宽度,随盒模型数据改变

  • clientWidth ——内容区的宽度(不加border)
    • box-sizing:border 的时候 :clientWidth = width - 左border - 右border
    • box-sizing:content 的时候:clientWidth = width - 左border - 右border + 左padding + 右padding
  • scrollWidth
    • 1.在内容区没有发生溢出的情况下,scrollWidth = clientWidth 因为它们都是代表内容区的宽度。
    • 2.在内容区发生了溢出,并且设置了 overflow-scroll 之类的属性的情况下,
      clientWidth 代表dom 当前状态下,实际上展示在可视区域的 内容区(content) 的宽度,而 scrollWidth 则代表了真实的内容区的宽度,包括了那些没有展现在用户面前的,需要滚动才可以看到的内容的宽度。这时候
      scrollWidth= clientWidth + 溢出的内容区的宽度。
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryYjogeo-1678762567154)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89dfb2a05d4340a2b55f8368c846213f~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)]

HTML

HTML & HTML5
  1. 指代上的区别:
    • HTML指的是HTML4.01
    • HTML值得是HTML的升级版,是公认的下一代Web语言,可以理解为第五代超文本标记语言标准。
  2. 文档类型的声明:在文档声明上,html有很长的一段代码,并且很难记住这段代码,而html5却不同,只有简简单单的声明,方便记忆。
    <!-- HTMl -->
    < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> < html
    xmlns="http://www.w3.org/1999/xhtml">
    
    <!doctype html>
    
  3. HTML5有一些语义标签
    <header/> <article/> <footer/> <nav/> <aside/> <audio/> <command/>
  4. HTML5为input标签添加了一些属性
    placeholder 、 required 、 pattern、 min/max 、 autofocus、 mulitiple
    
    type="number" // 限制用户只能输入数字
    type="tel"   // 手机号
    type="search" // 输入框
    
  5. 存储:sessionStorage localStorage
  6. 通信:webSocket
v-if为什么不能与v-for一起使用
  1. 优先级不同;v-for的执行优先比v-if要高(vue 2中如此,在vue 3中正好相反)
  2. 不管循环多少次,都得用v-if去判断,比较耗费性能
    • 优化
      • 将v-if写在v-for的外面,从而提高v-if的优先级
      • 用computed/filters去对v-for循环的数据进行处理
v-if与v-show的区别
  • 相同点:v-show和v-if都是控制dom元素的显示和隐藏的。
  • 不同点:
    1. 原理:
      • v-show是通过控制元素的样式属性display的值,来完成显示和隐藏;
      • v-if是通过dom元素的添加和删除,完成显示和隐藏
    2. 使用场景:由原理(做法)得出使用场景的区别
      • v-show:使用在dom元素频繁切换的场景
      • v-if:当dom元素的切换不频繁,可以使用。特别是,首次元素处于隐藏的情况下。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值