banner
Hi my new friend!

其他知识

Scroll down

其他知识

本篇文章包含了 vue源码知识 和 其他特色功能展示

vue源码部分核心解析

由于是单独提取需要上下相互学习

初始化data

new Vue的时候调用会调用_init方法,定义 $set、 $get 、$delete、$watch 等方法,调用$mount进行页面的挂载,挂载的时候主要是通过mountComponent方法,定义updateComponent更新函数,执行render生成虚拟DOM,_update将虚拟DOM生成真实DOM结构,并且渲染到页面中

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
   //html
new Vue({
el:"#app",
//data 两种情况
data:{

},
data(){
return {
msg:"hello"
}
}
watch:{}
})
//.$mount("#app")
function vue(options){
//初始化
this._init(options)
}
initMixin(vue)
lifeyvleMinin(vue)//添加生命周期
renderMin(Vue)//添加_rende
//全局方法
initGlobApi(Vue)
export default vue

初始化init
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
 export function initMixin(Vue){
Vue.prototype._init = function(options){
let vm = this //vm = 实例
vm.$options = merfeOptions(Vue.options,options)
callHook(vm,'beforeCreates')
//生命周期
vm.$options = mergerOption(Vue.options)
//初始化状态
initState(vm)
callHook(vm,;'creates')

//渲染模板
if(vm.$options.el){
vm.$mount(vm.$options.el)
}
}
//创建$mount
Vue.prototype.$mount = function(el){
//
let vm = this //实例
el = document.querySelector(el)
let options = vm.$options
if(!options.render){
let template= options.template
if(!template && el){
//获取到html
el=el.outerHTML
}
}
}
}
初始化状态
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
 export function initState(vm){
let ops = vm.$options
//判断
if(opts.props){
initProps(vm)
}
if(opts.data){
initData(vm)
}
if(opts.watch){
initWatch(vm)
}
if(opts.compoted){
initCompoted(vm)
}
if(opts.methods){
initMethods(vm)
}
}
function initProps(vm){}
//vue2对data初始化 //分为函数 和对象 两种类型
function initData(vm){
let data = vm.$options.data
//this 指向window 要指向new实例
data = typeof data == 'function'? data.call(vm):data
//劫持data
observer(data)
//data中的数据分为 数组(内部包含对象) 和对象两种情况
}
function initWatch(vm){}
function initCompoted(vm){}
function initMethods(vm){}

vue中的data

data分为两种形式 分为函数和对象 在远吗中通过typeof 判断是函数还是对象 将返回的结果传递给了observer函数进行类型判断取值
是把传入值所有的属性(包括嵌套属性)递归 进行响应式defineReactive,又通过 object.definProperty(缺点: 对象中的一个属性进行劫持) 定义get set方法
难点是对数组的劫持 首先对数组进行遍历 并获取数组原来的方法 并继承 然后对数组中所有能改变数组自身的方法,如 push、pop 等这些方法进行重写。重写后的方法会先执行它们本身原有的逻辑,并对能增加数组长度的 3 个方法 push、unshift、splice 方法做了判断,获取到插入的值,然后把新添加的值变成一个响应式对象

1
2
3
let data = vm.$options.data
data = typeof data === 'function'?data.call(vm):data
observer(data)//劫持data 对象/数组
data 中处理对象数组
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
export function observe(data){


//判断数据
if(typeod data != 'object' || data == null){
return data
}
//对象通过一个类劫持
return new Observer(data)
}
class Observer {
constructor(value){
//给value定义了一个属性
Object.defineProperty(value,"__ob__",{
enumerable:false,
value:this
})
//判断数据
if(Array.isArray(value)){
value.__proto__ == ArrayMethods
this.observeArray(value)//处理数组对象
}else{
this.walk(value) //遍历
}


}
walk(data){
let keys = object.keys(data)
for(let i=0;i<keys.length;i++){
//对我们的每个属性进行劫持
let key =keys[i]
let value = data[i]
defineReactive(data,key,value)
}
}
observeArray(value){
for(let let i=0;i<value.length;i++){
observer(value[i])
}
}
}
//对对象中的属性进行劫持
function definReactive(data,key,value){
observer(value)//深度代理

object.definProperty(data,key,{
get(){
return data[key]
},
set(newValue){
if(newValue == value) return ;
observer(value)//如果用户设置的是对象
value = newValue
}
})
}
data 中处理数组
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
//数组劫持    方法函数劫持  劫持数组方法
//重写数组
//1.获取原来数组方法
let oldArrayProtoMethods = Array.prototype
//2.继承
export let ArrayMethods = Object.create(oldArrayProtoMethods)
//3.劫持
let methods = [
'push',
'pop',
'unshift',
'shift',
'splice'
]
methods.forEach((item)=>{
ArrayMethods[item] = function(...args){
let result = oldArrayProtoMethods[item].apply(this,args)
//问题 数组追加对象的情况 要进行劫持
let inserted
Switch(item){
case 'push':
case'unshift':
inserted = args
break;
case 'splice':
inserted = args.splice()
}
let ob = this.__ob__
if(inserted){
ob.observeArray(inserted)
}
return result
}
})

