LanJamRom

LanJamRom 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

LanJamRom 提出了问题 · 10月18日

redux-thunk中使用ts异步action如何做类型推断

export interface DelUserInfoAction {
  type: typeof DEL_USER_INFO
}
export type UserInfoActionTypes =  DelUserInfoAction 

export function userInfoReducer(state = initUserState, action: UserInfoActionTypes): iUserInfoState {
  switch (action.type) {
    case DEL_USER_INFO:
      return { ...initUserState }
    default:
      return state
  }
}


// action
export function deleteUserInfo(): UserInfoActionTypes {
  return {
    type: DEL_USER_INFO
  }
}

export const asyncLogout = ():ThunkAction<void, RootState, null, Action<string>> => async (dispatch) => {
  await apiLogout()
  dispatch(deleteUserInfo())
}

// 组件中
const btnClick = () => {
    store.dispatch(asyncLogout)
}
// 报错
Argument of type 'ThunkAction<void, CombinedState<{ user: iUserInfoState; }>, null, Action<DelUserInfoAction>>' is not assignable to parameter of type 'AnyAction'.

关注 1 回答 0

LanJamRom 赞了文章 · 3月28日

CSS Grid 布局不好理解?可借助 CSS Grid Generator 快速上手并掌握 Grid 布局!

点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。


为了保证的可读性,本文采用意译而非直译。

CSS Grid Generator

CSS Grid Generator是一个由Sarah Drasner创建的免费工具。它是一个可视化设计工具,允许咱们创建一个基本的 grid 布局,然后就可以使用生成对应的代码,帮助咱们快速布局。

第一次进入是界面是这样子的:

clipboard.png

CSS Grid 布局示例

当我正在学习一些东西时,我发现最好的学习方法是使用现有的工具构建实用的东西。 在本文中,咱们先从一个简单的布局开始,然后使用CSS Grid Generator创建在实际项目中使用所需的代码。

首先从一个典型的布局开始,如下所示:

clipboard.png

接着在 CSS Grid Generator 界面的右侧更新对应的以下内容:

  • 行: 4
  • 列: 3
  • 列间距: 20
  • 行间距: 20

间距让咱们的内容之间有一定的空白。可以只使用列间距,但我想在 HeaderFooter 之前留出一些空白,所以还同时使用行间距。

clipboard.png

接下来,就是需要定义应用程序的不同区域。在 CSS Grid Generator 中,可以单击并拖动到需要合并地方来创建一个区域。咱们希望Footer跨越整个网格,侧边栏占用一个单元格,主内容区域跨越2列,Footer 跨越4列,最终效果,如下:

clipboard.png

这看起来有点像咱们想要的布局,但仍然需要定义一些具体的尺寸。 在CSS Grid Generator 会注意到每行和每列旁边都有一个输入框,可用于设置特定大小。

  • Header: 100px height
  • Sidebars: 200px width
  • Footer: 50px height

clipboard.png

这看起来更像更像咱们想要的布局,但是你可能会问1fr是多少。

轨道可以用任何长度单位来定义。Grid还引入了一个额外的长度单位,以帮助各位创建灵活的Grid轨道。新的fr单元表示网格容器中可用空间的一小部分。

第二行的1fr会告诉区域占用剩余的可用空间。如果将容器设置为100vh,就会占据整个页面的内容,列也是如此。

CSS Grid Generated 生成的代码

clipboard.png

点击“请给我示例中的代码”就可以查看对应布局生成的 CSS 代码:

.parent { 
display: grid; 
grid-template-columns: 200px 1fr 1fr 200px; 
grid-template-rows: 100px 1fr 50px; 
grid-column-gap: 20px;
grid-row-gap: 20px; 
.div1 { grid-area: 1 / 1 / 2 / 5; } 
.div2 { grid-area: 2 / 1 / 3 / 2; } 
.div3 { grid-area: 2 / 2 / 3 / 4; } 
.div4 { grid-area: 2 / 4 / 3 / 5; } 
.div5 { grid-area: 3 / 1 / 4 / 5; } 
}

创建一个simple-layout.htm并添加以下代码:

<!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>Simple Layout</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>

</body>
</html>

接下来添加上面生成的 CSS:

<!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>Simple Layout</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    .parent {
      display: grid;
      grid-template-columns: 200px 1fr 1fr 200px;
      grid-template-rows: 100px 1fr 50px;
      grid-column-gap: 20px;
      grid-row-gap: 20px;
      height: 100vh;
    }

    .div1 {
      grid-area: 1 / 1 / 2 / 5;
    }

    .div2 {
      grid-area: 2 / 1 / 3 / 2;
    }

    .div3 {
      grid-area: 2 / 2 / 3 / 4;
    }

    .div4 {
      grid-area: 2 / 4 / 3 / 5;
    }

    .div5 {
      grid-area: 3 / 1 / 4 / 5;
    }
  </style>
</head>
<body>

</body>
</html>

接着添加对应的标签:

<body>
  <div class="parent">
    <div class="div1">
      Header
    </div>
    <div class="div2">
      Left Sidebar
    </div>
    <div class="div3">
      Main Content
    </div>
    <div class="div4">
      Right Sidebar
    </div>
    <div class="div5">
      Footer
    </div>
  </div>

</body>

最后添加下面的CSS,它将为.div1 - .div5添加一些背景色:

div:not(.parent) {
  padding: 10px;
  background-color: rgb(199, 199, 199);
}

运行:

clipboard.png

这看起来很好,但你希望它占据整个浏览器窗口。所以需要向.parent类添加height: 100vh

.parent {
  display: grid;
  grid-template-columns: 200px 1fr 1fr 200px;
  grid-template-rows: 100px 1fr 50px;
  grid-column-gap: 20px;
  grid-row-gap: 20px;
  height: 100vh;
}

最终效果:

clipboard.png


大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

我和阿里云合作服务器,折扣价比较便宜:89/年,223/3年,比学生9.9每月还便宜,买了搭建个项目,熟悉技术栈比较香(老用户用家人账号买就好了,我用我妈的)推荐买三年的划算点,点击本条就可以查看。


