前端面试题

前端面试题

  • 自我介绍
  • 项目介绍
  • 面试题目

算法题-字符串处理

字符串 line 是字母+数字的组合,数字代表字母重复的个数。

写一个函数,要求把字符串 line 按照字母重复的个数的递增排序,如重复的个数相等,按照字母大小递增排序,并输出完整的字符

如给 const line = ‘c2b1a2’ 输出 ‘baacc’,给 const line = ‘a11b2bac3bae3bad3abcd2’ 输出 ‘bbabcdabcdbacbacbacbadbadbadbaebaebaeaaaaaaaaaaa’

思路:

字母+数字的依次组合,将字母、数字拆分开,然后把数字转成字母(字母重复),然后排序

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
function fun (line = 'a11b2bac3bae3bad3abcd2') {
// 按数字正则拆分,得到所有字母数组
const chars = line.split(/[0-9]/).filter(item => item)
// 按字母正则拆分,得到所有数字数组
const numbers = line.split(/[A-Za-z]/).filter(item => item)

// 把数字转成字母(字母重复)
const arr = chars.map((item, index) => {
const str = item.padStart(item.length * numbers[index], item)

return str
})

// 排序,按字母长度,长度相等按字母大小
arr.sort((prev, next) =>
// 如果不等,按字母长度
// 如果相等,按字母大小
prev.length != next.length ?
(prev.length - next.length) :
(prev > next ? 1 : -1)
)

// 数组转字符串
const result = arr.join('')

return result
}
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
39
40
41
42
43
function fun (line = 'a11b2bac3bae3bad3abcd2') {
const lineArr = []
for (let i = 0; i < line.length; i ++) {
// 前一个
const prev = lineArr[lineArr.length - 1]
// 当前元素
const now = line[i]

if (prev && ((+prev && +now) || (!(+prev) && !(+now)))) {
// 如果类型相等,都是数字或都是字母,则在前一个上递增
lineArr[lineArr.length - 1] += now
} else {
// 如果类型不同,则新增
lineArr.push(now)
}
}

// 得到的 lineArr 是字母、数字的数组排序

// 把数字转成字母(字母重复)
const arr = []
lineArr.forEach((item, index) => {
if (!(+item)) {
const str = !(index % 2) && item.padStart(item.length * lineArr[index + 1], item)

arr.push(str)
}
});

// 排序,按字母长度,长度相等按字母大小
arr.sort((prev, next) =>
// 如果不等,按字母长度
// 如果相等,按字母大小
prev.length != next.length ?
(prev.length - next.length) :
(prev > next ? 1 : -1)
)

// 数组转字符串
const result = arr.join('')

return result
}

https://www.nowcoder.com/practice/5190a1db6f4f4ddb92fd9c365c944584

编写一个程序,将输入字符串中的字符按如下规则排序。

规则 1 :英文字母从 A 到 Z 排列,不区分大小写。

如,输入: Type 输出: epTy

规则 2 :同一个英文字母的大小写同时存在时,按照输入顺序排列。

如,输入: BabA 输出: aABb

规则 3 :非英文字母的其它字符保持原来的位置。

如,输入: By?e 输出: Be?y

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
let line

while (line = readline()) {
const arr = line.split('')

const _arr = line
.replace(/[^A-Za-z]/g, '')
.split('').map((item, index) => ({
v: item,
lower: item.toLowerCase(),
index
})).sort((prev, next) =>
// 如果相等,按索性先后
// 如果不等,按字母大小
prev.lower === next.lower ?
(prev.index - next.index) :
(prev.lower > next.lower ? 1 : -1)
)

const __arr = _arr.map(item => item.v)

for (let i = 0; i < arr.length; i ++) {
if (/[A-Za-z]/g.test(arr[i])) {
arr[i] = __arr[0]

__arr.shift()
}
}

console.log(arr.join(''))
}

https://www.nowcoder.com/practice/fe298c55694f4ed39e256170ff2c205f

三个空汽水瓶,可以换一瓶汽水。
两个空瓶子,可以先跟老板借一瓶汽水,喝掉后用三个空瓶子换一瓶满的还给老板。

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
// 手上空瓶子的数量 count
let count

// 可换汽水数量 n
let n = 0
while (count = readline()) {
n = parseInt(count / 2)

n && console.log(n)
}

// 可换汽水数量 m
let m = 0
while (count = readline()) {
function fun (num) {
if (num < 2) {
return 0
} else if (num == 2) {
return 1
} else {
const huan = parseInt(num / 3)
const shengyu = huan + num % 3

return huan + fun(shengyu)
}
}

m = fun(count)

m && console.log(m)
}

ssr、spa

GraphQL

JS 类型:string,boolean,number,function,object,undefined,es6 的 symbol,以及最新的 bigint

JS 类型判断:typeof, instanceof, Object.toString.call

函数式编程

纯函数

柯里化 curry

1
2
3
4
5
function curry (fn, arr = []) {
return fn.length === arr.length ? fn.apply(null, arr) : function (...args) {
return curry (fn, arr.concat(args))
}
}
1
const curry = (fn, arr = []) => fn.length === arr.length ? fn(...arr) : (...args) => curry(fn, [...arr, ...args])

偏函数

es6 扩展:数值扩展,函数扩展,箭头函数,Proxy,Reflect,Promise,class,生成器函数,async/await,Promise

JS 异步编程进化史:callback -> 发布订阅模式 -> promise -> generator -> async + await

