面试2024

94 阅读13分钟

1、vue的双向绑定原理是什么?里面的关键点在哪里?

Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图 通过Object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时通 知Vue实例,触发相应的​getter​和​setter​回调函数。

2、实现水平垂直居中的方式?

  • 利用定位 + margin:auto
  • 利用定位 + margin: 负值
  • 利用定位 + transform
  • table 布局
  • flex 布局
  • grid 布局

3、常用伪元素有哪一些?

:hover 鼠标悬停 :active 当元素被用户激活(如点击)时的样式。 :focus 当元素获得焦点(如输入框被点击)时的样式。 :checked 用于单选框或复选框,表示元素是否被选中。 :visited 用于设置已访问链接的样式,通常与:link一起使用来区分未访问和已访问的链接。

4、移动端如何适配不同屏幕尺寸?

  1. 使用vw单位:vw是视窗宽度的百分比,可以根据不同设备的屏幕宽度来进行自适应。在Vue中可以通过设置全局CSS样式,将所有的尺寸单位改为vw。

  2. 使用Flexible.js:Flexible.js是一个用于淘宝移动端适配的库,可以根据屏幕宽度动态设置html的font-size。在Vue项目中,可以通过在入口文件main.js中引入Flexible.js来实现移动端适配。

  3. 使用第三方组件库:有一些优秀的移动端UI组件库可以帮助我们快速实现移动端适配,例如Vant、Mint UI等。这些组件库已经做好了移动端适配的工作,只需按照其文档使用即可。

5、本地存储有哪一些?他们三者有什么区别?

1,Cookie,类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。是为了解决 HTTP 无状态导致的问题。 作为一段一般不超过 4KB 的小型文本数据,它由一个名称(Name)、一个值(Value)和 其它几个用于控制 cookie 有效期、安全性、使用范围 的可选属性组成,以键值对的形式存储到本地。 2,localStorage HTML5 新方法,IE8 及以上浏览器都兼容。 特点:生命周期:持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的 存储的信息在同一域中是共享的。 当本页操作(新增、修改、删除)了 localStorage 的时候,本页面不会触发 storage 事件, 但是别的页面会触发 storage 事件。 大小:5M(跟浏览器厂商有关系) localStorage 本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡,受同源策略的限制 3,sessionStorage 和 localStorage 使用方法基本一致,唯一不同的是生命周期,一旦页面(会话)关闭, sessionStorage 将会删除数据。

6、JS的数据类型?如何判断js的数据类型?

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。 引用数据类型:对象(Object)、数组(Array)、函数(Function)。 1.typeof 2.instanceof 3.通过Object下的toString.call() 方法判断数据类型 4.通过contructor判断数据类型

7、说一下ES6的新特性有哪些?

一、symbol ES6新增了一种原始数据类型:创建symbol数据类型的值时,需要给Symbol函数传递一个字符串,另外,每个symbol类型值都是独一无二的,即使传递的是相同的字符串。 二、let和const ES6新增了两个声明变量的关键字:let、const。

let和const声明的变量不存在变量提升,并且同一变量不能重复声明 let和const关键字有个特性:"暂时性死区,",其内部使用let和const关键字声明的变量与外部作用域中的变量相互隔绝,互不影响。即使是同名变量。 三、解构赋值 数组,对象,字符串都可以进行解构赋值

8、Let、const、var三者有什么区别?

一.var 1.在ES5中,顶层对象的属性和全局变量是等价的,用var声明的变量既是全局变量,也是顶层变量 2.使用var声明的变量存在变量提升的情况 3.使用var,我们能够对一个变量进行多次声明,后面声明的变量会覆盖前面的变量声明 4.在函数中使用使用var声明变量时候,该变量是局部的

二.let 1.let是ES6新增的命令,用来声明变量,用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效 2.let不存在这表示在声明它之前,变量a是不存在的,这时如果用到它,就会抛出一个错误变量提升 3.let不允许在相同作用域中重复声明 4.我们不能在函数内部重新声明参数

三.const 1.const声明一个只读的常量,一旦声明,常量的值就不能改变 2.const一旦声明变量,就必须立即初始化,不能留到以后赋值 3.如果之前用var或let声明过变量,再用const声明同样会报错

四.三者的区别 变量提升 暂时性死区 块级作用域 重复声明 修改声明的变量

9、数组去重有哪些办法?

1.将数组的每一个元素依次与其他元素做比较,发现重复元素,删除
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5,5];
    console.log(arr);    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5, 5, 5, 5]
    function noRepeat1(arr) {
        for(var i = 0; i < arr.length-1; i++){
            for(var j = i+1; j < arr.length; j++){
                if(arr[i]===arr[j]){
                    arr.splice(j,1);
                    j--;
                }
            }
        }
        return arr;
    }
    var arr2 = noRepeat1(arr);
    console.log(arr2);    //[1, 23, 3, 5, 6, 7, 9, 8]

2.借助indexOf()方法判断此元素在该数组中首次出现的位置下标与循环的下标是否相等
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5];
    console.log(arr);    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5, 5, 5]
    function noRepeat2(arr) {
        for (var i = 0; i < arr.length; i++) {
            if (arr.indexOf(arr[i]) != i) {
                arr.splice(i,1);//删除数组元素后数组长度减1后面的元素前移
                i--;//数组下标回退
            }
        }
        return arr;
    }
    var newArr = noRepeat2(arr);
    console.log(newArr);    //[1, 23, 3, 5, 6, 7, 9, 8]

3.利用数组中的filter方法
var arr = ['apple','banana','pear','apple','orange','orange'];
console.log(arr)    //["apple", "banana", "pear", "apple", "orange", "orange"]
var newArr = arr.filter(function(value,index,self){
    return self.indexOf(value) === index;
});
console.log(newArr);    //["apple", "banana", "pear", "orange"]

4.借助新数组 通过indexOf方判断当前元素在数组中的索引如果与循环的下标相等则添加到新数组中
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5];
    console.log(arr)    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5, 5, 5]
    function noRepeat4(arr) {
        var ret = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr.indexOf(arr[i]) == i) {
                ret.push(arr[i]);
            }
        }
        return ret;
    }
    var arr2 = noRepeat4(arr);
    console.log(arr2);    //[1, 23, 3, 5, 6, 7, 9, 8]