vue模板编译

创建了一个baseCompile 函数 通过document.querySelector获取到html 通过正则解析 将template模板里面属性 指定等等 解析成ast树 并进行遍历 节点进行标记 生成render函数 render函数将模版内容生成对应的vnode 通过diff算法中的patch 等到渲染视图中的vnode 新旧节点的对比 创建真实的dom节点插入视图中完成渲染
*/

获取html转为ast语法
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/vue首次渲染 =>>  先初始化数据 =》  讲模板进行编译
//创建$mount
export function initMixin(Vue){
Vue.prototype._init = function(options){
let vm = this //vm = 实例
vm.$options = options
//初始化状态
initState(vm)
//渲染模板
if(vm.$options.el){
vm.$mount(vm.$options.el)
}
}
//创建$mount
Vue.prototype.$mount = function(el){
//
let vm = this //实例
el = document.querySelector(el)
let options = vm.$options
if(!options.render){
let template= options.template
if(!template && el){
//获取到html
el=el.outerHTML
//变成ast语法树
let ast = compileToFunction(el)
//变成rander()
options.render = render
//变成虚拟dom
}
}
//挂载组件
mounetComponent(vm,el){

}
}

///ast语法树{} vnode只能操作节点
/*
<div id="app">hello {{msg}}</div>
{
tag:'div',
attrs:[{id:"#app"}],
children:[{tag:"",text:"hello",},{tag:'div'.......}

*/

//遍历
const ncname = `[a-zA-Z_][\\-\\..-9_a-zA-Z]*`;
const qunameCapture = `((?:${ncname}\\:)?${ncnme})`;
const startTagOpen = new RegEp(`^<${quameCapture}`);
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s "'=<>` ]+)))?/;
//<div id='app'></div>
const startTagClose = /^\s*(/\?)>/;
const defaultTagRE = /\{\((?:.|\r?\n)+?)\}/g
//创建一个ast对象
function createAstElement(tag,attrs){
return {
tagName,//元素
attrs,//属性
children:[]//子节点
type:1,
parent:null//
}
}
let root;//根元素
let creatParent;//当前元素的父级
// 数据结构 栈
let stack = []


//遍历
function start(tag,attrs){//开始标签
let element = createAstElement(tag,attrs)
if(!root){
root = element
}
creatParent = element
stack.push(element)
}
function charts(text){//获取文本
text= text.replace(/a/g,'')
if(text){
createParent.children.push({type:3,text})
}
}
function end(){
let element = stack.pop()
creatParent = stack[stack.length-1]
if(creatParent){//元素的闭合
element.parent.creaateParent.tag
creaateParent.children.push(element)

}
}
function parseHTML(html){
while(html){ //html 为空时候结束
//判断标签
let textEnd = html.indexOf('<') //
if(textEnd === 0){
//第一种情况 开始标签
const startTagMatch = parseStartTag()//开始标签内容
if(startTagMatch){
start(startTagMatch.tagName,startTagMatch.attrs)
continue;
}

//结束标签
let endTagMatch = html.match(endTag)
if(endTagMatch){
advance(endTagMatch[0].length)
end(endTagMatch[1])
continue;
}
}
let text
//文本
if(textEnd>0){
//获取文本内容
text = html.substring(0,textEnd)
}
if(text){
advance(text.length)
charts(text)
}
}
function parseStartTag(){
//解析开始标签
const start = html.match(startTagOpen) //1.结果 2.false
if(start){
//创建ast 语法树
let match = {
tagName:start[1],
attrs:[],
}
advance(stsrt[0].length)

//删除开始标签

//属性 多个属性
let attr
let end
while((!end = html.match(startTagClose)) && (attrs=html.match(attribute))){
march.attrs.push({name:attr[1],vale:attrs[3]||attrs[4]||attrs[3]})
advance(attr[0])
brack;
}
if(end) {
advance(end[0].length)
}
}

}
function advance(n){
html = html.substring(n)
}
return root
}
export function compileToFunction(el){
let ast= parseHTML(el)
//ast 语法树变成 render函数 1.ast语法树 变成字符串 2.字符串变成函数
let code =generate(ast)
}
ast语法变为render函数

配合上面使用

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

/*
<div id="app">hello {{msg}}</div>
render(){
return _c('div',{id:app},_v('hello'+_s(msg)),_c)
}

*/
const defaultTagRE = /\{((?:.|\r?\n)+?)\}\}/g
//处理属性
function genPorps(attrs){
let str ='';
for(let i =0;i<attrs.length;i++){
let attr = attr[i];
if(attr.name == 'style'){
let obj = {}
// let [key,val] = sttr.value.split(';')
attr.vale.split(';').forEach(item=>{
let [key,val] =item.split(':')
obj[key] = val
})
attr.value = obj
}
str+= `${attr.name}:${JSON.stringify(attr.value)}`
}
return `{${str.slice(0,-1)}}`
}

