Code Monkey home page Code Monkey logo

codeblack's Introduction

about OBKoro1

Hi,大家好,我是OBKoro1,大家可以叫koro(扣肉)。

关于我的其他信息有兴趣的朋友可以去前端进阶积累-关于了解。

下面是我在github上的几个开源项目的演示gif,自认为还是比较好用的工具。

开源项目演示

  1. VSCode插件: 用于一键生成文件头部注释并自动更新最后编辑人和编辑时间、函数注释自动生成和参数提取。
  2. 插件可以帮助用户养成良好的编码习惯,规范整个团队风格。
  3. 经过多版迭代后,插件支持所有主流语言,灵活方便,文档齐全,食用简单!
  4. 从2018年5月维护至今, 3K+ Star,关闭issue 300+
  5. 目前拥有250K+的用户,VSCode图表统计日安装用户100多-400多人,

头部注释

函数注释

减少摸鱼的时间和频率的Chrome插件:在上班/学习期间很容易下意识的打开摸鱼网站,插件帮助我们减少摸鱼的时间和频率,提高我们上班和学习的效率,节省时间用于学习提升自己或者享受生活

  • 这是一个可以在任意时间范围自动提交commit的VSCode插件
  • 它可以自由控制每天的commit次数, 随机commit次数,使你的commit提交看起来更加逼真。
  • 它在平常不用运行,需要的时候花十几分钟一键刷commit,填满你的github首页绿色格子

收集和整理了一个大厂前端需要掌握能力的仓库。

其中分为JS基础能力,大厂场景题、大厂面试真题。

希望能够帮助大家提升自己的能力,在面试的时候能够游刃有余,轻松拿到高薪offer。

大厂前端需要掌握的能力

用爱发电,求赞助 😭

开源不易,本插件的开发与维护全都是利用业余时间。

开源工作对我来说就是用爱发电,从18年开始在社区开源到现在,可以说基本没有收益。

如果觉得这个效率工具还不错, 对你有所帮助,就赞助支持一下我的工作吧。

赞助

联系方式:

codeblack's People

Contributors

milyyy avatar obkoro1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

codeblack's Issues

模板字符串中的反引号和反斜杠 | 开箱即用的代码块

博客链接

# 模板字符串中的反引号和反斜杠

# 模板字符串使用反引号

如果不使用反斜杠就会报错。

let greeting = `\`反引号里面嵌套\` 反引号!`;

# 模板字符串使用反斜杠

通常正则会使用反斜杆,向上个栗子一样,反斜杠\在模板字符串中有特殊的意思,所以也需要转义一下,否则会报错。

test = `
let regString = /\\r\\n|\\r|\\n/`

# 点个Star支持我一下~

博客链接

fixed 从父原则导致 z-index 无效 | 开箱即用的代码块

博客链接

# fixed 从父原则导致 z-index 无效

# 什么是从父原则:

  1. 子元素在与父辈元素比较的时候,用父辈的 z-index 去比较
  2. 在与同级元素比较的时候,才有自己的 z-index 去比较

# 什么情况下出现从父原则

  1. 父元素通过fixed absolute relative定位的元素, 子元素也是fixed absolute relative定位的元素。
  2. 在父元素上设置了z-index
  3. 跟父元素同级的元素也是通过fixed absolute relative定位的元素,并且设定了z-index

# 这样说有点干,来看一下示例。

示例中带有详细的注释,可以自己动手调一调,就明白啦~

# codepen

fixed 从父原则

# 代码:

<!--
 * 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>

# 点个Star支持我一下~

博客链接

vue 小技巧&小问题2 | 开箱即用的代码块

博客链接

# vue 小技巧&小问题2

用Vue开发一个网页并不难,但是也经常会遇到一些问题,其实大部分的问题都在文档中有所提及,再不然我们通过谷歌也能成功搜索到问题的答案,为了帮助小伙伴们提前踩坑,在遇到问题的时候,心里大概有个谱知道该如何去解决问题。这篇文章是将自己知道的一些小技巧,结合查阅资料整理成的一篇文章。