5.利用空对象来记录新数组中已经存储过的元素
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
    console.log(arr)    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5]
    var obj={};
    var newArr=[];
    for(var i=0;i<arr.length;i++){
        if(!obj[arr[i]]){
            obj[arr[i]]=true;
            newArr.push(arr[i]);
        }
    }
    console.log(newArr);    //[1, 23, 3, 5, 6, 7, 9, 8]

6.借助新数组,判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
    console.log(arr);    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5]
    function noRepeat6(arr){
        var newArr = [];
        for(var i = 0; i < arr.length; i++){
            if(newArr.indexOf(arr[i]) == -1){
                newArr.push(arr[i]);
            }
        }
        return newArr;
    }
    var arr2 = noRepeat6(arr);
    console.log(arr2);    //[1, 23, 3, 5, 6, 7, 9, 8]

7.借助新数组,判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中(原数组长度不变但被按字符串顺序排序)
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
    console.log(arr);    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5]
    function noRepeat7(arr) {
        var ret = [],
            end;//临时变量用于对比重复元素
        arr.sort();//将数重新组排序
        end = arr[0];
        ret.push(arr[0]);
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] != end) {//当前元素如果和临时元素不等则将此元素添加到新数组中
                ret.push(arr[i]);
                end = arr[i];
            }
        }
        return ret;
    }
    var arr2 = noRepeat7(arr);
    console.log(arr2);    //[1, 23, 3, 5, 6, 7, 8, 9]

8.此方法没有借助新数组直接改变原数组,并且去重后的数组被排序
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
    console.log(arr);    //[1, 23, 1, 1, 1, 3, 23, 5, 6, 7, 9, 9, 8, 5]
    function noRepeat8(arr) {
        var end;//临时变量用于对比重复元素
        arr.sort();//将数重新组排序
        end = arr[0];
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] == end) {//当前元素如果和临时元素相等则将此元素从数组中删除
                arr.splice(i,1);
                i--;
            }else{
                end = arr[i];
            }
        }
        return arr;
    }
    var arr2 = noRepeat8(arr);
    console.log(arr2);    //[1, 23, 3, 5, 6, 7, 8, 9]

9.双层循环改变原数组
var arr = [1,1,2,2,3,3,4,4,5,5,4,3,1,2,6,6,6,6];
    console.log(arr);    //[1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 4, 3, 1, 2, 6, 6, 6, 6]
    function noRepeat9(arr){
        for (var i = 0; i < arr.length; i++) {
            for (var j = 0; j < arr.length; j++) {
                if (arr[i] == arr[j] && i != j) {//将后面重复的数删掉
                    arr.splice(j, 1);
                }
            }
        }
        return arr;
    }
    var arr2  = noRepeat9(arr);
    console.log(arr2);    //[1, 2, 3, 4, 5, 6]

10.借助新数组
var arr = [1,1,2,2,3,3,4,4,5,5,4,3,2,1,1,1];
    console.log(arr);    //[1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 4, 3, 2, 1, 1, 1]
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        var repArr = [];//接收重复数据后面的下标
        //内层循环找出有重复数据的下标
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[i] == arr[j]) {
                repArr.push(j);//找出后面重复数据的下标
            }
        }
        //console.log(repArr);
        if (repArr.length == 0) {//若重复数组没有值说明其不是重复数据
            newArr.push(arr[i]);
        }
    }
    console.log(newArr);    //[5, 4, 3, 2, 1]

11.借助ES6提供的Set结构
var arr = [1,1,2,2,3,3,4,4,5,5,4,3,2,1,1,1];
    console.log(arr);    //[1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 4, 3, 2, 1, 1, 1]
    function noRepeat11(arr){
        var newArr = [];
        var myset = new Set(arr);//利用了Set结构不能接收重复数据的特点
        for(var val of myset){
            newArr.push(val)
        }
        return newArr;
    }
    var arr2 = noRepeat11(arr)
    console.log(arr2);    //[1, 2, 3, 4, 5]

10、说一下深拷贝和浅拷贝,如何自己实现一个深拷贝?

深拷贝 & 浅拷贝

浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响

深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响

总的来说,深浅拷贝的主要区别就是:复制的是引用还是复制的是实例

浅拷贝

Array.prototype.slice()

Array.prototype.concat()

深拷贝

JSON.parse()和JSON.stringify()

jQuery的extend方法

var array = [1,2,3,4];

var newArray = $.extend(true,[],array);

动手实现深拷贝 利用递归来实现对对象或数组的深拷贝。递归思路:对属性中所有引用类型的值进行遍历,直到是基本类型值为止(需注意Date,Function,RegExp要特殊处理)。