网格轨道(Grid Track) 加餐

两个相邻的网络线之间为网络轨道。

clipboard.png

图中的同方向 1 和 2, 2 和 3 都是相邻的网络线,当然同方向的 1 和 3 或者不同方向的 1 和 2 就不是相邻的网络线。

相邻的网络线为网格轨道,如下,黑色1 和 2 之间就构成了网络轨道(背景深橘色):

clipboard.png

上面总共有 5 个网络轨道,水平方向灰色 1 和 2, 2 和 3, 3 和 4,竖直方向黑色的 1 和 2, 2 和 3,共 5 个。

网格单元(Grid Cell) 加餐

两个相邻的列网络线和两个相邻的行网络线组成的就是网络单元,如下面的深橘色背景就是网络单元。

clipboard.png

网络单元要与网络项(项目)区别开来,网络项是 Html 中可以找的到 Dom 元素,网络单元是在定义容器的时候,它就会分割出来的一个一个单元格。

网格区域(Grid Area) 加餐

四个网络线包围的总空间。

clipboard.png

fr单位(加餐)

剩余空间分配数,用于在一系列长度值中分配剩余空间,如果多个已指定了多个部分,则剩下的空间根据各自的数字按比例分配。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

参考

https://dev.to/therealdanvega...

慕课grid教程

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 19 收藏 14 评论 1

LanJamRom 收藏了文章 · 2019-12-17

前端常用的 59 个工具类【持续更新】

js-logo.jpg

前言

前端开发有时会处理一部分后台返回的数据,或者根据数据判断做一些处理; 这个时候就非常有必要将一些常用的工具类封装起来;
本文根据常用的一些工具类封装了 59 个方法,当然还有很多用的较少前期没有录入,后期持续跟新;
源码地址,utils-lan 源码地址,欢迎 star!

使用

1.方法一

npm i -S utils-lan  
import utils from 'utils-lan'  
console.log(utils.arrJudge(['1','2']))

2.方法二
git clone utils-lan 源码地址下来导入项目;

3.关于类名
是根据字面量来命名的,方法首个驼峰表示所属类型,后面是方法作用
如 arrAndSet 一看就是数组的方法,是处理交集的;
如果实在难以忍受,可以采用方法 2,导入本地对项目进行更改.
给大家推荐一款 bug 管理工具,请戳
bug 管理工具

arr

1.arrAndSet

并集

/**
 * 数组并集,只支持一维数组
 * @param {Array} arrOne
 * @param {Array} arrTwo
 */
export const arrAndSet = (arrOne, arrTwo) => {
  return arrOne.concat(arrTwo.filter(v => !arrOne.includes(v)))
}

2.arrIntersection

交集

/**
 * 数组交集,只支持一维数组
 * @param {Array} arrOne
 * @param {Array} arrTwo
 */
export const arrIntersection = (arrOne, arrTwo) => {
  return arrOne.filter(v => arrTwo.includes(v))
}

3.arrDifference

差集

/**
 * 数组差集,只支持一维数组
 * @param {Array} arrOne
 * @param {Array} arrTwo
 * eg: [1, 2, 3] [2, 4, 5] 差集为[1,3,4,5]
 */
export const arrDifference = (arrOne, arrTwo) => {
  return arrOne.concat(arrTwo).filter(v => !arrOne.includes(v) || !arrTwo.includes(v))
}

4.arrTwoToArrObj

两个数组合并成一个数组对象

/**
 * 两个数组合并成一个对象数组,考虑到复杂度,所以目前支持两个一维数组
 * @param {Array} arrOne
 * @param {Array} arrTwo
 * @param {oneKey} oneKey 选填,如果两个都未传,直接以 arrOne 的值作为 key,arrTwo 作为 value
 * @param {twoKey} twoKey
 */
export const arrTwoToArrObj = (arrOne, arrTwo, oneKey, twoKey) => {
  if(!oneKey&&!twoKey){
    return arrOne.map((oneKey, i) => ({ [oneKey]:arrTwo[i] }))
  }else{
    return arrOne.map((oneKey, i) => ({ oneKey, twoKey: arrTwo[i] }))
  }
}

5.arrObjSum

数组对象求和

/**
 * 数组对象求和
 * @param {Object} arrObj 数组对象
 * @param {String} key 数组对应的 key 值
 */
export const arrObjSum = (obj, key) => {
  return arrObj.reduce((prev, cur) => prev + cur.key, 0)
}

6.arrConcat

数组合并

/**
 * 数组合并,目前合并一维
 * @param {Array} arrOne 数组
 * @param {Array} arrTwo 数组
 */
export const arrConcat = (arrOne, arrTwo) => {
  return [...arrOne, ...arrTwo]
}

7.arrSum

数组求和

/**
 * 数组求和
 * @param {Array} arr 数组
 */
export const arrSum = arr => {
  return arr.reduce((prev, cur)=> {
    return prev + cur
  }, 0)
}

8.arrIncludeValue

数组是否包含某值

/**
 * 数组是否包含某值
 * @param {Array} arr 数组
 * @param {}  value 值,目前只支持 String,Number,Boolean
 */
export const arrIncludeValue = (arr,  value) => {
  return arr.includes( value)
}

9.arrMax

数组最大值

/**
 * 数组最大值
 * @param {Array} arr  数组
 */
export const arrMax = arr => {
  return Math.max(...arr)
}

10.arrRemoveRepeat

数组去重

/**
 * 数组去重
 * @param {Array} arr  数组
 */
export const arrRemoveRepeat = arr => {
  return Array.from(new Set(arr))
}

11.arrOrderAscend

数组排序

/**
 * 数组排序
 * @param {Array} arr  数组
 * @param {Boolean} ascendFlag   升序,默认为 true
 */
export const arrOrderAscend = (arr, ascendFlag=true) => {
  return arr.sort((a, b) => {
    return ascendFlag ? a - b : b - a
  })
}

12.arrJudge

判断是否是数组

/**
 * 判断是否是数组
 * @param {Array}} arr 数组
 */