JS 异步编程

Event Loop 机制

浏览器端的宏任务,微任务

nodejs 中的几个事件循环阶段

发布订阅模式,nodejs 中的 EventEmitter

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
class EventEmitter {
constructor () {
this._eventpool = {};
}

on (event, callback) {
this._eventpool[event] ? this._eventpool[event].push(callback) : this._eventpool[event] = [callback]
}

emit (event, ...args) {
this._eventpool[event] && this._eventpool[event].forEach(cb => cb(...args))
}

off (event) {
if (this._eventpool[event]) {
delete this._eventpool[event]
}
}

once (event, callback) {
this.on(event, (...args) => {
callback(...args);
this.off(event)
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function a() {
console.log(this); // window

this.b = 3; // window.b
}

a.prototype.b = 7; // 原型

var t = new a(); // 实例

var b = 2;

a(); // window

console.log(t.b); // 实例上

console.log(b); // window 上

PureComponent 纯组件

flex 布局

css 实现 Modal 模态窗口,上下左右弹出动画

跨域,JSONP 基本原理

网络安全 XSS、CSRF

节流函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function throttle (fn, time) {
var canRun = true;

return function () {
if (!canRun) return false;
canRun = false;

var that = this, args = arguments;

setTimeout(function () {
fn.apply(that, args);

canRun = true;
}, time || 500);
}
}

setInterval(throttle(function() {
console.log("hello world")
}), 100)

用 reduce 实现 map 的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Array.prototype.map = function (callback) {
var arr = this;

return arr.reduce((acc, cur, i) => {
acc.push(callback(cur, i, arr));

return acc;
}, []);
}

var m = [1, 2, 3, 4, 5].map(function (v, i, arr) {
return v * v;
});

console.log(m);

实现二叉树的后序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
function TreeNode (val) {
this.val = val;
this.left = null;
this.right = null;
}

function vist(root) {
if (root) {
vist(root.left);
vist(root.right);
console.log(root.val);
}
}

二叉树?判断是不是完全二叉树?

判断一个链表是否有环?

TCP 三次握手

https 的原理

缓存策略

原生 js 实现 bind 函数

输入 url 到页面渲染整个过程

回流和重绘

浏览器与浏览器内核

浏览器内核,作用是帮助浏览器来渲染网页的内容,将页面内容和排版代码转换为用户所见的视图。

浏览器内核分成两部分:渲染引擎 (Layout Engine 或者 Rendering Engine) 和 JS 引擎。

渲染引擎,负责取得网页的内容 (HTML、XML、图像等)、整理讯息 (CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

JS 引擎,负责解析 JavaScript 语言、执行 JavaScript 语言,来实现网页的动态效果。

browser

caniuse

browserslist

in package.json

1
2
3
4
5
6
7
8
9
{
"browserslist": [
"> 1%",
"last 3 versions",
"ie >= 9",
"ios >= 8",
"android >= 4.4"
]
}

in .browserslistrc

1
2
3
4
5
6
7
# Browsers that we support

> 1%
last 3 versions
ie >= 9
ios >= 8
android >= 4.4

PC端

Internet Explorer (IE 互联网探索者),内核是:Trident

Safari (游猎)、Google Chrome (谷歌),内核是:WebKit (极速)

Mozilla FireFox (FF 火狐),内核是:Gecko

Opera (欧朋),内核是:Presto

移动端

移动端的浏览器内核主要说的是系统内置浏览器的内核。

移动设备浏览器上常用的内核有 WebKit,Blink,Trident,Gecko 等。

其中 iPhone 和 iPad 等苹果 iOS 平台主要是 WebKit,Android 4.4 之前的 Android 系统浏览器内核是 WebKit,Android 4.4 系统浏览器切换到了 Chromium,内核是 Webkit 的分支 Blink,Windows Phone 8 系统浏览器内核是 Trident。

document.compatMode

当前浏览器采用的渲染方式

BackCompat:标准兼容模式关闭。浏览器客户区宽度是 document.body.clientWidth。

CSS1Compat:标准兼容模式开启。浏览器客户区宽度是 document.documentElement.clientWidth。

块元素、行内元素、行内块元素

块元素 display: block

div、p、nav、aside、header、footer、section、article、ul、li、address 等元素。

行内元素 display: inline

不换行,设置 width、height 都无效,宽高由内容决定,但可以设置 line-height 来控制高度。margin 上下无效但左右有效,padding 上下左右都有效。

a、b、i、span 等元素。

行内块元素 display: inline-block

除了具有行内元素不换行的特性,其他都是块元素的特性。设置 width、height 都有效,margin 上下左右都有效。

img、input 等元素。

盒模型

https://segmentfault.com/a/1190000005116275

https://segmentfault.com/a/1190000005155084

block box

content + padding + border + margin

line box

每一行称为一条 line Box,它又是由这一行的许多 inline box 组成,它的高度可以直接由 line-height 决定,line boxes 的高度垂直堆叠形成了 containing box 的高度,就是我们见到的 div 或是 p 标签之类的高度了。

box-sizing

box-sizing: content-box | border-box

影响的其实就是内边距和边框。content-box 向外发散,border-box 向内收敛。

content-box: 默认值。宽度和高度分别应用到元素的内容框。在宽度和高度之外绘制元素的内边距和边框(元素默认效果。

如果你设置一个元素的宽为 100px,那么这个元素的内容区会有 100px 宽,并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。

border-box: 元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度(推荐)。

如果你将一个元素的 width 设为 100px, 那么这 100px 会包含其它的 border 和 padding,内容区的实际宽度会是 width 减去 border + padding 的计算值。

大多数情况下 border-box 使得我们更容易的去设定一个元素的宽高。链接。所以我们常常这样设置:

1
2
3
*, *:before, *:after {
box-sizing: border-box;
}

BFC(边距重叠解决方案)

布局

传统布局

布局的传统解决方案,基于盒状模型,依赖 display + position + float

这种传统布局,对于那些特殊布局非常不方便,比如 水平垂直居中,就不容易实现。

flex 布局

flex 是 Flexible Box 的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。

采用 flex 布局的元素,称为 flex 容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为 flex 项目(flex item),简称”项目”。

容器的属性

  • display: flex;
  • flex-direction
  • flex-wrap
  • align-items
  • justify-content

flex-direction: row | row-reverse | column | column-reverse

  • row(默认值):主轴为水平方向,起点在左端
  • row-reverse:主轴为水平方向,起点在右端
  • column:主轴为垂直方向,起点在上沿
  • column-reverse:主轴为垂直方向,起点在下沿

其实就两种,row 横,column 纵

reverse 代表反转方向,排列顺序

flex-wrap: nowrap | wrap | wrap-reverse

  • nowrap(默认):不换行
  • wrap:换行,第一行在上方
  • wrap-reverse:换行,第一行在下方

align-items: flex-start | flex-end | center | baseline | stretch

垂直对齐方式

  • flex-start:交叉轴的起点对齐。居上
  • flex-end:交叉轴的终点对齐。居下
  • center:交叉轴的中点对齐。居中
  • baseline: 项目的第一行文字的基线对齐
  • stretch(默认值):如果项目未设置高度或设为 auto,将占满整个容器的高度

justify-content: flex-start | flex-end | center | space-between | space-around

水平对齐方式

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center:居中
  • space-between:两端对齐,项目之间的间隔都相等
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍

实现【水平垂直居中】

  • display: flex; + align-items: center; + justify-content: center;
  • position + top/left + margin
  • position + top/left + transform

position

static、fixed、relative、absolute、sticky

设置 position 的值为非 static,会提升元素的垂直地位 (z-index)。

设置 position 的值为 absolute、fixed,元素脱离文档流。

如果仅仅定义 position,不定义 top/margin/transform 等尾翼值,元素仍然停留在本身正常位置。

position: relative

相对位置,相对元素本身正常位置。

fixed 与 absolute 的区别

fixed 浮动定位是相对于浏览器视窗的。

absolute 绝对定位是相对于父级中设置 position 为 relative 或者 absolute 最近的父级元素。如果父级没有会向上查找,知道 html 根节点。

sticky

sticky 英文字面意思是粘,粘贴,所以可以把它称之为粘性定位。

position: sticky; 基于用户的滚动位置来定位。

粘性定位的元素是依赖于用户的滚动,在 position: relative 与 position: fixed 之间切换。

它的行为就像 position: relative; 而当页面滚动超出目标区域时,它的表现就像 position: fixed;,它会固定在目标位置。

元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。

这个特定阈值指的是 top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。

1
2
3
4
.sticky {
position: sticky;
top: 20px;
}

兼容性低

ios >= 10.3

transform

transform 对普通元素的 N 多渲染影响

transform 提升元素的垂直地位 (z-index)

当遭遇元素设置 margin 负值重叠的时候,如果没有 static 以外的 position 属性值的话,后面的元素是会覆盖前面的元素的。

但是,元素应用了 transform 属性之后,就会变得跟应用了 position: relative 一样,原本应该被覆盖的元素会雄起,变成覆盖其他元素。

transform 相当于给元素加了 position: relative。

transform 限制 position: fixed 的跟随效果

设置 transform 的元素,其内部元素的 position: fixed 会失效,降级变成 position: absolute。

transform 相当于给元素加了 position: relative。

内部元素 position: fixed 变成了 position: absolute。

1
2
3
4
5
<div style="transform: scale(1)">
<div style="position: fixed">
fixed 失效
</div>
</div>

Chrome / FireFox 有这种 bug。

利用 这个 bug 可以使 fixed 元素相对于父级定位。

transform 改变 overflow 对 absolute 元素的限制

absolute 绝对定位元素,如果含有 overflow 为非 visible 的父级元素,同时,该父级元素以及到该绝对定位元素之间任何嵌套元素都没有 position 为非 static 属性或 transform 属性的声明,则 overflow 对该 absolute 元素不起作用。

transform 相当于给元素加了 position: relative。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div style="width: 100px;height: 100px;overflow: hidden;background: red">
<div style="width: 200px;height: 200px;background: blue">被修剪,溢出部分不显示</div>
</div>

<div style="width: 100px;height: 100px;overflow: hidden;background: red">
<div style="width: 200px;height: 200px;background: blue;position: absolute">不会被修剪,溢出部分显示</div>
</div>

<div style="width: 100px;height: 100px;overflow: hidden;background: red;position: relative">
<div style="width: 200px;height: 200px;background: blue;position: absolute">被修剪,溢出部分不显示</div>
</div>

<div style="width: 100px;height: 100px;overflow: hidden;background: red;transform: scale(1)">
<div style="width: 200px;height: 200px;background: blue;position: absolute">被修剪,溢出部分不显示</div>
</div>

transform 限制 absolute 的 100% 宽度大小

设置 absolute 元素宽度 100%, 则都会参照第一个 非 static 值的 position 或 不具有 transform 的祖先元素计算,没有就 window。

transform 相当于给元素加了 position: relative。

1
2
3
4
5
6
7
8
9
10
11
<div style="width: 100px;height: 100px;background: red">
<div style="width: 100%;height: 200px;background: blue">溢出部分显示</div>
</div>

<div style="width: 100px;height: 100px;background: red;position: relative">
<div style="width: 100%;height: 200px;background: blue;position: absolute">被压缩在父级元素中显示</div>
</div>

<div style="width: 100px;height: 100px;overflow: hidden;background: red;transform: scale(1)">
<div style="width: 100%;height: 200px;background: blue;position: absolute">被压缩在父级元素中显示</div>
</div>

float 浮动

浮动出现的意义其实只是用来让文字环绕图片而已,仅此而已。

用浮动实现页面布局本不是浮动该干的事情。

浮动的本质定义为“包裹与破坏”。

CSS float浮动 一

CSS float浮动 二

包裹性

浮动就是个带有方位的 display: inline-block 属性。

破坏性

浮动破坏了正常的 line box 模型,就没有了 inline box 该有的高度,实际占据的高度为 0。

清除浮动造成的影响

所谓清除浮动,其实应该是 float: none。

而我们关注的其实是清除浮动造成的影响。

  • 父级 div 设置 zoom 值,并定义伪元素 ::after
  • 父级 div 定义 height,并定义 overflow: hidden/auto
  • 父级 div 定义 height,并也设置浮动
  • 最后一个浮动元素结尾处加
    ,设置 clear: both
1
2
3
4
5
6
7
8
9
10
11
.clear-float {
zoom: 1;
}

.clear-float::after {
display: block;
height: 0;
clear: both;
content: '';
visibility: hidden;
}

但自从用了 display: flex,再也没写过 float

页面导入样式的几种方式

行内样式

内嵌样式

导入样式

链接样式

iframe

  • iframe 会阻塞主页面的 onload 事件
  • iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载
  • iframe 页面样式调试麻烦,出现多个滚动条
  • 搜索引擎的检索程序无法解读 iframe,不利于 SEO

动态创建和动态销毁 iframe

例如在处理 App 或微信的 webview 单页面应用时,页面 title 不刷新的问题

iframe 与父页面的(跨域)通信

parent.html

1
2
3
4
<body>
<div>parent</div>
<iframe id="child" name="child" src="child.html"></iframe>
</body>

child.html

1
2
3
<body>
<div>child</div>
</body>

父页面访问子页面

1
2
3
4
5
6
7
8
9
var child = document.querySelector('#child') // child.html

var childWindow = child.contentWindow // child.html 的 window 对象

var childDocument = child.contentDocument // child.html 的 document 对象

var child2 = window.frames['child'] // child.html

child === child2 // true

子页面访问父页面

1
2
3
window.parent // 获取上一级容器的 window 对象
window.top // 获取最顶级容器的 window 对象,即是你打开页面的文档
window.self // 返回自身 window 的引用。可以理解 window === window.self (脑残)

判断当前页面是否为子页面

1
2
3
4
5
if (window.parent === window) {
console.log('当前页面不是子页面')
} else {
console.log('当前页面是子页面')
}

postMessage(message, targetOrigin) 方法与 监听 message 事件

子页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 监听 message 事件
window.addEventListener('message', (event) => {
console.log(event)

if (event.type === 'message') {
// && event.origin === '指定源'
const json = JSON.parse(event.data)

if (+json.code === 1001) {
console.log(json.data.word)

window.parent.postMessage(JSON.stringify({
code: 1001,
data: {
word: '我是你儿子'
},
message: '成功'
}), '*')
}
}
}, false)

父页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 监听 message 事件
window.addEventListener('message', (event) => {
console.log(event)

if (event.type === 'message') {
// && event.origin === '指定源'
const json = JSON.parse(event.data)

if (+json.code === 1001) {
console.log(json.data.word)
}
}
}, false)

// 给子页面发送消息
document.querySelector('#child').contentWindow.postMessage(JSON.stringify({
code: 1001,
data: {
word: '我是你爸爸'
},
message: '成功'
}), '*')

伪类与伪元素

伪类

状态伪类

  • :active
  • :focus
  • :hover
  • :link
  • :visited
  • :disabled
  • :checked
  • 其他…

结构性伪类

  • :lang
  • :first-child
  • :last-child
  • :nth-child(n)
  • :only-child
  • :empty
  • :not(selector)
  • 其他…

伪元素

伪元素的由两个冒号::开头,然后是伪元素的名称

使用两个冒号::是为了区别伪类和伪元素(CSS2 中并没有区别)。当然,考虑到兼容性,CSS2中已存的伪元素仍然可以使用一个冒号:的语法,但是 CSS3 中新增的伪元素必须使用两个冒号::

一个选择器只能使用一个伪元素,并且伪元素必须处于选择器语句的最后

伪元素的本质是在不增加dom结构的基础上添加的一个元素,在用法上跟真正的dom无本质区别。普通元素能实现的效果,伪元素都可以。有些用伪元素效果更好,代码更精简

  • ::first-letter // 向文本的第一个字母添加特殊样式
  • ::first-line // 向文本的首行添加特殊样式
  • ::before // 在元素之前添加内容
  • ::after // 在元素之后添加内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 可以设置 p 中文本的第一字母的样式 */
p::first-letter {
color: blue;
}
/* 可以设置 p 中文本的第一行的样式 */
p::first-line {
color: red;
}
/* 可以在 p 之前添加内容 */
p::before {
content: 'before';
}
/* 可以在 p 之后添加内容 */
p::after {
content: 'after';
}

css 权重

从 0 开始,一个行内样式 +1000,一个 id +100,一个属性选择器/class 或者伪类 +10,一个元素名或者标签名或者伪元素 +1,* 是 0

important > 内嵌样式 > ID > 类 > 标签 | 伪类 | 属性选择 > 伪对象 > 继承 > 通配符

important的权重为 1,0,0,0

ID的权重为 0,1,0,0

类的权重为 0,0,1,0

标签的权重为 0,0,0,1

伪类的权重为 0,0,1,0

属性的权重为 0,0,1,0

伪对象的权重为 0,0,0,1

通配符的权重为0,0,0,0

css 优先级

!important > id > 属性选择器/class/伪类 > tag/伪元素 > *

出现重复样式,后面写的样式大于前面写的样式

以权重决定样式规则:

  • 相同的权重:以后面出现的选择器为最后规则
  • 不同的权重,权重值高则生效

display: none 和 visibility: hidden 的区别

display: none 隐藏对应的元素,在文档布局中不再给它分配空间,它各边的元素会合拢,就当他从来不存在。

visibility: hidden 隐藏对应的元素,但是在文档布局中仍保留原来的空间。

<!DOCTYPE>

告知浏览器的解析器用什么文档标准解析这个文档。<!DOCTYPE> 不存在或格式不正确会导致文档以兼容模式呈现。

在标准模式中,页面的排版和 JS 运作模式都是以该浏览器支持的最高标准运行。

在兼容模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作。

HTML5 为什么只需要写

HTML5 不基于 SGML,因此不需要对 DTD (Document Type Definition) 进行引用,但是需要 DOCTYPE 来规范浏览器的行为(让浏览器按照它们应该的方式来运行)。

换言之,HTML4.01 及以下是基于 SGML,所以需要对 DTD 进行引用,这样才能告知浏览器文档所使用的文档类型。

SGML 是 Standard Generalized Markup language 标准通用标记语言。一种很强大但很复杂的标记语言,HTML、XML 就是从中衍生出来的。

DTD 是 Document Type Definition 文档类型定义,是一套为了进行程序间的数据交换而建立的关于标记符的语法规则。

有哪些惊奇的 HTML5 特性

async 属性

语义化,比如 article、footer、header、nav、section

表单控件,比如 calendar、date、time、email、url、search

video/audio 更加友好

drag/drop 拖放

拖放(drag 和 drop)

设置可拖 draggable=”true” 属性

监听拖事件 ondragstart=”drag”

1
<img id="img" draggable="true" ondragstart="drag">
1
2
3
4
drag (e) {
const data = e.target.id // 设置需要拖的数据
e.dataTransfer.setData('Text', data) // 将数据设置到拖对象中,指定数据类型
}

设置可放 allowDrop=”allowDrop” 属性

监听放事件 ondrop=”drop”

1
<div ondragover="allowDrop" ondrop="drop"></div>
1
2
3
4
5
6
7
8
9
allowDrop (e) {
e.preventDefault() // 阻止浏览器对数据的默认处理(drop 事件的默认行为是以链接形式打开)
}

drop (e) {
e.preventDefault() // 阻止浏览器对数据的默认处理(drop 事件的默认行为是以链接形式打开)
const data = e.dataTransfer.getData('Text') // 获取放过来的值
e.target.appendChild(document.getElementById(data)) // 处理
}

canvas

1
<canvas width="200" height="100"></canvas>

canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成

1
2
3
4
5
6
7
const canvas = document.querySelect('canvas')
const cxt = canvas.getContext('2d')
cxt.fillStyle = '#f00'
cxt.fillRect(0, 0, 150, 75)
const img = new Image()
img.src = 'https://cmspic-10004025.image.myqcloud.com/4273a6c5-7b92-40e2-a0d0-841741f87b6e'
cxt.drawImage(img, 0, 0)

svg

地理定位 Geolocation

依赖浏览器的定位。如果是谷歌浏览器,是依赖 googleapis。目前需要 VPN 代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ('geolocation' in navigator) {
// 地理位置服务可用
navigator.geolocation.getCurrentPosition(res => {
console.log(res, 'success')
}, error => {
console.log(error, 'error')
}, {
enableHighAccuracy: true, // 是否使用其最高精度
timeout: 10000, // 超时
maximumAge: 0, // 缓存时间
})
} else {
// 地理位置服务不可用
console.log('地理位置服务不可用')
}

如果项目中需要定位,可以引入高德地图、腾讯地图、百度地图等。

Web 存储

HTML5 提供了两种在客户端存储数据的新方法

  • localStorage - 没有时间限制的数据存储。本地离线存储,可长期存储数据,浏览器关闭后数据不丢失
  • sessionStorage - 针对一个 session 的数据存储,在浏览器关闭后自动删除

之前,这些都是由 cookie 完成的。但是 cookie 不适合大量数据的存储,因为它们由每个对服务器的请求来传递,这使得 cookie 速度很慢而且效率也不高。

在 HTML5 中,数据不是由每个服务器请求传递的,而是只有在请求时使用数据。它使在不影响网站性能的情况下存储大量数据成为可能。

对于不同的网站,数据存储于不同的区域,并且一个网站只能访问其自身的数据。

localStorage 和 sessionStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到5M或更大。

cookie 数据大小不能超过4k。设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
console.log(localStorage) // 是一个类对象

for (let key in localStorage) {
console.log(key, localStorage[key])
}

// 这样重新赋值是无效的
localStorage = {
...localStorage,
x: 1,
y: 2,
}

// localStorage 只允许添加某属性,而且值会被转成字符串形式,

localStorage['x'] = 1
localStorage.y = 2
localStorage.setItem('z', 3)

localStorage.setItem('garbage', true)

用处:

  • 利用 localStorage 来记录当前页面浏览位置。
  • 利用 localStorage 来记录音视控件播放的方式:单曲循环/列表循环/顺序播放/随机播放

Application Cache

应用程序缓存

  • 离线浏览 - 用户可在应用离线时使用它们
  • 速度 - 已缓存资源加载得更快
  • 减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源

Web Workers

当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成
web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行

worker.js

1
2
3
4
let i = 0
setTimeout(() => {
postMessage(++i)
}, 1000)
1
2
3
4
5
6
if (typeof(Worker) === 'function') {
const w = new Worker('worker.js')
w.onmessage = (e) => {
console.log(e.data)
}
}

HTML 5 服务器发送事件

postMessage

onopen

onmessage

onerror

js 基本数据类型

五种基本:Undefined、Null、Boolean、Number、String

NaN 是 Number 类型

一种特殊:Object

Object 包含三大引用类型:

  • Object: {}
  • Array: []
  • Function: () => {}

其实还有数据封装类对象:Boolean、Number、String

Math、Date、RegExp、Error 等

类数组:NodeList、Arguments

null 与 undefined 与 NaN

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
// null
null === null // true

!null // true

Number(null) // 0

typeof null // 'object',作为对象原型链的终点

// undefined
undefined === undefined // true

!undefined // true

Number(undefined) // NaN

typeof undefined // 'undefined'

// NaN
NaN == NaN // false

!NaN // true

Number(NaN) // NaN

typeof NaN // 'number'

isNaN(NaN) // true

this 关键词

其实就是作用域问题

ES6 后很少就关心了

偶尔涉及的时候,使用 that 管理一下

1
const that = this

apply 与 call

调用一个对象的一个方法,用另一个对象替换当前对象

将一个函数的对象上下文从初始的上下文改变为由 this 指定的新对象

apply

只接受两个参数,新 this 对象和一个参数数组 argArray

Function.apply(this, [1, 2, 3])

this 对象应用 Function 对象的方法

call

接受多个参数,第一个参数与 apply 一样,都是新 this 对象,后面则是一串参数列表

Function.call(this, 1, 2, 3)

Function 对象调用 this 对象的方法

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function add (a,b) {
return a + b
}

function sub (a, b) {
return a - b
}

var a1 = add.apply(sub, [4, 2]) // sub 应用 add 的方法

var a2 = sub.call(add, 4, 2) // sub 应用 add 的方法

console.log(a1); // 6
console.log(a2); // 2

实现继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Animal (name) {
this.name = name

this.showName = function () {
console.log(this.name)
}
}

function Cat (name) {
Animal.apply(this, [name])
}

function Dog (name) {
Animal.call(this, name)
}

var cat = new Cat('喵喵') // cat 通过 apply 继承了 Animal 的 name 属性和 showName 方法
cat.showName() // 喵喵

var dog = new Dog('汪汪') // dog 通过 call 继承了 Animal 的 name 属性和 showName 方法
dog.showName() // 汪汪

apply 和 call 的一些巧妙

apply 可以将一些接受参数列表转化为接受参数数组的形式

1
2
3
4
5
6
7
8
9
10
11
// Math.max(param1, param2...)

var arr = [1, 2, 3, 6, 5, 4] // 比较大小

Math.max(1, 2, 3, 6, 5, 4) // 6

function max (argArray) {
return Math.max.apply(null, argArray)
}

max(arr) // 6
1
2
3
4
5
6
7
8
9
// push(param1, param2...)

var arr = [1]

arr1.push(2, 3)

Array.prototype.push.apply(arr, [2, 3])

console.log(arr) // [1, 2, 3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [1]

arr.concat(2, 3) // [1, 2, 3]
arr.concat([2, 3]) // [1, 2, 3]
arr.concat([[2, 3]]) // [1, [2, 3]]

Array.prototype.concat(arr, 2, 3) // [1, 2, 3]

Array.prototype.concat(arr, [2, 3]) // [1, 2, 3]

Array.prototype.concat(arr, [[2, 3]]) // [1, [2, 3]]

Array.prototype.concat.apply(arr, [[2, 3]]) // [1, 2, 3]

// 看最后一个,[1] 应用 Array.prototype.concat 方法,[[2, 3]] 是参数列表,所以等价于:Array.prototype.concat([1], [2, 3])

navigator

navigator.userAgent

HTTP 状态码

100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未授权。
403 Forbidden 禁止访问。
404 Not Found 找不到如何与 URI 相匹配的资源。
500 Internal Server Error 最常见的服务器端错误。
503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。

哪些性能优化的方法?

减少http请求次数:CSS Sprites, htnl、JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器

前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数

用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能

当需要设置的样式很多时设置className而不是直接操作style

少用全局变量、缓存DOM节点查找的结果,减少IO读取操作

避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)

图片预加载

将样式表放在顶部,将脚本放在底部

内存泄漏

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。

垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。

闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

线程与进程

一个程序至少有一个进程,一个进程至少有一个线程。

线程的划分尺度小于进程,使得多线程程序的并发性高。

进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。

数组去重

对象的浅拷贝和深拷贝;浅复制和深复制

js 操作获取和设置 cookie

ajax 有那些优缺点

优点:

  • 通过异步模式,提升了用户体验.
  • 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用.
  • Ajax在客户端运行,承担了一部分本来由服务器承担的工作,减少了大用户量下的服务器负载。
  • Ajax可以实现动态不刷新(局部刷新)

缺点:

  • 安全问题 AJAX暴露了与服务器交互的细节。
  • 对搜索引擎的支持比较弱。
  • 不容易调试。

如何解决跨域问题?

jsonp、iframe、window.name、window.postMessage、服务器上设置代理页面

JavaScript 原型,原型链

原型也是普通的对象,是对象一个自带隐式的 proto 属性。

原型也有可能有自己的原型。如果一个原型的原型为非 null 的话,我们就称之为原型链。

原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。

Array

Array.proto

Array.proto.proto

Array.proto.proto.proto —–> null

GET 和 POST 的区别

GET:一般用于信息获取,使用 URL 传递参数,对所发送信息的数量也有限制,一般在2000个字符

POST:一般用于修改服务器上的资源,对所发送的信息没有限制。

GET 方式需要使用 Request.QueryString 来取得变量的值,而 POST 方式通过 Request.Form 来获取变量的值,也就是说 GET 是通过地址栏来传值,而 POST 是通过提交表单来传值。

然而,在以下情况中,请使用 POST 请求:

无法使用缓存文件(更新服务器上的文件或数据库)

向服务器发送大量数据(POST 没有数据量限制)

发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

同源策略

协议,域名,端口相同,同源策略是一种安全协议。

闭包

函数可以访问函数外的变量。

如何在函数外使用函数内的变量呢,闭包。

闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。

闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。

闭包是子函数可以使用父函数的局部变量和参数。

一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包就是一个函数引用另一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会增加内存消耗。

闭包的特点:

  • 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  • 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

javascript 里面的继承怎么实现,如何避免原型链上面的对象共享

用构造函数和原型链的混合模式去实现继承,避免对象共享可以参考经典的 extend() 函数,很多前端框架都有封装的,就是用一个空函数当做中间变量。

ajax 过程

  • 创建 XHR - XMLHttpRequest 对象,也就是创建一个异步调用对象
  • 创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL及验证信息
  • 设置响应 HTTP 请求状态变化的函数
  • 发送 HTTP 请求
  • 获取异步调用返回的数据.
  • 使用 JavaScript 和 DOM 实现局部刷新

游览器输入 URL 到页面加载显示完成,这个过程中都发生了什么?

(1)查找浏览器缓存-是否有缓存
(2)无缓存,做 DNS 解析、查找该域名对应的 IP 地址、重定向(301/302)、发出第二个 GET 请求
(3)进行 HTTP 协议会话
(4)客户端发送报头(请求报头)
(5)服务器回馈报头(响应报头)
(6)HTML 文档开始下载
(7)文档树建立,根据标记请求所需指定 MIME 类型的文件
(8)页面显示

URL 和 URI 有什么不同?

ES6

扩展运算符(spread)是三个点(…)

  • 替代函数的 apply 方法
  • 复制数组(浅拷贝)
  • 合并数组(浅拷贝)
  • 与解构赋值结合
  • 将字符串转为真正的数组
  • 任何 Iterator 接口的对象 (Set 和 Map) 和 类似数组的对象 (arrayLike,nodeList),都可以用扩展运算符转为真正的数组

扩展运算符(…) 调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。

1
2
3
4
5
6
7
8
9
const arr1 = [
{
key: 'value'
}
]

const arr2 = [...a1] // arr2 对 arr1 的浅拷贝,克隆

// 改变 arr1 中的值,会

Array.from()

Array.from 方法用于将两类对象转为真正的数组:类似数组的对象(arrayLike,nodeList)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过 Array.from 方法转为数组,而此时扩展运算符 (…) 就无法转换。

1
2
Array.from({ length: 3 })
// [ undefined, undefined, undefined ]

fill()

find() 和 findIndex()

entries(),keys()和values()

entries(),keys()和values()

vue

https://www.jianshu.com/p/b1dd80f4d542

计算属性 (computed) 与方法 (methods)

计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要相关依赖没有发生改变,多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数。

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

计算属性 (computed) 与侦听属性 (watch)

当需要在数据变化时执行异步或开销较大的操作时,侦听属性 (watch) 最有用。

使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

用 key 管理可复用的元素

Vue 为你提供了一种方式来表达 “这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可。

在使用 v-for 时,key 是必须的!

https://cn.vuejs.org/v2/guide/list.html#key

v-for 的 in 和 of

v-for=”todo in todos”

v-for=”todo of todos”

v-for=”n in 10”

v-if 和 v-show 区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

为什么避免 v-if 和 v-for 一起使用

当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中。[当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用]

组件通信

  • 父组件与子组件传值
    • 父组件通过标签上面定义属性传值 [v-on:property]
    • 子组件通过 props 选项接受数据
  • 子组件向父组件传递数据
    • 父组件通过标签上面定义事件 [v-on:function]
    • 子组件通过 $emit 触发事件,可以传递值
  • 组件上使用 v-model
  • 利用 vuex

vue 如何实现按需加载

异步组件

1
2
3
{
component: async () => await import('./wxarticles/wxarticle/index.vue')
}

或者 webpack 的 require.ensure

vue 的生命周期

创建前/后,载入前/后,更新前/后,销毁前/销毁后

beforeCreate

  • 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

created

  • 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

beforeMount

  • 在挂载开始之前被调用:相关的 render 函数首次被调用。
  • 该钩子在服务器端渲染期间不被调用。

mounted

  • el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。

  • 注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。

    1
    2
    3
    4
    5
    6
    mounted: function () {
    this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
    })
    }
  • 该钩子在服务器端渲染期间不被调用。

beforeUpdate

  • 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
  • 该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。

updated

  • 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

  • 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。

  • 注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated

    1
    2
    3
    4
    5
    6
    updated: function () {
    this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
    })
    }
  • 该钩子在服务器端渲染期间不被调用。

beforeDestroy