function deepCopy(obj) { if (!obj && typeof obj !== 'object') { throw new Error('error arguments'); } // const targetObj = obj.constructor === Array ? [] : {}; const targetObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { //只对对象自有属性进行拷贝 if (obj.hasOwnProperty(key)) { if (obj[key] && typeof obj[key] === 'object') { targetObj[key] = deepCopy(obj[key]); } else { targetObj[key] = obj[key]; } } } return targetObj; }

11、Vue的生命周期有哪一些?说一下它们每个阶段做什么操作?

一、生命周期有哪些

beforeCreate( 创建前 )、created ( 创建后 )、beforeMount(挂载前)、mounted(挂载后)、beforeUpdate(更新前)、updated(更新后)、beforeDestroy(销毁前)、destroyed(销毁后)这些生命周期函数。

12、组件通讯方式有哪一些?

第一种:props

第二种:自定义事件

适用场景:子组件给父组件传递数据

on、emit $off

第三种:全局事件总线$bus

适用场景:万能

第四种:消息订阅和发布 pubsub-js,在React框架中使用的比较多(发布、订阅)

适用场景:万能

第五种:VueX多组件数据共享

适用场景:万能

第六种:插槽

适用场景:父子组件通信--(一般传递的是结构)

默认插槽

具名插槽:

13、Vuex有几个属性及作用?

1:state

state:定义了应用程序的状态,就是我们要管理的数据。

2:getters

getters:主要用于对state进行逻辑上的组合和应用,类似于Vue组件中的计算属性。对state数据进行计算(会被缓存)

3:mutations

mutations:用于修改state中的数据,是唯一可以修改state的地方。mutations接收state作为第一个参数,接收payload作为第二个参数。 用于修改State中的状态,只能同步执行。Mutation必须是同步函数,因为它们不能处理异步行为,异步行为应该放在Action中处理。

4:actions

actions:用于异步操作和提交mutations,在actions中可以进行任何异步操作,最后再提交到mutations中同步修改state。actions接收context作为第一个参数,其中包含了state、getters和commit等属性。 可以包含任意异步操作(例如从服务器获取数据),可以用Mutation通过提交(commit)来修改State。

5:modules

modules:模块化管理store(仓库),每个模块拥有自己的 state、mutation、action、getter,以便提高应用程序的可维护性。 将State、Getter、Mutation、Action模块化,便于组件化和模块化开发。

14、Vue的监听属性和计算属性有什么区别?

1.计算属性性能更优。一个监听属性只能监听一个属性的变化,如果要同时监听多个,就要写多个监听属性,而计算属性可以同时监听多个数据的变化。

2.监听属性可以获取改变之前的属性值。

3.计算属性能做的,watch都能做,反之则不行。

4.能用计算属性尽量用计算属性。

15、说一下防抖和节流。怎么实现?

一、函数防抖

单位时间内,频繁触发一个事件,以最后一次触发为准。 防抖的实现:

1.声明一个全部变量存储定时器ID。

2.每一次触发交互的时候,先清除上一次的定时器,然后开启本次定时器。

二、函数节流

单位时间内,频繁触发一个事件,只会触发一次。

节流的实现:

1.声明一个全局变量存储触发事件。

2.每一次触发事件,获取当前时间。

3.判断当前时间与上一次触发事件,是否超过了间隔。

4.如果超过间隔时间,则执行事件处理代码,然后存储本次触发的时间。

16、Vue的导航守卫有哪一些?

一、全局守卫

  1. router.beforeEach (全局前置守卫)
  2. router.beforeResolve(全局解析守卫)
  3. router.afterEach(全局后置钩子)

三、组件内守卫

组件内守卫一共有三个:

  1. beforeRouterEnter(进入该路由时执行),
  2. beforeRouteUpdate(该路由中参数改变时执行),
  3. beforeRouteLeave(离开该路由时执行)

17、你的登录拦截怎么实现的?

  1. 创建你的路由页面,此处略了
  2. 导航守卫拦截判断思路
  3. 判断当前是不是登陆页面,如果是,那么点击登陆后,就放行,即调用next(),进入下一个页面
  4. 如果不是登录页,思路就是 判断有木有 登陆过,即有木有token,如果登陆过,就放行,即next(),可以查看任何页面,如果没有登陆过,就跳转到登录页

18、有用过图表吗?用的多吗?

ECharts

19、闭包是什么?如何实现?

闭包就是 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

function fn1() {
	var name = 'iceman';
	function fn2() {
		console.log(name);
	}
	return fn2;
}
// 当fn1被调用之后,fn1被销毁,当时fn2仍然可以访问变量name,此时就是形成了闭包
var fn3 = fn1();
fn3();

另一个很经典的例子就是for循环中使用定时器延迟打印的问题:

for (var i = 1; i <= 10; i++) {
	setTimeout(function () {
		console.log(i);
	}, 1000);
}

在这段代码中,我们对其的预期是输出1~10,但却输出10次11。这是因为setTimeout中的匿名函数执行的时候,for循环都已经结束了,for循环结束的条件是i大于10,所以当然是输出10次11咯。

究其原因:i是声明在全局作用中的,定时器中的匿名函数也是执行在全局作用域中,那当然是每次都输出11了。

原因知道了,解决起来就简单了,我们可以让i在每次迭代的时候,都产生一个私有的作用域,在这个私有的作用域中保存当前i的值。

for (var i = 1; i <= 10; i++) {
	(function () {
		var j = i;
		setTimeout(function () {
			console.log(j);
		}, 1000);
	})();
}

20、Vue2.0和vue3.0有什么区别?

1.速度更快

vue3`相比`vue2

重写了虚拟Dom实现

编译模板的优化

更高效的组件初始化

2undate性能提高1.3~2倍

SSR速度提高了2~3倍

体积更小通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的

能够tree-shaking,有两大好处:

对开发人员,能够对vue实现更多其他的功能,而不必担忧整体体积过大

对使用者,打包出来的包体积变小了

vue可以开发出更多其他的功能,而不必担忧vue打包出来的整体体积过多

3.更易维护

compositon Api

可与现有的Options API一起使用

灵活的逻辑组合与复用

Vue3模块可以和其他框架搭配使用

4.更好的Typescript支持

VUE3是基于typescipt编写的,可以享受到自动的类型定义提示

5.更接近原生

可以自定义渲染 API

6.更易使用

响应式 Api 暴露出来
Vue3新增特性 Vue 3 中需要关注的一些新功能包括:
framents
Teleport
composition Api
createRenderer
framents 在 Vue3.x 中,组件现在支持有多个根节点

21、Vue常用的指令有哪些?

v-text
v-html(不建议使用)
v-show
v-if / v-else-if / v-else
v-for
v-bind
v-on
v-model
v-slot
v-pre(使用频率很低)
v-once(使用频率很低)
v-cloak(使用频率极低,不细介绍)

22、v-If和v-show有什么区别?

v-if本质其实是动态的创建 或者 删除元素节点。一般不用频繁切换显示或隐藏的情况, 会使用 v-if。因为v-if 是惰性的, 如果初始值为 false, 那么这些元素就直接不创建了, 这样就可以节省一些初始渲染开销。v-show本质是在控制元素的 css 样式,display: none/block;,一般元素需要频繁的切换显示隐藏, 用 v-show。因为v-if在频繁切换时会频繁的操作dom创建和删除元素, 消耗性能。

23、v-for为什么要加一个key?

作用

  1.key的作用主要是为了高效的更新虚拟DOM,提高渲染性能。
  2.key属性可以避免数据混乱的情况出现。

原理:

  1.vue实现了一套虚拟DOM,使我们可以不直接操作DOM元素只操作数据,
  就可以重新渲染页面,而隐藏在背后的原理是高效的Diff算法
  2.当页面数据发生变化时,Diff算法只会比较同一层级的节点;
  3.如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,
  不会再比较这个节点后面的子节点;
    如果节点类型相同,则会重新设置该节点属性,从而实现节点更新
  4.使用key给每个节点做一个唯一标识,Diff算法就可以正确失败此节点,
  "就地更新"找到正确的位置插入新的节点。

24、你是如何封装一个组件的?

  1. 创建组件文件:在项目中创建一个新的组件文件,一般以.vue为后缀,例如MyComponent.vue。
  2. 编写组件模板:在组件文件中编写组件的 HTML 结构,使用Vue的模板语法,例如:
  3. 编写组件的样式:可以在组件文件中编写组件的样式,可以使用CSS、Sass、Less等预处理器,
  4. 编写组件的逻辑:在组件文件中编写组件的逻辑,可以使用Vue的计算属性、方法等
  5. 导出组件:在组件文件的底部使用export default导出组件
  6. 在其他组件中使用:在需要使用该组件的地方,引入该组件并在模板中使用

25、有自己从0到1搭建过项目吗?

一、开发环境配置 1、安装node.js 2、npm配置淘宝镜像 3、安装 Vue CLI 升级版本 4、创建一个项目 二、项目框架结构 三、常用的集成 1、Vue Router 安装 使用 2、Vuex 安装 使用 3、axios 安装 使用 四、CLI 服务 1、使用命令 2、vue-cli-service serve 3、vue-cli-service build 4、缓存和并行处理 五、模式和环境变量 1、模式 2、环境变量 只在本地有效的变量 六、本地预览

26、有用过uni-app吗?

1、Uni-App是一个开源跨平台的前端框架,能够快速地构建出用于微信小程序、H5、Android、iOS等平台的APP。Uni-App采用了Vue.js作为主要开发语言,通过封装完成了对多端的集成,实现了代码的跨平台。

2、从Uni-App的使用来看,Vue3与Uni-App的结合程度非常紧密。Uni-App在集成Vue3之后,第一时间将Vue3的composition API纳入进来,并由此打破了对Vue2对option API的激进依赖。由此,Uni-App拥有了更多的选项和更高效的开发方式。

3、除了composition API之外,Uni-App与Vue3的小幅调整还包括在render函数中调用h方法和在if语句中使用nullable类型,这些都可以使代码更简洁易懂。 三、Uni-App Vue3开发实践 1、创建Uni-App Vue3项目的方式与创建Vue2项目的方式相同,可以使用Vue CLI或Uni-CLI进行快速搭建,也可以使用Uni-App官方提供的脚手架工具(HBuilder X)进行自定义项目设置。

2、接下来,我们以创建一个简单的TODO LIST应用为例。首先,我们需要在components中创建两个组件:AddTodo和TodoList。AddTodo组件是用于添加一个TODO,TodoList组件用于显示所有的TODO列表。下面是AddTodo组件和TodoList组件的代码示例:

// AddTodo组件

// TodoList组件

3、接着,在store文件夹下创建一个index.js文件,用于管理应用状态。下面是index.js文件的代码示例:

// index.js import { reactive } from 'vue'

const state = reactive({ todos: [] })

const addTodo = (newTodo) => { state.todos.push(newTodo) }

export default { state, addTodo } 4、然后,我们需要在App.vue中通过Setup函数调用TodoList和AddTodo组件,并且使用Vuex4进行状态管理。下面是App.vue文件的代码示例:

// App.vue

5、最后,我们通过npm run dev命令进行编译和测试,以确保TODO LIST应用的正常运行。

四、总结 1、本文从Vue3基础、Uni-App框架与Vue3的结合、Uni-App Vue3开发实践等多个方面,对Uni-App Vue3进行了全面的分析和阐述。

2、通过本文的介绍,我们可以看出,Uni-App与Vue3的结合非常紧密,可以为开发者提供更多的选择和更高效的开发方式。

3、Uni-App是一个非常优秀的跨平台前端框架,适用于多端APP的构建。通过结合Vue3的composition API,Uni-App可以提高开发效率和代码质量,进一步提高移动端应用的性能和体验。

27、将字符串变为数组的方法有哪些

1split() 方法
const text = "abc????";
const chars = text.split('');
console.log(chars);
//(7) ['a', 'b', 'c', '?', '?', '?', '?']


2、展开运算符
const text = "abc????";
const chars = [ ...text ];
console.log(chars);
//(7) ['a', 'b', 'c', '?', '?', '?', '?']


3、解构赋值
const text = "abc????";
const [ ...chars ] = text;
console.log(chars);
//(7) ['a', 'b', 'c', '?', '?', '?', '?']


4、Array.from
const text = "abc????";
const chars = Array.from(text);
console.log(chars);
//(7) ['a', 'b', 'c', '?', '?', '?', '?']

28、vue重新赋值后,视图没有更新的几种解决方案

一、解决方法1:静默刷新(使用v-if的特性)在修改值之后将元素销毁,然后在修改后的下一次DOM渲染完成时再显示出来,
这样就会触发组件重新加载data的数据进行渲染,data中被修改的数据才是最新的。change(){
    this.info = {b:99}
    this.show = false
    this.$nextTick(()=>{
        this.show = true
    })
}


二、解决方法2:Vue.$set(官方推荐)使用这个api修改的数据会为其添加响应式getter和setter让其拥有数据响应的特性,
vm.$set(要操作的对象或数组, 要新增或者修改的数组或对象key, 对应的值)updateName(){ 
    this.$set('userInfo',name,'小红'); 
}


三、解决方法3: Vue.$forceUpdate(手动强制更新视图)因为Vue修改数据是异步执行的,所以视图不会立即更新,
会等到下一次dom更新循环结束后统一更新发生在这一次循环中修改的数据,
然后同步视图更新,所以我们可以修改后自己手动强制更新视图。
updateName(){
    this.userInfo.name='小红'//在此时,确实已经将userInfo对象修改完成
    console.log(this.userInfo.name);//输出结果: 小红
    this.$forceUpdate();//在这里,强制刷新之后,页面的结果变为'小红'
  }


四、解决方法4:Object.assign(使用修改栈能触发视图更新的特性)我们都知道Object.assign能拷贝合成一个新对象,
所以我们只需要将要修改的值合并成一个新对象然后赋值给data中的对象或数组,
这样栈的指向被修改了.触发视图更新

五、解决方法5:对于数组还可以使用splice方法
(Vue对于数组的操作能识别变化的api包括push()、pop()、shift()、unshift()、splice()、
sort()、reverse()这些都可被vue监测到)

一般使用都比较推荐使用$set的方式进行更改数据更新视图

 this.setData({
  List:[...this.data.List,...res.data]
})

29、Url到浏览器的一个过程有哪些步骤?

访问顺序

  1. 在浏览器输入网址(url)
  2. 域名解析
  3. TCP建立连接
  4. 浏览器向服务器发送http请求
  5. 服务器接收请求
  6. 页面渲染

30、如何实现小程序的request封装及拦截?

1.统一接口管理 如果后端提供的接口url前半段相同,可以新建文件夹request-request.js,并在其中创建axios实例,实现接口的统一管理。

import axios from "axios"
//创建axios实例(instance)
const instance = axios.create({
  baseURL: "http://kumanxuan1.f3322.net:8881/cms",
  //前半段url
  timeout: 5000
  //如果5s后未响应,就通知用户页面存在加载错误
})

2.封装请求拦截器 在请求进入浏览器组件前,要将其拦截进行处理,下文中对后端返回的数据进行了过滤。

//请求拦截器
instance.interceptors.request.use(config => {
  //什么时候执行这个函数?-发送请求之前
  //cofig是什么?-记录了本次请求相关信息的一个对象
  //这个函数能用来做什么?-做一些请求前可进行的操作(如添加请求头、获取token)
  console.log("执行了请求拦截器", config);
  return config
}, err => {
  return Promise.reject(err)
});

4.封装响应拦截器

//响应拦截器
instance.interceptors.response.use(res => {
  //什么时候执行这个函数?-在接收到后端服务器的相应之后,进入到组件内部的then方法之前执行这里的代码
  //res是什么?-记是axios封装好的一个响应对象
  //这个函数能用来做什么?-做一些统一的数据处理
  console.log("执行响应拦截器", res);
  return res.data//return后的值被组件中的请求的then方法的res接收
}, err => {
  return Promise.reject(err)
});

最后将它们导出:

//导出
export default instance
1
2
5.优化统一接口管理
在request中新建api.js,以精品页面的接口为例,代码如下:

import instance from "./request"

export const JingPinAPI = () => instance.get("/products/recommend");

同时,在App.vue中引入:

import {JingPinAPI} from "@/request/api"
...
async created(){
  
let res = await JingPinAPI()
//await后一般加Promise对象
//await new Promise
console.log(res)

为了防止每引入一次就造成多个接口的调用,这里将JingPinAPI设计为了一个箭头函数,只有JingPinAPI()时,函数才会调用。

此外,为了解决回调地狱问题,这里使用async&await的书写方式。

31、在vue的项目应用中,不使用框架,怎么封装?

1.获取时间(一般时间选择器默认时间用到)

//获取当前时间,day为number,getDay(-1):昨天的日期;getDay(0):今天的日期;getDay(1):明天的日期;
getDay(day) {
      let today = new Date(),
        targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day;
      today.setTime(targetday_milliseconds);
      let tYear = today.getFullYear(),
        tMonth = today.getMonth(),
        tDate = today.getDate(),
        tHour = today.getHours(),
        tMinute = today.getMinutes(),
        tSeconds = today.getSeconds();
      tMonth = this.doHandleMonth(tMonth + 1);
      tDate = this.doHandleMonth(tDate);
      tHour = this.doHandleMonth(tHour);
      tMinute = this.doHandleMonth(tMinute);
      tSeconds = this.doHandleMonth(tSeconds);
      return tYear + '-' + tMonth + '-' + tDate + ' ' + tHour + ':' + tMinute + ':' + tSeconds;
 },

32、什么是Js原型?原型链是什么?

js的原型和原型链是:

  1. 原型模式是用于创建重复的对象,同时又能保证性能,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式;
  2. 原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找。
  3. 原型的总结

1)原型是定义了一些公用的属性和方法,利用原型创建出来的新对象实例会共享原型的所有属性和方法

2)严格模式下,原型的属性和方法还是会被原型实例所共享的