export const arrJudge = arr => {
  if (Array.isArray(arr)) {
    return true
  }
}

check

13.checkNum

判断是否是数字

/**
 *  判断是否是数字
 * @param {Number} data
 */
export const checkNum = data => {
  const reg = /^\d{1,}$/g
  if (reg.test(data)) return true
}

14.checkLetter

判断是否是字母

/**
 *  判断是否是字母
 * @param {Number} data
 */
export const checkLetter = data => {
  const reg = /^[a-zA-Z]+$/g
  if (reg.test(data)) return true
}

15.checkLowercaseLetter

判断是否全部是小写字母

/**
 *  判断是否全部是小写字母
 * @param {Number} data
 */
export const checkLowercaseLetter = data => {
  const reg = /^[a-z]+$/g
  if (reg.test(data)) return true
}

16.checkCapitalLetter

判断是否是大写字母

/**
 *  判断是否是大写字母
 * @param {Number} data
 */
export const checkCapitalLetter = data => {
  const reg = /^[A-Z]+$/g
  if (reg.test(data)) return true
}

17.checkNumOrLetter

判断是否是字母或数字

/**
 * 判断是否是字母或数字
 * @param {Number || String} data  字符或数字
 */
export const checkNumOrLetter = data => {
  const reg = /^[0-9a-zA-Z]*$/g
  if (reg.test(data)) return true
}

18.checkChinese

判断是否是中文

/**
 * 判断是否是中文
 * @param {String} data  中文
 */
export const checkChinese = data => {
  const reg = /^[\u4E00-\u9FA5]+$/g
  if (reg.test(data)) return true
}

19.checkChineseNumberLettter

判断是否是中文,数字或字母

export const checkChineseNumberLettter = data => {
  const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]+$/g
  if (reg.test(data)) return true
}

20.checkEmail

判断是否是邮箱地址

/**
 * 判断是否是邮箱地址
 * @param {String} data
 */
export const checkEmail = data => {
  const reg = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/g
  if (reg.test(data)) return true
}

21.checkTelphone

判断是否是手机号

/**
 * 判断是否是手机号,只要是13,14,15,16,17,18,19开头即可
 * @param {String} data
 */
export const checkTelphone = data => {
  const reg = /^((\+|00)86)?1[3-9]\d{9}$/g
  if (reg.test(data)) return true
}

22.checkUrl

判断是否是正确的网址

/**
 * 判断是否是正确的网址
 * @param {String} url 网址
 */
export const checkUrl = url => {
  const a = document.createElement('a')
  a.href = url
  return [
    /^(http|https):$/.test(a.protocol),
    a.host,
    a.pathname !== url,
    a.pathname !== `/${url}`
  ].find(x => !x) === undefined
}

client

23.checkBrowser

/**
 * 判断是浏览器内核
 */
export const checkBrowser = () => {
  const u = navigator.userAgent;
  const obj = {
    trident: u.indexOf("Trident") > -1, //IE内核
    presto: u.indexOf("Presto") > -1, //opera内核
    webKit: u.indexOf("AppleWebKit") > -1, //苹果、谷歌内核
    gecko: u.indexOf("Gecko") > -1 && u.indexOf("KHTML") == -1, //火狐内核
  }
  return Object.keys(obj)[Object.values(obj).indexOf(true)]
};

24.checkIosAndroidIpad

判断是终端类型,值有ios,android,iPad

/**
 * 判断是终端类型,值有ios,android,iPad
 */
export const checkIosAndroidIpad = () => {
  const u = navigator.userAgent;
  const obj = {
    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
    android: u.indexOf("Android") > -1 || u.indexOf("Linux") > -1, //android终端或者uc浏览器
    iPad: u.indexOf("iPad") > -1, //是否iPad
  }
  return Object.keys(obj)[Object.values(obj).indexOf(true)]
};

25.checkWeixinQqUc

判断是否是微信,qq 或 uc

/**
 * 判断是否是微信,qq 或 uc
 */
export const checkWeixinQqUc = () => {
 
  const u = navigator.userAgent;
  const obj = {
    weixin: u.indexOf("MicroMessenger") > -1, //是否微信
    qq: u.match(/QQ/i) == "qq"&&!u.indexOf('MQQBrowser') > -1, //是否QQ
    uc: u.indexOf('UCBrowser') > -1
  }
  return Object.keys(obj)[Object.values(obj).indexOf(true)]
};

26.checkIsIphoneX

检查是否是 IphoneX

/**
 * 检查是否是 IphoneX
 */
export const checkIsIphoneX = () => {
  const u = navigator.userAgent;
  const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
  if (isIOS && screen.height >= 812) {
    return true;
  }
};

file

27.fileFormatSize

格式化文件单位

/**
 * 格式化文件单位
 * @param {String || Number} size  文件大小(kb)
 */
export const fileFormatSize = size => {
  var i
  var unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
  for (i = 0; i < unit.length && size >= 1024; i++) {
    size /= 1024
  }
  return (Math.round(size * 100) / 100 || 0) + unit[i]
}

obj

28.objIsEqual

判断两个对象是否相等,目前只支持对象值为简单数据类型的判断

/**
 * 判断两个对象是否相等,目前只支持对象值为简单数据类型的判断
 * @param {Object} oneObj  对象
 * @param {Object} twoObj 对象
 */
export const objIsEqual = (oneObj, twoObj) => {
  return JSON.stringify(oneObj) === JSON.stringify(twoObj)
}

29.objDeepClone

对象深度克隆;
1.JSON.stringify深度克隆对象;
2.无法对函数 、RegExp等特殊对象的克隆;
3.会抛弃对象的constructor,所有的构造函数会指向Object;
4.对象有循环引用,会报错

/**
 * 对象深度克隆,
 * JSON.stringify深度克隆对象
 * 无法对函数 、RegExp等特殊对象的克隆,
 * 会抛弃对象的constructor,所有的构造函数会指向Object
 * 对象有循环引用,会报错
 * @param {Object}  obj 克隆的对象
 */
