一年左右工作经验前端面试分享(vue 方向)

当前更新日期:2023-06-28

学习的时候也要记得做做题哦
我做了一份 前端笔试题记录 请服用
还有一份 让GPT给我出JS笔试题
还有一份 中级前端工程师基础面试一面口吻


HTML部分

  1. 什么是 块级元素 和 行内元素?
  2. p标签是块级元素,常理来说,块级元素是可以嵌套块级元素和行内元素的,但是p标签是个特殊,它里面不能嵌套块级元素
  3. 网页中,rem 作为元素尺寸单位时,是相对 文档根节点的 font-size 进行计算的。
  4. 块级元素实际占用的宽度与它的 background 属性有关。
  5. 语义化标签的使用(写html不要只会div哦)。

HTML5 的有什么新特性和改进?

  1. 新增标签:HTML5引入了一些新的标签,如<video>、<audio>、<canvas>、<svg>等,使得在网页中嵌入多媒体内容和图形变得更加方便和直观。

  2. 语义化标签:HTML5引入了一系列语义化标签,如<header>、<nav>、<aside>、<article>、<section>、<footer>等,这些标签能够更准确地描述网页的结构和内容,有助于提高可访问性、搜索引擎优化和代码可读性。

  3. 表单增强:HTML5为表单提供了许多增强功能,如新增的输入类型(例如日期、时间、邮箱、电话等)、表单验证、自动完成和本地存储等,使得表单交互更加强大和用户友好。

  4. 离线应用:HTML5引入了离线应用缓存(Application Cache)机制,允许开发者将网页资源缓存到用户本地,即使在离线状态下也能访问网页,提供了更好的离线体验。

  5. Web存储:HTML5提供了本地存储的能力,包括Web Storage(localStorage和sessionStorage)和IndexedDB,使得网页能够在客户端存储和检索数据,提供了更高效的数据管理。

  6. 多媒体和图形支持:HTML5通过新增的<video>、<audio><canvas>标签以及SVG(可缩放矢量图形)的支持,为多媒体内容和图形绘制提供了更直接、更强大的方式。

  7. Web Components:HTML5引入了Web Components标准,包括自定义元素(Custom Elements)、影子DOM(Shadow DOM)和HTML模板(HTML Templates),使得开发者能够创建可重用的自定义组件,提升了代码的可维护性和复用性。


CSS部分

- CSS盒模型 box-sizing (常问)

标准模型 和 IE模型的区别就在于 width 和 height 的计算方式不同。

  • 标准盒模型 content-box width 只等于内容区域,不包括border和padding
  • 怪异(IE)盒模型 border-box width = 内容区宽度 + border + padding

- CSS权重 (常问)