3)通过原型创建的新对象实例是相互独立的,为新对象实例添加的方法只有该实例拥有这个方法,其它实例是没有这个方法的

4)原型的总结

所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象

所有函数都有一个prototype(原型)属性,属性值是一个普通的对象

所有引用类型的__proto__属性指向它构造函数的prototype

5)函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址

6)函数的原型对象__proto__:所有对象都有__proto__属性, 当用构造函数实例化(new)一个对象时,会将新对象的__proto__属性指向 构造函数的prototype

33、组件通讯方式有哪些?

1、 props / $emit

(1)父组件向子组件传值(props的用法)

(2)子组件向父组件传递数据($emit的用法)

2、ref / $refs

用法:

3、eventBus事件总线(emit/on)

(1)创建事件中心管理组件之间的通信

(2)发送事件假设有两个兄弟组件

firstCom和secondCom的父组件:

在firstCom组件中发送事件:

(3)接收事件

4、依赖注入(provide / inject)

用法

5、parent/children

用法

注意

6、attrs/listeners

attrs/listeners的用法:

7、Vuex 状态管理器

总结

1、父子组件间通信

2、跨代组件间通信

3、兄弟组件间通信

4、任意组件间通信

34、用闭包的原理做过哪些?

闭包可以说无处不在,所以闭包的几个常用场景,很值得研究一番,如果对闭包还有疑问,可以翻看我之前 闭包 的文章。