export const objDeepClone = obj => {
  return clone(obj)
}

const isType = (obj, type) => {
  if (typeof obj !== 'object') return false;
  // 判断数据类型的经典方法:
  const typeString = Object.prototype.toString.call(obj);
  let flag;
  switch (type) {
    case 'Array':
      flag = typeString === '[object Array]';
      break;
    case 'Date':
      flag = typeString === '[object Date]';
      break;
    case 'RegExp':
      flag = typeString === '[object RegExp]';
      break;
    default:
      flag = false;
  }
  return flag;
};

/**
* deep clone
* @param  {[type]} parent object 需要进行克隆的对象
* @return {[type]}        深克隆后的对象
*/
const clone = parent => {
  // 维护两个储存循环引用的数组
  const parents = []
  const children = []

  const _clone = parent => {
    if (parent === null) return null
    if (typeof parent !== 'object') return parent

    let child, proto

    if (isType(parent, 'Array')) {
      // 对数组做特殊处理
      child = []
    } else if (isType(parent, 'RegExp')) {
      // 对正则对象做特殊处理
      child = new RegExp(parent.source, getRegExp(parent))
      if (parent.lastIndex) child.lastIndex = parent.lastIndex
    } else if (isType(parent, 'Date')) {
      // 对Date对象做特殊处理
      child = new Date(parent.getTime())
    } else {
      // 处理对象原型
      proto = Object.getPrototypeOf(parent)
      // 利用Object.create切断原型链
      child = Object.create(proto)
    }

    // 处理循环引用
    const index = parents.indexOf(parent)

    if (index !== -1) {
      // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
      return children[index]
    }
    parents.push(parent)
    children.push(child)

    for (const i in parent) {
      // 递归
      child[i] = _clone(parent[i])
    }

    return child
  }
  return _clone(parent)
}

storage

30.localStorageSet

localStorage 存贮
目前对象值如果是函数 、RegExp等特殊对象存贮会被忽略

/**
 * localStorage 存贮
 * 目前对象值如果是函数 、RegExp等特殊对象存贮会被忽略
 * @param {String} key  属性
 * @param {Object} value 值
 */
export const localStorageSet = (key, value) => {
  if (typeof (value) === 'object') value = JSON.stringify(value)
  localStorage.setItem(key, value)
}

31.localStorageGet

localStorage 获取

/**
 * localStorage 获取
 * @param {String} key  属性
 */
export const localStorageGet = (key) => {
  return JSON.parse(localStorage.getItem(key))
}

32.localStorageRemove

localStorage 移除

/**
 * localStorage 移除
 * @param {String} key  属性
 */
export const localStorageRemove = (key) => {
  localStorage.removeItem(key)
}

33.localStorageSetExpire

localStorage 存贮某一段时间失效

/**
 * localStorage 存贮某一段时间失效
 * @param {String} key  属性
 * @param {*} value 存贮值
 * @param {String} expire 过期时间,毫秒数
 */
export const localStorageSetExpire = (key, value, expire) => {
  if (typeof (value) === 'object') value = JSON.stringify(value)
  localStorage.setItem(key, value)
  setTimeout(() => {
    localStorage.removeItem(key)
  }, expire)
}

34.sessionStorageSet

sessionStorage 存贮

/**
 * sessionStorage 存贮
 * @param {String} key  属性
 * @param {*} value 值
 */
export const sessionStorageSet = (key, value) => {
  if (typeof (value) === 'object') value = JSON.stringify(value)
  sessionStorage.setItem(key, value)
}

35.sessionStorageGet

sessionStorage 获取

/**
 * sessionStorage 获取
 * @param {String} key  属性
 */
export const sessionStorageGet = (key) => {
  return JSON.parse(sessionStorage.getItem(key))
}

36.sessionStorageRemove

sessionStorage 删除

/**
 * sessionStorage 删除
 * @param {String} key  属性
 */
export const sessionStorageRemove = (key, value) => {
  sessionStorage.removeItem(key, value)
}

37.sessionStorageSetExpire

sessionStorage 存贮某一段时间失效

/**
 * sessionStorage 存贮某一段时间失效
 * @param {String} key  属性
 * @param {*} value 存贮值
 * @param {String} expire 过期时间,毫秒数
 */
export const sessionStorageSetExpire = (key, value, expire) => {
  if (typeof (value) === 'object') value = JSON.stringify(value)
  sessionStorage.setItem(key, value)
  setTimeout(() => {
    sessionStorage.removeItem(key)
  }, expire)
}

38.cookieSet

cookie 存贮

/**
 * cookie 存贮
 * @param {String} key  属性
 * @param {*} value  值
 * @param String expire  过期时间,单位天
 */
export const cookieSet = (key, value, expire) => {
  const d = new Date()
  d.setDate(d.getDate() + expire)
  document.cookie = `${key}=${value};expires=${d.toGMTString()}`
}

39.cookieGet

cookie 获取

/**
 * cookie 获取
 * @param {String} key  属性
 */
export const cookieGet = (key) => {
  const cookieStr = unescape(document.cookie)
  const arr = cookieStr.split('; ')
  let cookieValue = ''
  for (var i = 0; i < arr.length; i++) {
    const temp = arr[i].split('=')
    if (temp[0] === key) {
      cookieValue = temp[1]
      break
    }
  }
  return cookieValue
}

40.cookieRemove

cookie 删除

/**
 * cookie 删除
 * @param {String} key  属性
 */
export const cookieRemove = (key) => {
  document.cookie = `${encodeURIComponent(key)}=;expires=${new Date()}`
}

str

41.strTrimLeftOrRight

去掉字符左右空格

/**
 * 去掉字符左右空格
 * @param {String} str 字符
 */
export const strTrimLeftOrRight = str => {
  return str.replace(/(^\s*)|(\s*$)/g, "")
}

42.strInclude

判断字符是否包含某值

/**
 * 判断字符是否包含某值
 * @param {String} str 字符
 * @param {String} value 字符
 */
export const strInclude = (str, value) => {
  return str.includes(value)
}