# 文章内容总结:

  1. 组件style的scoped
  2. Vue 数组/对象更新 视图不更新
  3. vue filters 过滤器的使用
  4. 列表渲染相关
  5. 深度watch与watch立即触发回调
  6. 这些情况下不要使用箭头函数
  7. 路由懒加载写法
  8. 路由的项目启动页和404页面
  9. Vue调试神器:vue-devtools

# 组件style的scoped:

问题:在组件中用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的样式匹配起来,导致样式失效。

解决方式

  • 推荐:去掉该组件的scoped

每个组件的css并不会很多,当设计到动态添加dom,并为dom添加样式的时候,就可以去掉scoped,会比下面的方法方便很多。

  • 可以动态添加style
// 上面的栗子可以这样添加样式
newDom.style.height='100px';
newDom.style.width='100px';
newDom.style.background='red';

# Vue 数组/对象更新 视图不更新

很多时候,我们习惯于这样操作数组和对象:

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 不能检测以上数组的变动,以及对象的添加/删除,很多人会因为像上面这样操作,出现视图没有更新的问题。

解决方式:

  1. this.$set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
this.$set(this.arr, 0, "OBKoro1"); // 改变数组
this.$set(this.obj, "c", "OBKoro1"); // 改变对象

如果还是不懂的话,可以看看这个codependemo

  1. 数组原生方法触发视图更新:

Vue可以监测到数组变化的,数组原生方法:

splice()push()、pop()、shift()、unshift()、sort()、reverse()

意思是使用这些方法不用我们再进行额外的操作,视图自动进行更新

推荐使用splice方法会比较好自定义,因为slice可以在数组的任何位置进行删除/添加操作,这部分可以看看我前几天写的一篇文章:【干货】js 数组详细操作方法及解析合集

  1. 替换数组/对象

比方说:你想遍历这个数组/对象,对每个元素进行处理,然后触发视图更新。

// 文档中的栗子: filter遍历数组,返回一个新数组,用新数组替换旧数组
example1.items = example1.items.filter(function (item) {
    return item.message.match(/Foo/)
})

举一反三:可以先把这个数组/对象保存在一个变量中,然后对这个变量进行遍历,等遍历结束后再用变量替换对象/数组

并不会重新渲染整个列表:

Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

如果你还是很困惑,可以看看Vue文档中关于这部分的解释。


# vue filters 过滤器的使用:

过滤器,通常用于后台管理系统,或者一些约定类型,过滤。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立即触发回调

watch很多人都在用,但是这watch中的这两个选项deepimmediate,或许不是很多人都知道,我猜。

选项: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

还有下面这一点需要注意。


# 这些情况下不要使用箭头函数:

  • 不应该使用箭头函数来定义一个生命周期方法
  • 不应该使用箭头函数来定义 method 函数
  • 不应该使用箭头函数来定义计算属性函数
  • 不应该对 data 属性使用箭头函数
  • 不应该使用箭头函数来定义 watcher 函数

示例:

// 上面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上面,本质上是一样的。两种方式都可以的,大家自由选择哈。


# 路由的项目启动页和404页面

实际上这也就是一个设置而已:

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,显示的页面是一片空白。


# Vue调试神器:vue-devtools

每次调试的时候,写一堆console是否很烦?想要更快知道组件/Vuex内数据的变化

那么这款尤大开发的调试神器:vue-devtools,你真的要了解一下了。

这波稳赚不赔,真的能提高开发效率。

安装方法

  • 谷歌商店+科学上网,搜索vue-devtools即可安装。
  • 不会科学上网?手动安装

安装之后

在chrome开发者工具中会看一个vue的一栏,如下对我们网页应用内数据变化,组件层级等信息能够有更准确快速的了解。


# 前几个月也写过一篇类似的:

Vue 实践过程中的几个问题


# 结语

本文的内容很多都在Vue文档里面有过说明,推荐大家可以多看看Vue文档,不止看教程篇,还有文档的Api什么的,也都可以看。然后其实还有两三点想写的,因为预计篇幅都会比较长一点,所以准备留到以后的文章里面吧~

