0%

前端面试题-other(补充中...)


在地址栏里输入一个地址回车会发生那些事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(1)将域名进行DNS解析
- 浏览器DNS缓存
- 系统DNS缓存
- 路由器DNS缓存
- 网络运营商的DNS缓存

(2)Tcp 三次握手
- 客户端发送一个带有SYN(synchronize)标志的数据包给服务端
- 服务端接收成功后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了
- 客户端再回传一个带有ACK标志的数据包,表示我知道了,握手结束

(3)发送响应

(4)接受响应

(5)浏览器解析渲染页面 (也就是浏览器的运行机制)

(6)Tcp 四次挥手
- 客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态
- 服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务端进入CLOSE_WAIT状态
- 服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态
- 客户端收到FIN后,客户端t进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,服务端进入CLOSED状态,完成四次挥手

浏览器缓存机制

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的,强缓存优先于协商缓存进行

若强制缓存(Expires和Cache-Control)生效则直接使用缓存,不再走协商缓存路线

若时间已过期则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match)

协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效了,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;
生效则返回304,继续使用缓存。

实际场景应用缓存策略:

//不使用缓存资源
meta 缓存头设置为禁止缓存
在 html 的 head 标签中加入下面内容,就可以禁止浏览器读取缓存
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

js、css 加上版本号
当请求 js、css 的时候,给他们最后加上版本号,浏览器发现版本高了,就自然而然不会读取低版本的缓存了 版本号并不需要改变文件名,只需要在调用 js、css 的时候在最末尾加上?v=1.0即可
custom.css?v=1.0
main.js?v=2.0


//不使用缓存资源


频繁变动的资源:将Cache-Control字段设置为no-cache,表示每次请求都要验证资源的有效性
不常变化的资源:Cache-Control: max-age=31536000,将过期时间设置为1年。其实实际上在发送请求的时候会在路径中添加hash来更改url,发起新的请求。
输入url,回车:按照整个缓存机制进行,先走强缓存,再走协商缓存。
浏览器点击刷新按钮或者按F5刷新:发送的请求头带有:Cache-Control:max-age=0,表示不走强制缓存这条路,可以走协商缓存向服务器发起数据请求。
ctrl+F5强制刷新:发送的请求头带有Cache-Control:no-cache ,表示不走缓存这条路。直接向服务器发送新的请求。
浏览器的前进,后退,跳转是直接从缓存里取数据。

浏览器缓存的好处:
避免了冗余的数据传输,节省流量;
加快了用户访问网页的速度;
减小了服务器的压力

//拓展
缓存的位置:
service worker:单独的一个线程,用来实现缓存功能;

memory cache:内存中的缓存;容量小,存储时间短;

disk cache:硬盘上的缓存,容量大,时效性长;(绝大部分的缓存都来自 Disk Cache,与HTTP的缓存策略有很大的关联)

缓存过程的分析:
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识。

如果找不到则向服务器发送请求,拿到请求结果后又会根据资源响应头决定是否将该结果和缓存标识存入浏览器缓存中


强缓存
强缓存:客户端再次请求资源时,不会向服务器发送请求,而是直接从缓存中读取资源

两种实现方式:
1)Expires方法(设置过期时间)
2)Cache-Control方法(设置过期时间)
两者同时存在的话,Cache-Control优先级高于Expires;

协商缓存:
协商缓存:客户端再次请求资源时时,会向服务器发送请求验证当前资源的有效性

两种实现方法:
1)Last-Modified(根据文件修改时间来决定是否从缓存取数据)
2)Etag方法(根据文件内容是否修改来决定是否从缓存取数据)



HTTP和HTTPS协议的区别

1
2
3
4
5
6

HTTP 超文本传输协议,信息是明文传输的
HTTPS 则是具有安全性的SSL加密传输协议,比http更安全
HTTPS 协议需要CA证书,费用较高;而HTTP协议不需要

HTTP 协议端口是80, HTTPS 协议端口是443;

WebSocket 的应用场景主要有哪些

1
即时通信 数据监控 大文件下载

Webpack类

谈谈你对Webpack的理解(Webpack是什么?)

1
2
3
Webpack 是一个 模块打包器,可以分析各个模块的依赖关系,最终打包成bundle静态文件(js、css、jpg)


Webpack的打包过程/打包原理/构建流程?

1
2
3
初始化:启动构建,读取与合并配置参数,加载plugin,实例化Compiler
编译:从Entry出发,针对每个Module串行调用对应的Loader去翻译文件中的内容,再找到该Module依赖的Module,递归的进行编译处理
输出:将编译后的Module组合成Chunk,将Chunk转换成文件,输出到文件系统中