//处理子节点
function genChildren(el){
let children = el.children
if(children){
return children.map(child=>gen(child)).join(',')
}
}

function gen(node){
//形式 1.元素 2.div 3.文本
if(node.type === 1){ //元素
return generate(node)
}else {//文本 (1)只是文本 不带插值表达式 (2)插值表达式特殊处理
let text = node.text //获取文本
if(!defaultTagRE.test(text)){//检测是否有插值表达式
return `_v(${JSON.stringify(text)})`
}
//带有插值表达式
let tokens = []
let lastindex = defaultTagRE.lasteIndex = 0
let match
while(match = defaultTagRE.exec(text)){

let index = match.index
if(index>lastindex){//添加内容
tokens.push(JSON.stringify(text.slice(lastindex,index)))
}
tokens.push(`_s(${match[1].trim()})`)
lastindex = index+ match[0].length
if(lastindex<text.length){
tokens.push(JSON.sttingify(text.slice(lastindex)))
}
return `_v(${tokens.join('+')})`
}
}
}

export function generate(el){
//注意属性 {id:app,style:{color:red}}
let children = genChildren(el)
let code = `_c(${el.tag},${el.attrs.length?`${genPorps(el.attrs)}`:'null'},${children?`${children}`:'null'})`
return code
}
render字符串从函数到dom
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
//1. 变为ast  语法树
let ast = parseHTML(el)
// 2.ast语法树变为render函数
let code = generate(ast)
//3.将render字符串变成函数
let render = new Function(`with(this){return ${code}}`)
//将render函数变成vnode(虚拟dom)
return render

export function mountComponent(vm,el){
callHook(vm,'beforMounted')
vm._updata(vm._render()) //(1)vm._render 将render函数转为vnode (2)vm._updata将vnode转为真实dom
callHook(vm,'mounted')
}
//生命周期
export function lifeyvleMinin(vue){
Vue.prototype._updata = function(vnode){
let vm = this
//两个参数 旧dom 新dom
vm.$el=patch(vm.$el,vnode)
}
}

//生命周期调用
export function callHook(vm,hook){
const handlers = vm.$options[hook]
if(handlers){
for(let i=0;i<headers.length;i++){
handlers[i].call(this) //改变生命周期中this
}
}
}


export function renderMin(Vue){
Vue.prototype._c = function(){//处理标签
// 创建标签
return createElement(...arguments)
}
Vue.prototype._v = function(text){//处理文本
return createText(text)
}
Vue.prototype._s = function(value){//处理变量
return val == null ? "":(typeof val == 'object')?JSON.stringify(val):val
}
Vue.prototype._render = function(){
//render函数变成vnode
let vm =this //实例
let render = vm.$options.render
let vnode = = render.call(this)
}
}

