当前位置:   article > 正文

前端面试100问

前端面试100问

JS相关

全清本地存储

localStorage.clear()

js基本数据类型

bool string number obj undefined null

var let const 区别,以及在方法内声明,外面是否可以取到

1.var声明变量存在变量提升,let和const不存在变量提升
(在JavaScript中,在方法体外外用var定义的变量其它方法可以共享,在方法中用var定义的变量只有该方法内生效。)
2.let、const都是块级局部变量
(const 的特性和 let 完全一样,不同的只是声明时候const必须赋值,只能进行一次赋值,即声明后不能再修改)

undefined 和 null 的区别

(1)null是一个表示”无”的对象,转数值是为0,undefined是一个表示”无”的原始值,转为数值时为NaN。当声明的变量还未被初始化时,能量的默认值为undefined

(2)Null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象

(3)Undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:

a、变量被声明了,但没有赋值时,就等于undefined

b、调用函数时,应该提供的参数没有提供,该参数等于undefined

c、对象没有赋值属性,该属性的值为undefined

d、函数没有返回值时,默认返回undefined

(4)null表示”没有对象”,即该处不应该有值。典型用法是:

a、作为函数的参数,表示该函数的参数不是对象

b、作为对象原型链的终点

对象的合并方法

1.使用三点运算符…
eg:let new_obj = { …obj1, …obj2 };
特点:如果对象有相同的属性,后面的覆盖前面的
2.使用Object.assign()
eg:let ret1 = Object.assign({}, obj1, obj2);
特点:如果对象有相同的属性,后面的覆盖前面的
第一个参数会被改变

数组的一些操作命令

concat()连接两个或更多的数组,并返回结果。
join()把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
shift()删除并返回数组的第一个元素
pop()删除并返回数组的最后一个元素
unshift()向数组的开头添加一个或更多元素,并返回新的长度。
push()向数组的末尾添加一个或更多元素,并返回新的长度。
splice()删除元素,并向数组添加新元素。
slice()从某个已有的数组返回选定的元素
reverse()颠倒数组中元素的顺序。
sort()对数组的元素进行排序
toSource()返回该对象的源代码
toString()把数组转换为字符串,并返回结果。
toLocaleString()把数组转换为本地数组,并返回结果。
valueOf()返回数组对象的原始值。

数组去重

function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j,1);
                    j--;
                }
            }
        }
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}没有去重,两个null直接消失了
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

有关深拷贝、浅拷贝

一般基本类型不会产生深浅拷贝问题,因为是直接拷贝数据。但是因为引用类型的实际值存储在堆中,而引用地址存储在栈中,所以在进行复制操作时,会产生此问题。
最简单的浅拷贝
var arr1 = new Array(12,23,34)
Var arr2 = arr1;
当进行 arr2[0] = 1;的操作时,arr1[0]的值也进行了改变为1;
深拷贝一般使用循环数组或者对象的方式解决,如果对象的属性都是基本类型,那么直接进行循环即可,如果存在引用类型,则在判断出类型为obj时先new一个空值,再复制

var p2 = {};
for(let key in p){
	if(typeof p[key]=='object'){
		p2[key]=[];//因为,我上面写的是数组,所以,暂时赋值一个空数组.
		for(let i in p[key]){
			p2[key][i] = p[key][i]
		}
	}else{
		p2[key] = p[key];
	}
}
p2.books[0] ="四国";
console.log(p2);
console.log(p);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

如果对象的属性都是json对象,则要用到递归

var p = {
	"id":"007",
	"name":"刘德华",
	"wife":{
		"id":"008",
		"name":"刘德的妻子",
		"address":{
			"city":"北京",
			"area":"海淀区"
		}
	}
}
 
//写函数
function copyObj(obj){
	let newObj={};
	for(let key in obj){
		if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归
			newObj[key] = copyObj(obj[key])
		}else{//基本类型,直接赋值
			newObj[key] = obj[key];
		}
	}
	return newObj;
}
 