loader的作用

1
webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中

有哪些常见的Loader?他们是解决什么问题的?

1
2
3
4
5
6
7
8
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码

plugin的作用

1
plugin是一个类,类中有一个apply()方法,主要用于Plugin的安装,可以在其中监听一些来自编译器发出的事件,在合适的时机做一些事情。

有哪些常见的Plugin?他们是解决什么问题的?

1
2
3
4
5
6
7
8
html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
clean-webpack-plugin:重新打包自动清空 dist 目录
mini-css-extract-plugin:提取 js 中的 css 成单独文件
optimize-css-assets-webpack-plugin:压缩css
uglifyjs-webpack-plugin:压缩js
commons-chunk-plugin:提取公共代码
define-plugin:定义环境变量

Webpack中Loader和Plugin的区别

1
2
3
4
5
6
7
8
1、在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
2、webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。
3、webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念
4、webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,
同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。
webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。
5、决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

如何解决循环依赖问题

1
2
3

Webpack 中将 require 替换为 webpack_require,会根据 moduleId 到 installedModules 找是否加载过,加载过则直接返回之前的 export,不会重复加载

如何提高Webpack构建速度

1
2
3

组件懒加载、路由懒加载、开启gzip、公共的第三方包上cdn、配置include/exclude缩小Loader对文件的搜索范围、配置cache缓存Loader对文件的编译副本、配置resolve提高文件的搜索速度(@: src)

Git代码冲突怎么处理

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
一、暂存到暂存区,更新后,从暂存区取出合并解决冲突

1、git pull 出现冲突后可以暂存本地修改git stash

2、然后git pull 更新代码

3、git stash list 可查看暂存记录列表

4、释放本地暂存 git stash apply stash@{0} 就是刚才保存的标记

5、出现冲突文件,找到并解决

6、然后可以提交git add . 加入索引库

7、然后本地提交git commit -m '注释'

8、最后git push到远程
二、更新发现冲突,提交本地,再更新,找到冲突地方解决后,再次提交推送远程

1、git pull更新代码,发现error: Your local changes to the following files would be overwritten by merge:pom.xmlPlease commit your changes or stash them before you merge.这说明你的pom.xml与远程有冲突,你需要先提交本地的修改然后更新。

2、git add pom.xml

git commit -m '冲突解决'

提交本地的pom.xml文件,不进行推送远程

3、git pull

更新代码Auto-merging pom.xmlCONFLICT (content): Merge conflict in pom.xmlAutomatic merge failed; fix conflicts and then commit the result.更新后你的本地分支上会出现 (develop|MERGING)类似这种标志。

4、找到你本地的test.txt文件,并打开你会在文件中发现<<<<<<< HEAD ,======= ,>>>>>>> ae24sgwmfp2m2ojr2jaagwhhfawe2类似这样的标记。
<<<<<<< HEAD和=======中间的是你自己的代码, ======= 和>>>>>>>中间的是其他人修改的代码自己确定保留那一部分代码,最后删除<<<<<<< HEAD ,======= ,>>>>>>>这种标志。
5、git add test.txt && git commit -m '冲突解决结束' 再次将本地的test.txt文件提交。

6、git push将解决冲突后的文件推送到远程。

echarts

echarts问题

VUE引用和使用echarts4/echarts5的差别

1
2
3
4
5
6
7
8
9
10
1、echarts引用区别
v4:
import echarts from 'echarts'
Vue.prototype.$echarts = echarts
v5:
import * as echarts from 'echarts'
Vue.prototype.$echarts = echarts



WebSocket

WebSocket即时通讯

要实现客户端与服务器端的通信,最常接触的是http(https)协议,http通信只能是客户端发起请求,服务器响应。服务器不能主动向客户端传递消息。
HTTP 协议无法做到服务器主动向客户端推送信息,2008年诞生的WebSocket 协议可以实现客户端与服务器端的双向对话,即:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
WebSocket 协议的底层协议也是TCP协议

WebSocket 协议的标识符为ws,加密后为wss

WebSocket 协议没有同源限制,即WebSocket 协议可以跨域通信

WebSocket 协议是有状态的,是前后端交互的长连接,即建立连接后可以保持连接状态,通信时可以省略部分状态信息

WebSocket 协议可以发送文本,也可以发送二进制数据



建立WebSocket 连接,我们要知道 WebSocket 在前端工程 支持 4种属性

OnOpen 【建立连接】
OnClose 【关闭连接】
OnError 【错误信息】
OnMessage 【接收处理】

后端的WebSocket 支持五种:

