obkoro1 / codeblack Goto Github PK
View Code? Open in Web Editor NEW积累的小知识点合集,开箱即用的代码块。积跬步以致千里:http://obkoro1.com/web_accumulate/codeBlack/
积累的小知识点合集,开箱即用的代码块。积跬步以致千里:http://obkoro1.com/web_accumulate/codeBlack/
如果scroll事件不触发的话,很可能是scorll事件绑错div了!
let dom = document.querySelector('textarea'); // div滚动事件 dom.onscroll = () => {
// 意思就是内容总体的高度 - 滚动条的偏移值 === 元素的高度(包含内边)但不包含外边距,边框,以及滚动条
if (dom.scrollHeight - dom.scrollTop === dom.clientHeight) {
console.log('到达底部 do something');
}
// div滚到时:离底部不到30px时触发
if (dom.scrollHeight - dom.scrollTop - dom.clientHeight <= 30) {
console.log('离底部不到30px 提前发送请求');
}
};
这道题是我朋友发给我的,之前一开始看的时候,觉得很简单,但仔细往下看的时候,眉头一皱发现事情并不简单。
PS:这类题目之前也以不同形式出现过
let oldArr = [
{
'1_class': '工具',
'2_class': '备忘录',
'1_id': 1,
'2_id': 2
},
{
'1_class': '教育',
'2_class': '学历教育',
'3_class': '中等',
'1_id': 3,
'2_id': 4,
'3_id': 6
},
{
'1_class': '教育',
'2_class': '学历教育',
'3_class': '高等',
'1_id': 3,
'2_id': 4,
'3_id': 5
},
{
'1_class': '教育',
'2_class': '成人教育',
'1_id': 3,
'2_id': 7
}
];
let result = [
{
value: 1,
label: '工具',
children: [
{
value: 2,
label: '备忘录',
children: []
}
]
},
{
value: 3,
label: '教育',
children: [
{
value: 4,
label: '学历教育',
children: [
{
value: 6,
label: '中等',
children: []
},
{
value: 5,
label: '高等',
children: []
}
]
},
{
value: 7,
label: '成人教育',
children: []
}
]
}
];
先算出层级嵌套数
遍历数组,再遍历数组的元素,用parseInt
拿到每一个值,将最大的值取出来,即为层级嵌套数。
数组转成属性层级的对象。对象的属性是层级,值是数组,层级里面的值。
1.去重操作(比如:1_id
相等的话就不再重复添加)
2.提取需要的信息,最终的值和相应父级的信息(之后组装数组的时候放到对应的地方,需要的信息)。
最后输出的对象是这个样子
将属性层级的对象转成树状结构数组。
遍历对象,获取层级值。
遍历层级的每个元素。
找到对应的层级将值添加进去,否则递归继续找对应层级。
let listToTree = arr => {
let [levelNum, newArr, obj] = [1, [], {}];
// 层级嵌套数
arr.forEach(item => {
for (let proto in item) {
let protoLevel = parseInt(proto);
if (levelNum < protoLevel) levelNum = protoLevel; // 总共多少层级
}
});
// 分开层级 放在一个对象中
for (let i = 1; i < levelNum + 1; i++) {
levelClass(i);
}
// 将对象转化成数组
Object.keys(obj).forEach(item => {
let forNum = parseInt(item); // 当前层级数
obj[item].forEach(itemChildren => {
// 遍历每个层级的每个值
packageArr(itemChildren, forNum, newArr);
});
});
// 分开每个层级
function levelClass(name) {
arr.forEach(value => {
// 每个元素都遍历一次 分开对应层级
if (!value[`${name}_id`]) return; // 有的对象层级没那么多
let objFor = {};
// 其他层级的value 赋值
for (let j = 1; j < name; j++) {
objFor[`value${j}`] = value[`${j}_id`];
}
if (obj[`${name}_id`]) {
// 相同层级不重复添加 比如第一层级 id都为1 只添加一个
let status = obj[`${name}_id`].find(item => {
return item.value === value[`${name}_id`];
});
// 没有才添加
if (!status) {
obj[`${name}_id`].push(
Object.assign(objFor, {
value: value[`${name}_id`],
label: value[`${name}_class`]
})
);
}
} else {
// 初始化创建一个数组
obj[`${name}_id`] = [
Object.assign(objFor, {
value: value[`${name}_id`],
label: value[`${name}_class`]
})
];
}
});
}
// 组装每个值
function packageArr(sureName, index, arr, key = 1) {
if (key === index) {
// 当key和index相同时 即找到当前层级
return arr.push({
value: sureName.value,
label: sureName.label,
children: []
});
} else {
key++;
// 当前层级数组中对应的对象元素
let num = arr.findIndex(value => {
return value.value === sureName[`value${key - 1}`];
});
// 继续找或者已经找到
return packageArr(sureName, index, arr[num].children, key);
}
}
return newArr;
};
感谢MrHouBeiBei提供更好的解决方法:
function getNewArr(activeArr) { // 递归处理传进来的对象 function fn(arr, obj, id) { var objLen = Object.keys(obj).length / 2; // 对象总共的层级 var rtItem = arr.find(i => { return i.value === obj[`${id}_id`]; // 找该层级是否被组装过 }); // 没被组装过 就组装 // 被组装就跳过(覆盖) if (!rtItem) { rtItem = {}; rtItem.value = obj[`${id}_id`]; // 层级id rtItem.lable = obj[`${id}_class`]; rtItem.children = []; arr.push(rtItem); } id++; // 准备组装下一层级 // 如果层级结束就退出递归 if (id > objLen) return; // 递归继续组装 fn(rtItem.children, obj, id); }
var res = [];
for (let item of activeArr) {
// 遍历数组 处理每个对象
var id = 1;
fn(res, item, id);
}
return res;
}
像htmls
字符串可以加很多事件了,可以以变量的形式,添加dom
,function
等等,知道一下这个东西,以后说不定用得着。
// html 部分
<div id="aa"></div>
// js部分
function test() {
console.log('执行函数');
}
const htmls = `<div onclick="test()">插入的html</div>`; // 可以以变量的形式添加dom、函数等等
let a = document.querySelector('#aa');
a.innerHTML = htmls;
fixed absolute relative
定位的元素, 子元素也是fixed absolute relative
定位的元素。z-index
fixed absolute relative
定位的元素,并且设定了z-index
示例中带有详细的注释,可以自己动手调一调,就明白啦~
<!--
* Author: OBKoro1
* Date: 2019-12-24 13:55:54
* LastEditors: OBKoro1
* LastEditTime: 2019-12-24 14:52:04
* FilePath: /test/index.html
* Description: fixed 从父原则
* https://github.com/OBKoro1
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
margin: 0;
padding: 0;
}
#father1 {
width: 50px;
height: 50px;
background: red;
position: absolute;
/* position: fixed; */
/* position: relative; */
bottom: 0;
right: 0;
/* TODO: 比father2大 将覆盖father2 */
z-index: 3;
}
#son1 {
/* 子元素在与父辈元素比较的时候,用父辈的z-index去比较。*/
/* 在与同级元素比较的时候,才有自己的z-index去比较 */
width: 100vw;
height: 100vh;
background: orange;
/* position: absolute; */
/* position: fixed; */
position: relative;
top: 0;
left: 0;
z-index: 10;
}
#son2 {
width: 100px;
height: 100px;
background: black;
position: fixed;
top: 0;
left: 0;
color: #fff;
z-index: 8;
/* TODO: 打开将覆盖son1 因为比它们是同级,且son2 z-index比较大 */
/* z-index: 100; */
}
.father2 {
/* TODO: 出现从父原则的情况: 在fixed的父元素设relative和z-index */
/* position: fixed; */
/* position: relative; */
position: fixed;
z-index: 1;
/* TODO: 打开将覆盖father1 因为son1和father1不同级,然后father2比father1的z-index大 */
/* z-index: 10; */
}
</style>
</head>
<body>
<div id="father1">father1</div>
<div class="father2">
<div id="son1">子元素1</div>
<div id="son2">子元素2</div>
</div>
</body>
</html>
所谓的随机数数组就是:由随机数组成的数组(数组的长度和随机数的范围可自定义)
当然有很多方法,只是用下面这个API只用一行代码就可以实现这个功能,见猎心喜,然后放了上来。
length
属性)Array.from
的第二个参数,对每个元素进行处理(生成随机数)。/**
* 由随机数组成的数组:长度和随机数范围可自定义
* @param {number} length 数组的长度
* @param {number} limit 随机数的范围
*/
const genNumArr = (length, limit) => {
// Array.from第二个参数 类似数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组
return Array.from({ length }, () => Math.floor(Math.random() * limit));
};
console.log(genNumArr(1000, 100)); // 数组长度为1000,每个元素的范围在0-99之间
F12开启浏览器手机模拟器,在顶部鼠标按住移动
touchstart
、touchmove
、touchend
三个事件来监听触摸事件getBoundingClientRect()
API来判断当前dom是否在顶部(之前使用document.body.scrollTop
并不行,然而搜到的都是这个答案。)<!DOCTYPE html> <!-- * @Author: OBKoro1 * @Date: 2019-09-09 10:52:13 * @LastEditors: OBKoro1 * @LastEditTime: 2019-10-22 20:48:50 * @FilePath: /my_test/tets.html * @Description: 手写移动端下拉刷新 PS:PC端也是一个原理 只是改为鼠标按下和抬起事件 * @Github: https://github.com/OBKoro1 --> <html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.content_father div {
height: 200px;
background: gray;
margin-bottom: 10px;
}
</style>
</head><body>
<div>F12开启浏览器手机模拟器,在顶部鼠标按住移动</div>
<div class="content_father">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<script>
let content = document.querySelector('.content_father')
let startSite = 0 // 触摸的起始位置
let sendAjax = false // 发送请求
content.ontouchstart = (e) => {
let contentSite = content.getBoundingClientRect()
if (contentSite.y >= 0) {
console.log('在顶部')
startSite = e.touches[0].pageY
// 在顶部才绑定事件
content.ontouchmove = touchmoveFn
content.ontouchend = touchendFn
}
}
// 开始移动
function touchmoveFn(e) {
// 在浏览器顶部
let moveDistance = e.touches[0].pageY - startSite // 相差高度
const DISTANCE = 100 // 下滑距离超过100就刷新
const DISTANCE_FONT = 50 // 下滑距离超过50 就显示文案
if (moveDistance > DISTANCE) {
// 下滑足够距离
sendAjax = true
console.log('展示 刷新页面文案')
} else if (0 < moveDistance < DISTANCE_FONT) {
// 下滑距离不足
sendAjax = false // 实时更改是否发送请求
console.log('展示:继续下滑刷新页面文案')
} else {
// 上拉 取消请求
sendAjax = false // 实时更改是否发送请求
}
console.log('滑动距离', moveDistance)
}
// 触摸结束
function touchendFn(e) {
let contentSite = content.getBoundingClientRect()
// 判断在顶部
if (contentSite.y >= 0) {
// 并且上次上滑超过100
if (sendAjax) {
console.log('发送 请求')
}
}
// 清除事件
content.ontouchmove = null
content.ontouchend = null
}
</script>
</body>
</html>
判断用户所使用的浏览器主要用到的 api 是navigator.userAgent,这是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值,不同浏览器的userAgent
值都不相同,所以我们可以根据这个字符串来判断用户是从哪个浏览器进入。
下面两个是刚做的 demo 获取的值,仔细观察下面两个字符串,会发现有些值是不一样的,并且浏览器特有的,依据这个我们就可以作为不同浏览器的判断条件。
mozilla/5.0 (iphone; cpu iphone os 11_1_2 like mac os x) applewebkit/604.3.5 (khtml, like gecko) mobile/15b202 qq/7.5.8.422 v1_iph_sq_7.5.8_1_app_a pixel/1080 core/uiwebview device/apple(iphone 8plus) nettype/wifi qbwebviewtype/1
mozilla/5.0 (iphone; cpu iphone os 11_1_2 like mac os x) applewebkit/604.3.5 (khtml, like gecko) mobile/15b202 micromessenger/6.6.6 nettype/wifi language/zh_cn
使用方式,直接使用这个 api 读取值,然后根据事先观察userAgent
字符串的不同之处来判断:
let url = navigator.userAgent.toLowerCase();
//使用toLowerCase将字符串全部转为小写 方便我们判断使用
if (url.indexOf('15b202 qq') > -1) {
//单独判断QQ内置浏览器
alert('QQ APP 内置浏览器,做你想做的操作');
}
if (url.indexOf('micromessenger') > -1) {
//单独判断微信内置浏览器
alert('微信内置浏览器,做你想做的操作');
}
if (url.indexOf('15b202') > -1) {
//判断微信内置浏览器,QQ内置浏览器
alert('QQ和微信内置浏览器,做你想做的操作');
}
上面判断了微信和 QQ 的内置浏览器,如果有更多不同的需求的话,可以按照上面的方式,先获取userAgent
的字符串,然后再根据观察,使用indexOf
判断是否含有指定的字符,来对不同浏览器进行不同的操作。
以上2018.5.5
右边变量的原型存在于左边变量的原型链上
关于原型链不太懂的同学可以看一下,我的这篇文章:JS基础-函数、对象和原型、原型链的关系
function myInstanceOf(left, right) {
let leftValue = left.__proto__
let rightValue = right.prototype
while (true) {
if (leftValue === null) {
return false
}
if (leftValue === rightValue) {
return true
}
leftValue = leftValue.__proto__
}
}
进度条很多人都是手写的,通过div+js
的形式。 实际上并不需要这么麻烦,因为原生就支持该标签,只要更改一下value
值,即可改变进度,简单又方便!
<progress class="progress_class" max="100" value="80" />
.progress_class { width: 500px; height: 8px; // 外部圆角 overflow: hidden; border-radius: 8px 8px 8px 8px; /*设置iOS下清除灰色背景*/ appearance: none; -webkit-appearance: none; }
// 进度条的进度样式
.progress_class::-webkit-progress-value {
background: linear-gradient(90deg, rgba(20, 96, 181, 1) 0%, rgba(4, 195, 250, 1) 100%);
border-radius:8px; // 内部的圆角
}
// 进度条未达到部分
.progress_class::-webkit-progress-bar {
background-color: #d7d7d7; // 进度条未进度 部分
}
有任意两个数组,每个数组里面的元素不重复,找出它们的交集、差集和并集。
由所有属于集合 A 且属于集合 B 的元素所组成的集合,叫做集合 A 与集合 B 的交集(intersection),记作 A∩B
以属于 A 而不属于 B 的元素为元素的集合成为 A 与 B 的差。(本文栗子,还会求出属于 B 不属于 A 的集合)
给定两个集合 A,B,把他们所有的元素合并在一起组成的集合,叫做集合 A 与集合 B 的并集,记作 A∪B,读作 A 并 B。
let one = [1, 2, 3, 4, 5]; let two = [2, 3, 4, 7]; const intersection = (a, b) => { // a b数组的交集 let arr = a.filter(v => { // b是否包含a的元素,包含就返回true,交集 return b.includes(v); }); return arr; }; const difference = (a, b) => { // a b 数组的差集 let arr = [...a, ...b].filter(v => { // a和b是否包含v 不包含返回!false 出现有一个不包含(另一个数组就包含 找到差值),就返回true 添加进数组 return !a.includes(v) || !b.includes(v); }); return arr; };
const unionArr = (a, b) => {
return Array.from(new Set([...a, ...b])); // 并集可以理解为合并数组去重,直接用set即可
};
console.log(difference(one, two), intersection(one, two), unionArr(one, two));
事实上,还可以改成indexOf
来判断是否包含,但是要注意indexOf
是不能识别NaN
的。
function arrSet(a, b, type) {
let set;
a = new Set(a);
b = new Set(b);
if (type === 'difference') {
// ab数组差集
set = new Set([...a, ...b].filter(x => !b.has(x) || !a.has(x)));
} else if (type === 'intersection') {
// ab数组交集
set = new Set([...a].filter(x => b.has(x)));
} else {
// ab数组并集
set = new Set([...a, ...b]);
}
return Array.from(set); // 转为set
}
console.log(
arrSet(one, two, 'difference'),
arrSet(one, two, 'intersection'),
arrSet(one, two, 'union')
);
concat
连接成一个数组const flatten = arr => {
return arr.reduce((flat, next) => {
console.log(flat, next); // flat:初始值或累加的值 next:当前值
return flat.concat(Array.isArray(next) ? flatten(next) : next);
// 判断当前元素是否为数组 决定是否递归 将值返回到下次循环
}, []);
};
// 运行示例:
let nestedArr = [1, 2, [3, 4, [5, [6, 7]]]]; // 四维数组 展开
console.log(flatten(nestedArr)); // [1,2,3,4,5,6,7]
老早之前的技能了,整理笔记发现的,不知道的可以看下:
html:
<p class="one">单行文本溢出显示省略号</p>
<p class="moreLine">多行文本溢出显示省略号 啦啦啦啦啦啦 哈哈哈哈</p>
css:
.one{ width:100px; /* 记住要限定宽度 */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .moreLine{ width:100px; /* 记住要限定宽度 */ /* height:300px; */ /* 也要限制高度,这边是自适应了 */ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; /* 限定几行文字溢出 */ overflow: hidden;
}
用Vue开发一个网页并不难,但是也经常会遇到一些问题,其实大部分的问题都在文档中有所提及,再不然我们通过谷歌也能成功搜索到问题的答案,为了帮助小伙伴们提前踩坑,在遇到问题的时候,心里大概有个谱知道该如何去解决问题。这篇文章是将自己知道的一些小技巧,结合查阅资料整理成的一篇文章。
问题:在组件中用js动态创建的dom,添加样式不生效。
场景:
<template>
<div class="test"></div>
</template>
<script>
let a=document.querySelector('.test');
let newDom=document.createElement("div"); // 创建dom
newDom.setAttribute("class","testAdd" ); // 添加样式
a.appendChild(newDom); // 插入dom
</script>
<style scoped>
.test{
background:blue;
height:100px;
width:100px;
}
.testAdd{
background:red;
height:100px;
width:100px;
}
</style>
结果:
// test生效 testAdd 不生效 <div data-v-1b971ada class="test"><div class="testAdd"></div></div>
.test[data-v-1b971ada]{ // 注意data-v-1b971ada
background:blue;
height:100px;
width:100px;
}
原因:
当 <style>
标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。
它会为组件中所有的标签和class样式添加一个scoped
标识,就像上面结果中的data-v-1b971ada
。
所以原因就很清楚了:因为动态添加的dom没有scoped
添加的标识,没有跟testAdd
的样式匹配起来,导致样式失效。
解决方式
每个组件的css并不会很多,当设计到动态添加dom,并为dom添加样式的时候,就可以去掉scoped,会比下面的方法方便很多。
// 上面的栗子可以这样添加样式
newDom.style.height='100px';
newDom.style.width='100px';
newDom.style.background='red';
很多时候,我们习惯于这样操作数组和对象:
data() { // data数据
return {
arr: [1,2,3],
obj:{
a: 1,
b: 2
}
};
},
// 数据更新 数组视图不更新
this.arr[0] = 'OBKoro1';
this.arr.length = 1;
console.log(arr);// ['OBKoro1'];
// 数据更新 对象视图不更新
this.obj.c = 'OBKoro1';
delete this.obj.a;
console.log(obj); // {b:2,c:'OBKoro1'}
由于js的限制,Vue 不能检测以上数组的变动,以及对象的添加/删除,很多人会因为像上面这样操作,出现视图没有更新的问题。
解决方式:
this.$set(this.arr, 0, "OBKoro1"); // 改变数组
this.$set(this.obj, "c", "OBKoro1"); // 改变对象
如果还是不懂的话,可以看看这个codependemo。
Vue可以监测到数组变化的,数组原生方法:
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
意思是使用这些方法不用我们再进行额外的操作,视图自动进行更新。
推荐使用splice
方法会比较好自定义,因为slice可以在数组的任何位置进行删除/添加操作,这部分可以看看我前几天写的一篇文章:【干货】js 数组详细操作方法及解析合集
比方说:你想遍历这个数组/对象,对每个元素进行处理,然后触发视图更新。
// 文档中的栗子: filter遍历数组,返回一个新数组,用新数组替换旧数组
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
举一反三:可以先把这个数组/对象保存在一个变量中,然后对这个变量进行遍历,等遍历结束后再用变量替换对象/数组。
并不会重新渲染整个列表:
Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
如果你还是很困惑,可以看看Vue文档中关于这部分的解释。
过滤器,通常用于后台管理系统,或者一些约定类型,过滤。Vue过滤器用法是很简单,但是很多朋友可能都没有用过,这里稍微讲解一下。
在html模板中的两种用法:
<!-- 在双花括号中 -->
{{ message | filterTest }}
<!-- 在 `v-bind` 中 -->
<div :id="message | filterTest"></div>
在组件script
中的用法:
export default {
data() {
return {
message:1
}
},
filters: {
filterTest(value) {
// value在这里是message的值
if(value===1){
return '最后输出这个值';
}
}
}
}
用法就是上面讲的这样,可以自己在组件中试一试就知道了,很简单很好用的。
如果不想自己试,可以点这个demo里面修改代码就可以了,demo中包括过滤器串联、过滤器传参。
推荐看Vue过滤器文档,你会更了解它的。
v-for循环绑定model:
input在v-for中可以像如下这么进行绑定,我敢打赌很多人不知道。
// 数据
data() {
return{
obj: {
ob: "OB",
koro1: "Koro1"
},
model: {
ob: "默认ob",
koro1: "默认koro1"
}
}
},
// html模板
<div v-for="(value,key) in obj">
<input type="text" v-model="model[key]">
</div>
// input就跟数据绑定在一起了,那两个默认数据也会在input中显示
为此,我做了个demo,你可以点进去试试。
一段取值的v-for
如果我们有一段重复的html模板要渲染,又没有数据关联,我们可以:
<div v-for="n in 5">
<span>这里会被渲染5次,渲染模板{{n}}</span>
</div>
v-if尽量不要与v-for在同一节点使用:
v-for 的优先级比 v-if 更高,如果它们处于同一节点的话,那么每一个循环都会运行一遍v-if。
如果你想根据循环中的每一项的数据来判断是否渲染,那么你这样做是对的:
<li v-for="todo in todos" v-if="todo.type===1">
{{ todo }}
</li>
如果你想要根据某些条件跳过循环,而又跟将要渲染的每一项数据没有关系的话,你可以将v-if放在v-for的父节点:
// 根据elseData是否为true 来判断是否渲染,跟每个元素没有关系
<ul v-if="elseData">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
// 数组是否有数据 跟每个元素没有关系
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
如上,正确使用v-for与v-if优先级的关系,可以为你节省大量的性能。
watch很多人都在用,但是这watch中的这两个选项deep
、immediate
,或许不是很多人都知道,我猜。
选项:deep
在选项参数中指定 deep: true
,可以监听对象中属性的变化。
选项:immediate
在选项参数中指定 immediate: true, 将立即以表达式的当前值触发回调,也就是立即触发一次。
watch: {
obj: {
handler(val, oldVal) {
console.log('属性发生变化触发这个回调',val, oldVal);
},
deep: true // 监听这个对象中的每一个属性变化
},
step: { // 属性
//watch
handler(val, oldVal) {
console.log("默认立即触发一次", val, oldVal);
},
immediate: true // 默认立即触发一次
},
},
这两个选项可以同时使用,另外:是的,又有一个demo。
还有下面这一点需要注意。
示例:
// 上面watch的栗子:
handler:(val, oldVal)=> { // 可以执行
console.log("默认触发一次", val, oldVal);
},
// method:
methods: {
plus: () => { // 可以执行
// do something
}
}
// 生命周期:
created:()=>{ // 可以执行
console.log('lala',this.obj)
},
是的,没错,这些都能执行。
but:
箭头函数绑定了父级作用域的上下文,this 将不会按照期望指向 Vue 实例。
也就是说,你不能使用this来访问你组件中的data数据以及method方法了。
this将会指向undefined。
// 我所采用的方法,个人感觉比较简洁一些,少了一步引入赋值。
const router = new VueRouter({
routes: [
path: '/app',
component: () => import('./app'), // 引入组件
]
})
// Vue路由文档的写法:
const app = () => import('./app.vue') // 引入组件
const router = new VueRouter({
routes: [
{ path: '/app', component: app }
]
})
文档的写法在于问题在于:如果我们的路由比较多的话,是不是要在路由上方引入赋值十几行组件?
第一种跟第二种方法相比就是把引入赋值的一步,直接写在component
上面,本质上是一样的。两种方式都可以的,大家自由选择哈。
实际上这也就是一个设置而已:
export default new Router({
routes: [
{
path: '/', // 项目启动页
redirect:'/login' // 重定向到下方声明的路由
},
{
path: '*', // 404 页面
component: () => import('./notFind') // 或者使用component也可以的
},
]
})
比如你的域名为:www.baidu.com
项目启动页指的是: 当你进入www.baidu.com
,会自动跳转到login登录页。
404页面指的是: 当进入一个没有 声明/没有匹配 的路由页面时就会跳转到404页面。
比如进入www.baidu.com/testRouter
,就会自动跳转到notFind
页面。
当你没有声明一个404页面,进入www.baidu.com/testRouter
,显示的页面是一片空白。
每次调试的时候,写一堆console
是否很烦?想要更快知道组件/Vuex内数据的变化?
那么这款尤大开发的调试神器:vue-devtools,你真的要了解一下了。
这波稳赚不赔,真的能提高开发效率。
安装方法:
安装之后:
在chrome开发者工具中会看一个vue的一栏,如下对我们网页应用内数据变化,组件层级等信息能够有更准确快速的了解。
本文的内容很多都在Vue文档里面有过说明,推荐大家可以多看看Vue文档,不止看教程篇,还有文档的Api什么的,也都可以看。然后其实还有两三点想写的,因为预计篇幅都会比较长一点,所以准备留到以后的文章里面吧~
描述:
浏览器:目前我遇到的是Android和IPhone的Safari浏览器还有PC端的IE。
表现:浏览器会自动对看起来像是电话号码的数字转成电话号码,并在数字的下方添加下划线,字体变为蓝色,手机端点击之后还会询问用户是否想要拨打该号码
解决方式:
在html头部添加这段meta
即可:
<meta name="format-detection" content="telephone=no" />
启用电话号码:
有些时候,真的是我们的电话号码,并且希望唤起拨打功能,像下面这样就可以了:
<a href="tel:18888888888">18888888888</a>
vuex 是为了解决复杂项目组件之间的数据通信的一个全局状态管理机制,相信很多人都听说过这个东西。有部分人还没有在项目中使用Vuex
管理过数据状态,实际上Vuex
的起步使用非常之简单,看完本文之后,赶紧在项目中用起来吧!
npm install vuex --save
store.js
文件:Vuex 必需的内容都在下面这个文件中,文件中做了详细的注释,注意其中的异步操作actions
和同步操作mutations
。
// store.js
// 引入vue 和 vuex
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 使用vuex插件,跟router一样
// 直接导出 一个 Store 的实例
export default new Vuex.Store({
// 这里是要读取或者写入数据的地方,跟组件里的data项一样
state: {
name: 'oldName'
},
// 通过actions的commit触发mutations来修改state的数据
// 这里可以包含任意的异步操作,只要最后
actions: {
// 第一个参数是用于触发mutations,第二个参数是使用的地方传过来的数据
nameAction({ commit }, data) {
// do something 可以是ajax、promise等异步操作
commit('updateName', data);
}
},
// 同步操作直接修改state里面的数据
mutations: {
// 第一个参数是上面的state数据,第二个参数是commit传过来的数据,用以修改state数据。
updateName(state, data) {
state.name = data; // 更改state里的数据
}
}
});
main.js
入口文件中 - 最后一步这是最后一步了,做完这步,然后我们就可以在项目中使用Vuex
了。
// main.js import Vue from 'vue'; import App from './App'; import store from './store'; // 引入store
new Vue({
el: '#app',
store, // 挂载在Vue的配置项中
components: { App },
template: '<App/>'
});
在组件中的使用如下,省略了template
部分:
// 组件中
<script>
export default {
mounted(){
console.log('vuex的数据',this.$store.state.name)
}
methods:{
changeName () {
// commit只接受一个参数,�数据多的话,就用对象传递
this.$store.dispatch('nameAction', '传过去的新名字') // 先触发actions,再由commit触发mutations来修改数据
}
}
</script>
使用方式是一样的,只是调用的�名字,稍微有些改变。
重复引用问题:
现在项目中基本使用的都是Webpack打包,所以我们不用担心重复引用的问题。
webpack会记忆你之前有没有引用过这个文件/包,整个项目只会引用一次。
// some.js
import store from './store'; // 引入vuex
console.log('vuex的数据', store.state.name);
store.dispatch('nameAction', '传过去的新名字')
实际上使用 Vuex 只需要store.js
文件,然后再把文件引到main.js
入门文件中,挂在new Vue
的配置项中即可使用。
如此之简单,快点来试试吧!
场景:比如文章详情数据,依赖路由的
params
参数获取的(ajax 写在 created 生命周期里面),因为路由懒加载的关系,退出页面再进入另一个文章页面并不会运行 created 组件生命周期,导致文章数据还是上一个文章的数据。
解决方法:watch 监听路由是否变化
watch: {
'$route' (to, from) { // 监听路由是否变化
if(this.$route.params.articleId){// 是否有文章id
// 获取文章数据
}
}
}
场景:
mounted(){
// this指向改变
setTimeout(function () { // setInterval同理
console.log(this);// 此时this指向Window对象
},1000);
}
解决方法:使用箭头函数
// 箭头函数访问this实例
// 因为箭头函数本身没有绑定this 继承上一个不是箭头函数的函数的this
setTimeout(() => {
console.log(this);
}, 500);
// 使用变量保存this指向 通过变量访问this实例
let self = this;
setTimeout(function() {
console.log(self); // 使用self变量访问this实例
}, 1000);
场景:
比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是 setInterval 还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。
解决办法:在组件生命周期 beforeDestroy 停止 setInterval
组件销毁前执行的钩子函数,跟其他生命周期钩子函数的用法相同。
beforeDestroy(){
//我通常是把setInterval()定时器赋值给this实例,然后就可以像下面这么暂停。
clearInterval(this.intervalid);
},
这个我当时做的时候以为很难,后来做好了才发现就是一个设置而已(前提是要开启路由的 History 模式),下面做一个简单的分享。
路由设置
如果之前一直使用的是hash
模式(默认模式),项目已经开发了一段时间,然后转 history 模式很可能会遇到:这些问题
const router = new VueRouter({
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) { // 如果savedPosition存在,滚动条会自动跳到记录的值的地方
return savedPosition
} else {
return { x: 0, y: 0 }// savedPosition也是一个记录x轴和y轴位置的对象
}
},
routes: [...]
})
vue 滚动行为文档,可以进到这里看看更详细的信息。
场景:
为了防止用户突然离开,没有保存已输入的信息。
用法:
//在路由组件中:
mounted(){
},
beforeRouteLeave (to, from, next) {
if(用户已经输入信息){
// 出现弹窗提醒保存草稿,或者自动后台为其保存
}else{
next(true);// 用户离开
}
}
类似的还有beforeEach
、beforeRouteUpdate
,也分为全局钩子和组件钩子,见路由文档。
觉得v-once这个 api 蛮 6 的,应该很多小伙伴都没有注意到这个 api。
文档介绍:
这个 api 在我看来主要用于那些一次性渲染,并且不会再有操作更改这些渲染的值,这样就可以优化双向绑定的更新性能。
文档推荐:对低开销的静态组件使用 v-once
尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样:
Vue.component('terms-of-service', {
template: '\
<div v-once>\
<h1>Terms of Service</h1>\
...很多静态内容...\
</div>\
'
})
vue 框架还有一个风格指南推荐,如下图所示,大家也可以学习一波。
一提到数组乱序,大家可能就会想到sort
方法,也就是下面这种实现方法:
arr.sort(() => 0.5 - Math.random());
但是sort
并不是真正意义上的乱序,一些元素间并没有机会相互比较(也就没有了随机交换的可能性),所有数组元素在大概率上还停留在自己初始位置。
这里不再展开了,省的懵逼,如果对此有兴趣的话,看这篇文章来解惑。
原理:
let oldArr = [1, 2, 3, 4, 5, 2, 8, 9, 10, 11];
let randomFn = actionArr => {
let newArr = [];
let randomArr = arr => {
let ranDomNum = Math.floor(Math.random() * arr.length); // 随机数
newArr.push(actionArr.splice(ranDomNum, 1)[0]); // 删除原数组元素 将删除的值添加到新数组
if (arr.length !== 0) {
return randomArr(actionArr);
} else {
return; // 数组清空 退出递归
}
};
randomArr(actionArr);
return newArr;
};
console.log(randomFn(oldArr), '新数组');
原理都一样,将递归换成了循环,这种方式无疑是更优雅的(虽然上面才是我写的...)。
let randomFn = actionArr => {
let newArr = [],
n = actionArr.length,
i;
while (n) {
i = Math.floor(Math.random() * n--); // 获取随机数(0~数组的长度-1)
newArr.push(array.splice(i, 1)[0]); // 删除原数组元素 将删除的值添加到新数组
}
return newArr;
};
上述方法创建了一个新数组,开辟了新的内存空间。
原理:
function shuffle(array) {
var m = array.length,
t,
i;
while (m) {
i = Math.floor(Math.random() * m--);
[array[i], array[m]] = [array[m], array[i]]; // 交换变量 ES6的写法
}
return array;
}
可能动画没有那么顺畅,可以自己在代码中调试一下参数,包括loading大小、动画参数。
html模板
<div class="loadding_father">
<div class="loadding1"></div>
<div class="loadding2"></div>
<div class="loadding3"></div>
</div>
Css
/* 外层居中 */ .loadding_father { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; justify-content: center; align-items: center; } /* 三个小圆圈 */ .loadding_father div { border-radius: 50%; background: rgba(216, 216, 216, 1); } /* 动画和圆圈大小 */ .loadding1 { /* 1.2s 是持续时间 */ animation: myfirst 1.2s infinite linear; width: 10px; height: 10px; }
.loadding2 {
animation: myfirst2 1.2s infinite linear;
/* 0.3s是延迟时间 为了有层次感 */
animation-delay: 0.3s;
margin: 0 7px 0 5px;
width: 8px;
height: 8px;
}.loadding3 {
animation: myfirst3 1.2s infinite linear;
animation-delay: 0.5s;
width: 6px;
height: 6px;
}/* 动画 上下浮动值 */
@keyframes myfirst {
0% {
transform: translate(0px, 0px);
}50% {
transform: translate(0px, -10px);
}100% {
transform: translate(0px, 0px);
}
}@keyframes myfirst2 {
0% {
transform: translate(0px, 0px);
}50% {
transform: translate(0px, -10px);
}100% {
transform: translate(0px, 0px);
}
}@keyframes myfirst3 {
0% {
transform: translate(0px, 0px);
}50% {
transform: translate(0px, -10px);
}
100% {
transform: translate(0px, 0px);
}
}
演示一下如何在node中使用git add .
等命令行。
在项目根目录创建deploy.js
, 使用node
运行该文件:
node deploy.js '参数1' '参数2'
对的, 启动node进程是可以传入参数的:
process.argv是node
自带的属性,这是一个数组,数组的前两个元素是默认值:
process.argv[0]
: process.execPath
(返回启动 Node.js 进程的可执行文件的绝对路径名)process.argv[1]
: 正在执行的 JavaScript 文件的路径process.argv[2]、process.argv[3]...
: 这里是传入的参数// process.argv
[ '/usr/local/bin/node',
'/Users/obkoro1/work/itemName/deploy.js',
'参数1', '参数2' ]
执行命令行主要是靠node自带模块:child_process
的execSync方法来创建一个子进程运行命令。
运行方法如上所示,拷贝下面的代码来试一试就知道了。
// deploy.js
// node内置模块 同步执行命令行
const execSync = require('child_process').execSync;
const commitParam = process.argv[2] // commit 参数
myExecSync(`git add . && git commit -m ${commitParam} && git pull && git push`);
/**
- @description: 同步执行命令行
- @param {string} cmd 字符串
@Date: 2019-08-02 17:43:41
/
function myExecSync(cmd) {
// 除了该方法直到子进程完全关闭后才返回 执行完毕 返回
try {
var output = execSync(
cmd,
{
encoding: 'utf8',
timeout: 0,
maxBuffer: 200 1024,
killSignal: 'SIGTERM',
cwd: null,
env: null
},
function(err, stdout, stderr) {
// 进程错误时 回调
if (err) {
console.log(执行命令</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>cmd<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">出错:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>err<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
);
return;
}
}
);
} catch (err) {
console.log(执行命令</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>cmd<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">出错:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>err<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
);
}
}
为了避免用户胡乱输入就通过验证,很多时候我们都会采用正则表达式来验证一下用户输入的信息是否符合规范。这部分的内容基本上是在网上收集来的,这里跟大家一起分享一下,有需要的可以记在自己的有道云笔记里面。
验证的方式当然是很多了,这里推荐采用test()方法来验证。
let isTrue = RegExpObject.test(string);// RegExpObject为正则 string是要检测的字符串
// 如果字符串 string 中含有与 RegExpObject 匹配的文本,则返回 true,否则返回 false。
if (isTrue){
//验证成功 do something
}elseP{
//验证失败
}
第一代身份证只有 15 位数,第二代身份证有 18 位数,各位按照需求来选择表达式。
//第二代身份证号码正则
let isTrue = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
//第一代身份证正则表达式(15位)
let isTrue = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/;
时间截止为:2018 年 1 月 11 日
移动号段:134 135 136 137 138 139 147 148 150 151 152 157 158 159 172 178 182 183 184 187 188 198
联通号段:130 131 132 145 146 155 156 166 171 175 176 185 186
电信号段:133 149 153 173 174 177 180 181 189 199
虚拟运营商:170
let isTrue = /^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$/;
let isTrue = /^([A-Za-z0-9_\-\.\u4e00-\u9fa5])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,8})$/;
////用户名正则,4到16位(字母,数字,下划线,减号)
let isTrue = /^[a-zA-Z0-9_-]{4,16}$/;
密码正则,以字母开头,长度在 6~18 之间,只能包含字母、数字和下划线
let isTrue =^[a-zA-Z]\w{5,17}$;
强密码正则,最少 6 位,包括至少 1 个大写字母,1 个小写字母,1 个数字,1 个特殊字符
let isTrue = /^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/;
let isTrue = /^[1-9][0-9]{4,10}$/;
//微信号正则,6至20位,以字母开头,字母,数字,减号,下划线
let isTrue = /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/;
let isTrue = /["'<>%;)(&+]+-!!@#$~/;
let isTrue=[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?;
let isTrue = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/;
let isTrue = /[\u4E00-\u9FA5]/;
//这个可以用于验证用户的真实姓名。
let isTrue = /^(P\d{7}|G\d{7,8}|TH\d{7,8}|S\d{7,8}|A\d{7,8}|L\d{7,8}|\d{9}|D\d+|1[4,5]\d{7})$/;
let isTrue=(\(\d{3,4}\)|\d{3,4}-|\s)?\d{8};
let isTrue=\d+\.\d+\.\d+\.\d+;
let isTrue=[1-9]{1}(\d+){5};
//经度正则
let isTrue = /^(\-|\+)?(((\d|[1-9]\d|1[0-7]\d|0{1,3})\.\d{0,6})|(\d|[1-9]\d|1[0-7]\d|0{1,3})|180\.0{0,6}|180)$/;
//纬度正则
let isTrue = /^(\-|\+)?([0-8]?\d{1}\.\d{0,6}|90\.0{0,6}|[0-8]?\d{1}|90)$/;
常用的正则表达式大概就是上面这些了,如果大家还有其他干货的话,欢迎关注我的公众号给我留言。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.