//创建元素
function createElement(tag,data = {},...children){
return vnode(tag,data,data.key,children)
}
//创建文本
function createText(text){
return vnode(undefined,undefined,undefined,undefined,text)
}
// 创建虚拟dom
function vnode(tag,data,key,children,text){
return {
tag,
data,
key,
children,
text
}
}

export function patch(oldVnode,vnode){
//vnode => 真实dom
//(1) 创建新dom
let el = createEl(vnode)
//(2)替换 获取父节点 插入 删除
let parentEl = oldVnode.parentNode //body
parentEl.insertBefore(el,oldVnode.nextsibling)
parentEl.removeChild(oldVnode)
return el
}

function createEl(vnode){
let {tag,children,key,data,text} = vnode
if(typeof tag == 'string'){//表示tag是一个元素
vnode.el = document.createElement(tag)//创建元素
if(children.length>0){
children.forEach(child=>{
vnode.el.appendChild(createEl(child))
})

}
}else{
vnode.el = document.createTextNode(text)
}
return vnode.el
}

//render()函数=>vnode=>真是的dom
/*
//vnode 节点
{
tag, 标签
text, 文本
children 子级
}
//vue的渲染流程=>数据初始化=>对模块进行编译=>变成rednder函数=>ender函数 变成vnode =>变成真实dom


*/

vue源码后续待更新

diff算法

diff 算法是一种通过同层的树节点进行比较的高效算法,比较只会在同层级进行, 不会跨层级比较 在diff比较的过程中,循环从两边向中间比较
原理:当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图
通过isSameVnode进行判断,相同则调用patchVnode方法
patchVnode做了以下操作:
找到对应的真实dom,称为el
如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点
如果oldVnode有子节点而VNode没有,则删除el子节点
如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el
如果两者都有子节点,则执行updateChildren函数比较子节点
updateChildren主要做了以下操作:
设置新旧VNode的头尾指针
新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作

vue中的双向数据绑定

new Vue()首先执行初始化,在Observe函数中对data执行响应化处理,同时对模板执行编译,找到其中动态绑定的数据,从data中获取并初始化视图,同时定义⼀个更新函数和Watcher,将来对应数据变化时Watcher会调用更新函数 因为datra中的某个属性会多次出现 定义了一个dep 管理watcher 将来data中数据⼀旦发生变化,会首先找到对应的Dep,通知所有Watcher执行更新函数

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
class Vue {  
constructor(options) {
this.$options = options;
this.$data = options.data;

// 对data选项做响应式处理
observe(this.$data);

// 代理data到vm上
proxy(this);

// 执行编译
new Compile(options.el, this);
}
}
function observe(obj) {
if (typeof obj !== "object" || obj == null) {
return;
}
new Observer(obj);
}

class Observer {
constructor(value) {
this.value = value;
this.walk(value);
}
walk(obj) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key]);
});
}
}
class Compile {
constructor(el, vm) {
this.$vm = vm;
this.$el = document.querySelector(el); // 获取dom
if (this.$el) {
this.compile(this.$el);
}
}
compile(el) {
const childNodes = el.childNodes;
Array.from(childNodes).forEach((node) => { // 遍历子元素
if (this.isElement(node)) { // 判断是否为节点
console.log("编译元素" + node.nodeName);
} else if (this.isInterpolation(node)) {
console.log("编译插值⽂本" + node.textContent); // 判断是否为插值文本 {{}}
}
if (node.childNodes && node.childNodes.length > 0) { // 判断是否有子元素
this.compile(node); // 对子元素进行递归遍历
}
});
}
isElement(node) {
return node.nodeType == 1;
}
isInterpolation(node) {
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
}
}
// 负责更新视图
class Watcher {
constructor(vm, key, updater) {
this.vm = vm
this.key = key
this.updaterFn = updater

// 创建实例时,把当前实例指定到Dep.target静态属性上
Dep.target = this
// 读一下key,触发get
vm[key]
// 置空
Dep.target = null
}

// 未来执行dom更新函数,由dep调用的
update() {
this.updaterFn.call(this.vm, this.vm[this.key])
}
}
class Dep {
constructor() {
this.deps = []; // 依赖管理
}
addDep(dep) {
this.deps.push(dep);
}
notify() {
this.deps.forEach((dep) => dep.update());
}
}
class Watcher {
constructor(vm, key, updateFn) {
Dep.target = this;
this.vm[this.key];
Dep.target = null;
}

}