  • 实例销毁之前调用。在这一步,实例仍然完全可用。
  • 该钩子在服务器端渲染期间不被调用。

destroyed

  • Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
  • 该钩子在服务器端渲染期间不被调用。

节点、树以及虚拟 DOM

VNode,Virtual Node 虚拟节点

组件树中的所有 VNodes 必须是唯一的

Vue 通过建立一个虚拟 DOM 对真实 DOM 发生的变化保持追踪

什么是 MVVM

Model-View-ViewModel,MVC 的改进版。

Model 层代表数据模型,在 Model 中定义数据修改和操作的业务逻辑。

View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来。

ViewModel 是一个同步 View 和 Model 的对象。

Model 和 View 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的,因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

  1. 低耦合。视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 View 重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。

MVVM 和 MVC 区别

Model View Controller

Model(模型)表示应用程序核心(比如数据库记录列表)。
View(视图)显示数据(数据库记录)。
Controller(控制器)处理输入(写入数据库记录)。

区别并不大。主要就是 MVC 中 Controller 演变成 MMVVM 中的 ViewModel。

MVC 中改变 View 需要大量的 DOM 操作,这使页面渲染性能降低,加载速度变慢,影响用户体验。

MVVM 是数据驱动,通过数据来显示视图层而不是节点操作。数据操作比较多的场景,更加便捷。

vue 的优点