43.strBeginWith

判断字符是否以某个字符开头

/**
 * 判断字符是否以某个字符开头
 * @param {String} str 字符
 * @param {String} value 字符
 */
export const strBeginWith = (str, value) => {
  return str.indexOf(value) === 0
}

44.strReplace

全局替换某个字符为另一个字符

/**
 * 全局替换某个字符为另一个字符
 * @param {String} str 字符
 * @param {String} valueOne 包含的字符
 * @param {String} valueTwo 要替换的字符,选填
 */
export const strReplace = (str, valueOne, valueTwo) => {
  return str.replace(new RegExp(valueOne,'g'), valueTwo)
}

45.strToCapital

将字母全部转化成大写

/**
 * 将字母全部转化成大写
 * @param {String} str 字符
 */
export const strToCapital = (str) => {
  return str.toUpperCase()
}

46.strToLowercase

将字母全部转化成小写

/**
 * 将字母全部转化成小写
 * @param {String} str 字符
 */
export const strToLowercase = (str) => {
  return str.toLowerCase()
}

47.strToCapitalLetter

将字母全部转化成以大写开头

/**
 * 将字母全部转化成以大写开头
 * @param {String} str 字符
 */
export const strToCapitalLetter = (str) => {
  const strOne = str.toLowerCase()
  return strOne.charAt(0).toUpperCase() + strOne.slice(1)
}

thrDeb

48.throttle

节流

/**
 * 节流
 * @param {*} func 执行函数
 * @param {*} delay 节流时间,毫秒
 */
export const throttle = function(func, delay) {
  let timer = null
  return function() {
    if (!timer) {
      timer = setTimeout(() => {
        func.apply(this, arguments)
        // 或者直接 func()
        timer = null
      }, delay)
    }
  }
}

49.debounce

防抖

/**
 * 防抖
 * @param {*} fn 执行函数
 * @param {*} wait 防抖时间,毫秒
 */
export const debounce = function(fn, wait) {
  let timeout = null
  return function() {
    if (timeout !== null) clearTimeout(timeout)// 如果多次触发将上次记录延迟清除掉
    timeout = setTimeout(() => {
      fn.apply(this, arguments)
      // 或者直接 fn()
      timeout = null
    }, wait)
  }
}

time

50.getYear

获取年份

/**
 * 获取年份
 */
export const getYear = () => {
  return new Date().getFullYear()
}

51.getMonth

获取月份

/**
 * 获取当前月份
 * @param {Boolean} fillFlag 布尔值,是否补 0,默认为 true
 */
export const getMonth = (fillFlag=true) => {
  const mon = new Date().getMonth() + 1
  const monRe = mon
  if (fillFlag) mon < 10 ? `0${mon}` : mon
  return monRe
}

52.getDay

获取日

/**
 * 获取日
 * @param {Boolean} fillFlag 布尔值,是否补 0
 */
export const getDay = (fillFlag=true) => {
  const day = new Date().getDate()
  const dayRe = day
  if (fillFlag) day < 10 ? `0${day}` : day
  return dayRe
}

53.getWhatDay

星期几

/**
 * 获取星期几
 */
export const getWhatDay = () => {
  return new Date().getDay() ? new Date().getDay() : 7
}

54.getMonthNum

获取当前月天数

/**
 * 获取当前月天数
 * @param {String} year 年份
 * @param {String} month 月份
 */
export const getMonthNum = (year, month) => {
  var d = new Date(year, month, 0)
  return d.getDate()
}

55.getYyMmDdHhMmSs

获取当前时间 yyyy-mm-dd,hh:mm:ss

/**
 * 获取当前时间 yyyy-mm-dd,hh:mm:ss
 */
export const getYyMmDdHhMmSs = () => {
  const date = new Date()
  const year = date.getFullYear()
  const month = date.getMonth() + 1
  const day = date.getDate()
  const hours = date.getHours()
  const minu = date.getMinutes()
  const second = date.getSeconds()
  const arr = [month, day, hours, minu, second]
  arr.forEach(item => {
    item < 10 ? '0' + item : item
  })
  return (
    year +
    '-' +
    arr[0] +
    '-' +
    arr[1] +
    ' ' +
    arr[2] +
    ':' +
    arr[3] +
    ':' +
    arr[4]
  )
}

56.timesToYyMmDd

时间戳转化为年月日

/**
 * 时间戳转化为年月日
 * @param times 时间戳
 * @param ymd 格式类型(yyyy-mm-dd,yyyy/mm/dd)
 * @param hms 可选,格式类型(hh,hh:mm,hh:mm:ss)
 * @returns {年月日}
 */
export const timesToYyMmDd = (times, ymd,  hms) => {
  const oDate = new Date(times)
  const oYear = oDate.getFullYear()
  const oMonth = oDate.getMonth() + 1
  const oDay = oDate.getDate()
  const oHour = oDate.getHours()
  const oMin = oDate.getMinutes()
  const oSec = oDate.getSeconds()
  let oTime // 最后拼接时间
  // 年月日格式
  switch (ymd) {
    case 'yyyy-mm-dd':
      oTime = oYear + '-' + getzf(oMonth) + '-' + getzf(oDay)
      break
    case 'yyyy/mm/dd':
      oTime = oYear + '/' + getzf(oMonth) + '/' + getzf(oDay)
      break
  }
  // 时分秒格式
  switch (hms) {
    case 'hh':
      oTime = ' '+oTime + getzf(oHour)
      break
    case 'hh:mm':
      oTime = oTime + getzf(oHour) + ':' + getzf(oMin)
      break
    case 'hh:mm:ss':
      oTime = oTime + getzf(oHour) + ':' + getzf(oMin) + ':' + getzf(oSec)
      break
  }
  return oTime
}

57.YyMmDdToTimes

将年月日转化成时间戳

/**
 * 将年月日转化成时间戳
 * @param {String} time yyyy/mm/dd 或yyyy-mm-dd 或yyyy-mm-dd hh:mm 或yyyy-mm-dd hh:mm:ss
 */