OnOpen 【建立连接】
OnClose 【关闭连接】
OnError 【错误信息】
OnMessage 【连接处理】
connection 【信息处理函数】


客户端实现
客户端可以通过WebSocket 构造函数创建WebSocket 对象创建和管理 WebSocket 连接,并通过该连接发送和接收数据的 API

created() {
// 页面创建 生命周期函数
this.initWebSocket()
},
destroyed: function () {
// 页面销毁生命周期函数
this.websocketclose();
},
methods: {
initWebSocket: function () {
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
this.websock = new WebSocket("ws://127.0.0.1:9007/websocket");
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onmessage = this.websocketonmessage;
this.websock.onclose = this.websocketclose;
},
websocketonopen: function () {
console.log("WebSocket连接成功...");
},
websocketonerror: function (e) {
console.log("WebSocket连接发生错误...");
console.log(e)
},
websocketonmessage: function (e) {
console.log(e.data);
var alarm = JSON.parse(e.data)
console.log(alarm.alarmName)
if (e.data !== undefined) {
this.onlineUser = e.data
}
},
websocketclose: function (e) {
console.log("connection closed (" + e.code + ")");
}
}



前端支付功能实现(如何唤起支付页面)

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
判断用户所属环境,根据环境不同,执行不同的支付程序
if (/MicroMessenger/.test(window.navigator.userAgent)) {
// alert('微信');
} else if (/AlipayClient/.test(window.navigator.userAgent)) {
//alert('支付宝');
} else {
//alert('其他浏览器');
}

1、在H5中唤起微信支付和支付宝支付
//微信支付
H5页面调起微信支付有两种办法,
一是利用内置接口(WeixinJSBridge),二是通过引用微信的js sdk(使用wx.chooseWXpay()调起支付页面),从写法上来看用内置对象方法比较简单

1)内置接口(WeixinJSBridge),在从后台拿到签名、时间戳这些数据后,直接调用微信浏览器提供的内置接口WeixinJSBridge即可完成支付功能。
getRequestPayment(data) {
function onBridgeReady() {
WeixinJSBridge.invoke(
"getBrandWCPayRequest", {
"appId": data.appId, //公众号ID,由商户传入
"timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": data.nonceStr, //随机串
"package": data.package,
"signType": data.signType, //微信签名方式:
"paySign": data.paySign //微信签名
},
function(res) {
alert(JSON.stringify(res));
// get_brand_wcpay_request
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
}
);
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener(
"WeixinJSBridgeReady",
onBridgeReady,
false
);
} else if (document.attachEvent) {
document.attachEvent("WeixinJSBridgeReady", onBridgeReady);
document.attachEvent("onWeixinJSBridgeReady", onBridgeReady);
}
} else {
onBridgeReady();
}
},


// wxPay(userId,Id,grade_id){//发起请求,参数:价格、id、商品id、等自己写的方法,点击微信支付按钮调用<br>
// $.ajax({
// type: "post",
// url: URLS.WXPAY_URL,//后端给的接口
// data: {id: Id,user_id:userId,grade_id:grade_id},//传递后端需要的参数id什么的
// success: function (res) {
// var resData = JSON.parse(res);//先转换一下格式<br>
// window.location.href = resData.data //直接跳转到接口返回来的支付链接
// }
// })
// },

//支付宝支付
支付宝支付相对于微信来说,前端这块工作更简单 ,后台会返回给前端一个form表单,我们要做的就是把这个表单进行提交(唤起支付宝)即可
AliPay(userId,Id,grade_id){//自己写的点击事件,点击支付宝支付按钮调用
$.ajax({
type: "post",
url: URLS.ALIPAY_URL,//后端给的支付宝支付请求接口
data: {id: Id,user_id:userId,grade_id:grade_id},//需要的参数
success: function (res) {
var resData = JSON.parse(res);//转换一下格式
const div = document.createElement('div')//后端返回的数据类似一个form表单,创建一个div后执行表单的submit就OK了
div.id = 'alipay'
div.innerHTML = resData.data
document.body.appendChild(div)
document.querySelector('#alipay').children[0].submit() // 执行后会唤起支付宝
}
})
}
// this.$api.alipayPay(data).then((res) => {
// // console.log('支付宝参数', res.data)
// if (res.code == 200) {
// var resData =res.data
// const div = document.createElement('div')
// div.id = 'alipay'
// div.innerHTML = resData
// document.body.appendChild(div)
// document.querySelector('#alipay').children[0].submit() // 执行后会唤起支付宝
// }

// }).catch((err) => {

// })


2、uniapp支付之微信支付和支付宝支付
uni.requestPayment