!important > 内联样式(1000) > id选择器(100) > class选择器(10) = 伪类选择器(10) = 属性选择器(10) > 元素选择器(1)

  • 内联样式: 写在标签里的style id选择器: #id-selector
  • class选择器: .class-selector
  • 伪类选择器: .class-selector:first-child
  • 属性选择器: a[href=“https://example.org”]
  • 元素选择器: .class-selector > div

- CSS居中的方法 (常问)

这个光背没有用,自己去codepen写一下!CodePen

  1. Flex布局: display: flex; justify-content: center; align-items: center;
  2. 定位: position: fixed/ absolute/ relative
  3. 位移:transform: translate(x, y);
  4. Margin: margin: auto;
  5. textAlign文字居中: text-align: center

- CSS让元素"看不见"的方法

  1. display: display: none
  2. visibility: visibility: hidden
  3. 透明: opacity: 0
  4. 定位: position: fixed/ absolute/ relative
  5. 缩放: transform: scale(0)
  6. 宽高: width: 0px; height: 0px; overflow: hidden;
  7. 让元素的颜色与背景色一致

- Flex布局 (常问)

Flex布局

参考:Flex 布局教程:语法篇 - 阮一峰的网络日志

Flex作为当前最流行的布局, 是否了解它的所有属性了?

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

这一段描述就很好的解释了,为什么用flex定义的区域编写”超出部分省略号“不能生效,因为子元素也自动变成了flex item。

flex布局的基本属性在文章中很详细的解析了。最需要注意及面试官最喜欢问到的问题就是,flex: 1 属性是由哪些属性组成?

flex-grow属性

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

flex-shrink属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,属性为0的项目不缩小。负值对该属性无效。

flex-basis属性

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

flex属性

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

- CSS的position

  1. static 默认值 默认值,元素按照正常的文档流排布
  2. relative 相对定位 相对于当前文档排布的位置进行一个定位,不会脱离文档流,与absolute不同
  3. absolute 绝对定位 相对于一直往上找父亲元素直到找到position的属性值不为static的元素定位,定位以后元素会脱离文档流,影响排布
  4. fixed 固定 相对于浏览器视口进行定位
  5. sticky …

- 伪类和伪元素(常问)

这个东西,经常分不清楚,我是用这种方式来区分的

< 伪类和伪元素的区别,最关键的点在于如果没有伪元素(或伪类),是否需要添加元素才能达到目的,如果是则是伪元素,反之则是伪类。>

举个例子:
我想在<span>你好</span>这个标签前面加一个小图标,如果没有伪元素(或伪类),可以通过添加一个图片或者背景图来实现这个效果的,这个图片/背景图就是我们添加的元素。同样的使用after或者before也能达到这个效果。所以after和before是伪元素。

伪类: 用来选择那些不能够被普通选择器选择的文档之外的元素,比如:hover。

伪元素:创建通常不存在于文档中的元素,比如::before

gtp告诉我这样区分:

  1. 伪类选择器选择的是已经存在于文档中的元素,并且它们通过检查元素的状态、位置或属性来进行选择。伪类选择器通常用于为元素的特定状态应用样式,比如悬停、被选中、第一个子元素等。
  2. 伪元素选择器选择的是不存在于文档中的虚拟元素,它们用于在元素的内容之前或之后插入额外的样式化元素。常见的伪元素有 ::before、::after、::first-letter、::first-line 等。

- BFC

BFC(Block formatting context): 称为块级格式化上下文,是CSS中的一种渲染机制。是一个拥有独立渲染区域的盒子(也可以理解为结界),规定了内部元素如何布局,并且盒子内部元素与外部元素互不影响。
画重点!盒子内部元素与外部元素互不影响

所以通过设置BFC可以解决很多问题,例如:清除浮动,上下容器margin值重叠的问题。

设置BFC的方式:

  • float的值不为none。
  • position的值不为static或者relative。
  • display的值是inline-block、table-cell、flex、grid、table-caption或者inline-flex等
  • overflow的值不为visible

- 可继承元素 和 不可继承元素

参考:CSS常见可继承属性与不可继承属性_风从北方来的博客-CSDN博客

- link和@import的区别(没被问过很多资料却有的问题):

  1. link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
  2. link可以加载CSS,Javascript;@import只能加载CSS。
  3. link加载的内容是与页面同时加载;@import需要页面网页完全载入以后加载。

JS部分

- this相关 (常问)

  1. 如果一个函数有this,但是它没有被上一级对象所调用,严格模式下 this 指向 undefined, 否则指向window
  2. 如果一个函数中有this,这个函数有被上一级所调用,那么它指向的就是调用它的对象。
  3. 如果是一个链式的调用,则最内层的this指向最后一个调用他的对象
  • 箭头函数

this 绑定的是最近一层非箭头函数的 this

  • this的指向

call / apply / bind 方法

   let a = {
	  func1: function() {
		console.log('hi, i am func1');
	  }
   }

   setTimeout(function(){
        this.func1()
   }.apply(a),100);

   setTimeout(function(){
        this.func1()
   }.call(a),100);

   //bind()方法创建一个新的函数 需要手动调用
   setTimeout(function(){
        this.func1()
   }.bind(a)(),100);
  • new 的过程
var a = new myFunction("Li","Cherry");

new myFunction{
    var obj = {};
    obj.__proto__ = myFunction.prototype;
    var result = myFunction.call(obj,"Li","Cherry");
    return typeof result === 'obj'? result : obj;
}
  1. 创建一个空对象 obj;
  2. 将新创建的空对象的隐式原型指向其构造函数的显示原型。
  3. 使用 call 改变 this 的指向
  4. 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。

- 闭包 (常问)

闭包 - 阮一峰

如果经常去读别人的代码,就会发现闭包这东西基本都藏在每一个关键的知识点中。

很多很牛杯的东西,基本都离不开闭包,而能不能发现他并运用在自己的项目中,成为衡量一个程序员是否成长的标准。

闭包是指函数和它所处的环境(定义时的作用域)一起组成的实体。简单来说,就是在一个函数内部声明的变量或函数可以被外部访问。

JavaScript 中的函数都有一个内部属性 [[Scope]],用于存储这个函数所在的作用域链。当函数执行完毕后,其内部声明的变量仍然存在,只是无法被外部访问到,因为作用域链已经销毁了。如果要让函数内部变量持久化,我们就需要使用闭包。

使用闭包的方法是在一个函数内部再返回一个函数,这样就可以将内部变量保存下来了。如下例子:

function add(x) {
  return function(y) {
    console.log(x+y);
  }
}

const add5 = add(5); // 接收一个参数x,并返回一个新函数
add5(3); // 在新函数中传递参数y

闭包可以帮助我们将函数相关的变量隐藏起来,避免变量被外部篡改,并且可以让我们在一定程度上模拟面向对象编程中的私有成员和方法。但是需要注意,当闭包引用的变量发生变化时,会影响到整个应用程序的执行效率和内存消耗,所以在使用闭包时应该谨慎权衡利弊。

变量的作用域

在一个作用域中使用了另一个作用域的变量,这是我对闭包最简单的一个表述。

而在这当中最重要的就是作用域这个知识点。

变量的作用域无非就是两种:全局变量和局部变量。


var cool = '全局变量'

function func() {
  var instr = '局部变量‘
  outstr = '全局变量' // 实际上声明了一个全局变量
}

在外部读取局部变量

function f1(){
  var n = 999;

  return function() {
    alert(n)
  }
}

var result = f1()
result() // 999

通过以上的方法,在函数f1内定义一个子函数,子函数中引用函数内部的局部变量n,最后再将这个子函数返回。

因为只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包案例

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。闭包返回的变量不会在调用结束后,被垃圾回收机制(garbage collection)回收。所以不能滥用闭包,否则会造成网页的性能问题.

  1. 防抖和节流函数
  2. vuex store
  3. vue2双向绑定的原理,for in遍历data数据劫持的过程中,定义的变量取得的当前值也是通过闭包来保存,不会被清除, get/set时修改的也是该值。
  4. 以下以前的写法
for (var i = 0; i < 5; i++) {
  console.log('直接打印', i);
  (function(i) {
    setTimeout(() => { console.log('延迟打印', i) });
  })(i); // 包裹一层立即执行函数,创建一个作用域
}

- 原型和原型链 (常问)

原型和原型链

在 JavaScript 中,每一个引用数据类型都有一个内部属性 _proto_,该属性指向另外一个对象,该对象被称之为原型(prototype)。

let arr = [];
let obj = {};
let func = function() {};

arr.__proto__ === Array.prototype; // true
obj.__proto__ === Object.prototype; // true
func.__proto__ === Function.prototype; // true

在这些引用数据类型的原型上,定义了一些公用的变量和方法。当我们定义对应类型的变量时,就可以通过原型链的方式继承到这些公用的属性和方法。

构造函数

原型(prototype)通常在构造函数中定义:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.say = function() {
  console.log('hi!, i am ' + this.name);
}

let ming = new Person('小明', 18)
ming.say(); // hi!, i am 小明

如上所示,定义了一个名为Person的构造函数,在构造函数的原型上定义了say方法。通过Person构造函数创建出来的实例对象中,其原型上都会存在say方法。当执行ming.say()时,会现在ming实例对象中查找是否存在say方法,如果没有找到,则通过原型链的方式找到这个对象的原型,从而执行原型上的say方法。

ming.valueOf(); // Person {name: '小明', age: 18}

如上所示,在ming这个实例对象和它的原型上,都没有定义valueOf方法,为什么调用了valueOf方法没有报错呢?继续往下探究。

前面说到,每一个引用数据类型都有一个内部属性 _proto_,该属性指向另外一个对象,该对象被称之为原型。而ming这个实例对象的原型,本身也是一个对象,它也有__proto__属性,所以它也会有自己的原型,也就是Object这个构造函数的原型。而Object构造函数的原型上定义了我们常见的对象处理方法,例如hasOwnProperty/isPrototypeOf/toString等等,当然也包括例子中的valueOf

实际上,还是通过原型链的方式,找到了Object的原型,从而调用了对应的方法。

需要注意的是,Object构造函数的原型也是对象,也存在__proto__属性,但是它为null,原型链的顶端就到null为止。

总结

综上所述,我认为原型就是一个用来存放公用的变量和方法的对象。通过原型链的方式可以继承一些公用的变量和方法,从而让代码更加的清晰和简洁,每一个对象或方法可以更专注于本身所需要处理的业务逻辑。

- let 和 var 的区别 (常问)

  1. let定义的变量不能重复声明
  2. var 定义的变量重复声明会覆盖之前的值
  3. let定义的变量值在块级作用域内有效
  4. var 定义变量存在声明提前的问题
  5. let 定义的变量不存在声明提前的问题

- undefined 和 null 的区别 (常问)

关于JS类型的比较我专门写了一篇文章: JS类型比较

  1. undefined == null // true
  2. undefined === null //false
  3. undefined: 没有定义
  4. null:定义了没有给值
  5. typeof undefined // ‘undefined’
  6. typeof null // ‘object’

- 判断类型的方式 (常问)

  • typeof

可以较好的判断基本数据类型 , 对引用数据类型的判断不友好,不能区分数组和对象和null,结果都为object。

  • instanceof

运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。对引用数据类型判断较好,对基本数据类型的判断不友好。

  • Object.prototype.toString.call()

自认为最准确的判断方式

Object.prototype.toString.call() // "[object Undefined]"
Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(2) //"[object Number]"
Object.prototype.toString.call(false) // "[object Boolean]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call([1,2]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"

- JS的事件机制 (常问)

JavaScript 事件触发有三个阶段。
CAPTURING_PHASE ,即捕获阶段
AT_TARGET ,即目标阶段
BUBBLING_PHASE ,即冒泡阶段

addEventListener(‘click’, function(){}, false); // 默认为false 冒泡阶段

继续往下问可能会问到 事件委托,简述使用场景,好处。其实是性能优化的一种方式

举个例子,手写一个计算器,可以把事件绑定在父亲最外层,点击后通过currentTarget / target 来判断当前点击的元素是哪个,从而编写我处理逻辑。

- currentTarget和target的区别是什么

e.targete.currentTarget 是事件对象(Event Object)中的两个属性,用于获取触发事件的元素和当前绑定事件处理程序的元素。它们的区别如下:

  1. e.target:表示触发事件的最具体的(原始)目标元素。它指向实际触发事件的元素,即用户进行交互的元素。

  2. e.currentTarget:表示当前正在处理事件的元素,即绑定事件处理程序的元素。它始终指向绑定事件处理程序的元素,不会随事件冒泡而改变。

用一个事件委托的例子来看,马上就能明白:

<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
var list = document.getElementById("list");

function handleClick(event) {
  console.log("target:", event.target.textContent);
  console.log("currentTarget:", event.currentTarget.id);
}

list.addEventListener("click", handleClick);

当点击列表Item 1项时,控制台会输出以下结果:

target: Item 1
currentTarget: list

这表明 e.target 是指向触发事件的最具体目标元素,即被点击的 <li> 元素。而 e.currentTarget 是指向当前绑定事件处理程序的元素,即 <ul> 元素。

- EventLoop JS的事件循环机制 (常问)

参考文章: 阮一峰 - EventLoop

  1. 所有同步任务都在主线程上执行,形成一个执行栈 (execution context stack)。
  2. 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
  3. 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。

- 异步的问题

经典,非常经典的问题题目,笔试面试经常提问!

setTimeout(() => {
	console.log(1);
}, 0);

new Promise((resolve) => {
	console.log(2);
	resolve();
	console.log(3);
}).then(() => {
	console.log(4);
})

console.log(5);

搞懂它!跟事件循环/微任务 有关。

  • 线程和进程

参考:从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

setTimeout导致往后延了才执行 是为什么呢
setTimeout和setInterval的运行机制是,将指定的代码移出本次执行,放到任务队列中,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout指定的代码,必须等到本次执行的所有代码都执行完,才会执行。Promise的任务也是如此。

为什么Promise的then操作输出比setTimeout的先执行?
promise属于一种微任务,当他的代码执行完以后会推入到本次的事件循环中,而setTimeout要等到下一次的事件循环,所以promise比setTimeout先执行。

再往下衍生,就会开始问promise的问题

- Promise / async awiat (常问)

直接看下面的链接看内容,一般牵涉的问题有以下几点。

参考文章:ES6 Promise && async

  1. 对promise的理解
  2. 简述一下平常怎么使用,使用场景
  3. 什么是回调地狱
  4. 这两者的区别是什么 promise 和 async await
  • 回调函数

参考:深入理解 JavaScript 回调函数
按照 MDN 的描述:回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。

当多个异步函数一个接一个地执行时,会产生回调地狱。
如何解决?1. 使用promise 2.借助 async-await

  • async/await: 回调地狱的终极解决方案
 async function request () {
     try {
         const result = await doSomething()
         const newResult = await doSomethingElse(result)
         const finalResult = await doThirdThing(newResult)
         console.log('Got the final result: ' + finalResult)
    } catch (error) {
         failureCallback(error)
    }
 }

- Cookie 、LocalStorage和SessionStorage的区别 (常问)

这篇文章可以看一看 讲解了所有数据存储的方式 JS数据存储

  • Cookie
  1. cookie 的存放数据大小为4K左右。一般由服务器生成, 可设置失效时间
  2. 如果在浏览器端生成Cookie, 默认是关闭浏览器后失效
  3. cookie因为保存在客户端中, 其中包含的任何数据都可以被他人访问, cookie安全性比较低。
  4. 与服务器通信时, 每次都会携带在HTTP头中。
  5. cookie需要在客户端和服务器端之间来回传送, 会浪费不必要的资源如果过多会带来性能问题。
  • localStorage
  1. localStorage的大小一般为5MB, 保存在本地, 除非被清除, 否则永久保存。
  2. 仅在客户端中保存, 不参与和服务器的通信。
  3. 是以key/value的形式进行存储, value值只能存储字符串。如果想要存储对象, 可以用JSON.stringify()进行转换为字符串, 取值的时候再使用JSON.parse()解析。
// 存储的两种方式
localStorage.setItem("isConfirm", "true");
localStorage.isConfirm= "true";

// 读取
localStorage.getItem("isConfirm");
localStorage.isConfirm; 
  • sessionStorage
  1. 一般为5MB,仅在当前会话下有效,关闭页面或浏览器后被清除。
  2. 仅在客户端(即浏览器)中保存,不参与和服务器的通信。
  3. 存储在sessionStorage中的数据可以跨越页面刷新而存在。
  4. 受到同源策略限制,若想在不同页面之间对同一个sessionStorage进行操作,这些页面必须在同一协议、同一主机名和同一端口下。
  5. 是以key/value的形式进行存储,value值只能存储字符串
// 存储的两种方式
sessionStorage.setItem("isConfirm", "true");
sessionStorage.isConfirm= "true";

// 读取
sessionStorage.getItem("isConfirm");
sessionStorage.isConfirm; 

- 如何解决跨域问题 (偶尔问)

JSONP / img标签 或者 script标签等 / nginx


- 前端性能优化的方式 (常问)

我的另一篇文章写了 < 前端性能优化 >


Vue相关

(下面就不更新了,看我的另一片vue文章)
写给自己的Vue知识

简述vue双向绑定的原理 (常问)

1.数据劫持
2.构建模板
3.发布订阅模式

Vue的生命周期 及 每个阶段做了什么事情 (常问)

官方的vue生命周期图,来自vue生命周期图
在这里插入图片描述

// vue生命周期
beforeCreate
created
beforeMount
mounted
updated
beforeUpdate
updated
beforeDestroy
destroyed
// 使用了keep-alive 后存在这两个生命周期
activated
deactivated

beforeCreate的时候,数据/事件还未初始化,无法访问到数据和真实到dom。

beforeCreated —> created 这一段时间内,进行初始化事件和数据。

created的时候,数据和事件已经初始化了,在这个阶段可以对数据进行更改,在这里更改数据不会触发updated函数。

created —> beforeMounted 这一段时间内,会先判断vue内有没有el这个元素,如果有则继续往下执行,如果没有则停止编译,直到在该vue实例上调用vm.$mount(el)。

然后会进行判断,在vue对象中有没有定义template,如果有的话则使用定义的template作为模版,如果没有则定义使用vue的el属性绑定的dom区域作为模版。

beforeMount —> mounted 这一段时间内,开始编译模板 (下面一个板块Vue的编译过程),挂载$el。替换真实节点。

mounted —> 挂载完成,模板中的html渲染到了页面中,mounted只会执行一次。这个时候页面的事件和数据都已经挂载了,真实dom也渲染好了。在这一步可以执行DOM操作。

在vue的对象中,当data的值发生改变时就会先调用beforeUpdate函数。

beforeUpdate --> updated vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染。渲染完以后就会执行updated钩子函数。

beforeDestroy钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。


vif 和 vshow的区别 回流和重绘 (常问)

这是我自己的理解哈

v-if 和 v-show 都会触发回流。
v-if操作的是dom。
v-show 操作的是display。

当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。


Vue的组件通信方式 (常问)

参考: Vue组件通信

一、父子间通信 props / $emit (太常见了不举例了)

父亲组件通过将值传递给子组件,子组件在props中定义并接住父组件传递的值。

子组件通过调用$emit传递一个名称和值,在父亲组件中用@名称的形式定义一个方法,并在方法中接住子组件传递过来的值

二、通过 $parent / $children

父组件

<template>
	<div>
		<sonComponent :msg="msg" />
		<button @click="updateSonValue">点击我修改子组件的sonValue值</button>
	</div>
</template>
<script>
import sonComponent from './sonComponent.vue';
export default {
	name: 'FatherComponent',
	components: { sonComponent },
	data() {
		return {
			msg: '我来自父亲组件'
		}
	},
	methods: {
		updateSonValue() {
			this.$children[0].sonValue = '子组件的值被父组件修改拉!'
		}
	}
}
</script>

子组件

<template>
	<div>
		<span>{{msg}}</span>
		<button @click="onClick()">点击修改父组件的值!</button>
	</div>
</template>
<script>
export default {
	name: 'sonComponent',
	props: ['msg']
	data() {
		return {
			sonValue: '我来自子组件'
		}
	},
	methods: {
		onClick() {
			this.$parent.msg = '父组件的值被子组件修改了!';
		}
	}
}
</script>

三、provide / inject
可以用于父子组件,隔代组件之间的通信

父组件

<template>
	<div>
		<sonComponent :msg="msg" />
	</div>
</template>
<script>
import sonComponent from './sonComponent.vue';
export default {
	components: { sonComponent },
	data() {
		return {
			msg: '我来自父亲组件'
		}
	},
	provide: {
		aaprovide: "aaprovide"
	}
}
</script>

子组件或者孙子组件

<template>
	<div>
		<span>{{provide}}</span>
	</div>
</template>
<script>
export default {
	data() {
		return {
			provide: this.aaprovide
		}
	},
	inject: ['aaprovide']
}
</script>

四、EventBus (参考自标题文章)
eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难

// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 需要在组件中先引入EventBus
import {EventBus} from './event-bus.js'

// 在代码中调用即可
EventBus.$emit('addition', {
	num: 5 // 可以传入当前组件的变量
})

// 响应
EventBus.$on('addition', param => {
	this.count = this.count + param.num;
})

// 移除该事件
EventBus.$off('addition', {})

五、Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。分为五个模块:

state类似与vue的data,用于数据的存储。
getter类似与computed,计算属性。基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算。
mutation中定义了同步的方法,通过它来修改state,是修改state数据的唯一途径。
action中定义了异步的方法,一般用于请求,可以通过调用mutation来修改state。
modules用于项目中将各个模块的状态分开定义和操作,便于维护。

将需要进行通信的数据存储在store中,需要数据的组件通过连接store获取state的值即可实现组件间的通信。该方式可实现全部组件的通信。

六、localStorage / sessionStorage
该方法是最常见的组件通信方法之一了。在不同的组件,跨页面都可以进行通信的一种方式。

// 存储的两种方式
localStorage.setItem("isConfirm", "true");
localStorage.isConfirm= "true";

// 读取
localStorage.getItem("isConfirm");
localStorage.isConfirm; 
// 存储的两种方式
sessionStorage.setItem("isConfirm", "true");
sessionStorage.isConfirm= "true";

// 读取
sessionStorage.getItem("isConfirm");
sessionStorage.isConfirm; 

七、$attrs / $listeners(参考自标题文章)

<template>
	<div>
		<child-com1
			:name="name"
			:age="age"
			:gender="gender"
			title="程序员成长指北"
		></child-com1>
	</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
	components: { childCom1 },
	data() {
		return {
			name: "zhang",
			age: "18",
			gender: "女"
		};
	}
};
</script>
// childCom1.vue
<template class="border">
	<div>
		<p>name: {{ name}}</p>
		<p>childCom1的$attrs: {{ $attrs }}</p>
		<child-com2 v-bind="$attrs"></child-com2>
	</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
	components: {
		childCom2
	},
	inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
	props: {
		name: String // name作为props属性绑定
	},
	created() {
		console.log(this.$attrs);
		// { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }
	}
};
</script>
// childCom2.vue
<template>
	<div class="border">
		<p>age: {{ age}}</p>
		<p>childCom2: {{ $attrs }}</p>
	</div>
</template>
<script>

export default {
	inheritAttrs: false,
	props: {
		age: String
	},
	created() {
		console.log(this.$attrs); 
		// { "gender": "女", "height": "158", "title": "程序员成长指北" }
	}
};
</script>

computed 和 watch 的区别 (常问)

computed是计算属性,需要依赖一个数据,基于data中声明过或者父组件传递的props中的数据通过计算得到的值,当依赖当数据发生改变时,会重新进行计算得到新值。是不支持异步的。如果computed的属性值是函数,那么默认会调用get方法,函数的返回值就是属性值。

watch是一个监听函数,如下,watch是支持异步的。当他监听的属性发生改变时则会调用这个监听方法handler,该监听方法接收两个参数,一个值属性改变后的值,一个是修改前的值。还可以为该监听方法定义两个属性,deep和immediate。immediate为true时,则组件一加载就立即触发handler函数的执行。deep为true时,进行深度监听,为了发现对象内部值的变化,复杂类型的数据时使用。

watch: {
	cityName: {
		handler(newName, oldName) {},
		deep: true,
		immediate: true
	}
}