只适合(初级)入门前端看看
浏览器如何渲染网页
浏览器渲染过程
1 HTML 解析,构建 DOM 树
浏览器从网络或者硬盘中获取 HTML 字节数据经过一下流程将 html 字节解析为 DOM 树
- 将原始的 html 字节数据转化为文件制定字符
- 然后浏览器会根据 HTML 规范来将字符串转换成各种令牌(如 html、body、p 这样的标签以及标签中的字符串和属性等都会被转化为令牌,每个令牌具有特殊含义和规则)。
- 接着每个令牌都会被转换成定义其属性和规则的对象,即节点对象。\
- 最后将节点对象构建成树形结构,即 DOM 树。HTML 标签之间有复杂的父子关系,树形结构刚好可以诠释这样的关系。
接下来通过一段 HTML 代码与配图更好的理解“字节 -> 字符 -> 令牌-> 节点对象 -> 对象模型”这个过程
1 | <html> |
2 CSS 解析,构建 CSSOM 树
浏览器解析遇到 link 标签时,浏览器就开始解析 CSS,像构建 DOM 树一样构建 CSSOM 树。style.css 的代码如下:
1 | body { |
3 Render Tree
在构建了 DOM 树和 CSSOM 树之后,浏览器只是拥有 2 个相互独立的对象集合,DOM 树描述的文档结构和内容,CSSOM 树描述了对应文档的样式规则,想要渲染出页面,就需要将 DOM 树、CSSOM 树结合在一起,构建渲染树。
4 layout(布局)
渲染树构建好后,浏览器得到了每个节点的内容与样式,下一步就是需要计算每个节点在浏览器窗口的确切位置与大小,即 layout 布局。
布局阶段,从渲染树的根节点开始遍历,采用盒子模型的模式来表示每个节点与其他元素之间的距离,从而确定每个元素在屏幕内的位置与大小。
盒子模型:包括外边距(margin),内边距(padding),边框(border),内容(content)。标准盒子模型 width/height = content;IE 盒子模型 width/height = content + padding + border。
5 只知道流程不知道原理.前端真难
页面渲染优化
优化渲染文件
优化 JS
JavaScript 文件加载会阻塞 DOM 树的构建,可以给 script 标签添加异步属性 async,这样浏览器的 HTML 解析就不会被 js 文件阻塞。
优化 CSS
浏览器每次遇到 link 标签时,浏览器就需要向服务器发出请求获得 CSS 文件,然后才继续构建 DOM 树和 CSSOM 树,可以合并所有 CSS 成一个文件,减少 HTTP 请求,减少关键资源往返加载的时间,优化渲染速度。
代码压缩
对 HTML、CSS、JavaScript 这些文件去除冗余字符(例如不必要的注释、空格符和换行符等),再进行压缩,减小文件数据大小,加快浏览器解析文件编码。
图片加载优化
图片加载较多时,采用懒加载的方案,用户滚动页面可视区时再加载渲染图片
JS 继承
原型链继承
1 | //父类 |
- 特点
- son 实例可继承的属性有
- 实例的构造函数的属性
- 父类的构造函数属性
- 父类原型的属性
- 但不会继承父类实例上的属性
- 缺点
- son 实例无法向父类构造函数传参
- 所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
构造函数继承
1 | function Person(name) { |
- 特点
- 只继承了父类构造函数的属性,没有继承父类原型的属性。
- 解决了原型链继承缺点
- 在子实例中可向父实例传参
- 缺点
- 只能继承父类构造函数的属性
- 无法实现构造函数的复用。(每次用每次都要重新调用)
- 每个新实例都有父类构造函数的副本,臃肿。
组合继承(组合原型链继承和借用构造函数继承)
1 | function Person(name) { |
- 特点
- 可以继承父类原型上的属性,可以传参,可复用。
- 每个新实例引入的构造函数属性是私有的。
- 缺点
- 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
还有很多种继承方式.写多了也记不住,等理解完了再补
JS 中 new 一个对象时到底发生了什么
创建一个 Person 的实例,必须使用到 new 操作符.以这种方式调用构造函数实际上会经历以下 4 个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象。
new 操作符
在有上面的基础概念的介绍之后,在加上 new 操作符,我们就能完成传统面向对象的 class + new 的方式创建对象,在 JavaScript 中,我们将这类方式成为 Pseudoclassical。
基于上面的例子,我们执行如下代码
1 | function Base() {} |
这样代码的结果是什么,我们在 Javascript 引擎中看到的对象模型是:
如图所示 new 操作符干了三件事
1 | let obj = {}; |
第一行,我们创建了一个空对象 obj
第二行,我们将这个空对象的__proto__成员指向了 Base 函数对象 prototype 成员对象
第三行,我们将 Base 函数对象的 this 指针替换成 obj,然后再调用 Base 函数,
于是我们就给 obj 对象赋值了一个 id 成员变量,这个成员变量的值是 base
1 | Base.prototype.tostring = function () { |
当我们 Base 原型添加一些方法,那么 new 出来的实例对象,根据__proto__的特性也能够使用 tostring 方法
下面的例子中分别通过构造函数与 class 类实现了一个简单的创建实例的过程。
1 | // ES5构造函数 |
new 操作中发生了什么
在《JavaScript 模式》这本书中,new 的过程说的比较直白,当我们 new 一个构造器,主要有三步:
- 创建一个空对象,将它的引用赋给 this,继承函数的原型。
- 通过 this 将属性和方法添加至这个对象
- 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)
改写上面的例子:
1 | // ES5构造函数 |
实现一个简单的 new 方法
1 | // 构造器函数 |
Vuex
什么是 Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件。它采用集中式存储管理应用的所有组件的状态,而更改状态的唯一方法是提交 mutation,例 this.$store.commit(‘SET_VIDEO_PAUSE’, video_pause,SET_VIDEO_PAUSE 为 mutations 属性中定义的方法
什么时候使用
- 多个组件依赖于同一状态时。
- 来自不同组件的行为需要变更同一状态。
Vuex 的 5 个核心属性是什么
state、getters、mutations、actions、modules
Vuex 中状态储存在哪里,怎么改变它
存储在 state 中,改变 Vuex 中的状态的唯一途径就是显式地提交 (commit) mutation。
怎么在组件中批量使用 Vuex 的 state 状态
使用 mapState 辅助函数, 利用对象展开运算符将 state 混入 computed 对象中
Vuex 中 action 和 mutation 有什么区别
- action 提交的是 mutation,而不是直接变更状态。mutation 可以直接变更状态。
- action 可以包含任意异步操作。mutation 只能是同步操作。
- 提交方式不同,action 是用 this.$store.dispatch(‘ACTION_NAME’,data)来提交。mutation是用this.$store.commit(‘SET_NUMBER’,10)来提交。
vue 中 router 与 route 的区别
router 是 VueRouter 的一个对象,通过 Vue.use(VueRouter)和 VueRouter 构造函数得到一个 router 的实例对象,他包含了所有的路由包含了许多关键的对象和属性
route 是一个跳转的路由对象,每一个路由都会有一个 route 对象,是一个局部的对象,可以获取对应的 name,path,params,query 等
$route.path
- 字符串,等于当前路由对象的路径,会被解析为绝对路径,如 “/home/news” 。
$route.params
- 对象,包含路由中的动态片段和全匹配片段的键值对
$route.query
- 对象,包含路由中查询参数的键值对。例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite == ‘yes’ 。
$route.router
- 路由规则所属的路由器(以及其所属的组件)。
$route.matched
- 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
$route.name
- 当前路径的名字,如果没有使用具名路径,则名字为空。
$route.path, $route.params, $route.name, $route.query 这几个属性,主要用于接收路由传递的参数
Vue
监听和计算属性的区别
computed 特性
- 计算值
- 就是简化 tempalte 里面计算和处理 props 或$emit 的传值
- 具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数
- 计算属性还可以依赖多个 Vue 实例的数据,只要其中任一数据变化,计算属性就会重新执行,视图也会更新。
watch 特性
- 观察的动作
- 监听 props,$emit 或本组件的值执行异步操作
- 无缓存性,页面重新渲染时值不变化也会执行
生命周期有哪些
- beforeCreate:实例创建前被调用;
- created:实例创建后被调用,完成数据观测,属性和方法的运算,watch/event 事件回调,模板渲染成 html 前(vm.$el 未定义)故数据初始化最好在这阶段完成;
- beforeMount:在$el 挂载前被调用,相关的 render 函数首次被调用,期间将模块渲染成 html,此时 vm.$el 还是未定义;
- mounted:在$el 挂载后被调用,此时 vm.$el 可以调用,不能保证所有的子组件都挂载,要等视图全部更新完毕用 vm.$nextTick();
- beforeUpdate:数据更新时调用;
- updated:数据更新后调用;
- activated:keep-alive/keep-alive 包裹的组件激活时调用;
- deactivated:keep-alive 包裹的组件离开时调用;keep-alive 包裹的组件离开时调用;
- beforeDestroy:实例销毁之前调用,此时实例仍然完全可用;
- destroyed:实例销毁之后调用,此时实例的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
vue 常用指令
v-show
,切换元素的 display 属性,来控制元素显示隐藏,初始化会渲染,适用频繁显示隐藏的元素,不能用在 template 上;v-if
,通过销毁并重建组件,来控制组件显示隐藏,初始化不会渲染,不适用频繁显示隐藏的组件,可以用在 template 上v-else-if
,必须和v-if
一起使用v-else
,必须和v-if
一起使用;v-for
,将 Array、Object、Number、String 数据循环渲染元素或者组件,渲染组件必须带上 key,key 要为数据中每项特定值比如 ID;
1 | <div v-for="(item,index) in Array">{{index}}-{{item}}</div> |
v-on
,缩写@,监听事件,如:@click、@submit、@dblclick- 怎么获取 div 上点击的鼠标位置
1
2
3
4
5
6
7
8
9
10<div @click="a"></div>
<div @click="b(1,2,$event)"></div>
methods:{
a(){
console.log(event.clientX,event.clientY)
},
b(num1,num2,$event){
console.log($event.clientX,$event.clientY)
},
}- 怎么阻止冒泡,怎么阻止默认事件
1
2
3
4
5
6//阻止冒泡
<div @click.stop="a"></div>
//阻止默认
<div @click.prevent="b"></div>
//阻止冒泡阻止默认
<div @click.stop.prevent="c"></div>- 监听组件根元素的原生事件
1
<myVue @click.native="d"></myVue>
- 监听组件自定义事件
1
<myVue @diy-event="f"></myVue> //组件中这样触发 this.$emit('diyEvent',data)
v-bind,缩写:,绑定动态属性;
v-model,限制应用在 input textarea select 表单 元素和组件上创建双向绑定,修饰符 v-model.lazy 懒监听、v-model.number 将值转成有效的数字、v-model.trim 过滤首尾空格;
v-text,
<div v-text="data"></div>等同<div></div>
v-html,直接输出 HTML,不会按 Vue 模板编译,会有 XSS 攻击分析,不要用在用户提交内容上;
计算属性
- 计算属性有什么特性?
计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。
计算属性的 getter 和 setter 是什么,有什么用。
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
33
34
35
36
37
38<template>
<div>
<span>{{ a }}</span
><span>加</span><span>{{ b }}</span
><span>等于</span><span>{{ c }}</span> <span>{{ a }}</span
><span>乘</span><span>{{ b }}</span
><span>等于</span><span>{{ d }}</span>
<button @click="add">加1</button>
</div>
</template>
<script>
export default {
data() {
return {
a: 1,
b: 2,
};
},
computed: {
c: function () {
return this.a + this.b;
},
d: {
get: function () {
return this.a * this.b;
},
set: function (val) {
this.a = val / this.b;
},
},
},
methods: {
add() {
this.d = this.d * 2;
},
},
};
</script>
Vue 中操作 data 中数组的方法中哪些可以触发视图更新,哪些不可以,不可以的话有什么解决办法
push()、pop()、shift()、unshift()、splice()、sort()、reverse()这些方法会改变被操作的数组;
filter()、concat()、slice()这些方法不会改变被操作的数组,返回一个新的数组;
以上方法都可以触发视图更新。
利用索引直接设置一个数组项,例:this.array[index] = newValue
直接修改数组的长度,例:this.array.length = newLength
以上两种方法不可以触发视图更新
- 可以用 this.$set(this.array,index,newValue)或 this.array.splice(index,1,newValue)解决方法 1
- 可以用 this.array.splice(newLength)解决方法 2
v-for 和 v-if 能共同使用吗
在处于同一节点上,因为 v-for 的优先级比 v-if 更高,v-if 将分别重复运行于每个 v-for 循环中。如果要实现渲染满足条件的 li 节点时,可以这样用
1 | <ul> |
父子组件怎么通信
父组件到子组件的通信用 props 来完成。
子组件到父组件的通信,通过在父组件中自定义事件,在子组件用 this.$emit(‘父组件自定义事件’,’要传到父组件的数据’)实现
使用$bus 方法实现组件通信
$nextTick()
- 参数为 callback,等待视图全部更新后执行,回调函数的 this 自动绑定到调用它的实例上;
v-model 原理
1 | <template> |
1 | <template> |
let 造成块级作用域的原因
空
总结
这次面试让我知道了自己几斤几两.
参考链接 https://juejin.im/post/5d136700f265da1b7c6128db
参考链接 https://www.cnblogs.com/ranyonsue/p/11201730.html
参考链接 https://blog.csdn.net/qq_27674439/article/details/99095336
参考链接 https://juejin.im/post/5dba91e4518825647e4ef18b