支付如何用户重复点击

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
1、防抖

2、变量控制
在按钮点击事件处理函数中,可以使用变量来控制按钮是否可以被点击。当按钮被点击后,设置变量为 true,在事件处理函数执行完毕后再将其设置为 false。这样可以防止用户重复点击按钮

let buttonClicked = false;

function handleClick(event) {
if (buttonClicked) {
return;
}

buttonClicked = true;

// 执行其他代码...

buttonClicked = false;
}

3、禁用按钮
在按钮点击事件处理函数中,可以将按钮设置为禁用状态,防止用户重复点
function handleClick(event) {
const button = event.target;
button.disabled = true;

// 执行其他代码...
}

4、使用定时器
在按钮点击事件处理函数中,可以使用定时器延迟一段时间后再次启用按钮。这样可以确保用户不会在按钮被禁用期间重复点击
function handleClick(event) {
const button = event.target;
button.disabled = true;

// 在 2 秒后启用按钮
setTimeout(() => {
button.disabled = false;
}, 2000);

// 执行其他代码...
}

后端返回大量数据到前端,前端如何展示(像地图热点图)

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
普通
1、防抖节流
该防抖函数可以实现 this指向,参数,立即执行,取消功能,返回值
function debounce(fn, delay, immediate = false,resultCall) {
let timer = null;
let isInvoke = false;
let result = null;//返回给元素的真正函数
const _debounce = function (...args) {//取消上一次的操作
if (timer) clearTimeout(timer);//如果是第一次执行或者已经执行完的,采取立即执行

if (immediate && !isInvoke) {
result =fn.apply(this, args);
if(resultCall){
resultCall(result)
}
isInvoke = true;}//开启防抖
else {
timer = setTimeout(() => {//执行真正的函数
result =fn.apply(this, args);
if(resultCall){
resultCall(result)
}
isInvoke = false;
}, delay);
}
};
//取消事件
_debounce.cancel = function () {
if (timer) clearTimeout(timer);
};
return _debounce;
}

该节流函数可以实现 this指向,参数,立即执行,取消功能,返回值
function throttle(fn, interval, options = { leading: true, trailing: false }) {
// 1.记录上一次的开始时间
//leading记录要不要第一次触发,即要不要同步执行
//trailing记录要不要进行定时器触发
const { leading, trailing } = optionslet
lastTime = 0let timer = null
// 2.事件触发时, 真正执行的函数
const _throttle = function(...args) {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()//要不要同步执行
if (!lastTime && !leading) lastTime = nowTime
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer)timer = null
}
// 2.3.真正触发函数
fn.apply(this, args)
// 2.4.保留上次触发的时间
lastTime = nowTimereturn
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = nulllastTime = !leading ? 0: new Date().getTime()
fn.apply(this, args)}
, remainTime)
}
}
_throttle.cancel = function() {
if(timer) clearTimeout(timer)
timer = nulllastTime = 0
}
return _throttle
}
2、分页处理

3、懒加载

4、定时处理
当我们接受到成千上万的数据要进行渲染时,可能较多的人会使用setTineout来作为定时器,通过分块的方式定时渲染。 但使用 _requestAnimationFrame_的效果是会比 setTimeout 的性能更好,因为使用前者的时候浏览器会帮我们进行处理优化,在最优的时候进行调用,而不是像后者设置一个时间。

另外,让我们往一个dom节点插入数据的时候,我们99%都会使用document.createELement,但是如果在该场景下,你插入一条数据都会造成DOM树的重新渲染,当插入的元素多时会造成没必要的开销。但是我们可以使用document.Fragment来使用,它就像vue里面的template后者react的<></>,不是一个真实的元素标签。

与_createElement_相比,它最大的好处就是不会触发DOM树的重新渲染,且不会对性能造成影响。因为所有的节点会被_一次性_插入到文档中,所以仅会发生一个重渲染的操作,而不是每个节点分别被插入到文档中从而发生多次重渲染的操作

<ul id="list"></ul>

const total = 10000;
const num = 20;
const times = Math.ceil(total / num);
let currentNums = 0;
const ul = document.querySelector("#list");
function add() {
let frag = new DocumentFragment();
for (let i = 0; i < num; i++) {
const li = document.createElement("li");
li.innerHTML = Math.floor(i + currentNums * num);
frag.appendChild(li);
}
currentNums++;
ul.appendChild(frag);
if (currentNums < times) {
window.requestAnimationFrame(add);
}
}
window.requestAnimationFrame(add);


5、想全部展示,还不分页(例如热力图之类的)
MapVGL,是一款基于WebGL的地理信息可视化库,可以用来展示大量基于3D的地理信息点线面数据