# 点个Star支持我一下~

博客链接

多维数组展开 | 开箱即用的代码块

博客链接

# 多维数组展开

# 原理:

  • 利用reduce累加,将数组平铺。
  • 判断每次传入的第二个参数是否为数组,如果是数组的话,递归。
  • 然后每层都平铺,一层一层用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]

# 点个Star支持我一下~

博客链接

判断用户浏览器 | 开箱即用的代码块

博客链接

# 判断用户浏览器

# navigator.userAgent

判断用户所使用的浏览器主要用到的 api 是navigator.userAgent,这是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值,不同浏览器的userAgent值都不相同,所以我们可以根据这个字符串来判断用户是从哪个浏览器进入

# 判断方式:

下面两个是刚做的 demo 获取的值,仔细观察下面两个字符串,会发现有些值是不一样的,并且浏览器特有的,依据这个我们就可以作为不同浏览器的判断条件。

# QQ 内置浏览器的 userAgent 值:

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

# 微信内置浏览器的 userAgent 值:

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

# 示例:判断QQ和微信内置浏览器

使用方式,直接使用这个 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

# 点个Star支持我一下~

博客链接

论如何在node使用命令行 | 开箱即用的代码块

博客链接

# 论如何在node使用命令行

演示一下如何在node中使用git add .等命令行。

# 项目代码一键上传

在项目根目录创建deploy.js, 使用node运行该文件:

node deploy.js '参数1' '参数2'

对的, 启动node进程是可以传入参数的:

# 启动node进程时传入参数:

process.argvnode自带的属性,这是一个数组,数组的前两个元素是默认值:

  1. process.argv[0]: process.execPath(返回启动 Node.js 进程的可执行文件的绝对路径名)
  2. process.argv[1]: 正在执行的 JavaScript 文件的路径
  3. process.argv[2]、process.argv[3]...: 这里是传入的参数
// process.argv
[ '/usr/local/bin/node',
  '/Users/obkoro1/work/itemName/deploy.js',
  '参数1', '参数2' ]

# deploy.js

执行命令行主要是靠node自带模块:child_processexecSync方法来创建一个子进程运行命令。

运行方法如上所示,拷贝下面的代码来试一试就知道了。

# 代码:

// 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">);
    }
    }

# 点个Star支持我一下~

博客链接

浏览器自动识别数字成电话号码 | 开箱即用的代码块

博客链接

# 浏览器自动识别数字成电话号码

描述

浏览器:目前我遇到的是Android和IPhone的Safari浏览器还有PC端的IE。

表现:浏览器会自动对看起来像是电话号码的数字转成电话号码,并在数字的下方添加下划线,字体变为蓝色,手机端点击之后还会询问用户是否想要拨打该号码

解决方式

在html头部添加这段meta即可:

<meta name="format-detection" content="telephone=no" />

启用电话号码

有些时候,真的是我们的电话号码,并且希望唤起拨打功能,像下面这样就可以了:

<a href="tel:18888888888">18888888888</a>

# 点个Star支持我一下~

博客链接

instanceof实现原理 | 开箱即用的代码块

博客链接

# instanceof实现原理

# 思路

右边变量的原型存在于左边变量的原型链上

关于原型链不太懂的同学可以看一下,我的这篇文章: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__
  }
}

# 点个Star支持我一下~

博客链接

开箱即用的代码块

博客链接

# 开箱即用的代码块

一直以来我都有做笔记的习惯,每次有学到新的东西,我都会记在自己的有道云笔记上,久而久之就记录了很多比较小的代码块/知识点。

这类代码块/知识点,因为比较小,一段话,一段代码就能说清楚, 很难整理成一个博客,这里就是用来记录这些东西的。

这类代码块也通常比较偏,所以在今后积累的比较多了,这也是很有价值的内容,可以快速学习到很多内容!

如果对这个项目有兴趣的话,可以给我的项目点个Star。

