浏览器的渲染过程
解析HTML,生成DOM树,解析CSS,生成CSS树,将DOM树与CSS树结合起来生成渲染树。根据生成的渲染树,进行页面回流,得到元素的几何信息(位置,大小),根据得到渲染树和几何信息,得到具体的像素点,进行页面的重绘。将像素发给GPU,在页面上进行显示。
回流
构造了渲染树,可以将DOM元素与它对应的样式结合起来,得到对应dom元素在视口内的位置和大小。只要页面布局发生变化就会触发回流
重绘
上面我们知道了渲染树以及回流知道了对应元素的具体位置及大小信息,得到具体的像素值这时候将渲染树上的节点根据大小位置信息,渲染到页面上,叫做重绘。只要改变某个元素的背景色,文字颜色,边框颜色等不影响它周围或内部布局的属性,就会发生重绘
性能优化
尽量减少页面的回流和重绘
概念:BFC(块级格式化上下文)是一块独立渲染的区域,独立且不会影响外部元素
怎样形成BFC:
1.设置浮动float
2.设置定位,absoulte或者fixed
3.行内块显示模式,inline-block
4.设置overflow,即hidden,auto,scroll
5.表格单元格,table-cell
6.弹性布局,flex
利用BFC解决什么问题
1.解决margin塌陷(垂直方向)
2.清除浮动
3.BFC可以阻止标准流元素被浮动元素覆盖
数据类型
1.基本数据类型:Number、Boolean、String、undefined、null
2.复杂数据类型:数组、对象、函数
数据类型判断方法
1.typeof 可以检测string、Boolean、number、undefined、function,其中null和array(检测出来是object),原因在于,null和数组被当作一个空对象去引用
2.instanseof(推荐使用)
用来判定对象的具体类型,用法:a instanse of A ,意思是判断a是否是A的实例,返回值是布尔值
3.Array.isArray(需要检测的数组)可以检测一个数据是否是数组
4.Object.prototype.toString.call(),会返回一个形如 “[object XXX]” 的字符串
1.若使用数组index当作key,当向数组中新插入一个元素后,这时会更新索引,对应着后面的虚拟dom的key都会全部更新,这些更新都是不必要的
2.如果没有key的话,默认的是就地复用的策略,如果数据项的顺序发生改变,vue不是移动dom来匹配数据项的改变,而是简单复用原来位置的每个元素,当比较到这个元素的值不一样的时候,将新的值放到该位置,以此类推,key的作用主要是为了高效的更新虚拟DOM
#box {
height: 44px //用postcss-px2rem插件配置后相当于0.44rem
width: 100%
font-size: 24px;/*no*/ //如果不想用插件转换可以用/*no*/标识符
}
创建:beforecreate、created
beforecreate:
第一个钩子,这个阶段的data、methods、computed以及watch的数据和方法不能被访问
created:
这个阶段完成数据观测,数据初始化完成,可以使用数据,更改数据
无法与dom进行交互,想要的话可以用过nextTick来访问,nextTick延迟回调,更新数据后立即操作dom
挂载:beforeMount、mounted
beforeMount:
发生在页面渲染之前,当前阶段虚拟Dom已经创建完成,即将开始渲染。
在此时也可以对数据进行修改,不会触发updated
mounted:
数据挂载到dom上,数据完成双向绑定,可以访问到dom节点,使用$refs属性对dom进行操作
补:第一次页面加载就会触发 创建和挂载钩子
更新 beforeUpdate、updated
beforeUpdate:
发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发
可以在当前阶段进行更改数据,不会造成重复渲染
uodated:
发生在更新完成以后,当前阶段组件dom已完成更新
需注意的是避免在此期间更改数据,因为这可能导致无限循环的更新
销毁 beforeDestory、destroyed
beforeDestory:
发生在市里销毁之前,在当前阶段实例完全可以被使用
可以在这进行善后收尾工作,如清楚定时器
destroyed:
发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被清除,监听被移出,子实例也统统被销毁
补充:
activited:
keep-alive专属,组件被激活时调用
deactivited:
keep-alive专属,组件被销毁时调用
v-if 只有条件为真的时候才会真正渲染标签,不适合频繁渲染
v-show 基于display切换 适合频繁渲染
不推荐v-if与v-for使用,因为会把每个元素都增加v-if,造成性能问题
1.父传子:
父组件使用 :参数名=要传递的参数 :num=1000
子组件使用props接收 (props:[属性名])
子组件接收的另一种方式:
props:{
num:{
type:NUmber,//数据类型
required:true,//必须传值
default:{
100
},//默认携带的数字
}
}
2.子传父
1.**传递**:在调用子组件的位置,添加自定义事件,子组件使用$emit传递数据
3.父组件调用子组件的方法获取子组件的参数
父组件上面定义ref="child"
父组件里调用子组件的方法:this.$refs.child.showPhone() showPhone是子组件里面的方法
父组件里获取到子组件的参数:this.$refs.child.phone phone是子组件里的变量
4.子组件直接调取父组件数据参数和方法
子组件中获取数据参数:this.$parent.parentData parentData为父元素的变量
子组件中获取方法:this.$parent.showParent() showParent为父元素的方法
1.computed属性
1.支持缓存,只在依赖的数据发生变化时,才会重新计算,得到的为一个数据结果
2.不支持异步,当computed内有异步操作时无效,无法监听数据的变化
3.使用方式
- 模版
用插值表达式{{计算属性名}}
- 在实例内
this.计算属性名
代码演示:
{
data(){
},
methods:{},
computed:{
计算属性名1(){
//对依赖的属性进行处理,且进行return
return
},
计算属性2(){
//对依赖的属性进行处理,且进行return
return
}
}
}
2.watch监听
1.不支持缓存,每调用一次就计算一次
2.支持异步
3.用于监听data里面的数据是否被修改,一旦修改就可以执行一些其他的操作【方法】
4.在监听的时候,可以有二次参数,第一次参数为新数据,第二次为旧数据
5.高级监听:可以监听单数据、数组,但是当监听对象的时候,明明数据修改了,却没有触发监听,此时需要开启深度监听,watch只会监听第一层
{
data:{},
watch:{
//监听器的作用就是监听数据是否发生了变化,变化后可以进行一些其他的操作
//只要没有发生变化,就没有办法进行其他操作
text(newValue,oldValue){
},
deep: true, // 是否深度监听
immediate: true, // 是否在组件创建时立即执行回调函数
}
}
1.由于vue会在初始化实例的时候进行双向数据绑定,所以属性必须是在data对象存在才能让他响应,如果要给对象添加新的属性,此时新属性并没有进行上述过程,不是响应式的,所以会出现数据变化,页面不变的情况,此时需要用到$set
2.实例
myInfo:{
name:'xiaohua',
age:'18'
}
this.$set(this.myInfo,'age',24)
1.概念:路由指的是应用程序中的一个页面,是一个js对象
2.vue-router路由模式有几种?
- hash路由:在地址栏会有一个#号,hash发生变化的url都会被浏览器记录下来,浏览器的前进后退可以用
- history路由:
history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。提供了对历史记录进行修改的功能。它能让开发人员在不刷新网页的情况下改变站点的 URL。History 有 URL 重定向问题,需要在服务端去配置 url 重定向,否则会报 404 错误。
3.router与route的区别
- $router 路由实例,用来操作路由,包括push、replace、go、forward等方法,可以动态更改url,从而实现页面间的无刷新跳转
- $route 路由信息对象 包括URL 路径、查询参数、路径参数等信息,path、params、query参数,只读对象
4.params传参与query传参
- params传参类似于post请求,参数不会写到地址栏中(但是不能刷新),params只能配合name使用,假如使用动态路由'/user:id',则可以解决这个问题。
- query传参类似于get传参,传过去的参数会显示在地址栏中,query既可以配合name使用,又可以配合path使用
- 接收参数this.$route.query.id this.$route.params.id
// 路由配置
{
path: '/user',
component: User,
props: { id: userId }
}
然后再目标页面里使用props去接收参数
// 在目标组件中获取传递的参数
export default {
props: ['id'],
created() {
console.log(this.id)
}
}
需要注意的是,在使用属性传参时,必须在目标组件中声明`props`,否则会抛出警告。同时,在使用属性传参时,可以使用`props: true`来将路由参数自动注入到目标组件的props中。
5.路由跳转
- 声明式路由router-link to=`/path?参数名=参数值`
- 编程式路由跳转
this.$router.push({name:'hello',query:{id:1}})
this.$rouer.push({name:'hello',params:{id:1}})
this.$router.push({path:'/hello',query:{id:1}})
6.vue路由重定向
const routes = [
{ path: '/', redirect: '/index'},
{ path: '/index', component: index }
]
7.路由守卫
to为目标路由、from为当前路由 next()为跳转 可以用来做登录拦截
router.beforeEach((to, from, next) => {
document.title = to.meta.title || '卖座电影';
if (to.meta.needLogin && !$store.state.isLogin) {
next({
path: '/login'
})
} else {
next(
{
path:'/',
query:{
redirect: to.fullPath
}
})
}
})
8.路由懒加载
- 为什么需要懒加载,像vue这种单页面应用,如果没有懒加载,运用webpack打包后的文件将会异常的打,造成首页加载长时间的白屏,并且加载时间长。运用懒加载可以减少首页加载用时
{
path: '/login',
name: 'Login',
component: () => import('../views/login/login.vue')
},
9.路由监听
watch:{$route(to,from){ to代表的是当前路由,from指的是上一个页面路由,监听到路由变化后可以拿参数等东西 比如to.query.name等 }}
页面切换的时候会进行销毁,当我们不想让它销毁的时候就需要用keep-alive包裹着组件
在组件切换过程中将状态保留在内存中,防止重复渲染dom,减少加载时间以及性能消耗,提高用户体验
根据条件缓存页面
APP.js页面
<keep-alive>
<!-- 添加meta的则添加 keep-alive -->
<router-view v-if="$route.meta.keep"></router-view>
</keep-alive>
<!-- 不添加 -->
<router-view v-if="!$route.meta.keep"></router-view>
路由页面给需要的添加meta
{
path: '/',
name: 'home',
alias: '/home',
components:{ home },
meta:{
keep:true //需要缓存
}
},
concat :连接两个或多个数组,并返回结果
join:将数组的所有元素放入一个字符串,元素通过指定的分隔符进行分割
pop:删除并返回数组的最后一个元素
push:向数组的末尾添加一个或者更多元素,并返回新的长度
shift:删除并返回数组的第一个元素
unshift:向数组的开头添加一个或者更多元素,并返回新的长度
reverse:颠倒数组中元素的排列顺序
slice:从某个已有的数组返回截取的元素,返回是新数组,不改变原数组
sort:对数组的元素进行排序
splice:删除元素,并向数组中添加新元素。返回截取的参数改变原数组
toString:将数组转换成字符串,并返回结果
indexOf:查找是否有符合的元素,无则输出-1
map:调用每个元素进行运算,返回新的数组
foreach:列出数组的每个元素,并不会改变原数组
filter:过滤出一个新的子数组,返回条件为true的值
some:查询是否有元素符合条件,输出布尔值
every:查询是否所有元素符合条件,输出布尔值
reduce:计算数组元素相加后的总和
reduceRight:同上,从数组的末尾向前相加
1.解释一下Promise的概念
promis是一种处理异步操作的一种方式,它代表着一个异步操作的最终的处理结果。具有3种状态:pending(进行中)、reject(已失败)、fulfilled(已成功),可以通过promise.then或者catch方法来处理成功或者失败的返回结果.它可以解决回调地狱,即多层嵌套的回调函数导致代码难以理解和维护的情况。如果需要处理多个异步操作,那么可以使用promise.all去处理
async 用于申明一个 function 是异步的,而 await是用来等待异步方法执行。 实际上async函数返回一个 Promise 对象,可以使用then方法添加回调函数。
- 先执行执行栈中的同步任务
- 遇到异步任务(回调函数)就放入任务队列中
- 一旦执行栈中的同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,被读取的异步任务结束等待状态,进入执行栈开始执行。
面试官你好,我是今天的面试者XX,应聘贵公司的web前端一职。我有3年的工作经验,擅长使用vue、jquery、js、微信小程序腾讯api、uni-app进行项目开发,在项目中负责封装公共组件、负责从0-1的架构和开发,快速还原设计稿,熟练运用elmentUI、ant-design、vant、iview等第三方框架,熟练运用echrts制作图表,具有大屏开发数据经验。对前端性能优化也有一定的涉猎。除了专业的前端技能外,我还具有良好的代码风格和团队合作精神。在遇到问题的时候,也能快速的去定位问题所在,并把问题解决掉。以上是我的自我介绍,请问下还有什么需要了解的吗,我们可以在详细沟通一下
鉴于不同设备屏幕的大小不同,为了实现屏幕的自动适配,rpx 把所有设备的屏幕,在宽度上等分为 750 份(即:当前屏幕的总宽度为 750rpx),在较小的设备上,1rpx 所代表的宽度较小,在较大的设备上,1rpx 所代表的宽度较大