1.返回值(最常用)

	//1.返回值 最常用的
		function fn(){
			var name="hello";
			return function(){
				return name;
			}
		}
		var fnc = fn();
		console.log(fnc())//hello

2.函数赋值

        var fn2;
		function fn(){
			var name="hello";
			//将函数赋值给fn2
			fn2 = function(){
				return name;
			}
		}
		fn()//要先执行进行赋值,
		console.log(fn2())//执行输出fn2

3.函数参数

		function fn(){
			var name="hello";
			return function callback(){
				return name;
			}
		}
		var fn1 = fn()//执行函数将返回值(callback函数)赋值给fn1,
		
		function fn2(f){
			//将函数作为参数传入
			console.log(f());//执行函数,并输出
		}
		fn2(fn1)//执行输出fn2

4.IIFE(自执行函数)

	(function(){
			var name="hello";
			var fn1= function(){
				return name;
			}
			//直接在自执行函数里面调用fn2,将fn1作为参数传入
			fn2(fn1);
		})()
		function fn2(f){
			//将函数作为参数传入
			console.log(f());//执行函数,并输出
		}

5.循环赋值

	//每秒执行1次,分别输出1-10
	for(var i=1;i<=10;i++){
		(function(j){
			//j来接收
			setTimeout(function(){
				console.log(j);
			},j*1000);
		})(i)//i作为实参传入
	}