# 来社区关注我,不错过最新文章:

# 点个Star支持我一下~

博客链接

单行、多行文本溢出 | 开箱即用的代码块

博客链接

# 单行、多行文本溢出

老早之前的技能了,整理笔记发现的,不知道的可以看下:

# codepen

# 代码

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;

}

# 点个Star支持我一下~

博客链接

loadding效果-移动端小球上下浮动 | 开箱即用的代码块

博客链接

# loadding效果-移动端小球上下浮动

# 效果图

可能动画没有那么顺畅,可以自己在代码中调试一下参数,包括loading大小、动画参数。

# codepen

loadding效果-移动端小球上下浮动

# 代码:

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);
}
}

# 点个Star支持我一下~

博客链接

数组交集差集并集 | 开箱即用的代码块

博客链接

# 数组交集差集并集

有任意两个数组,每个数组里面的元素不重复,找出它们的交集、差集和并集。

# 交集、差集和并集是什么鬼?

  1. 交集

由所有属于集合 A 且属于集合 B 的元素所组成的集合,叫做集合 A 与集合 B 的交集(intersection),记作 A∩B

交集百度百科

  1. 差集

以属于 A 而不属于 B 的元素为元素的集合成为 A 与 B 的差。(本文栗子,还会求出属于 B 不属于 A 的集合)

差集百度百科

  1. 并集

给定两个集合 A,B,把他们所有的元素合并在一起组成的集合,叫做集合 A 与集合 B 的并集,记作 A∪B,读作 A 并 B。

并集百度百科

# includes 判断是否包含:

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的。

# 使用 Set 来判断:

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')
);

# 点个Star支持我一下~

博客链接

网页标题/标签 闪烁 | 开箱即用的代码块

博客链接

# 网页标题/标签 闪烁

# 效果:

# 实现代码

let beforeTitle = document.title; // 原标签
let count = 0; // 切换title
function blinkNewMsg(val) {
  document.title =
    count % 2 == 0 ? `【   】${beforeTitle}` : `【${val}${beforeTitle}`;
  count++;
}
setInterval(() => {
  blinkNewMsg('新消息');
}, 1000);

# 点个Star支持我一下~

博客链接

Vuex 的使用入门-极简使用 | 开箱即用的代码块

博客链接

# Vuex 的使用入门-极简使用

vuex 是为了解决复杂项目组件之间的数据通信的一个全局状态管理机制,相信很多人都听说过这个东西。有部分人还没有在项目中使用Vuex管理过数据状态,实际上Vuex的起步使用非常之简单,看完本文之后,赶紧在项目中用起来吧!

# 1. 安装 Vuex 包

npm install vuex --save

# 2. 新建一个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里的数据
    }
  }
});

# 3. 引入到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/>'
});

# 在组件中使用 vuex:

在组件中的使用如下,省略了template部分:

// 组件中
<script>
export default {
  mounted(){
      console.log('vuex的数据'this.$store.state.name)
  }
  methods:{
    changeName () {
      // commit只接受一个参数,�数据多的话,就用对象传递
      this.$store.dispatch('nameAction', '传过去的新名字') // 先触发actions,再由commit触发mutations来修改数据
  }
}
</script>

# 在 js 文件中使用 vuex:

使用方式是一样的,只是调用的�名字,稍微有些改变。

重复引用问题:

现在项目中基本使用的都是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的配置项中即可使用。

如此之简单,快点来试试吧!

# 点个Star支持我一下~

博客链接

html字符串绑定点击事件 | 开箱即用的代码块

博客链接

# html字符串绑定点击事件

htmls字符串可以加很多事件了,可以以变量的形式,添加domfunction等等,知道一下这个东西,以后说不定用得着。

// 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;

# 点个Star支持我一下~

博客链接

随机数数组 | 开箱即用的代码块

博客链接

# 随机数数组

所谓的随机数数组就是:由随机数组成的数组(数组的长度和随机数的范围可自定义)

当然有很多方法,只是用下面这个API只用一行代码就可以实现这个功能,见猎心喜,然后放了上来。

# 原理

  1. 先创建一个类似数组的对象(即对象拥有length属性)
  2. 利用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之间

CodePen

# 点个Star支持我一下~

博客链接

树状数组结构转化 | 开箱即用的代码块

博客链接

# 树状数组结构转化

# 题目如下:

这道题是我朋友发给我的,之前一开始看的时候,觉得很简单,但仔细往下看的时候,眉头一皱发现事情并不简单。

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: []
      }
    ]
  }
];