let pNew = copyObj(p);
pNew.wife.name="张三疯";
pNew.wife.address.city = "香港";
console.log(pNew);
console.log(p);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

如果深拷贝_属性是数组等非键值对的对象
就得单独处理:要么给数组增加一个自我复制的函数(建议这样做),要么单独判断。


//给数组对象增加一个方法,用来复制自己
Array.prototype.copyself = function(){
	let arr = new Array();
	for(let i in this){
		arr[i]  = this[i]
	}
	return arr;
}
 
var p = {
	"id":"007",
	"name":"刘德华",
	"books":new Array("三国演义","红楼梦","水浒传")//这是引用类型
}
 
function copyObj(obj){
	let newObj={};
	for(let key in obj){
		if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归
			newObj[key] = obj[key].copyself();
		}else{//基本类型,直接赋值
			newObj[key] = obj[key];
		}
	}
	return newObj;
}
 
var pNew = copyObj(p);
pNew.books[0] = "四国";
console.log(pNew);
console.log(p);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

判断数据类型的方式

  1. typeof {} // object
  2. console.log(arr instanceof Array ); // true arr是数组
  3. console.log('arr.constructor === Array);//true arr是数组

localStorage,seessionStorage,cookie区别,特性,优点

三者区别
用途
cookie 存token 判断是否登录
localStorage 存储购物车数据 或者 H5游戏的一些本地数据

懒加载图片

js实现 :img标签只有在src有有效值的时候才会发起请求。所以先将src写为一个占位图的路径,再给src设置一个data-src属性为真实图片路径,当document.body.scrollTop(被卷进去的高度)刚好为HTMLElement.offsetTop(标签离顶部的距离)把data-src赋值给src;
vue可以使用:vue-lazyload插件

解构赋值,扩展运算符,promise

1.解构赋值可以用于交换变量的值

let x = 1,y =2;
[x,y] = [y,x];
x//2
y//1
  • 1
  • 2
  • 3
  • 4

2.从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将他们放在数组或者对象返回,利用解构赋值,取出这些值就非常方便

function example () {
          return [1,2,3];
}
let [a,b,c] = example();

function example() {
 return {name:"MGT360124",age:18}
}
let {name,age} =example();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

扩展运算符
对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中

let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }
上述方法实际上等价于:
let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }
  • 1
  • 2
  • 3
  • 4
  • 5

promise
查看此处
promise的出现主要是为了避免页面冻结.
三个状态
pending 待定 初始状态
fulfilled 实现 操作成功
rejected 被否决 操作失败

CSS相关

页面自适应 rem 和 em

rem 是当前HTML根元素的font-size
em 会继承父级元素的字体大小

css定位position 属性

1.static
静态定位的元素不受 top、bottom、left 和 right 属性的影响。
position: static; 的元素不会以任何特殊方式定位;它始终根据页面的正常流进行定位
2.relative
设置相对定位的元素的 top、right、bottom 和 left 属性将导致其偏离其正常位置进行调整。不会对其余内容进行调整来适应元素留下的任何空间。
3.fixed;
元素是相对于视口定位的,这意味着即使滚动页面,它也始终位于同一位置。 top、right、bottom 和 left 属性用于定位此元素。
4.absolute;
position: absolute; 的元素相对于最近的定位祖先元素进行定位。
然而,如果绝对定位的元素没有祖先,它将使用文档主体(body),并随页面滚动一起移动。
注意:“被定位的”元素是其位置除 static 以外的任何元素。
5.position: sticky;
元素根据用户的滚动位置进行定位。
粘性元素根据滚动位置在相对(relative)和固定(fixed)之间切换。起先它会被相对定位,直到在视口中遇到给定的偏移位置为止 - 然后将其“粘贴”在适当的位置(比如 position:fixed)

css水平垂直居中