6.getter和setter

function fn(){
		var name='hello'
		setName=function(n){
			name = n;
		}
		getName=function(){
			return name;
		}
		
		//将setName,getName作为对象的属性返回
		return {
			setName:setName,
			getName:getName
		}
	}
	var fn1 = fn();//返回对象,属性setName和getName是两个函数
	console.log(fn1.getName());//getter
		fn1.setName('world');//setter修改闭包里面的name
	console.log(fn1.getName());//getter

7.迭代器(执行一次函数往下取一个值)

		var arr =['aa','bb','cc'];
		function incre(arr){
			var i=0;
			return function(){
				//这个函数每次被执行都返回数组arr中 i下标对应的元素
				 return arr[i++] || '数组值已经遍历完';
			}
		}
		var next = incre(arr);
		console.log(next());//aa
		console.log(next());//bb
		console.log(next());//cc
		console.log(next());//数组值已经遍历完

8.首次区分(相同的参数,函数不会重复执行)

 var fn = (function(){
				var arr=[];//用来缓存的数组
					return function(val){
						if(arr.indexOf(val)==-1){//缓存中没有则表示需要执行
							arr.push(val);//将参数push到缓存数组中
							console.log('函数被执行了',arr);
							//这里写想要执行的函数
						}else{
							console.log('此次函数不需要执行');
						}
						console.log('函数调用完打印一下,方便查看已缓存的数组:',arr);
					}
				})();
		
		fn(10);
		fn(10);
		fn(1000);
		fn(200);
		fn(1000);

9.缓存

//比如求和操作,如果没有缓存,每次调用都要重复计算,采用缓存已经执行过的去查找,查找到了就直接返回,不需要重新计算
	 
	 var fn=(function(){
	 	var cache={};//缓存对象
	 	var calc=function(arr){//计算函数
	 		var sum=0;
	 		//求和
	 		for(var i=0;i<arr.length;i++){
		 		sum+=arr[i];
		 	}
		 	return sum;
	 	}
	 	
	 	return function(){
	 		var args = Array.prototype.slice.call(arguments,0);//arguments转换成数组
	 		var key=args.join(",");//将args用逗号连接成字符串
		 	var result , tSum = cache[key];
		 	if(tSum){//如果缓存有	
		 		console.log('从缓存中取:',cache)//打印方便查看
		 		result = tSum;
		 	}else{
		 		//重新计算,并存入缓存同时赋值给result
			 	result = cache[key]=calc(args);
			 	console.log('存入缓存:',cache)//打印方便查看
		 	}
		 	return result;
	 	}
	 })();
	fn(1,2,3,4,5);
	fn(1,2,3,4,5);
	fn(1,2,3,4,5,6);
	fn(1,2,3,4,5,8);
	fn(1,2,3,4,5,6);

10.节流函数

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=GBK">
        <style>
        *{
        	margin:0;padding:0;
        }
        </style>
        </head>
        <body>
       		<div class="box" id="div_box" >
       			<button onclick="fn1()">test</button>
       		</div>
        </body>
        
<script>
//节流函数
function throttle(fn,delay){
	var flag=false;
	var timer=null;
	return function(){
		var args=[].slice.call(arguments,0);//将参数转成数组
		var context=this;
		if(flag){//如果在限定的时间内 flag是true 则直接返回,不让执行
			return;
		}
		flag=true; //函数正在控制中
		//执行函数
		fn.apply(context,args);
		clearTimeout(timer);//清除定时器
		timer =setTimeout(function(){
			flag=false;//延时时间过了以后,放开函数控制
		},delay)
		
	}	
}
function fn(){
	console.log(123);
}
 
var fn1 = throttle(fn,2000);//绑定节流函数 
 
 
</script>
</html>

35、作用域是什么?

  1. 作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。
  2. 全局作用域和函数作用域 在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域: 最外层函数和在最外层函数外面定义的变量拥有全局作用域