export const YyMmDdToTimes = (time) => {
  return new Date(time.replace(/-/g, '/')).getTime()
}

58.compareTimeOneLessTwo

/**
 *  比较时间 1 小于时间 2
 * @param {String} timeOne  时间 1
 * @param {String} timeTwo  时间 2
 */
export const compareTimeOneLessTwo = (timeOne, timeTwo) => {
  // 判断 timeOne 和 timeTwo 是否
  return new Date(timeOne.replace(/-/g, '/')).getTime()<new Date(timeTwo.replace(/-/g, '/')).getTime()
}

url

59.getQueryString

获取 url 后面通过?传参的参数~~~~

/**
 *  获取 url 后面通过?传参的参数
 * @param {String} name
 */
export function getQueryString(name) {
  const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
  const url = window.location.href
  const search = url.substring(url.lastIndexOf('?') + 1)
  const r = search.match(reg)
  if (r != null) return unescape(r[2])
  return null
}

总结

码字不易,持续更新中,欢迎 start!
给大家推荐一款 bug 管理工具,请戳
bug 管理工具

查看原文

LanJamRom 收藏了文章 · 2019-12-17

vue 实现文字无缝滚动效果(从下往上滚动)

需求:对中奖用户进行滚动效果展示

Kapture 2019-12-04 at 14.36.51.gif

这种效果需求还是蛮多的,想起之前用JQuery实现的一个无缝滚动( 缅怀过去),是通过jq的animate方法实现的,动画结束之后就将第一个元素appendTo追加到最后面,实现循环滚动特效,不得不感叹技术更新换代真的很快。

回到现在Vue的项目,本来想用插件,但是觉得这么一个小动画用插件有点太重了,还是自己写一个比较好。

实现原理

实现原理也比较简单

  1. 对整个列表实现上移动画
  2. 将列表的第一个数据移动到最后一个

因为vue是基于数据驱动的,所以,对我们开发者来说,直接操控数组,删除第一个数组数据,然后追加到数组后面就好了。

点此查看实现效果以及源码预览可以略过下面的内容

template 部分

  <!-- 无缝滚动效果 -->
  <div class="marquee-wrap">
    <ul class="marquee-list" :class="{'animate-up': animateUp}">
      <li v-for="(item, index) in listData" :key="index">{{item}}</li>
    </ul>
  </div>
  

script部分

export default {
  name: "marquee-up",
  data() {
    return {
      animateUp: false,
      listData: ['12***ve 成功邀请12人 已获奖金60元', 'l***e 成功邀请5人 已获奖金40元', 'l***e 成功邀请1人 已获奖金5元'],
      timer: null
    }
  },
  mounted() {
    this.timer = setInterval(this.scrollAnimate, 1500);
  },
  methods: {
    scrollAnimate() {
      this.animateUp = true
      setTimeout(() => {
        this.listData.push(this.listData[0])
        this.listData.shift()
        this.animateUp = false
      }, 500)
    }
  },
  destroyed() {
    clearInterval(this.timer)
  }
};

style 部分