1.弹性布局

	display: flex;
	align-items: center;//侧轴居中(默认是纵轴)
	justify-content: center;//主轴居中
  • 1
  • 2
  • 3

2.绝对定位+transform: translate(x, y);

3.text-align + line-height实现单行文本水平垂直居中
4.text-align + vertical-align
在父元素设置text-align和vertical-align,并将父元素设置为table-cell元素,子元素设置为inline-block元素

画三角形状

主要利用边框的transparent实现

实现字体小于10px边框为0.5px

对于需要四边0.5像素边框,可以用以下方式:

方式:定位 + 伪元素 + transfrom缩放(scale);
字体小于12的需求 也可以计算出与12的比例进行缩放
<div class="border">0.5像素边框~~~~</div>
<style>
    .border {
        width: 200px;
        height: 200px;
        margin: 0 auto;
        position: relative;
    }
    .border::before {
        content: " ";
        position: absolute;
        top: 0;
        left: 0;
        width: 200%;
        height: 200%;
        border: 1px solid red;
        transform-origin: 0 0;
        transform: scale(0.5);
    }
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

VUE相关

VUE双向绑定原理

首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;

然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会;

接着为input会添加监听事件,修改值就等于为该属性赋值,则会触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。(转自此处

VUE的生命周期以及渲染过程

vue 的生命周期是: vue 实例从创建到销毁,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程。
生命周期
整体流程

VUE 路由跳转传参

1.点击验证动画效果
2.this.$router.push({ path:’/user’})

 this.$router.push({  //核心语句
        path:'/select',   //跳转的路径
        query:{           //路由传参时push和query搭配使用 ,作用时传递参数
          id:this.id ,
        },
        params: {
            id: id
          }       
      })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

query拼接在路径后面,刷新不会丢失

VUE强制更新页面

1.简单粗暴的方式
this. f o r c e U p d a t e ( ) ; / / 强制更新 2. 增减元素 s p l i c e ( ) p u s h ( ) 3. 通过 forceUpdate();//强制更新 2.增减元素 splice() push() 3.通过 forceUpdate();//强制更新2.增减元素splice()push()3.通过set进行更新
this.$set(v, ‘edit’, false)
4.异步dom更新
Vue.nextTick()
.then(function () {
// DOM 更新了
})

VUE 父子组件通信

1.父传子
子组件用props接收
2.子传父
子组件通过事件向父组件传值
先在父组件里面写一个人 fuc1 用@fuc1=fuc1传进子组件,子组件调用this.$emit(‘fuc1’, this.message); this.message就是从子组件里面传递出来的参数
3.组件之间的传值

VUEX

vue中vuex的五个属性和基本用法
VueX 是一个专门为 Vue.js 应用设计的状态管理构架,统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。
Vuex有五个核心概念:
  state, getters, mutations, actions, modules。
  1. state:vuex的基本数据,用来存储变量
   2. geeter:从基本数据(state)派生的数据,相当于state的计算属性
   3. mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
   回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。
   4. action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。
   5. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

onst user = {
        state:{
            self: null,
            token: '',
        },
        mutations:{
            SET_SELF: (state, self) => {
                 state.self = self
             },
             SET_TOKEN: (state, token) => {
                 state.token = token
             }
        },
        actions:{
             login ({ commit }, res) {
                  commit('SET_SELF', res.self)
                  commit('SET_TOKEN', res.token
            }       
}
export default user   
    dispatch:异步操作,写法: this.$store.dispatch('mutations方法名',)

  commit:同步操作,写法:this.$store.commit('mutations方法名',)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

watch 和 computed的区别

通俗来讲,既能用computed 实现又可以用 watch 监听来实现的功能,推荐用 computed,重点在于 computed 的缓存功能

computed计算属性是用来声明式的描述一个值依赖了其它的值,当所依赖的值或者变量改变时,计算属性也会跟着改变;

watch 监听的是已经在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法
在这里插入图片描述

vue中使用element框架修改样式不生效问题

解决办法:
1.修改为全局样式 //去掉scoped
2、全局及组件作用于CSS混用

<style scoped>
...
</style>
<style>
.el-table .warning-row {
    background: oldlace;
  }
  .el-table .success-row {
    background: #f0f9eb;
  }
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、深度修改样式

<style scoped>
//方法一
/deep/ .el-table .warning-row {
    background: oldlace;
}
//方法二,page_bar为外层类名,是否为直接父级均可
.page_bar >>> .el-table .warning-row {
    background: oldlace;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

vue路由钩子

路由钩子主要作用是拦截导航,让他完成跳转或者取消跳转。比如登录界面输入了账号、密码,主界面需要展示账号,但是你没有把 “账号” 这个字段保存到 vuex 或者 session 里面,直接跳转会导致主界面显示空白,这个时候你就需要一个 beforeRouteLeave 路由钩子,还没有数据的情况下,禁止界面跳转,举例子(伪代码):

beforeRouteLeave(to,from,next){
  if('account' === ' '){
      next(false);
  }else{
    next();
  };
};
//参数 to ——是要跳转到的界面
//from —— 从哪个界面离开
//next() —— 是否允许跳转
	1.如果是 next(false) ——禁止跳转
	2.next({name:LOGIN}) —— 跳转到登录界面(需要自己手动配置路由)
	3.next() 或者 next(true) ——允许跳转
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

路由钩子的实现方式有三种
1.全局实现 beforeEach

var routes = [{
  path:'/route1',
  name:'route1',
  component:() = > import('./index.vue')  //调用的时候再开始加载
}]
 const router = new VueRouter({ 
    routes;
  })
router.beforeEach((to, from, next) => {
  next(false);
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.路由独享 配置路由的时候可以直接增加beforeEnter

var routes = [{
  path:'/route1',
  name:'route1',
  component:() = > import('./index.vue')  //调用的时候再开始加载
  beforeEnter: (to, from, next) => {
       next();
  }
}]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.组件内实现的钩子
beforeRouteEnter:在导航确认之前调用,新组件的 beforeCreate 之前调用,所以特别注意它的 this 是 undefined
beforeRouteUpdate:点击更新二级导航时调用。
beforeRouteLeave:离开当前界面之前调用,用法:1. 需要的保存或者删除的数据没有完成当前操作等等原因,禁止界面跳转。
引自这里

vue路由跳转的方式

1.router-link(声明式路由)

1. 不带参数
<router-link :to="{name:'home'}"> 
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name  
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
2.带参数
<router-link :to="{name:'home', params: {id:1}}">  
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id" 
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参  $route.params.id
// script 取参  this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}"> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.router.push(编程式路由)

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:

const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
  • 1
  • 2
  • 3
  • 4
  • 5

3.this.$router.push()(函数里面调用)

1.  不带参数
 
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
 
2. query传参 
 
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
 
// html 取参  $route.query.id
// script 取参  this.$route.query.id
 
3. params传参
 
this.$router.push({name:'home',params: {id:'1'}})  // 只能用 name
 
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
 
// html 取参  $route.params.id
// script 取参  this.$route.params.id
 
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

4.replace()(用法同上,push)
5.go(n)

this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
ps : 区别
this.$router.push
跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
————————————————
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意:获取路由上面的参数,用的是$route,后面没有r
params是路由的一部分,必须要有。query是拼接在url后面的参数,没有也没关系。
params一旦设置在路由,params就是路由的一部分,如果这个路由有params传参,但是在跳转的时候没有传这个参数,会导致跳转失败或者页面会没有内容。
params、query不设置也可以传参,但是params不设置的时候,刷新页面或者返回参数会丢失,

两者都可以传递参数,区别是什么?
query 传参配置的是path,而params传参配置的是name,在params中配置path无效
query在路由配置不需要设置参数,而params必须设置
query传递的参数会显示在地址栏中
params传参刷新会无效,但是query会保存传递过来的值,刷新不变

vue项目搭建

1、全局安装vue-cli

npm install --global vue-cli

2、进入你的项目目录,创建一个基于 webpack 模板的新项目: vue init webpack 项目名
进入项目:cd vue-demo,安装依赖

VUE 组件间传值的六种方式

摘抄: 点击跳转

1.props / emit
2.emit / on

Var Event=new Vue();//定义一个空的Vue实例
Event.$emit(事件名,数据); 调用自定义事件
Event.$on(事件名,data => {})//mounted里面写监听自定义事件
  • 1
  • 2
  • 3

3.vuex
在这里插入图片描述
4.attrs / listeners
attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了 attrs , listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。

简单来说: attrs与 listeners 是两个对象, attrs 里存放的是父组件中绑定的非 Props 属性, listeners里存放的是父组件中绑定的非原生事件
5.provide/inject
Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
6.parent / children  ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
parent / children:访问父 / 子实例
总结
常见使用场景可以分为三类:

父子通信: 父向子传递数据是通过 props,子向父是通过 events( emit);通过父链 / 子链也可以通信( parent / children);ref 也可以访问组件实例;provide / inject API; attrs/listeners
兄弟通信: Bus;Vuex
跨级通信: Bus;Vuex;provide / inject API、 attrs/listeners

gulp、webpack 打包原理

1.webpack打包原理
把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载。
2.webpack 和 gulp 的区别?
webpack是一个模块打包器,强调的是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源都看成是模块,通过loader和plugin对资源进行处理。

gulp是一个前端自动化构建工具,强调的是前端开发的工作流程,可以通过配置一系列的task,第一task处理的事情(如代码压缩,合并,编译以及浏览器实时更新等)。然后定义这些执行顺序,来让glup执行这些task,从而构建项目的整个开发流程。自动化构建工具并不能把所有的模块打包到一起,也不能构建不同模块之间的依赖关系。

axios的封装

小程序相关

微信小程序 h5 保存图片

微信小程序 调用 wx.saveImageToPhotosAlbum
h5 生成一个a标签

小程序传值

url 本地 全局App对象

小程序分包

小程序官方API
开发者通过在 app.json subpackages 字段声明项目分包结构
小程序分包详解,以及优劣

八股文系列

闭包优缺点

1.什么是闭包
闭包是指有权访问另外一个函数作用域中的变量的函数.可以理解为(能够读取其他函数内部变量的函数)
2. 闭包的作用
正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的 变量,在函数执行完之后依旧保持没有被垃圾回收处理掉
1.密闭的容器,存储数据的
2.闭包是一个对象,存放数据
3.内部函数引用外部函数的局部变量
4.闭包优缺点:
优点:延长外部函数局部变量生命周期
缺点:长时间占用容易内存泄露
在上面优缺点中写道能延长外部函数局部变量的声明周期,但是长时间占用内存;当闭包不在使用时记得及时释放。

原型链(空缺)

MVC、MVVM模式的概念与区别

这些模式是依次进化而形成MVC/MVP—>MVVM。在以前传统的开发模式当中即MVC模式,前端人员只负责Model(数据库) View(视图) Controller /Presenter/ViewModel(控制器) 当中的View(视图)部分,写好页面交由后端创建渲染模板并提供数据,随着MVVM模式的出现前端已经可以自己写业务逻辑以及渲染模板,后端只负责提供数据即可,前端所能做的事情越来越多.

节流和防抖函数(空缺)

不规则形状标签

canvas

行级元素,块级元素有哪些

(一)块级元素
div、p、h1-h6、form、ul、ol、dl、dt、dd、li、table、tr、td、th、hr、blockquote、address、table、menu、pre
性质:块级元素独占一行,当没有设置宽高时,它默认设置为100%(其宽度自动填满其父元素宽度)
块级元素允许设置宽高,width、height、margin、padding、border都可控制
注:块级元素设置了width宽度属性后仍然独占一行,块级元素可以包行内元素、块级元素
(二)行内元素(内联函数)及行内块元素

(1)span、img、a、label、code、input、abbr、em、b、big、cite、i、q、textarea、select、small、sub、sup,strong、u
button(display:inline-block)
HTML5:header、section、article、footer等
性质:
1.行内元素不能独占一行,与其他行内元素排成一行,其宽度随元素的内容变化而变化
2.行内元素不能设置width、height、margin、padding
3.行内元素默认宽度为其content宽度
4.行内元素只能包括文字或行内元素、行内块元素,不能包括块级元素
display:inline-block:行内块元素与行内元素属性基本相同即不能独占一行,但是可以设置width及height
5.行内元素的水平方向的padding-left和padding-right都会产生边距效果,但是竖直方向上的padding-top和padding-bottom都不会产生边距效果
(2)有一些特别的行内元素可设置宽高
替换元素:< img>、< input>、< textarea>、< select>、< object>

这些元素与其他行内元素不同的是,它有内在尺寸。因为它像是一个框,比如img元素,它能显示出图片是由于src的值,在审查元素时就不能直接看到图片,而input是输入框或是复选框也是因为其type的不同。

这种需要通过属性值显示的元素,其本身是一个空元素,像一个空的框架。

新增语义化标签

- <header>:头部标签
- <nav>:导航标签
- <article>:内容标签
- <section>:块级标签
- <aside>:侧边栏标签
- <footer>:尾部标签
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

国际化是否可以使用插槽

可以使用

<template>
  <div>
    <p>{{ $t('greeting') }}</p >
    <p>{{ $t('message', { name: 'John' }) }}</p >
  </div>
</template>

<script>
export default {
  data() {
    return {
      greeting: 'Hello',
      message: 'Welcome, {name}!',
    };
  },
};
</script>

<!-- 在 i18n 文件中定义国际化文本 -->
<i18n>
{
  "en": {
    "greeting": "Hello",
    "message": "Welcome, {name}!"
  },
  "zh-CN": {
    "greeting": "你好",
    "message": "欢迎,{name}!"
  }
}
</i18n>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

什么时候触发重绘重排

重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。

重排:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

浏览器解析渲染机制如下:
①.解析HTML,生成DOM树,解析CSS,生成CSSOM树
②.将DOM树和CSSOM树结合,生成渲染树(Render Tree)
③.Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
④.Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
⑤.Display:将像素发送给GPU,展示在页面上
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变。
当我们对 DOM 的修改引发了 DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来。
常见的引起重绘的属性
color border-style visibility background text-decoration
background-image background-position background-repeat outline-color outline
outline-style border-radius box-shadow background-size outline-width
常见引起重排属性和方法
width height margin padding display
border-width border position overflow font-size
vertical-align min-height clientWidth clientHeight clientTop
clientLeft scrollWidth scrollHeight scrollTop
scrollLeft getComputedStyle() getBoundingClientRect() scrollIntoViewIfNeeded() 伪类:如:hover
什么情况下会触发重绘
触发回流时一定会触发重绘,除此之外,例如颜色修改,设置圆角、文本方向修改,阴影修改等。
什么情况下会触发回流
回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:
①.添加或删除可见的DOM元素
②.元素的位置发生变化
③.元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
④.内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
⑤.页面一开始渲染的时候(这避免不了)
⑥.浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
提示:重绘不一定导致重排,但重排一定会导致重绘。

浏览器从输入URL到显示页面的过程

1.输入网址,浏览器智能匹配URL。
2.浏览器查找域名的IP地址。
3.浏览器向web服务器发送一个HTTP请求。
4.服务器处理请求。
5.服务器返回一个HTTP响应。
6.浏览器跟踪重定向地址。
7.服务器的永久重定向响应。
8.浏览器显示HTML。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/611632
推荐阅读
相关标签
  

闽ICP备14008679号