36、操作数组的方式有哪些?

  1. Array.push(),向数组的末尾添加一个或多个元素,并返回新的数组长度。原数组改变。
  2. Array.pop(),删除并返回数组的最后一个元素,若该数组为空,则返回undefined。原数组改变。
  3. Array.unshift(),向数组的开头添加一个或多个元素,并返回新的数组长度。原数组改变。
  4. Array.shift(),删除数组的第一项,并返回第一个元素的值。若该数组为空,则返回undefined。原数组改变。
  5. Array.concat(arr1,arr2…),合并两个或多个数组,生成一个新的数组。原数组不变。
  6. Array.join(),将数组的每一项用指定字符连接形成一个字符串。默认连接字符为 “,” 逗号。
  7. Array.splice(index,howmany,arr1,arr2…) ,用于添加或删除数组中的元素。从index位置开始删除howmany个元素,并将arr1、arr2…数据从index位置依次插入。howmany为0时,则不删除元素。 原数组改变。
  8. Array.sort(),对数组元素进行排序。按照字符串UniCode码排序,原数组改变。
  9. Array.map(function),原数组的每一项执行函数后,返回一个新的数组。原数组不变。(注意该方法和forEach的区别)。
  10. Array.slice() 按照条件查找出其中的部分内容
  11. Array.splice(index,howmany,arr1,arr2…) ,用于添加或删除数组中的元素。从index位置开始删除howmany个元素,并将arr1、arr2…数据从index位置依次插入。howmany为0时,则不删除元素。 原数组改变。
  12. Array.forEach(function),用于调用数组的每个元素,并将元素传递给回调函数。原数组不变。(注意该方法和map的区别,若直接打印Array.forEach,结果为undefined)。
  13. Array.filter(function),过滤数组中,符合条件的元素并返回一个新的数组。
  14. Array.every(function),对数组中的每一项进行判断,若都符合则返回true,否则返回false。
  15. Array.some(function),对数组中的每一项进行判断,若都不符合则返回false,否则返回true。
  16. Array.reduce(function), reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。 语法:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
  17. indexOf() 可返回某个指定的字符串值在字符串中首次出现的位置。
  18. includes() 判断一个数组是否包含一个指定的值 参数:指定的内容 返回值:布尔值,如果找到匹配的字符串则返回 true,否则返回 false。 是否改变原数组:不改变。

37、0.1 + 0.2 等于 0.3吗?为什么?如何解决?

计算机采用了IEEE 754标准,在计算时,将0.1和0.2转换为二进制,由于0.1和0.2的二进制数无限循环,并且尾数有限,所以丢失了精度,所以导致计算结果并不准确,

38、keep-alive是什么?有哪几个生命周期阶段?

在 Vue.js 中, 是一个抽象组件,用于缓存动态组件。当动态组件被包裹在 标签中时,这些动态组件将会被缓存起来,而不是每次切换都重新渲染。

本身并没有生命周期钩子函数,但它包裹的动态组件具有自己的生命周期。当一个被 缓存的组件被激活或停用时,它会触发一系列特定的生命周期钩子函数。

对于被 缓存的组件,以下是其生命周期钩子函数:

activated: 当被包含在 中的组件被激活时调用。这表示该组件从缓存中被加载。 deactivated: 当被包含在 中的组件被停用时调用。这表示该组件被缓存起来。

39、判断一个变量是否是数组,有哪些办法?

方式一:isArray

    var arr=[1,2,3];
console.log(Array.isArray(arr)) 

方式二:instanceof

    var arr=[1,2,3];
console.log(arr instanceof  Array) 

方式三:原型prototype

    var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );

方式四:isPrototypeOf()

    var arr = [1,2,3];
console.log(  Array.prototype.isPrototypeOf(arr) )

方式五:constructor

    var arr = [1,2,3];
console.log(  arr.constructor.toString().indexOf('Array') > -1 )

40、判断一个变量是否是对象,有哪些办法

1、使用toString()来判断;

2、使用“obj.constructor === Object”来判断;

3、使用“typeof obj === Object”来判断;

4、利用instanceof关键字来判断。

5、$.isPlainObject()?

41、对象常用方法有哪些?

1、Object.create()

参数:一个对象或者是定义好的对象

作用:创建一个空对象并使其原型指向传入的对象(vue的组件指向有用到)

2、Object.assign()

参数:一个对象或多个对象

作用:复制对象或合并对象,(复制可以是深拷贝也可以是浅拷贝,拼接空对象,没有深层次时为深拷贝,不拼接时为浅拷贝,拼接对象有深层对象时深层的对象为浅拷贝)

3、Object.defineProperty()

参数:一般收到三个参数,对象,字符串,属性对象,具体看代码

作用:添加或修改对象(vue实现响应式数据的方法)

4、Object.defineProperties()

参数:一般收到两个参数,对象、对象

作用:跟Object.defineProperty()一样的,但这个方法可以同时操作多个属性

5、Object.keys(obj)

参数:一般接收一个参数,(对象)

作用:遍历对象的key返回数组

6、Object.values(obj)

参数:一般接收一个参数,(对象)

作用:遍历对象的value返回数组

7、Object.entries(obj)

参数:一般接收一个参数,(对象)

作用:遍历对象的key与value返回二维数组

8、Object.hasOwnProperty('string')

参数:接收一个字符串

作用:检查对象中是否存在某个属性

42、创建一个空数组/空对象有哪些方式?

1.通过字面量: {}

2.通过Object

new Object()

3.通过函数

function a() {

    this.b = 2

}

let c = new a()

4.通过Object.create

Object.create()

43、哪些遍历方式会改变原数组?

一、会改变原数组的方法

1.push方法:用于在元素后面推入增加元素

2.pop方法:在数组后面删除元素

3.unshift方法:在数组前面添加元素

4.shift方法:在数组前面删除元素

5.sort方法:对数组进行排序

6.splice方法:添加或删除元素

8.reverse方法:对数组进行颠倒

二、不会改变原数组的方法

1.concat方法:用于连接两个或多个数组

2.join方法:把元素通过指定到的分隔符进行分析

3.map方法:循环遍历数组每元素

4.forEach方法:循环遍历数组每个元素(与map不同之处在于没有返回值)

5.filter方法:通过指定条件筛选出符合条件的新数组

6.findIndex方法:通过指定条件筛选出符合条件元素的索引,如果有多个就返回第一个满足条件的索引,如果没有符合条件的,就返回-1

7.reduce方法:用来累加数组中的元素求和

44、Set和Map各是什么?

区别?

共同点:集合、字典都可以存储不重复的值

不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储

二、Set

1.Set数据结构定义

用于存储任何类型的唯一值,无论是基本类型还是对象引用。

2.Set数据结构的特性

(1) 只能保存值没有键名

(2) 严格类型检测如字符串数字不等于数值型数字