  1. 低耦合。视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 View 重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。

vue-router

路由之间跳转

声明式(标签跳转) 编程式( js跳转)

全局守卫、路由独享的守卫、组件内的守卫

vuex

小程序

wepy

mpvue

  • 请勿在 scroll-view 中使用 textarea、map、canvas、video 组件。在 中滚动时,

  • wx.showShareMenu 与 wx.updateShareMenu 的区别

  • 去除点击态效果

  • 微信版本 6.3.30, focus 属性设置无效;placeholder 在聚焦时出现重影问题;字体是系统字体,所以无法设置 font-family;在 input 聚焦期间,避免使用 css 动画

  • hidden 属性的作用,其实是 *[hidden]{display:none},存在被覆盖从而失效的可能

  • 前端 wx.login 换取 code (后端通过 code 换取 session_key),前端 shareTicket 解析 encryptedData、iv,后端拿 session_key、encryptedData、iv 解析群信息如 openGId 存在失败的可能。执行顺序很关键!得先解析 wx.login 换取 code,再用 shareTicket 解析 encryptedData、iv

  • 开发版二维码可以在微信开发者工具中生成,可带参数;体验版二维码可以在微信公众平台中[开发管理->开发版本->修改页面路径带参数]

  • 并发请求限制

    1
    2
    3
    4
    5
    6
    7
    每次切换后,同时请求三条接口(并发),会导致手机卡顿,所有改成请求一条接口后,再请求其他接口。

    当前tab下请求发出,打一个loading=true,只要当前tab下请求不结束,不再触发同一请求。

    tab有5个item。点击每个item,需要请求对应的数据。当用户点击第1个item,请求第1类数据时,因为接口异步,无法及时返回;而用户迅速点击第2个item,会去请求第2类数据;类似操作,用户在5个item之间来回切换,网速慢,会导致有很多接口在同时请求(请求发出但迟迟不返回视为请求中),超出了小程序的限制,导致页面操作卡顿!

    所以需要对请求加以限制(节流)。当用户点击第1个item请求第1类接口,然后来回切换,切换回第1个item时,如果第1类接口不结束那就不应该再次发起。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let apiLoading1 = false

    const requestApi1 = async () => {
    if (apiLoading1) return
    apiLoading1 = true

    await request(api1)

    apiLoading1 = false
    }

    clickItem1 () {
    // 点击事件
    requestApi()
    }
  • cover-view 覆盖在原生组件之上的文本视图,可覆盖的原生组件包括 map、video、canvas、camera、live-player、live-pusher

  • cover-view 内只能嵌套 cover-view、cover-image、button。

zhouyu1993 wechat
扫一扫上面的二维码,奇妙的世界等着你!
坚持技术分享,您的支持将鼓励我!