# 建议大家好好想想,争取能够自己解出来

# 参考一下我的解题方法:

  1. 先算出层级嵌套数

    遍历数组,再遍历数组的元素,用parseInt拿到每一个值,将最大的值取出来,即为层级嵌套数。

  2. 数组转成属性层级的对象。对象的属性是层级,值是数组,层级里面的值。

    1.去重操作(比如:1_id相等的话就不再重复添加)

    2.提取需要的信息,最终的值和相应父级的信息(之后组装数组的时候放到对应的地方,需要的信息)。

    最后输出的对象是这个样子

  1. 将属性层级的对象转成树状结构数组。

    遍历对象,获取层级值。

    遍历层级的每个元素。

    找到对应的层级将值添加进去,否则递归继续找对应层级。

# 代码:

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;
}

# 点个Star支持我一下~

博客链接

Object.create实现 | 开箱即用的代码块

博客链接

# Object.create实现

# 关键思路:

将传入的对象作为新对象原型

# 代码:

function myCreate(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

# 修改原对象的属性会影响新对象的原型:

var obj1 = { p: 1 };
var obj2 = Object.create(obj1);
obj1.p = 2;
console.log('obj', obj1, obj2,)

# 点个Star支持我一下~

博客链接

vue 小技巧&小问题1 | 开箱即用的代码块

博客链接

# vue 小技巧&小问题1

# 路由变化页面数据不刷新问题

场景:比如文章详情数据,依赖路由的params参数获取的(ajax 写在 created 生命周期里面),因为路由懒加载的关系,退出页面再进入另一个文章页面并不会运行 created 组件生命周期,导致文章数据还是上一个文章的数据。

解决方法:watch 监听路由是否变化

     watch: {
      '$route' (to, from) { // 监听路由是否变化
        if(this.$route.params.articleId){// 是否有文章id
          // 获取文章数据
        }
      }
    }

# setTimeout/setInterval 无法用 this 访问 VUe 实例

场景

      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 路由跳转继续运行并没有销毁

场景

比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是 setInterval 还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。

解决办法:在组件生命周期 beforeDestroy 停止 setInterval

组件销毁前执行的钩子函数,跟其他生命周期钩子函数的用法相同。

beforeDestroy(){
    //我通常是把setInterval()定时器赋值给this实例,然后就可以像下面这么暂停。
   clearInterval(this.intervalid);
},

# vue 滚动行为(浏览器回退记忆位置)用法

这个我当时做的时候以为很难,后来做好了才发现就是一个设置而已(前提是要开启路由的 History 模式),下面做一个简单的分享。

路由设置

  1. 要使用这一功能,首先需要开启 vue-router 的 history 模式

如果之前一直使用的是hash模式(默认模式),项目已经开发了一段时间,然后转 history 模式很可能会遇到:这些问题

  1. 滚动行为具体设置如下:
        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 滚动行为文档,可以进到这里看看更详细的信息。

# vue 路由拦截浏览器后退实现草稿保存类似需求

场景

为了防止用户突然离开,没有保存已输入的信息。

用法

    //在路由组件中:
    mounted(){
    },
    beforeRouteLeave (to, from, next) {
      if(用户已经输入信息){
        // 出现弹窗提醒保存草稿,或者自动后台为其保存
      }else{
        next(true);// 用户离开
      }
    }

类似的还有beforeEachbeforeRouteUpdate,也分为全局钩子和组件钩子,见路由文档

# v-once 只渲染元素和组件一次,优化更新渲染性能

觉得v-once这个 api 蛮 6 的,应该很多小伙伴都没有注意到这个 api。

文档介绍

v-once文档介绍

这个 api 在我看来主要用于那些一次性渲染,并且不会再有操作更改这些渲染的值,这样就可以优化双向绑定的更新性能。

文档推荐:对低开销的静态组件使用 v-once

尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样:

    Vue.component('terms-of-service', {
      template: '\
        <div v-once>\
          <h1>Terms of Service</h1>\
          ...很多静态内容...\
        </div>\
      '
    })

# vue 风格指南推荐:

vue 框架还有一个风格指南推荐,如下图所示,大家也可以学习一波。

vue风格指南

# 点个Star支持我一下~

博客链接

判断div滚动到底部 | 开箱即用的代码块

博客链接

# 判断div滚动到底部

# codepen

demo

# 代码:

如果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 提前发送请求');
}
};