.marquee-wrap  {
  width: 80%;
  height: 40px;
  border-radius: 20px;
  background: rgba($color: #000000, $alpha: 0.6);
  margin: 0 auto;
  overflow: hidden;
  .marquee-list {
    li {
      width: 100%;
      height: 100%;
      text-overflow: ellipsis;
      overflow: hidden;
      white-space: nowrap;
      padding: 0 20px;
      list-style: none;
      line-height: 40px;
      text-align: center;
      color: #fff;
      font-size: 18px;
      font-weight: 400;
    }
  }
  .animate-up {
    transition: all 0.5s ease-in-out;
    transform: translateY(-40px);
  }
}
查看原文

LanJamRom 收藏了文章 · 2019-12-17

如何更好的去除谷歌浏览器中input自动填充背景?

input可能是平时网页中使用最多的标签之一了,但凡需要输入的地方都少不了。
毕竟输入是一件比较麻烦的事情,所以浏览器也做出了许多便于输入的地方,比方说自动填充

input1

一般情况下,在表单提交的时候浏览器会自动记录提交的内容

这个功能本来是很好的,不过当自动填充后,输入框背景会自动变成淡紫色(以前是x黄色,仅限谷歌浏览器),设计师(但凡有点审美的前端)看到了肯定就不干了,毕竟还是挺违和的,那么何如去除这个默认的背景呢?

背景是如何产生的

按照以往的经验,如果要修改标签默认的样式,首先肯定是查看标签的默认样式,如下

image

比较好理解,当自动填充时会激活:-internal-autofill-selected伪类,然后就变成了淡紫色。

如果我们设置样式覆盖

input:-internal-autofill-selected{
    background: red!important;
}

虽然样式覆盖了,然而并没有什么用,仍然是默认的淡紫色(要是起作用的话就不会专门去研究这个了),原因不明...

现有的解决方式

网上普遍的解决方式主要有两种:

1. 使用内阴影进行覆盖

既然背景改变不了,那么就找个东西覆盖呗

input{
    box-shadow: 0 0 0px 1000px white inset;
}

image

大部分情况使用这种方式即可解决问题

2. 关闭自动填充功能

既然是由于自动填充导致的问题,关掉就行了呀

<input  autocomplete="off">

image

有些对信息比较敏感的情况下(不想自动填充)可以使用这个功能

现有解决方式的局限

首先说第二种方式,虽然这种方式比较简单,但是却失去了自动填充功能,这对于用户体验是很不友好的(手动输入是一件费时费力的事情),如果说要自定义这个功能,那可能还需要不少的js脚本(还需考虑键盘访问),而且可能还会有意料之外的bug,所以一般情况下不建议去除默认功能。

那么第一种方式呢?如果输入框是纯色的背景,那么给一个和背景一样的阴影颜色就好了,如果而我们设计需要的是一种透明的输入框,那基本就凉掉了。

image

比如类似这样的效果,如果被填充了还是相当难看的

image

更好的解决方式

下面给出两种解决方式

方式一:利用background-clip: content-box

既然背景改变不了,那么就把它裁掉呗。

背景颜色默认是渲染到padding-box的,我们可以设置background-clip: content-box只渲染到content-box,这样背景就看不到了(当然还需指定一下高度为0。)~

input{
  height: 0;
  padding: 1.2em .5em;
  background-clip: content-box;
}

image

可以看到,自动填充时的背景已经消失了,不过还有一个小问题,填充的文字颜色也是无法直接修改的(默认为rgb(0,0,0)),原因相同,这里我们可以借助一下::first-line伪类

input::first-line{
  color: #fff
}

image

应该算比较完美了~

https://codepen.io/xboxyan/pen/VwYKJzR

如果demo里面没有自动填充选项,可以先输入一些字符,然后submit,手动创建一些填充项,下同

方式二:利用animation-fill-mode:forwards

设置animation-fill-mode:forwards后,动画会一直停留在最后一帧,这个已经和默认的样式不是一个维度了,不管设置什么样式,都会保留最后一帧状态,比如

<style>
div{
    animation:resetBg .1s forwards;
}
@keyframes resetBg {
  to {
    background: blue;
  }
}
</style>
<div style="background:red!important">div</div>

image

可以看到背景很轻易的就被改成了蓝色。

利用这个特性,上面的问题就很容易了,只需设置一个动画即可

input{
  animation: resetBg .1s forwards;
}
@keyframes resetBg {
  to {
    color: #fff;
    background: transparent;
  }
}

image

效果也是立竿见影~

https://codepen.io/xboxyan/pen/ZEYpdva

小结

上面两种方式均实现了同样的效果,相比第一种方式,第二种对现有代码的改动最小,基本无副作用,无需改变原有尺寸,所以更推荐第二种方式,当然第一种方式的思路还是值得推荐的,可能在其他场景很更适合。

如果有其他实现思路,欢迎在下方进行留言讨论,谢谢~

查看原文

LanJamRom 收藏了文章 · 2019-12-17

20 个新的且值得关注的 Vue 开源项目

译者:前端小智
作者:Nastassia Ovchinnikova
来源:flatlogic.com
点赞再看,微信搜索【大迁世界】关注这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。
点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。

Vue 相对不于 React 的一个优点是它易于理解和学习,且在国内占大多数。咱们可以在 Vue 的帮助下创建任何 Web 应用程序。 因此,时时了解一些新出现又好用的Vue 开源项目也是挺重要,一方面可以帮助咱们更加高效的开发,另一方面,咱们也可以模范学习其精华部分。

接下来看看新出的有哪些好用的开源项目。

uiGradients

网址:http://uigradients.com/

GitHub:https://github.com/ghosh/uiGr...

GitHub Stars:4.6k

clipboard.png

彩色阵列和出色的UX使是这个项目的一个亮点,渐变仍然是网页设计中日益增长的趋势。 咱们可以选择所需的颜色,并可以获得所有可能的渐变,并获取对应的 CSS 代码, 赶紧收藏起来吧。

CSSFX

CSS 过度效果的集合

网址:https://cssfx.dev

GitHub:https://github.com/jolaleye/c...

GitHub Stars:3.5k

图片描述

CSSFX 里面有很多 CSS 过滤效果,咱们可以根据需求选择特定的动画,点击对应的效果即可看到生成的 CSS 代码,动手搞起来吧。

Sing App Vue Dashboard

一个管理模板

网址:https://flatlogic.com/templat...

GitHub:https://github.com/flatlogic/...

GitHub Stars:254

事例:https://flatlogic.com/templat...

文档:https://demo.flatlogic.com/si...

clipboard.png

这是基于最新 Vue 和 Bootstrap 免费和开源的管理模板,其实跟咱们国内的 vue-admin-template 差不多。咱们不一定要使用它,但可以研究学习源码,相信可以学到很多实用的技巧,加油少年。

Vue Storefront

网址:https://www.vuestorefront.io

GitHub:https://github.com/DivanteLtd...

GitHub Stars:5.8k

clipboard.png

这是一个PWA,可以连接到任何后端(或几乎任何后端)。这个项目的主要优点是使用了无头架构。这是一种全面的解决方案,为咱们提供了许多可能性(巨大的支持稳步增长的社区,服务器端渲染,将改善网页SEO,移动优先的方法和离线模式。

Faviator

图标生成的库

网址:https://www.faviator.xyz

GitHub:https://www.faviator.xyz/play...

GitHub Stars:94

clipboard.png

如果需要创建一个图标增加体验度。 可以使用任何 Google 字体以及任何颜色。只需通过首选的配置,然后选择PNG,SVG或JPG格式即可。

iView

Vue UI 组件库

网址:https://iviewui.com/

GitHub:https://github.com/iview/iview

GitHub Stars:22.8k

clipboard.png

不断迭代更新使这组UI组件成为具有任何技能水平的开发人员的不错选择。

要使用iView,需要对单一文件组件有充分的了解,该项目具有友好的API和大量文档。

Postwoman

API请求构建器

网址:https://postwoman.io/

GitHub:https://github.com/liyasthoma...

GitHub Stars:10.5k

clipboard.png

这个与 Postman 类似。 它是免费的,具有许多参与者,并且具有多平台和多设备支持。 这个工具真的非常快,并且有大量的更新。 该工具的创建者声称在不久的将来会有更多功能。

Vue Virtual Scroller

快速滚动

网址:https://akryum.github.io/vue-...

GitHub:https://github.com/Akryum/vue...

GitHub Stars:3.4k

clipboard.png

Vue Virtual Scroller具有四个主要组件。 RecycleScroller可以渲染列表中的可见项。 如果咱们不知道数据具体的数量,最好使用DynamicScrollerDynamicScrollerItem将所有内容包装在DynamicScroller中(以处理大小更改)。 IdState简化了本地状态管理(在RecycleScroller内部)。

Mint UI

移动端的 UI 库

网址:http://mint-ui.github.io/#!/en

GitHub:https://github.com/ElemeFE/mi...

GitHub Stars:15.2k

clipboard.png

使用现成的CSS和JS组件更快地构建移动应用程序。使用此工具,咱们不必承担文件大小过大的风险,因为可以按需加载。动画由CSS3处理,由此来提高性能。

V Calendar

用于构建日历的无依赖插件

网址:https://vcalendar.io

GitHub:https://github.com/nathanreye...

GitHub Stars:1.6k

clipboard.png

您可以选择不同的视觉指示器来装饰日历。 V Calendar还为咱们提供了三种日期选择模式:

  • 单选
  • 多选
  • 日期范围

Vue Design System

一组UI工具

网址:https://vueds.com/

GitHub:https://github.com/viljamis/v...

GitHub Stars:1.7k

clipboard.png

这是一种组织良好的工具,对于任何web开发团队来说,它的命名都很容易理解。其中一个很大的优点是使用了更漂亮的代码格式化器,它可以在提交到Git之前自动排列代码。

Proppy

UI组件的功能道具组合

网址:https://proppyjs.com

GitHub:https://github.com/fahad19/pr...

GitHub Stars:856

clipboard.png

ProppyJS 是一个很小的库,用于组合道具,它附带了各种集成包,让您可以自由地使用它流行的渲染库。

我们的想法是首先将Component的行为表达为props,然后使用Proppy的相同API将其连接到您的Component(可以是React,Vue.js或Preact)。

API还允许您访问其他应用程序范围的依赖项(如使用Redux的商店),以方便组件树中的任何位置。

Light Blue Vue Admin

vue 后台展示模板

网址:https://flatlogic.com/templat...

GitHub:https://github.com/flatlogic/...

GitHub Stars:79

图片描述

事例:https://demo.flatlogic.com/li...

文档:https://demo.flatlogic.com/li...

模板是用Vue CLIBootstrap 4构建的。从演示中可以看到,这个模板有一组非常基本的页面:排版、地图、图表、聊天界面等。如果咱们需要一个扩展的模板,可以看看Light Blue Vue Full,它有60多个组件,无 jquery,有两个颜色主题。

Vue API Query

为 REST API 构建请求

GitHub:https://github.com/robsonteno...
GitHub Stars: 1.1k

clipboard.png

关于这个项目没什么好说的。它所做的与描述行中所写的完全一样:它帮助咱们构建REST API的请求。

Vue Grid Layout

Vue 的网格布局

Website:https://jbaysolutions.github....
GitHub:https://github.com/jbaysoluti...
GitHub Stars: 3.1k

clipboard.png

所有网格相关问题的简单解决方案。它有静态的、可调整大小的和可拖动的小部件。还是响应和布局可以恢复和序列化。如果还需要再添加一个小部件,则不必重新构建所有网格。

Vue Content Loader

创建一个占位符加载

Website:http://danilowoz.com/create-v...
GitHub:https://github.com/egoist/vue...
GitHub Stars: 2k

clipboard.png

当咱们开发网站或者 APP 时,遇到内容过多加载速度慢时,会导致用户打开页面有大量空白页,vue-content-loader正是解决这个问题的一个组件,使加载内容之前生成一个dom模板,提高用户体验。

Echarts with Vue2.0

数据可视化

Website:https://simonzhangiter.github...
GitHub:https://github.com/SimonZhang...
GitHub Stars: 1.3k

clipboard.png

在图片中,咱们可以看到非常漂亮的图表。这个项目使任何数据都更具可读性,更容易理解和解释。它允许咱们在任何数据集中轻松地检测趋势和模式。

Vue.js Modal

高度可定制的模态框

Website:http://vue-js-modal.yev.io/
GitHub:https://github.com/euvl/vue-j...
GitHub Stars: 2.9k

clipboard.png

可以在该网站上查看所有不同类型的模态。 有15个按钮,按任意一个按钮,看到一个模态示例。

Vuesax

框架组件

Website:https://lusaxweb.github.io/vu...
GitHub:https://github.com/lusaxweb/v...
GitHub Stars: 3.7k

clipboard.png

这个项目在社区中很受欢迎。 它使咱们可以为每个组件设计不同的风格。 Vuesax的创建者强调,每个Web开发人员在进行Web设计时都应有选择的自由。

Vue2 Animate

vue2.0 —使用animate.css 构建项目和创建组件

Website:https://the-allstars.com/vue2...
GitHub:https://github.com/asika32764...
GitHub Stars: 1.1k

clipboard.png

这个库是跨浏览器的,咱们可以选择从5种类型的动画: rotateslidefadebouncezoom。在网站上有一个演示。动画的默认持续时间是1秒,但是咱们可以自定义该参数。

Vuetensils

Vue.js的工具集

Website:https://vuetensils.stegosourc...
GitHub:https://github.com/stegosourc...
GitHub Stars: 111

clipboard.png

这个UI库有一个标准的功能,但是最酷的是它没有额外的样式。你可以让设计尽可能的个性化,应用所有的需求。只需编写需要的样式,将其添加到项目中,并包含需要的尽可能多的组件。


编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:

https://flatlogic.com/blog/ne...
https://flatlogic.com/blog/ne...


交流

文章每周持续更新,可以微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

查看原文

LanJamRom 关注了用户 · 2019-11-24

mousycoder @mousycoder

思考者浩哥 http://mousycoder.com/
只为更深的思考和更好的知识体系

关注 692

LanJamRom 关注了用户 · 2019-11-24

皮小蛋 @scaukk

The Best Way to Improve Yourself:

  1. Build Stuffs
  2. Help Others
  3. Teach

PS: Shopee 招人, 薪酬福利待遇好

感兴趣的话, 可以联系我内推。

关注 712

LanJamRom 关注了用户 · 2019-11-24

维子 @isgiker

Don't view things as static and isolated.

关注 398

LanJamRom 关注了用户 · 2019-11-22

Peter谭金杰 @jerrytanjinjie

前端架构师

微信公众号:前端巅峰

欢迎技术探讨~

个人微信:CALASFxiaotan

关注 4233

认证与成就

  • 获得 0 次点赞
  • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-10-24
个人主页被 69 人浏览