ios开发H5页面怎么结合的 交互方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
APP 可以通过 WebView 组件来嵌入 H5 页面。WebView 是一种原生组件,可以在 APP 中加载网页,并支持与网页的交互。

在 Android 中,可以使用 Android 的 WebView 组件,

在 iOS 中,可以使用 iOS 的 UIWebView 或者 WKWebView 组件。

将 H5 页面嵌入 APP 中的一般流程如下:

在 APP 中创建 WebView 组件,并将其放置在需要显示 H5 页面的位置。使用 WebView 的 loadUrl() 方法加载 H5 页面的 URL 地址。也可以使用 loadData() 方法加载页面的 HTML 内容。在 H5 页面中添加相应的 JavaScript 代码,通过 WebView 提供的接口来与 APP 进行交互。在 APP 中注册 JavaScript 接口,以便 H5 页面中的 JavaScript 代码可以调用。

在 Android 中,可以使用 addJavascriptInterface() 方法注册接口;

在 iOS 中,可以使用 WKWebView 的 WKScriptMessageHandler 接口。

在 H5 页面中通过 JavaScript 接口调用 APP 提供的方法,实现与 APP 的交互

elementui的表格嵌套表单内及校验(复杂表格校验)

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
因为el-form是最外层的,所以formData是个对象,对象里放el-table用到的数组和rules
这样定义是为了校验时用到rules

el-form绑定: rules使用formData.rules整个对象,同时定义一个ref
<el-form :model="formData" :rules="formData.rules" ref="formRef">

保证prop必须唯一
自定义prop:使用 列表数据属性名+列的下标scope.$index+列的数据属性名
注意:如果不是使用列表数据属性名tableData,会出现报错
Error: please transfer a valid prop path to form item
<el-form-item :prop="'tableData.'+scope.$index+'.price'" :rules="formData.rules.price">

保证每一个el-form-item都要配置rules属性
rules使用formData.rules对应的属性的规则

代码如下(参考:https://blog.csdn.net/m0_66722601/article/details/128254991)
<template>
<div>
<el-form :model="formData" :rules="formData.rules" ref="formRef">
<el-table :data="formData.tableData">
<el-table-column
label="价格">
<template slot-scope="scope">
<el-form-item :prop="'tableData.'+scope.$index+'.price'" :rules="formData.rules.price">
<el-input v-model="scope.row.price"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="数量">
<template slot-scope="scope">
<el-form-item :prop="'tableData.'+scope.$index+'.num'" :rules="formData.rules.price">
<el-input v-model="scope.row.num"></el-input>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
<el-button type="primary" @click="submitForm('formRef')">批量开票</el-button>
</div>
</template>

<script>
export default {
name: "table",
data(){
return {
formData:{
id:1,
...,

rules: {
price: {
required: true,
message: '单价不能为空',
tirgger: ['blur', 'change']
},
num: {
required: true,
message: '数量不能为空',
tirgger: ['blur', 'change']
},
selected: {
required: true,
message: '请选择',
tirgger: 'change'
}
},
tableData:[ //表格数据
{
num:1, //数量
price:291.37, //价格
}]
}
}
},
methods:{
submitForm(formName){
this.$refs[formName].validate((valid)=>{
if(valid){
console.log(1)
}
})
}
}
}

第二种没验证(参考:https://www.jianshu.com/p/f036aae3539b)
<template>
<div>
<el-form :model="forms" ref="forms" :rules="rules">
<el-table :data="forms.voList">
<el-table-column
label="价格">
<template slot-scope="scope">
<el-form-item :prop="'voList.'+scope.$index+'.unitPrice'" :rules="rules.unitPrice">
<el-input v-model="scope.row.unitPrice"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="数量">
<template slot-scope="scope">
<el-form-item :prop="'voList.'+scope.$index+'.num'" :rules="rules.unitPrice">
<el-input v-model="scope.row.num"></el-input>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
<el-button type="primary" @click="save">批量开票</el-button>
</div>
</template>

<script>
export default {
name: "table",
data(){
return {
forms:{
id:1,
...,
voList:[
{
num:1,
unitPrice:291.37,
}
]
},
rules:{
num:[{required:true,message:'数量不能为空',trigger:'blur'}],
unitPrice:[{required:true,message:'单价不能为空',trigger:'blur'}]
}
}
},
methods:{
save(){
this.$refs['forms'].validate((valid)=>{
if(valid){
console.log(1)
}
})
}
}
}
</script>
</script>
1
2

...补充中
------ The End ------
您的认可是我不断进步的动力!