# 点个Star支持我一下~

博客链接

移动端手写下拉刷新 | 开箱即用的代码块

博客链接

# 移动端手写下拉刷新

# codepen

F12开启浏览器手机模拟器,在顶部鼠标按住移动

demo

# 思路

  1. 使用touchstarttouchmovetouchend三个事件来监听触摸事件
  2. 使用getBoundingClientRect()API来判断当前dom是否在顶部(之前使用document.body.scrollTop并不行,然而搜到的都是这个答案。)
  3. 最后一个是根据事件传递进来的参数用以计算距离。
  4. 剩下的就是具体的逻辑问题

# html源代码:

<!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>

# 点个Star支持我一下~

博客链接

跳转/刷新保存请求/console | 开箱即用的代码块

博客链接

# 跳转/刷新保存请求/console

# 页面跳转/刷新太快了,看不到保存的数据

有时候我们在页面跳转之前会发送一些请求,打印一些数据,但是因为页面跳转的太快了,导致还未看清发生了什么就已经离开了该页面。

# 这是一个设置

打开控制台,console和network有一个设置:Preseve log,把对应的设置勾上就可以保存对应的数据了。

查看数据和设置都在控制台上,使用起来非常方便,而不用切换到编辑器中去修改代码打debugger, 不使用了还要去掉。

# 点个Star支持我一下~

博客链接

浏览器原生进度条-progress | 开箱即用的代码块

博客链接

# 浏览器原生进度条-progress

# 原生支持

进度条很多人都是手写的,通过div+js的形式。 实际上并不需要这么麻烦,因为原生就支持该标签,只要更改一下value值,即可改变进度,简单又方便!

# codepen

demo

# 代码:

<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; // 进度条未进度 部分
}

# 点个Star支持我一下~

博客链接

数组完全乱序 | 开箱即用的代码块

博客链接

# 数组完全乱序

一提到数组乱序,大家可能就会想到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;
};

# 不开新数组(最终):

上述方法创建了一个新数组,开辟了新的内存空间。

原理

  • 因为每次循环,数组的长度都要减去1,数组长度的最大值是一个一个减少的
  • 把当前找到的随机元素,保存在每次循环的最大值的位置
  • 再把最后一个被替换的元素,放到随机元素原来的位置
  • 就是把这两个元素交换变量
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;
}

# 点个Star支持我一下~

博客链接

常见正则表达式合集 | 开箱即用的代码块

博客链接

# 常见正则表达式合集

为了避免用户胡乱输入就通过验证,很多时候我们都会采用正则表达式来验证一下用户输入的信息是否符合规范。这部分的内容基本上是在网上收集来的,这里跟大家一起分享一下,有需要的可以记在自己的有道云笔记里面。

# 如何验证?

验证的方式当然是很多了,这里推荐采用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])(?=.*[!@#$%^&*? ]).*$/;

# QQ 号码正则:

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};

# IP 地址正则:

    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)$/;

常用的正则表达式大概就是上面这些了,如果大家还有其他干货的话,欢迎关注我的公众号给我留言。

# 点个Star支持我一下~

博客链接

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.