(3) 值是唯一的

(4) 遍历顺序是添加的顺序,方便保存回调函数

3.Set数据结构的基本使用

(1) add: 添加元素

使用 add 添加元素,不允许重复添加相同的值

(2) has: 检测元素是否存在,返回布尔值,存在返回 true

(3) size 返回 Set 长度

(4) delete: 删除单个元素方法,返回值为boolean类型

(5) clear: 清空所有成员,没有返回值

(6) 数组转换

可以使用 点语法 或 Array.form 静态方法将Set类型转为数组,这样就可以使用数组处理函数了

三、Map

1.Map数据结构定义

Map是一组键值对的结构,用于解决以往不能用对象做为键的问题

2.Map数据结构的特性

(1) 具有极快的查找速度

(2) 函数、对象、基本类型都可以作为键或值

3.Map数据结构的基本使用

(1) set 添加元素,支持链式操作

(2).get 获取元素

(3).size 获取数量,返回map长度

(4).has 利用key值检测是否存在,返回布尔值,存在返回true

(5).delete方法 删除单个元素

(6).clear方法 清空map所有元素

(7).数据转换

可以使用 展开语法 或 Array.form 静态方法将Map类型转为数组,这样就可以使用数组处理函数了

45、介绍一下promise。

  1. promise简介

在JavaScript中,Promise是一种处理异步操作的机制。它是ES6(ECMAScript 2015)引入的一种语言特性,用于解决回调地狱(callback hell)问题,并使异步代码更具可读性和可维护性。

Promise可以看作是对异步操作的封装,它代表了一个未完成到已完成的操作,并可以返回操作的结果或错误。一个Promise对象有三个状态:

Pending(进行中): 初始状态,操作正在进行中,尚未完成。

Fulfilled(已完成): 操作已经成功完成。

Rejected(已失败): 操作失败或出错。

finally(onFinally): 该方法在Promise对象无论是被解析(fulfilled)还是被拒绝(rejected)时都会执行,无论前面的操作结果如何。它接收一个回调函数作为参数,在Promise执行结束后调用该回调函数。

all(iterable): 该方法接收一个可迭代对象(比如数组)作为参数,返回一个新的Promise对象。这个新的Promise对象在可迭代对象中的所有Promise对象都解析(fulfilled)后才会解析,或者只要有一个Promise对象被拒绝(rejected)就会被拒绝。返回的Promise对象的解析值是一个数组,包含了可迭代对象中所有Promise对象的解析值,顺序与可迭代对象中的顺序一致。

race(iterable): 该方法接收一个可迭代对象(比如数组)作为参数,返回一个新的Promise对象。这个新的Promise对象将解析(fulfilled)或拒绝(rejected)取决于可迭代对象中最先解析或拒绝的Promise对象。返回的Promise对象将具有第一个解析或拒绝的Promise对象的解析值或拒绝原因。

46、Promise通常会解决三种问题

(1)链式回调

(2)同时发起几个异步请求,谁先有结果就拿谁的

(3)发起多个请求,等到所有请求后再做下一步处理

这三种方式promise是怎么处理的?

47、如何改变一个函数a的上下文?

1.将函数挂载在对象上

    function getdata(fn,obj){
 obj.fn = fn
 return obj.fn()
}

2.call

    function getdata(fn,obj){
 return fn.call(obj)
}

3.apply

    function getdata(fn,obj){
 return fn.apply(obj)
}

4.bind

   function getdata(fn,obj){
 return fn.bind(obj)()
} 

48、Call和replay有什么区别?

call、apply、bind的区别

call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined

call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组

bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入

值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)

49、Evenbus是什么东西?

EventBus又称为事件总线。在Vue中可以使用EventBus来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所有组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。

vue项目中如何使用EventBus?
1.首先创建事件总线并将其导出,以便其它模块可以使用或者监听它。我们可以通过两种方式来处理。先来看第一种,新创建一个.js文件,比如event-bus.js

50、Vue中普通的生命周期大概有哪些?

1.beforeCreate(实例创建前)

Vue实例开始初始化事件和生命周期函数,data为空,el为空。(可加loading效果)。

2.created(实例创建后)

Vue实例已创建好,事件配置完毕。data有值,el为空。(结束loading效果,可发请求获取数据)。

3.beforeMount(挂载前)

首次调用render函数,编译模板把data数据和模版生成html,但html未挂载。data有值,el有值。

4.mounted(挂载后)

把编译好的html(vm.$el)替换el属性指向的dom对象,渲染完成。

5.beforeUpdate(更新前)

当数据发生改变和视图更新之前,新数据和旧视图未同步。

6.updated(更新后)

虚拟dom重新渲染结束,视图更新完成。可操作更新后的dom。

7.beforeDestroy(销毁前)

将要销毁组件,此时Vue实例仍然可用。

8.destroyed(销毁后)

Vue实例所有的指令都解除绑定,然后销毁组件和移除事件监听器。

51、父子组件生命周期执行顺序是怎么样的?

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

加载渲染过程

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

52、mixins有几个生命周期阶段?

mixins有两个生命周期阶段,分别是beforeCreate和created。

53、弹性布局,一行两列,一列固定宽,如何实现?

可以使用flex布局,将父元素的display属性设置为flex,然后设置flex-direction为row,即一行两列的布局;然后在第一列的子元素中设置固定宽度,可以使用flex-basis属性来设置。

54、Flex:1 包含哪三种属性

1、flex-grow

flex-grow属性:flex容器有剩余空间时可用,用于分配剩余空间的属性,默认值为0,表示不伸展。

2、flex-shrink

flex-shrink属性:flex容器空间不足时可用,默认值为1,代表当项目空间不足时子item等比例收缩,设置为0时则不会收缩,item会超出容器范围

3、flex-basis

flex-basis属性:想要平均分配item所占空间时可用,或自定义每个item占多少空间。 例子:可定义每个子item占容器多少空间,如有十个item,则比例可定义为百分之10%,平均分配空间

55、vw,vh和%的区别

vh / vw 与 %的区别在于,一个针对窗口计算,一个针对父元素计算