function defineReactive(obj, key, val) {
this.observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例
return val;
},
set(newVal) {
if (newVal === val) return;
dep.notify(); // 通知dep执行更新方法
},
});
}

js运行机制

javaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
avaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
事件循环是js实现异步的一种方法,也是js的执行机制。
javascript的执行和运行
执行和运行有很大的区别,javascript在不同的环境下,比如node,浏览器,Ringo等等,执行方式是不同的。而运行大多指javascript解析引擎,是统一的。
js 轮询机制
所有任务都在主线程上执行,形成一个执行栈。
2、主线程发现有异步任务,如果是微任务就把他放到微任务的消息队列里,如果是宏任务就把他
放到宏任务的消息队列里。
3、执行栈所有同步任务执行完毕。
4、执行微任务队列,之后再执行宏任务队列。

浏览器跨域原理

核心思想:浏览器的script、img、iframe标签是不受同源策略限制的,所以通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的callback函数,并把我们需要的json数据作为参数传入。在服务器端,当req.params参数中带有callback属性时,则把数据作为callback的参数执行,并拼接成一个字符串后返回。
优点:兼容性好,简单易用,支持浏览器与服务器双向通信
缺点:只支持GET请求,且只支持跨域HTTP请求这种情况(不支持HTTPS)
在js中,直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。

CORS请求原理
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

核心思想:在服务器端通过检查请求头部的origin,从而决定请求应该成功还是失败
修改document.domain来跨子域
浏览器都有一个同源策略,限制其一:不能通过ajax的方法去请求不同源中的文档; 其二:浏览器中不同域的框架间不能进行js的交互操作。
不同的框架之间(父子或同辈),能够获取到彼此的window对象的,但不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器如ie6也可以使用top、parent等少数几个属性)。

实用方法

复制文本到切板

1
2
const copy= (text)=>navigator.clipboard.writeText(text)
copy("你好")

获取某个日期相当于当年的第几天

1
2
const time = (date)=>Math.floor((date - new Date(date.getFullYear(),0,0))/100./60/60/24)
console.log(time(new Date(2023,12,30))

解析url参数

1
2
3
4
5
6
7
const parseurl = (url)=>{
q={}
url.replace(/([^?&=]+)=([^&+])/g,(_,k,v)=>(q[k]=v))
return q
}
console.log(parseurl("http://a.com/?a=1&b=2"))
console.log(parseurl("a=1&b=2"));

生成随机颜色

1
2
3
const color = () => '#'+Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6,'0');
console.log(color());

去掉字符串中的元素标记

1
2
3
4
5
6
7
8
9
const removetap = (tag) => new DOMParser().parseFromString(tag,'text/html').body.textContent || ' ';
console.log(removetap("<div>55555555</div>")) //
```
#### 获取对象的基本数据类型
```js
const getType = (tag) => {
return Object.prototype.toString.call(tag).slice(8,-1)
}
console.log(getType(()=>{}));

反转字符串

1
2
3
4
const reverString = (str) =>{
return str.split('').reverse().join("")
}
console.log(reverString("efesgfrgrd"))

获取两个数字之间的随机数

1
2
3
4
const random =(min,max)=>{
return Math.floor(Math.random() * (max-min+1)+min)
}
console.log(random(5,8))

检查日期是否合法

1
2
3
4
const ifTimeDate = (...time) =>{
return !Number.isNaN(new Date(...time).valueOf())
}
console.log(ifTimeDate(" 1999 03:16:18"));

检查日期是否在两个日期之间

1
2
3
4
5
6
7
const ifData = (data,min,max)=>{
return +max>= +data && data >= +min
}
const data = new Date("2010-12-8")
const min = new Date("2003-05-6")
const max = new Date("2008-05-16")
console.log(ifData(data,min,max))

判断此页面标签是否激活

1
2
const isTabView =()=>!document.hidden
isTabView()

