定义

数组的定义

  • 数组是一组具有相同数据类型的变量的集合,这些变量称之为集合的元素
  • 每个元素都有一个编号,称之为下标,可以通过下标来区别并访问数组元素,数组元素的个数叫做数据的长度

链表的定义

  • 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
  • 链表的特性是在中间任意位置插入和删除元素都非常快,不需要移动其它元素
  • 对于单向链表而言,链表中的每一个元素都要保存一个指向下一个元素的指针
  • 对于双向链表而言,链表中的每个元素既要保存指向下一个元素的指针,又要保存指向上一个元素的指针
  • 对于双向循环链表而言,链表中的最后一个元素保存一个指向第一个元素的指针
阅读全文 »

箭头函数:

1
2
3
let fun = () => {
console.log("lalalala");
};

普通函数:

1
2
3
function fun() {
console.log("lalla");
}

箭头函数相当于匿名函数,并且简化了函数定义。
箭头函数有两种格式:一种只包含一个表达式,连{ … }和 return 都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ … }和 return。

阅读全文 »

关键字 new 在调用构造函数的时候实际上进行了如下的几个步骤

  1. 创建一个新的对象
  2. 将构造函数的作用域赋值给这个新的对象(因此 this 指向了这个新的对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

模拟 new 的效果

1
2
3
4
5
6
7
8
function Person() {
// ......
}

// 使用 new
var student = new Person(/*......*/);
// 使用 objectFactory
var student = objectFactory(Person /*......*/);
1
2
3
4
5
6
7
8
9
10
11
12
13
function objectFactory() {
// 创建一个新的对象
const obj = {};
// 获取第一个参数,arguments是类数组,不可直接调用shift方法
//此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
const Constructor = [].shift.call(arguments);
// 将obj的原型指向构造函数的原型对象,这样obj就可以访问构造函数原型上的属性
obj.__proto__ = Constructor.prototype;
// 将构造函数的this指向obj,这样obj就可以访问到构造函数中的属性
Constructor.apply(obj, arguments);
// 返回 obj
return obj;
}
阅读全文 »

树形结构数据如下

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
const tree = [
{
id: "1",
children: [
{
id: "2",
children: [
{
id: "3",
children: [{ id: "4" }]
},
{ id: "5" },
{
id: "6",
children: [{ id: "7" }]
}
]
},
{
id: "8",
children: [{ id: "9" }]
}
]
},
{
id: "a",
children: [{ id: "b" }]
}
];

将树形结构转换为有父子关系属性的数组结构

思路一:初始化一个空数组,从 tree 的顶端开始遍历,当前节点有子节点时,一边继续遍历子节点,一边在当前节点上删除子节点,将当前节点 push 到空数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function flatten1(tree) {
var arr = [];
function spread(tree, pid) {
for (var i = 0; i < tree.length; i++) {
ele = tree[i];
let { children, ...item } = ele;
arr.push({ ...item, pid });
if (ele.children) {
spread(ele.children, ele.id);
delete ele.children;
}
}
}
spread(tree, 0);
return arr;
}

newArr = flatten1(tree);
阅读全文 »

  • 编码阶段
    尽量减少 data 中的数据,data 中的数据都会增加 getter 和 setter,会收集对应的 watcher

  • v-if 和 v-for 不能连用
    如果需要使用 v-for 给每项元素绑定事件时使用事件代理

  • SPA 页面采用 keep-alive 缓存组件

  • 在更多的情况下,使用 v-if 替代 v-show

  • key 保证唯一

  • 使用路由懒加载、异步组件

  • 防抖、节流

  • 第三方模块按需导入

  • 长列表滚动到可视区域动态加载

  • 图片懒加载

  • SEO 优化

    • 预渲染
    • 服务端渲染 SSR
  • 打包优化

    • 压缩代码
    • Tree Shaking/Scope Hoisting
    • 使用 cdn 加载第三方模块
    • 多线程打包 happypack
    • splitChunks 抽离公共文件
    • sourceMap 优化
  • 用户体验

    骨架屏
    PWA

  • 使用缓存(客户端缓存、服务端缓存)优化、服务端开启 gzip 压缩等。

diff 算法有以下过程

同级比较,再比较子节点

先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)

比较都有子节点的情况(核心 diff)

递归比较子节点

阅读全文 »

虚拟 Dom 的产生原因

由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。

Vue2 的虚拟 DOM

Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。

Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点。是对真实 DOM 的一层抽象。(也就是源码中的 VNode 类,它定义在 src/core/vdom/vnode.js 中。)

阅读全文 »

概念

模板编译的作用是生成渲染函数,通过执行渲染函数生成最新的 vnode,最后根据 vnode 进行渲染。

模板编译在整个渲染过程中的位置

将模板编译成渲染函数

此过程可以分成两个步骤:先将模板解析成 AST(abstract syntax tree,抽象语法树),然后使用 AST 生成渲染函数

阅读全文 »

hash 模式

hash 模式是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 hashchange 事件。

示例:新建一个 hash.html 文件,内容为:

1
2
3
4
5
6
7
8
9
10
11
<a href="#/a">A 页面</a>
<a href="#/b">B 页面</a>

<div id="app"></div>
<script>
function render() {
app.innerHTML = window.location.hash;
}
window.addEventListener("hashchange", render);
render();
</script>

在上面的例子中,我们利用 a 标签设置了两个路由导航,把 app 当做视图渲染容器,当切换路由的时候触发视图容器的更新,这其实就是大多数前端框架哈希路由的实现原理。

阅读全文 »