判断一段字符串是否是url

1
2
3
4
const isUrl = (url) =>{
return /^(http|https):\/\/([\w.]+\/?)\S*/.test(url)
}
console.log(isUrl("http://baidu.com"))

打乱数组顺序

1
2
3
let arr = [1,2,3,4,5]
arr = arr.sort(()=>0.5 - Math.random())
console.log(arr);

去除数字之外的所有字符

1
2
3
const str = "5415sfs5f15sdf48sdf5ds6666666668ss5ds88"
const number = str.replace(/\D/g,'')
console.log(number);

删除数组重复项

1
2
3
let arr = [1,2,2,3,5,5,8]
const arr2= (arr)=>[...new Set(arr)]
console.log(arr2(arr));

过滤数组为false得值

1
2
3
let arr = ['undefined',0,2,false]
const arr2 = arr.filter(Boolean)
console.log(arr2);

滚动到页面顶部

1
2
3
const scrllToTop = () =>{
window.scrollTo({top:0,left:0,behavior:"smooth"})
}

滚动到页面底部

1
2
3
const scrllToTop = () =>{
window.scrollTo({top:document.documentElement.offsetHeight,left:0,behavior:"smooth"})
}

调用摄像头

1
2
3
4
5
document.getElementById("btn_start").addEventListener('click',function(){
navigator.mediaDevices.getUserMedia({video:true}).then(stream=>{
document.getElementById("video").srcObject = stream
})
})

y隐藏手机号中间四位

1
2
3
4
const tel = '15237903806'
const reg = /^(\d{3})\d{4}(\d{4})$/
const str = tel.replace(reg,"$1****$2")
console.log(str)

禁止浏览器缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window.addEventListener(
"mousewheel",
function (event) {
if (event.ctrlKey === true || event.metaKey) {
event.preventDefault();
}
},
{ passive: false }
);

//firefox
window.addEventListener(
"DOMMouseScroll",
function (event) {
if (event.ctrlKey === true || event.metaKey) {
event.preventDefault();
}
},
{ passive: false }
);

vue自定义拖拽指令

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
Vue.directive('drag', (el) => {
const oDiv = el // 当前元素
const minTop = oDiv.getAttribute('drag-min-top') //用以获取HTML元素的属性
const ifMoveSizeArea = 20
oDiv.onmousedown = e => {
let target = oDiv
while (window.getComputedStyle(target).position !== 'absolute' && target !== document.body) {
target = target.parentElement
}

document.onselectstart = () => {
return false
}
if (!target.getAttribute('init_x')) {
target.setAttribute('init_x', target.offsetLeft)
target.setAttribute('init_y', target.offsetTop)
}

const initX = parseInt(target.getAttribute('init_x'))
const initY = parseInt(target.getAttribute('init_y'))

// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - target.offsetLeft
const disY = e.clientY - target.offsetTop
document.onmousemove = e => {
// 通过事件委托,计算移动的距离
// 因为浏览器里并不能直接取到并且使用clientX、clientY,所以使用事件委托在内部做完赋值
const l = e.clientX - disX
const t = e.clientY - disY
// 计算移动当前元素的位置,并且给该元素样式中的left和top值赋值
target.style.left = l + 'px'
target.style.top = (t < minTop ? minTop : t) + 'px'
if (Math.abs(l - initX) > ifMoveSizeArea || Math.abs(t - initY) > ifMoveSizeArea) {
target.setAttribute('dragged', '')
} else {
target.removeAttribute('dragged')
}
}
document.onmouseup = e => {
document.onmousemove = null
document.onmouseup = null
document.onselectstart = null
}
// return false不加的话可能导致黏连,拖到一个地方时div粘在鼠标上不下来,相当于onmouseup失效
return false
}
})

调用摄像头

1
2
3
4
5
document.getElementById("btn_start").addEventListener('click',function(){
navigator.mediaDevices.getUserMedia({video:true}).then(stream=>{
document.getElementById("video").srcObject = stream
})
})

觉得写的不错 就可怜可怜博主吧.

其他文章
cover
Nuxtjs学习
  • 21/10/19
  • 10:20
  • 1.2k
  • 4
cover
vue2内容
  • 21/09/15
  • 10:46
  • 15.1k
  • 70